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

openclaw-websocket

v0.0.9

Published

OpenClaw WebSocket chat channel plugin - Connect your apps to OpenClaw via WebSocket

Readme

openclaw-websocket

npm version License: MIT

OpenClaw WebSocket channel plugin - Connect your applications to OpenClaw AI agents via WebSocket.

中文文档

Features

  • Real-time bidirectional communication via WebSocket
  • Stream responses from OpenClaw AI agents
  • Independent session memory for each user
  • Support for direct messages and group chats
  • Media attachments and reply quotes
  • Easy integration with any platform

Installation

# Install from npm
openclaw plugins install @taichi-labs/openclaw-websocket

# Or install from GitHub
openclaw plugins install github:Taichi-Labs/openclaw-websocket

Configuration

Add to your OpenClaw configuration file:

{
  "channels": {
    "websocket": {
      "enabled": true,
      "port": 18800,
      "host": "0.0.0.0",
      "path": "/ws"
    }
  }
}

Or in YAML format:

channels:
  websocket:
    enabled: true
    port: 18800
    host: "0.0.0.0"
    path: "/ws"

Authentication

Auth is enabled by default. The plugin calls your auth service endpoint on connection and uses the returned userId / username as the session identity.

{
  "channels": {
    "websocket": {
      "enabled": true,
      "port": 18800,
      "auth": {
        "enabled": true,
        "endpoint": "http://localhost:3000/api/auth/verify",
        "timeout": 5000,
        "required": true
      }
    }
  }
}

When auth.enabled is true, clients must pass a token via URL parameter:

ws://127.0.0.1:18800/ws?token=YOUR_JWT_TOKEN

The plugin sends a POST request to auth.endpoint:

{ "token": "YOUR_JWT_TOKEN" }

Expected response:

{
  "success": true,
  "data": {
    "userId": "user_123",
    "username": "John"
  }
}

If auth.required is false, unauthenticated connections are allowed as anonymous users.

Environment Variables

Auth settings can also be configured via environment variables (they override config file values):

| Variable | Description | Example | |----------|-------------|---------| | WS_AUTH_ENABLED | Enable/disable auth (true/false/1/0) | WS_AUTH_ENABLED=true | | WS_AUTH_ENDPOINT | Auth service verify URL | WS_AUTH_ENDPOINT=http://auth:3000/api/auth/verify |

# Example: start with env vars
WS_AUTH_ENABLED=true WS_AUTH_ENDPOINT=http://auth-service:3000/api/auth/verify openclaw start

Configuration Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | true | Enable/disable the WebSocket channel | | port | number | 18800 | WebSocket server port | | host | string | "0.0.0.0" | Server bind address | | path | string | "/ws" | WebSocket endpoint path | | auth.enabled | boolean | true | Enable external auth service | | auth.endpoint | string | "http://localhost:3000/api/auth/verify" | Auth service verify URL | | auth.timeout | number | 5000 | Auth request timeout (ms) | | auth.required | boolean | true | Reject connections without valid token | | dynamicAgentCreation.enabled | boolean | false | Auto-create agent per DM user | | dynamicAgentCreation.workspaceTemplate | string | "~/.openclaw/workspace-{agentId}" | Workspace path template | | dynamicAgentCreation.agentDirTemplate | string | "~/.openclaw/agents/{agentId}/agent" | Agent dir path template | | dynamicAgentCreation.maxAgents | number | - | Max dynamic agents allowed |

Dynamic Agent Creation

When enabled, each DM user automatically gets an independent agent with its own workspace. This is useful for multi-tenant scenarios.

{
  "channels": {
    "websocket": {
      "enabled": true,
      "dynamicAgentCreation": {
        "enabled": true,
        "workspaceTemplate": "~/.openclaw/workspace-{agentId}",
        "agentDirTemplate": "~/.openclaw/agents/{agentId}/agent",
        "maxAgents": 100
      }
    }
  }
}

Template variables:

  • {userId} — the senderId of the connecting user
  • {agentId} — auto-generated agent ID (ws-{senderId})

Quick Start

# Install wscat if not already installed
npm install -g wscat

# Start OpenClaw
openclaw start

# Connect to WebSocket server
wscat -c "ws://127.0.0.1:18800/ws?senderId=user1&senderName=John"

After connecting, try these test cases:

# Basic message
{"type":"chat.send","content":"Hello!"}

# Message with custom sender
{"type":"chat.send","content":"Hi there!","senderId":"user_123","senderName":"Alice"}

# Multi-turn conversation (agent remembers context)
{"type":"chat.send","content":"My name is John"}
{"type":"chat.send","content":"What is my name?"}

# Reply to a message
{"type":"chat.send","content":"I agree","replyToBody":"Let's use Python for this project"}

# Group chat message
{"type":"chat.send","content":"Hello everyone!","chatType":"group","groupId":"dev-team","groupSubject":"Dev Team"}

# Message with custom data
{"type":"chat.send","content":"Process this","customData":{"priority":"high","source":"api"}}

