npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vclaw/vrv

v0.1.2

Published

OpenClaw VRV channel plugin for inbound HTTP messaging and outbound pushApi replies

Readme

@vclaw/vrv

npm version license: MIT

OpenClaw vrv 渠道插件,用来把你自己的业务系统通过 HTTP 或 WebSocket 接到 OpenClaw。

它做的事情很简单:

  1. 你的系统可以调用 POST /vrv/message,也可以通过插件主动建立的 websocket connector 长连接推送入站消息
  2. 插件把请求转成 OpenClaw 入站消息
  3. Agent 生成回复
  4. 回复按“websocket connector > SSE > pushApi”的顺序自动选路

能力概览

  • 单一 pushApi 回调地址
  • 可选的 websocket connector,可同时用于入站消息和优先回推回复
  • 自动 SSE 选路
  • 兼容传统文本回调和结构化消息回调
  • 多账户支持
  • 自动 webhook 验签
  • OpenClaw status / security / actions / streaming / threading / setupEntry

兼容性

  • openclaw >= 2026.2.2
  • openclaw <= 2026.3.13

安装

openclaw plugins install @vclaw/vrv

配置

选择VRV渠道配置:

openclaw config --section channels

websocket URL和pushApi至少配置一个,webhook不配置签名时不强制验签

配置完需要重启网关:

openclaw gateway restart

核心点

1. 回复通道自动选路

插件会自动按下面的顺序选回复通道:

  1. 如果配置了 channels.vrv.websocket.url,并且外部长连接已连上:优先走 websocket connector
  2. 如果当前会话已经订阅了 SSE:走 /vrv/stream
  3. 其他情况:回退到 pushApi

2. 结构化能力由 capabilities 决定

现在不需要任何协议版本配置。
是否支持 richer message 能力,只看 capabilities

{
  "capabilities": {
    "media": true,
    "threads": true,
    "reactions": true,
    "edit": true,
    "unsend": true,
    "polls": true,
    "cards": true,
    "mentions": true
  }
}

capabilities 表示“外部 VRV 客户端支持哪些结构化能力”。
它的作用是告诉插件:

  • 哪些 OpenClaw action 可以对外暴露
  • 哪些回复可以安全地按结构化消息形态发出去
  • 哪些能力需要退回成普通文本处理

字段说明:

  • media 支持图片、文件等媒体消息。
  • threads 支持线程回复。
  • reactions 支持表情反应。
  • edit 支持编辑已发送消息。
  • unsend 支持撤回或删除已发送消息。
  • polls 支持投票消息。
  • cards 支持卡片、按钮等富消息。
  • mentions 支持提及用户等元数据。

两种消息形态

vrv 对外只需要理解两种消息形态:

  • 文本消息形态
  • 结构化消息形态

一句话理解

  • 文本消息形态适合“发一段文本,收一段文本”
  • 结构化消息形态适合“除了文本,还要表达线程、媒体、reaction、edit、poll 这些结构化语义”

入站区别

文本消息形态的入站是扁平文本:

{
  "text": "你好",
  "to": "user-001",
  "sessionKey": "session-001"
}

结构化消息形态的入站是带 message envelope 的结构化请求:

{
  "eventType": "message.created",
  "message": {
    "messageId": "msg-001",
    "conversationId": "group-001",
    "threadId": "thread-001",
    "chatType": "thread",
    "from": {
      "id": "user-001",
      "name": "Alice"
    },
    "to": {
      "id": "group-001",
      "name": "Project Group"
    },
    "text": "你好",
    "media": [],
    "mentions": [],
    "replyToMessageId": "msg-000"
  }
}

其中 message 是结构化消息的主体对象,用来表达一条完整消息,而不仅仅是一段文本。

最核心的区别是:

  • 文本消息形态只有 text / to / sessionKey
  • 结构化消息形态有完整的 message 对象,可以表达消息 ID、会话 ID、线程、媒体、mentions、replyTo

出站区别

文本消息形态的出站是文本回调:

{
  "reply": "Agent reply text",
  "to": "user-001",
  "sessionKey": "session-001",
  "end": true
}

结构化消息形态的出站是结构化 action:

