@anwenhappy2026/x-channel
v0.1.0
Published
HTTP bridge channel integrated with OpenClaw server at /v1/chat/completions
Maintainers
Readme
x-channel
x-channel provides a lightweight inbound bridge into OpenClaw so you can push test messages via HTTP or allow your own WebSocket server to dispatch inbound events. It is built on the OpenClaw channel runtime and reuses the same dispatch/streaming flow as the core platform.
Auto install + first-start bootstrap
If you want OpenClaw to install and configure this plugin automatically on first startup, use the bootstrap scripts in /scripts:
- Build a local package tarball (optional, for offline/local install):
cd extensions/x-channel
npm packThis creates a file like xclaw-x-channel-0.1.0.tgz.
- Start OpenClaw through the wrapper script:
cd ../..
./scripts/start-openclaw-with-x-channel.ps1To install from a local tarball instead of npm:
./scripts/start-openclaw-with-x-channel.ps1 -PluginSpec ".\extensions\x-channel\xclaw-x-channel-0.1.0.tgz"What happens on first run:
- Runs
openclaw plugins install <PluginSpec> - Creates/merges
~/.openclaw/openclaw.json - Adds default
channels.x-channelandagents.defaultsvalues if missing - Writes a one-time marker file
~/.openclaw/.x-channel-bootstrap.done
Subsequent runs skip bootstrap unless you delete the marker file.
Implementation outline
- The plugin registers a channel with the identifier
x-channeland an HTTP route on the OpenClaw server. - When the gateway starts an account, it instantiates:
- An HTTP route registered at
/v1/chat/completionson the OpenClaw server (no separate port needed). - An optional outgoing WS client that connects to your server, handles reconnect/backoff, and forwards WS payloads into OpenClaw.
- An HTTP route registered at
- Both HTTP and WS inputs run through the shared channel routing stack:
runtime.channel.routing.resolveAgentRoute(...)runtime.channel.reply.finalizeInboundContext(...)runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher(...)
- WS replies use the streaming protocol defined below (accepted → zero/more delta → done). The last
deltaemitted by the dispatcher carriesdone: true. If no incremental blocks are produced, the connector falls back to thedoneframe.
Configuration
Add or merge the following into your OpenClaw config (typically ~/.openclaw/openclaw.json):
{
"channels": {
"x-channel": {
"defaultAccountId": "default",
"defaultConversationId": "server-tests",
"ws": {
"enabled": true,
"url": "ws://127.0.0.1:9001/openclaw",
"protocols": [],
"initialRetryMs": 1000,
"maxRetryMs": 30000,
"backoffFactor": 2,
"maxRetries": -1,
"connectTimeoutMs": 10000,
"handshake": {
"client": "x-channel"
}
}
}
},
"agents": {
"defaults": {
"blockStreamingDefault": "on",
"blockStreamingBreak": "text_end"
}
}
}Note: The
host,port, andendpointPathconfiguration options are deprecated. The HTTP route is now integrated directly into the OpenClaw server at/v1/chat/completions.
Changing blockStreamingDefault/blockStreamingBreak affects how OpenClaw generates streaming deltas. Set blockStreaming in the CLI or config when you need fine-grained token-level streaming.
HTTP inbound protocol
Endpoint
POST http://<openclaw-host>:<openclaw-port>/v1/chat/completions
For local development, this is typically: http://127.0.0.1:18789/v1/chat/completions
Request body
OpenAI-compatible request shape:
| Field | Type | Description |
| --- | --- | --- |
| model | string | Optional model label echoed in response |
| content | string/array | Preferred inbound text source; when empty request is ignored |
| messages | array | Required message list (OpenAI format) |
| stream | boolean | true for SSE streaming chunks |
| user | string | Optional user ID (mapped to channel sender) |
| metadata | object | Optional passthrough metadata |
Sample curl (PowerShell):
curl.exe --noproxy "*" -i -X POST "http://127.0.0.1:18789/v1/chat/completions" `
-H "Content-Type: application/json" `
--data-raw '{"model":"x-channel","messages":[{"role":"user","content":"hello"}],"stream":false,"user":"u-1"}'When stream=false, response follows OpenAI chat.completion JSON schema.
When stream=true (or omitted), response is SSE with chat.completion.chunk events and final data: [DONE].
WS-driven inbound data
The outbound WS client becomes active when channels.x-channel.ws.enabled is true and channels.x-channel.ws.url is provided. The client:
- Connects to your server using the configured
protocolslist. - Supports exponential backoff reconnects controlled by
initialRetryMs,maxRetryMs,backoffFactor, andmaxRetries. - Imposes a per-attempt timeout (
connectTimeoutMs) and immediately retries if the handshake fails. - Sends the optional
handshakepayload after the connection opens. - Emits detailed logs prefixed by
[x-channel:accountId]for lifecycle events, connection attempts, and errors.
WS request format (server → plugin client)
{
"requestId": "req-1",
"text": "hello from ws",
"userId": "u-1",
"accountId": "default",
"conversationId": "c-1",
"metadata": { "source": "ws-server" }
}WS reply protocol (client → server)
- Accepted acknowledgement
{ "ok": true, "requestId": "req-1", "type": "accepted", "message": "request accepted" }- Streaming delta frames
Each block produced by OpenClaw is pushed as a delta. Deltas include index, kind, and text. The dispatcher flags done: true on the final delta.
{
"ok": true,
"requestId": "req-1",
"type": "delta",
"index": 0,
"kind": "block",
"text": "...",
"done": false
}- Completion frame
When OpenClaw generated no deltas, the plugin sends a standalone done event:
{
"ok": true,
"requestId": "req-1",
"type": "done",
"done": true,
"event": { "...": "..." }
}When there are deltas, the final delta carries done: true and optional event/replies payloads derived from the final message.