# Long message
{"type":"chat.send","content":"Please write a Python function that calculates the factorial of a number recursively and iteratively, then compare their performance."}

Quick Test Script

# One-liner test (send and disconnect)
echo '{"type":"chat.send","content":"Hello!"}' | wscat -c "ws://127.0.0.1:18800/ws?senderId=test"

# Test different users (separate sessions)
wscat -c "ws://127.0.0.1:18800/ws?senderId=alice" -x '{"type":"chat.send","content":"I am Alice"}'
wscat -c "ws://127.0.0.1:18800/ws?senderId=bob" -x '{"type":"chat.send","content":"I am Bob"}'

Message Protocol

Sending Messages

{
  "type": "chat.send",
  "content": "Your message here",
  
  "messageId": "msg_001",
  "senderId": "user_123",
  "senderName": "John Doe",
  
  "chatType": "direct",
  "groupId": "group_001",
  "groupSubject": "Tech Discussion",
  
  "replyToMessageId": "msg_000",
  "replyToBody": "Original message being replied to",
  
  "mediaPath": "/path/to/file.png",
  "mediaType": "image/png",
  
  "customData": {
    "key": "value"
  }
}

Request Parameters

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | type | string | ✅ | Must be "chat.send" | | content | string | ✅ | Message content | | messageId | string | ❌ | Custom message ID (auto-generated if omitted) | | senderId | string | ❌ | Sender identifier for session routing | | senderName | string | ❌ | Display name of the sender | | chatType | string | ❌ | "direct" or "group" (default: "direct") | | groupId | string | ❌ | Group identifier (required for group chats) | | groupSubject | string | ❌ | Group name/subject | | replyToMessageId | string | ❌ | ID of the message being replied to | | replyToBody | string | ❌ | Content of the quoted message | | mediaPath | string | ❌ | Path to media attachment | | mediaType | string | ❌ | MIME type of the media | | mediaPaths | string[] | ❌ | Multiple media file paths | | mediaTypes | string[] | ❌ | MIME types for multiple media | | customData | object | ❌ | Custom data passed to the agent |

Receiving Responses

// Typing indicator
{ "type": "chat.typing" }

// Streaming response (partial content)
{ "type": "chat.stream", "messageId": "xxx", "content": "Partial...", "done": false }

// Final response
{ "type": "chat.response", "messageId": "xxx", "content": "Complete response", "done": true }

// Error response
{ "type": "chat.error", "messageId": "xxx", "error": "Error message" }

Response Types

| Type | Description | |------|-------------| | chat.typing | Agent is processing the request | | chat.stream | Partial streaming response | | chat.response | Final complete response | | chat.error | Error occurred during processing |

Code Examples

JavaScript / Node.js

const WebSocket = require('ws');

const ws = new WebSocket('ws://127.0.0.1:18800/ws?senderId=user1&senderName=John');

ws.on('open', () => {
  console.log('Connected');
  ws.send(JSON.stringify({
    type: 'chat.send',
    content: 'Hello, how are you?'
  }));
});

ws.on('message', (data) => {
  const msg = JSON.parse(data);
  switch (msg.type) {
    case 'chat.typing':
      console.log('Agent is typing...');
      break;
    case 'chat.stream':
      process.stdout.write(msg.content);
      break;
    case 'chat.response':
      console.log('\nAgent:', msg.content);
      break;
    case 'chat.error':
      console.error('Error:', msg.error);
      break;
  }
});

Python

import websocket
import json

def on_message(ws, message):
    msg = json.loads(message)
    if msg['type'] == 'chat.response':
        print(f"Agent: {msg['content']}")
    elif msg['type'] == 'chat.error':
        print(f"Error: {msg['error']}")

def on_open(ws):
    ws.send(json.dumps({
        'type': 'chat.send',
        'content': 'Hello!'
    }))

ws = websocket.WebSocketApp(
    'ws://127.0.0.1:18800/ws?senderId=user1&senderName=John',
    on_message=on_message,
    on_open=on_open
)
ws.run_forever()

cURL (via websocat)

echo '{"type":"chat.send","content":"Hello!"}' | \
  websocat 'ws://127.0.0.1:18800/ws?senderId=user1'

Session Management

Each unique senderId maintains an independent conversation session with the AI agent. This means:

  • User A's conversation history is separate from User B's
  • The agent remembers context within each user's session
  • Group chats share a session based on groupId
// User A's session
ws.send(JSON.stringify({ type: 'chat.send', senderId: 'user_A', content: 'My name is Alice' }));

// User B's session (independent)
ws.send(JSON.stringify({ type: 'chat.send', senderId: 'user_B', content: 'My name is Bob' }));

// User A asking (agent remembers "Alice")
ws.send(JSON.stringify({ type: 'chat.send', senderId: 'user_A', content: 'What is my name?' }));

URL Parameters

You can pass user information via URL query parameters when connecting:

ws://127.0.0.1:18800/ws?senderId=user123&senderName=John%20Doe

| Parameter | Description | |-----------|-------------| | senderId | Default sender ID for this connection | | senderName | Default display name for this connection |

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT