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

@smart-link/openclaw-channel

v0.1.3

Published

OpenClaw channel plugin for Smart Link IM (gedim): bot-account realtime text, image, voice and file send/receive. Multiple accounts configured via accounts[] each with a robot secretKey; user identity (userId/userName/avatars) is fetched from /auth/me aft

Readme

openclaw-channel

OpenClaw 的 channel plugin(channel id = smart-link-chat):让 OpenClaw agent 通过一个机器人账号接入 smart-link IM(gedim,即 @smart-link/im-base 对应的 IM 服务端),用户在政务 App 内与 AI 单聊 / 群内 @ 对话(文本、图片、语音与文件)。

实现语言:Node.js (≥20) + TypeScript + socket.io-client。依赖 OpenClaw ≥ 2026.6(原生插件 SDK:openclaw.plugin.json manifest + defineChannelPluginEntry)。

本工程采用"无头薄协议客户端"路线:不复用 im-base 的 RN 依赖(sqlite/react-native/Redux),只按 im-base 的协议语义实现实时收发所需子集。

形态与现状

已适配 OpenClaw 2026.6.x 原生 channel 插件格式,覆盖 收发双向:

  • openclaw.plugin.json — plugin manifest:id: smart-link-chat、channels、channelConfigs.smart-link-chat.schema/uiHints(校验并渲染 channels.smart-link-chat)、configSchema、activation
  • src/index.ts — extension entry:defineChannelPluginEntry(自动 api.registerChannel),注册 channelPlugin
  • src/channel.ts — ChannelPlugin 对象:
    • config.resolveAccount 读取 channels.smart-link-chat(server/userId/userName/cookie…),缺失即抛错提示
    • outbound.sendText:按需懒建 GedimClient,messagePrepare → emit c2cChatMessage/c2gChatMessage 发文本
    • outbound.sendMedia:加载媒体字节 → 按 MIME kind 路由(图片→picture,音频→voice,文档/视频→file)→ 上传文件服务取 fileId → emit 对应 payload 消息
    • outbound.sendPayload:按“文本+媒体”序列复用 sendText/sendMedia
    • 出站目标解析:group:* → C2G,其余(含 user:/无前缀)→ C2C
  • src/inbound.ts — 入站(挂 gateway.startAccount,账号 enabled+configured 时常驻):
    • 常驻 socket 监听 c2cChatMessage/c2gChatMessage(与出站共享同一个 GedimClient 单例)
    • 过滤:自己发的消息(echo)/ 不支持的载荷类型 / ignoreUserIds / ignoreConversationIds / 入站去重(TTL)
    • 媒体(图片/语音/文件):从文件服务下载 → 落盘入宿主媒体库 → 写入 MsgContext.MediaPaths/MediaTypes 交给 agent
    • 群聊默认 requireMention:atList 含机器人 userId 或文本含 @显示名 才回复(可配置关闭)
    • 经宿主标准管线 dispatch(routing.resolveAgentRoute + dispatchInboundMessageWithBufferedDispatcher)交给 agent, 回复(文本/媒体)通过 deliver 回调逐条发回原会话(消费回复)
  • src/setup-entry.ts — 轻量 setup 入口(defineSetupPluginEntry)
  • src/gedim/file.ts — 文件服务 HTTP 客户端(图片/语音/文件上传/下载,复用 TLS 配置)

未实现: 撤回/已读;cookie 过期自动刷新;登录接口自动化。

目录结构

packages/openclaw-channel/
├── openclaw.plugin.json    # 插件 manifest(必须随包分发)
├── src/
│   ├── index.ts            # extension entry:defineChannelPluginEntry(default export)
│   ├── channel.ts          # ChannelPlugin:config 解析 + outbound.sendText + gateway 挂载
│   ├── inbound.ts          # 入站:c2c/c2g 监听 → 过滤 → agent dispatch → 消费回复发回
│   ├── setup-entry.ts      # setup 入口:defineSetupPluginEntry
│   └── gedim/
│       ├── protocol.ts     # 事件/枚举/报文类型(契约)
│       ├── client.ts       # socket.io 无头客户端(收发/重连)
│       └── file.ts         # 文件服务 HTTP 客户端(图片上传/下载)
├── config.example.json5    # channels.smart-link-chat 配置示例
└── tsconfig.json

构建

pnpm install                          # workspace 根执行(openclaw 作为 dev/peer 依赖安装)
pnpm --filter openclaw-channel build  # tsc → dist/(ESM)
pnpm --filter openclaw-channel typecheck

devDependencies.openclaw 固定为 2026.6.34(与宿主同版本),peerDependencies.openclaw 为 >=2026.6.0 —— 插件 API 实验性,务必钉住并测试所声明的版本。

配置与加载

当前接入方式:bot 身份与 cookie 由外部获取后填入配置(后续后端完善登录接口后可优化为 setup 向导)。

两种配置模式(二选一):

模式 A — 单账号扁平格式(向后兼容)

// ~/.openclaw/openclaw.json
{
    channels: {
        'smart-link-chat': {
            enabled: true,
            server: 'https://im.example.gov.cn',
            userId: 'bot-user-id',
            userName: 'AI助手',
            cookie: '${GEDIM_BOT_COOKIE}', // 完整 Cookie 头值,OpenClaw 会做 ${ENV} 展开
            requireMention: true, // 群聊需 @ 机器人才回复
            mediaMaxMb: 20, // 媒体收发大小上限(MB)
        },
    },
}

模式 B — 多账号 accounts 数组(推荐)

// ~/.openclaw/openclaw.json
{
    channels: {
        'smart-link-chat': {
            enabled: true,
            accounts: [
                {
                    id: 'bot-1',                    // 可选,默认 = server|userId
                    server: 'https://im.example.gov.cn',
                    userId: 'bot-user-id-1',
                    userName: 'AI助手一号',
                    cookie: '${GEDIM_BOT_COOKIE_1}',
                    requireMention: true,
                    mediaMaxMb: 20,
                },
                {
                    id: 'bot-2',
                    server: 'https://im.example.gov.cn',
                    userId: 'bot-user-id-2',
                    userName: 'AI助手二号',
                    cookie: '${GEDIM_BOT_COOKIE_2}',
                    requireMention: true,
                    mediaMaxMb: 20,
                },
            ],
        },
    },
}

多账号模式下,每个账号独立创建 socket 连接,独立接收消息。出站时 ctx.accountId 由路由绑定自动传递,确保回复发回正确的账号。

安装与加载:

cd packages/openclaw-channel
pnpm pack   # → smart-link-openclaw-channel-0.1.0.tgz(内含 openclaw.plugin.json 与 dist/ 双入口)

openclaw plugins install ./smart-link-openclaw-channel-0.1.0.tgz
openclaw status                 # 应能看到 smart-link-chat 通道
openclaw channel status smart-link-chat

⚠️ 不要 openclaw plugins install <本包源码目录>:pnpm workspace 里 devDep(rimraf 等)是指向 store 的符号链接,会被安全扫描拒绝(symlink target outside install root)。tarball/npm 安装的 files 白名单不含 node_modules,无此问题。

常见问题(排障)

症状:网关日志 connect_error: xhr poll error / websocket error 反复出现(auto-restart attempt N/10)。

多数情况下是 TLS 证书校验失败:Node 使用内置 Mozilla 信任库,若服务器证书链使用它不认识的根证书(例如 UAT 证书链到 Let's Encrypt 新根 ISRG Root YR),curl 浏览器正常但 Node 连不上,表现为 xhr poll error。排查:

# 1. 确认是 TLS 问题
node -e "require('tls').connect({host:'你的IM域名',port:443},()=>process.exit(0)).on('error',e=>console.log(e.message))"
#    → self-signed certificate in certificate chain = TLS 信任问题

# 2. 首选:把服务器根证书(PEM)配进 tlsCa(仍校验证书)
echo | openssl s_client -connect 你的IM域名:443 -servername 你的IM域名 -showcerts 2>/dev/null \
  | awk '/BEGIN CERT/{n++} n==3'   # 第 3 张通常是根证书;加到配置 tlsCa

# 3. 应急(仅测试环境):channels.smart-link-chat.tlsRejectUnauthorized = false

其它排查:

  • cookie 无效/过期 → 服务端 401(curl -I https://你的IM域名/socket.io/ 无 cookie 返回 401 属正常;带 cookie 应返回 200 handshake)
  • 连接地址不对:[gedim] connecting <url> 里应能看到正确 host/path;socket.io 若挂在根路径就不要配 namespace
  • 一直 auto-restart:说明 gateway.startAccount 抛错退出(看日志最后一条 error / channel status 的 lastError),修好后重启 gateway 观察

待确认契约(对接前必读 ⚠️)

| # | 项 | 说明 | 落点 | |---|----|------|------| | 1 | socket 握手 | URL server+namespace?client_type=、Cookie/User-Agent 头、是否需要先 session 初始化才推送 | gedim/client.ts | | 2 | 收发报文样例 | c2c/c2g 消息实际字段、messagePrepare 响应格式、ack code 语义 | gedim/protocol.ts/client.ts | | 3 | cookie 有效时长 | bot cookie 过期策略、是否需重连前刷新 | gedim/client.ts | | 4 | 文件服务报文 | /api-file/file/uploadFile、/downloadFile 的实际响应/鉴权(是否需 Cookie、是否重定向) | gedim/file.ts | | 5 | 风控/多端 | 常驻 bot 是否被踢(kickout/mulClientKickout)、是否需限速 | gedim/client.ts |

路线图

  • [x] 原生插件壳:manifest + entry + setup entry(OpenClaw 2026.6.x 可安装/加载/注册通道)
  • [x] 配置解析(server/userId/userName/cookie)+ 缺失校验 + 目录元数据
  • [x] outbound.sendText:懒建 GedimClient 两段式发文本(messagePrepare → emit c2c/c2g)
  • [x] 入站:socket 常驻 + c2cChatMessage/c2gChatMessage 监听 → agent dispatch → 消费回复发回 (gateway.startAccount,与出站共享单例客户端;过滤:自己的消息 / 不支持的载荷 / ignore 名单 / 去重)
  • [x] 群聊 @ 门控(requireMention,默认 true;ignoreUserIds/ignoreConversationIds 已在 schema)
  • [x] 图片:入站下载 → 宿主媒体库 → MediaPaths;出站 sendMedia 上传文件服务 → picture 消息
  • [x] 语音/文件:入站下载(voice/file 同上)→ 宿主媒体库;出站 sendMedia 按 MIME kind 路由 → voice/file 消息
  • [ ] 撤回/已读回执语义映射
  • [ ] cookie 过期检测/刷新
  • [ ] 登录接口完善后补 setup 向导

说明

  • 会话身份:单聊 conversation.id = 对方 userId、群 = groupId(与 im-base 会话键一致),保证 agent 对同一用户记忆连续。
  • 本工程是 monorepo 普通 workspace 成员,不依赖 RN;主仓库 .gitignore 的 packages/im-base 忽略项与本工程无关。
  • 发布到 npm/ClawHub 前:把 package.json 的 "private": true 去掉,并核对 compat/build 版本声明与 peerDependencies。