{
  "action": "send",
  "accountId": "tenant-a",
  "conversationId": "group-001",
  "threadId": "thread-001",
  "message": {
    "text": "这里是回复",
    "media": [
      {
        "url": "https://example.com/file.png"
      }
    ],
    "replyToMessageId": "msg-000"
  }
}

什么时候使用结构化消息形态

只要出现下面这些需求之一,就应该使用结构化消息形态:

  • 需要 conversationId:要表达稳定会话 ID
  • 需要 threadId:要表达线程 ID
  • 需要 messageId:要表达消息唯一 ID
  • 需要 replyToMessageId:要表达“回复哪一条消息”
  • 需要 media:要发送图片、文件等媒体内容
  • 需要 mentions:要表达提及的用户或对象
  • 需要 poll:要发送投票消息
  • 需要 react:要对消息添加或移除表情反应
  • 需要 edit:要编辑已发送消息
  • 需要 unsend:要撤回或删除已发送消息

自动判断规则

插件现在的判断方式是:

  • 入站请求里有 message 对象:按结构化消息形态解析
  • 入站请求里没有 message 对象:按文本消息形态解析
  • 出站如果只是普通文本:优先发文本消息形态的回调 payload
  • 出站如果需要结构化表达:发结构化消息形态的 action payload

外部客户端应该怎么选

如果你的客户端只会:

  • 发文本
  • 收文本

那按文本消息形态接入就够了。

如果你的客户端需要支持下面任意能力:

  • 线程
  • 图片或其他媒体
  • reaction
  • 编辑消息
  • 删除消息
  • poll

那就应该按结构化消息形态接入。

配置

最小配置

最小可用配置:

openclaw config set channels.vrv '{
  "enabled": true,
  "pushApi": "http://127.0.0.1:19090/callback"
}' --json

建议生产环境至少加上签名密钥:

openclaw config set channels.vrv '{
  "enabled": true,
  "pushApi": "http://127.0.0.1:19090/callback",
  "webhookSecret": "replace-with-a-long-random-secret"
}' --json

websocket connector 配置

如果你的外部 VRV 服务提供了 WebSocket 长连接入口,推荐这样配:

openclaw config set channels.vrv '{
  "enabled": true,
  "websocket": {
    "enabled": true,
    "url": "ws://127.0.0.1:19091/ws"
  },
  "webhookSecret": "replace-with-a-long-random-secret"
}' --json

说明:

  • websocket.url:外部 VRV 服务的 WebSocket 地址
  • websocket.enabled:是否启用这条外连 connector
  • webhookSecret:推荐,用于保护 POST /vrv/message

这是一种更接近 OpenClaw 官方渠道的接法:

  • 插件主动连接外部 VRV WebSocket 服务
  • 外部服务可以通过这条长连接把消息推回插件,作为入站消息
  • 外部服务也会优先通过这条长连接接收插件回复

纯 SSE 配置

如果你希望主要通过 SSE 收回复,channels.vrv 最少可以只配:

openclaw config set channels.vrv '{
  "enabled": true
}' --json

更推荐:

openclaw config set channels.vrv '{
  "enabled": true,
  "webhookSecret": "replace-with-a-long-random-secret"
}' --json

说明:

  • enabled: 必填
  • webhookSecret: 推荐,开启自动验签
  • pushApi: 纯 SSE 场景可不配
  • capabilities: 只有需要结构化能力时才需要配

注意:

  • 没有 pushApiwebsocket 时,只有“当前会话已经有活跃 SSE 订阅”才能正常收回复
  • 如果没有活跃 SSE 订阅,请求会被视为当前账户未配置完成

带结构化能力的配置

如果你的外部 VRV 客户端支持媒体、线程、reaction、edit、poll 等能力,可以这样配:

openclaw config set channels.vrv '{
  "enabled": true,
  "pushApi": "https://example.com/vrv/callback",
  "capabilities": {
    "media": true,
    "threads": true,
    "reactions": true,
    "edit": true,
    "unsend": true,
    "polls": true,
    "cards": true,
    "mentions": true
  },
  "webhookSecret": "replace-with-a-long-random-secret"
}' --json

