websocket-connector

lqxhgd/websocket-connector

Otheropenclawby lqxhgd

Summary

OpenClaw plugin exposing 0 skills.

Install to Claude Code

openclaw plugin add lqxhgd/websocket-connector

Run in Claude Code. Add the marketplace first with /plugin marketplace add lqxhgd/websocket-connector if you haven't already.

README.md

OpenClaw WebSocket Connector

通用 WebSocket 插件,让任意 Web 项目通过 WebSocket 连接即可与 OpenClaw 通信。支持流式对话、文件双向传输、会话管理和异步任务管理。

功能特性

  • 流式对话 — 实时逐字推送 AI 回复,支持多轮对话上下文
  • 文件传输 — 双向分块传输,支持大文件上传/下载
  • 文件转换 — PDF→DOC 等格式转换(通过 AI Agent 完成)
  • 会话管理 — 多会话并行、断线恢复、超时自动清理
  • 任务管理 — 异步任务提交、状态查询、结果推送
  • 通用接入 — 任何支持 WebSocket 的平台均可接入(浏览器、Node.js、Python、移动端等)

前置要求

  • OpenClaw 已部署并运行(Docker 或本地)
  • OpenClaw Gateway 已配置好 AI 模型(如 Kimi、GPT 等)

安装

方式一:通过 npm 安装(推荐)

在 OpenClaw 运行环境中执行:

openclaw plugins install openclaw-websocket-connector

方式二:Docker 环境安装

如果 OpenClaw 运行在 Docker 中:

# 进入容器
docker compose exec openclaw-gateway sh

# 安装插件
node dist/index.js plugins install openclaw-websocket-connector

# 退出容器
exit

# 重启容器加载插件
docker compose restart openclaw-gateway

方式三:从源码安装

git clone https://github.com/lqxhgd/websocket-connector.git
cd websocket-connector
npm install

# 在 OpenClaw 容器中安装
docker cp ./websocket-connector 容器名:/tmp/ws-plugin
docker compose exec openclaw-gateway node dist/index.js plugins install /tmp/ws-plugin
docker compose restart openclaw-gateway

配置

1. 开启 Gateway HTTP 端点

~/.openclaw/openclaw.jsongateway 配置中启用 chatCompletions

{
  "gateway": {
    "http": {
      "endpoints": {
        "chatCompletions": {
          "enabled": true
        }
      }
    }
  }
}

2. 端口映射(Docker 环境)

docker-compose.ymlports 中添加 WebSocket 端口:

ports:
  - "18789:18789"
  - "18800:18800"   # WebSocket Connector 端口

3. 插件配置项

安装后可在 openclaw.json 中配置:

{
  "channels": {
    "websocket-connector": {
      "enabled": true,
      "port": 18800,
      "authToken": "",
      "sessionTimeout": 1800000,
      "maxFileSize": 20971520
    }
  }
}

| 配置项 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | enabled | boolean | true | 是否启用插件 | | port | number | 18800 | WebSocket 服务监听端口 | | authToken | string | "" | 客户端认证 Token(为空不校验) | | sessionTimeout | number | 1800000 | 会话超时(毫秒),默认 30 分钟 | | maxFileSize | number | 20971520 | 单文件大小限制(字节),默认 20MB |

环境变量覆盖:WS_PORTWS_AUTH_TOKENLOG_LEVEL(debug/info/warn/error)

快速接入

基本流程

连接 → 认证 → 创建会话 → 发送消息 → 接收流式响应

JavaScript / 浏览器

const ws = new WebSocket('ws://your-server:18800');

// 1. 认证(连接成功后自动触发)
ws.onopen = () => {
  ws.send(JSON.stringify({
    type: 'auth',
    id: '1',
    payload: { token: 'your-token' },  // 服务端未设 authToken 时留空即可
    timestamp: Date.now()
  }));
};

let sessionId = null;

// 2. 监听消息
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  switch (msg.type) {
    case 'auth.success':
      // 认证成功,创建会话
      ws.send(JSON.stringify({
        type: 'session.create',
        id: '2',
        payload: { name: '我的会话' },
        timestamp: Date.now()
      }));
      break;

    case 'session.info':
      // 会话创建成功,保存 sessionId
      sessionId = msg.payload.sessionId;
      console.log('会话已创建:', sessionId);
      break;

    case 'chat.stream':
      // 流式响应,逐字显示
      process.stdout.write(msg.payload.chunk);
      break;

    case 'chat.done':
      // 对话完成
      console.log('\n[完成]');
      break;

    case 'error':
      console.error('错误:', msg.payload.message);
      break;
  }
};

// 3. 发送对话
function sendMessage(content) {
  ws.send(JSON.stringify({
    type: 'chat',
    id: String(Date.now()),
    payload: { sessionId, content },
    timestamp: Date.now()
  }));
}

Node.js

import WebSocket from 'ws';

const ws = new WebSocket('ws://your-server:18800');

ws.on('open', () => {
  ws.send(JSON.stringify({
    type: 'auth',
    id: '1',
    payload: { token: '' },
    timestamp: Date.now()
  }));
});

ws.on('message', (data) => {
  const msg = JSON.parse(data.toString());
  console.log(`[${msg.type}]`, JSON.stringify(msg.payload));
});

Python

import asyncio
import websockets
import json

async def main():
    uri = "ws://your-server:18800"
    async with websockets.connect(uri) as ws:
        # 认证
        await ws.send(json.dumps({
            "type": "auth",
            "id": "1",
            "payload": {"token": ""},
            "timestamp": 0
        }))

        # 创建会话
        auth_resp = json.loads(await ws.recv())
        print("认证:", auth_resp["type"])

        await ws.send(json.dumps({
            "type": "session.create",
            "id": "2",
            "payload": {"name": "test"},
            "timestamp": 0
        }))

        session_resp = json.loads(await ws.recv())
        session_id = session_resp["payload"]["sessionId"]

        # 发送对话
        await ws.send(json.dumps({
            "type": "chat",
            "id": "3",
            "payload": {"sessionId": session_id, "content": "你好"},
            "timestamp": 0
        }))

        # 接收流式响应
        while True:
            msg = json.loads(await ws.recv())
            if msg["type"] == "chat.stream":
                print(msg["payload"]["chunk"], end="", flush=True)
            elif msg["type"] == "chat.done":
                print("\n[完成]")
                break

asyncio.run(main())

文件传输

上传文件

// 1. 发起上传
ws.send(JSON.stringify({
  type: 'file.upload.start',
  id: '10',
  payload: {
    fileName: 'document.pdf',
    fileSize: file.size,
    mimeType: 'application/pdf'
  },
  timestamp: Date.now()
}));

// 2. 收到 file.upload.ready 后,按返回的 chunkSize 分块发送
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === 'file.upload.ready') {
    const chunkSize = msg.payload.chunkSize;  // 默认 64KB
    let offset = 0;
    while (offset < file.size) {
      const chunk = file.slice(offset, offset + chunkSize);
      ws.send(chunk);  // 发送二进制帧
      offset += chunkSize;
    }
    // 3. 所有块发送完成后通知
    ws.send(JSON.stringify({
      type: 'file.upload.complete',
      id: '11',
      payload: { uploadId: msg.payload.uploadId },
      timestamp: Date.now()
    }));
  }
};

// 4. 收到 file.upload.done,获取 fileId
// { type: "file.upload.done", payload: { fileId: "xxx", fileName: "document.pdf", fileSize: 1024000 } }

下载文件

ws.send(JSON.stringify({
  type: 'file.download',
  id: '20',
  payload: { fileId: 'your-file-id' },
  timestamp: Date.now()
}));

// 服务端依次发送:
// 1. file.download.start — 文件元信息(文件名、大小)
// 2. 多个二进制帧    — 文件数据块
// 3. file.download.done — 下载完成通知

文件格式转换

ws.send(JSON.stringify({
  type: 'file.convert',
  id: '30',
  payload: {
    fileId: 'uploaded-file-id',
    targetFormat: 'docx'
  },
  timestamp: Date.now()
}));

// 收到 file.convert.done:
// { payload: { resultFileId: "xxx", resultFileName: "document.docx" } }
// 用 resultFileId 请求下载即可获取转换后的文件

消息协议

所有消息为 JSON 格式,统一结构:

{
  type: string,      // 消息类型
  id: string,        // 唯一 ID(用于请求-响应匹配)
  payload: object,   // 业务数据
  timestamp: number  // 时间戳
}

消息类型清单

| 类型 | 方向 | 说明 | |------|------|------| | 系统 | | | | auth | C→S | 客户端认证 | | auth.success | S→C | 认证成功,返回 connectionId | | ping / pong | 双向 | 心跳检测 | | error | S→C | 错误通知 | | 对话 | | | | chat | C→S | 发送对话消息 | | chat.stream | S→C | 流式响应片段 | | chat.done | S→C | 对话完成,返回完整内容 | | chat.error | S→C | 对话错误 | | 文件 | | | | file.upload.start | C→S | 发起文件上传 | | file.upload.ready | S→C | 返回 uploadId 和 chunkSize | | file.upload.complete | C→S | 通知上传完成 | | file.upload.done | S→C | 服务端处理完成,返回 fileId | | file.download | C→S | 请求下载文件 | | file.download.start | S→C | 下载开始,含文件元信息 | | file.download.done | S→C | 下载完成 | | file.convert | C→S | 请求文件格式转换 | | file.convert.done | S→C | 转换完成,返回新 fileId | | 会话 | | | | session.create | C→S | 创建新会话 | | session.resume | C→S | 恢复已有会话(断线重连) | | session.destroy | C→S | 销毁会话 | | session.list | C→S | 列出当前连接的所有会话 | | session.info | S→C | 返回会话信息 | | 任务 | | | | task.submit | C→S | 提交异步任务 | | task.status | C→S / S→C | 查询/推送任务状态 | | task.result | S→C | 任务完成,返回结果 | | task.cancel | C→S | 取消任务 |

项目结构

websocket-connector/
├── src/
│   ├── index.ts              # 插件入口(注册到 OpenClaw)
│   ├── config.ts             # 配置管理
│   ├── types.ts              # 全局类型定义
│   ├── server/               # 传输层:WebSocket 服务
│   │   ├── WebSocketServer.ts
│   │   └── ConnectionManager.ts
│   ├── protocol/             # 协议层:消息路由与序列化
│   │   ├── MessageTypes.ts
│   │   ├── MessageRouter.ts
│   │   └── Serializer.ts
│   ├── handlers/             # 处理层:业务逻辑
│   │   ├── ChatHandler.ts
│   │   ├── FileHandler.ts
│   │   ├── TaskHandler.ts
│   │   └── SessionHandler.ts
│   ├── services/             # 服务层:核心能力
│   │   ├── gateway/          # OpenClaw Gateway 通信
│   │   ├── file/             # 文件管理与传输
│   │   └── session/          # 会话生命周期管理
│   └── utils/                # 工具层
├── test/
│   └── index.html            # 测试面板(浏览器打开即可测试)
├── openclaw.plugin.json      # OpenClaw 插件元数据
├── package.json
└── ARCHITECTURE.md           # 架构设计文档

测试

项目自带一个测试面板 test/index.html,用浏览器直接打开即可:

  • 连接 / 断开 WebSocket
  • 创建 / 切换 / 删除会话
  • 发送对话并查看流式响应
  • 上传 / 下载文件
  • 提交异步任务
  • 查看完整通信日志

常见问题

连接被拒绝?

  • 确认 OpenClaw 容器已启动且插件已加载(查看日志中 WebSocket 服务已启动, 端口: 18800
  • 确认 Docker 端口映射包含 18800:18800
  • 确认防火墙未拦截该端口

对话返回 404?

openclaw.jsongateway.http.endpoints.chatCompletions.enabled 设为 true,详见上方配置说明。

插件加载失败(world-writable path)?

Windows + Docker 环境下,挂载卷权限显示为 777 被安全检查拦截。解决方案: 1. 在 docker-compose.yml 中为 extensions 使用 Docker 命名卷 2. 或在 Linux 服务器上部署(无此问题)

详见 ARCHITECTURE.md 中的部署说明。

License

MIT

Related plugins

Browse all →