含义:

  • 普通文本回复仍可能走传统文本 payload
  • 真的需要结构化表达时,插件会优先尝试走结构化 action
  • 如果当前回复走的是 pushApi,结构化内容会发成 action payload
  • 如果当前回复走的是 websocket connector,普通 Agent 文本回复仍然是 websocket reply/final;sendMediareacteditpoll 这类显式结构化动作才会发成 websocket action

多账户配置

{
  "enabled": true,
  "webhookSecret": "replace-with-a-long-random-secret",
  "defaultAccount": "tenant-a",
  "accounts": {
    "tenant-a": {
      "enabled": true,
      "name": "Tenant A"
    },
    "tenant-b": {
      "enabled": true,
      "name": "Tenant B",
      "pushApi": "https://b.example.com/callback",
      "capabilities": {
        "media": true,
        "threads": true
      }
    }
  }
}

入站请求可通过 accountIdchannelAccountId 指定账户;不传时使用 defaultAccount

入站 API

插件会注册:

POST /vrv/message
GET /vrv/stream

文本消息形态入站

{
  "text": "你好",
  "to": "user-001",
  "sessionKey": "session-001",
  "accountId": "tenant-a"
}

字段:

  • text: 必填
  • to: 目标用户或会话 ID,可选
  • sessionKey: 会话键,可选,默认等于 to
  • accountId: 多账户场景可选

结构化消息形态入站

当请求里带 message 对象时,插件会按结构化消息解析:

{
  "eventType": "message.created",
  "accountId": "tenant-a",
  "capabilities": {
    "media": true,
    "threads": true
  },
  "message": {
    "messageId": "msg-001",
    "conversationId": "group-001",
    "threadId": "thread-001",
    "chatType": "thread",
    "from": {
      "id": "user-001",
      "name": "Alice"
    },
    "to": {
      "id": "group-001",
      "name": "Project Group"
    },
    "text": "你好",
    "media": [
      {
        "url": "https://example.com/file.png",
        "mimeType": "image/png",
        "filename": "file.png"
      }
    ],
    "mentions": ["user-002"],
    "replyToMessageId": "msg-000"
  }
}

关键字段:

  • messageId: 当前消息的唯一 ID
  • conversationId: 稳定会话 ID
  • threadId: 线程 ID
  • chatType: direct / group / thread
  • from: 发送方信息
  • to: 目标用户或目标会话信息
  • text: 文本内容
  • media[]: 媒体列表
  • mentions[]: 被提及对象
  • replyToMessageId: 回复目标消息

出站规则

出站只有这 3 条规则:

  1. 当前账户的 websocket connector 已连接:优先走 websocket connector
  2. 没有 websocket connector,但当前 sessionKey 有活跃 SSE 订阅:走 SSE
  3. 其他情况:走 pushApi

文本消息形态回调 payload

{
  "reply": "Agent reply text",
  "to": "user-001",
  "sessionKey": "session-001",
  "end": true
}

结构化消息形态 action payload

{
  "action": "send",
  "accountId": "tenant-a",
  "conversationId": "group-001",
  "threadId": "thread-001",
  "message": {
    "text": "这里是回复",
    "media": [
      {
        "url": "https://example.com/file.png"
      }
    ],
    "replyToMessageId": "msg-000"
  }
}

当前会发出的 action:

  • send
  • poll
  • react
  • edit
  • unsend

websocket connector 消息格式

当 websocket connector 建立后:

  • 外部服务可以通过这条长连接向插件发送入站消息,不必再调用 POST /vrv/message
  • 当回复走 websocket connector 时,插件也会向外部 WebSocket 服务发送 JSON 消息

文本回复示例:

{
  "type": "reply",
  "transport": "websocket",
  "accountId": "tenant-a",
  "payload": {
    "reply": "Agent reply text",
    "to": "user-001",
    "sessionKey": "session-001",
    "end": false
  }
}

最终回复示例:

{
  "type": "final",
  "transport": "websocket",
  "accountId": "tenant-a",
  "payload": {
    "reply": "最终回复",
    "to": "user-001",
    "sessionKey": "session-001",
    "end": true
  }
}

结构化 action 示例:

{
  "type": "action",
  "transport": "websocket",
  "accountId": "tenant-a",
  "payload": {
    "action": "send",
    "accountId": "tenant-a",
    "conversationId": "group-001",
    "threadId": "thread-001",
    "message": {
      "text": "这里是回复"
    }
  }
}

SSE 协议

GET /vrv/stream?sessionKey=session-001[&accountId=tenant-a]
Content-Type: text/event-stream

事件:

  • ready
  • reply
  • final

示例:

---- sse event ready ----
data:
{"sessionKey":"session-001","accountId":"default"}
---- sse event reply ----
data:
{"reply":"第一段回复","to":"user-001","sessionKey":"session-001","end":false}
---- sse event final ----
data:
{"reply":"最终回复","to":"user-001","sessionKey":"session-001","end":true}

入站鉴权

规则很简单:

  • 没配 webhookSecret:不验签
  • 配了 webhookSecret:自动强制验签

请求头:

  • x-vrv-timestamp
  • x-vrv-signature

签名算法:

hex(HMAC_SHA256(webhookSecret, timestamp + "." + rawBody))

默认接受 5 分钟内的请求。

OpenClaw 标准适配面

当前已实现:

  • onboarding 安装后可以通过引导页面配置 pushApi、签名密钥和结构化能力。
  • status 可以查看渠道是否启用、是否配置了回调地址、是否只依赖 SSE 等状态信息。
  • security 可以查看当前 webhook 是否开启了签名校验。
  • actions.send 支持从 OpenClaw 主动发送消息到 VRV 客户端。
  • actions.poll 支持发送投票消息。
  • actions.react 支持对消息添加或移除表情反应。
  • actions.edit 支持编辑已发送消息。
  • actions.unsend 支持撤回或删除已发送消息。
  • outbound.sendText 支持发送文本消息。
  • outbound.sendMedia 支持发送媒体消息。
  • outbound.sendPoll 支持发送投票消息。
  • threading 支持线程上下文,比如在线程里回复、回复某条已有消息。
  • streaming 支持流式回复输出,在没有 websocket connector 时优先通过 SSE 发送。
  • setupEntry 支持在安装、修复、引导配置时走轻量入口,不提前加载完整运行逻辑。

当前仍不支持:

  • nativeCommands

本地调试

统一使用:

npm run debug:vrv

脚本会让你选两次:

  • 消息形态:文本 / 结构化
  • 回复模式:pushApi / SSE stream / websocket connector

回车默认:

  • 文本消息形态
  • pushApi
  • 发送 scripts/vrv-debug-config.json 里配置的默认文本

覆盖链路:

  • 文本消息形态 + pushApi:验证文本回调 payload
  • 结构化消息形态 + pushApi:验证结构化 action 回调
  • 两种消息形态 + SSE stream:验证自动 SSE
  • 两种消息形态 + websocket connector:验证插件主动连接外部 WebSocket 服务后的回流

交互方式:

  • 直接回车:发送配置文件里的默认文本
  • 输入一段文本再回车:发送你刚输入的文本

调试配置文件:

scripts/vrv-debug-config.json

常见调试流程:

  1. 配好 OpenClaw channels.vrv
  2. openclaw gateway restart
  3. 运行 npm run debug:vrv
  4. 选择要测的模式

快速验证

查看日志:

openclaw logs --follow

发送一条最小请求:

curl -X POST "http://127.0.0.1:18789/vrv/message" \
  -H "Content-Type: application/json" \
  -d '{"text":"你好","to":"user-001","sessionKey":"session-001"}'

本地开发

常用命令:

npm test
npm run test:unit
npm run build
npm run debug:vrv
npm run release:check

本地安装调试:

openclaw plugins uninstall vrv --force
openclaw plugins install -l /Users/yalejian/IdeaProjects/Work/ai/vrv-claw-plugin
openclaw gateway restart

项目结构

  • dist/:发布产物
  • src/core/:账户、能力、常量
  • src/http/:HTTP 入站、SSE、监控日志
  • src/outbound/:channel 声明、actions、出站逻辑
  • src/status.ts / src/security.ts / src/onboarding.ts:标准 adapter
  • tests/:单测和 e2e

发布包内容

发布到 npm 的内容包括:

  • dist/
  • openclaw.plugin.json
  • vrv.channel.schema.json
  • 文档

设计与许可