← Back to Plugins
Tools

Cowlab

DREAMDREAM66 By DREAMDREAM66 👁 35 views ▲ 0 votes

openclaw plugin, fork from wechat-clawbot

GitHub

Install

npm install

#

Configuration Example

{
  "messages": [
    { "id": "msg-1", "from": "user-alice", "text": "Hi", "timestamp": 1700000000000, "contextToken": "ctx-abc" }
  ],
  "cursor": "next-cursor-value"
}

README

# openclaw-cowlab

An [OpenClaw](https://docs.openclaw.ai) channel plugin that bridges **your own chat app** (via a small HTTP backend) to the OpenClaw agent gateway.

> Forked from `@tencent-weixin/openclaw-weixin` with all WeChat-specific code replaced. The OpenClaw framework glue (channel wiring, message processing, storage, hooks) is carried over verbatim.

## What it does

- **Inbound**: long-polls your backend for new messages, then dispatches them to the configured OpenClaw agent.
- **Outbound**: when the agent replies, POSTs the text back to your backend.
- **Auth**: static bearer token from your config (or env var). No QR login, no session refresh dance.

## Status

MVP / text-only. No images, files, voice, or video yet. See [Out of scope](#out-of-scope) below.

## Backend contract

Your backend must expose two endpoints, JSON over HTTP, with `Authorization: Bearer <token>`.

### `GET /v1/messages/poll?timeout=<sec>&cursor=<opaque>`

Long-poll for new messages. Hold the request up to `timeout` seconds, then return.

Response:
```json
{
  "messages": [
    { "id": "msg-1", "from": "user-alice", "text": "Hi", "timestamp": 1700000000000, "contextToken": "ctx-abc" }
  ],
  "cursor": "next-cursor-value"
}
```

- `cursor` is opaque to the plugin — the backend defines its meaning (e.g. last-id, watermark, JWT). The plugin stores whatever you return and sends it back on the next poll.
- On timeout with no new messages, return `{ "messages": [], "cursor": "<unchanged>" }`.
- `contextToken` is echoed back on the reply so your backend can correlate; treat it as opaque.

### `POST /v1/messages`

Send a text message to a user.

Request:
```json
{ "to": "user-alice", "text": "Hello back", "contextToken": "ctx-abc" }
```

Response: `{ "messageId": "msg-456" }` (the plugin uses this for tracing; the local synthetic id is used as a fallback).

## Configuration

In your `openclaw.json`:

```json
{
  "channels": {
    "openclaw-cowlab": {
      "apiUrl": "https://your-backend.example.com",
      "apiToken": "your-secret-token"
    }
  }
}
```

Or via env vars (override the config):

```
OPENCLAW_COWLAB_API_URL=https://your-backend.example.com
OPENCLAW_COWLAB_API_TOKEN=your-secret-token
```

Then run:

```bash
openclaw channels login --channel openclaw-cowlab
openclaw channels start  --channel openclaw-cowlab
```

## Local development / testing

The repo ships with a mock backend that implements the contract above, plus an integration test that drives the full round-trip.

```bash
# Install
npm install

# Typecheck + tests
npm run typecheck
npm test

# Build
npm run build

# Run the mock backend on :4001 (requires `npx tsx` — installed lazily on first run)
npx tsx tests/mock-backend/server.ts 4001
```

The mock backend also exposes test-only endpoints:
- `POST /v1/test/inject` — push a message into the inbound queue
- `GET  /v1/test/outbound` — list received outbound messages
- `POST /v1/test/reset`   — clear all queues

## Out of scope (MVP)

- Media (images, files, voice, video) — `outbound.sendMedia` throws.
- Multi-account — single fixed account id `"main"`.
- Typing indicators — no equivalent; the inbound pipeline doesn't call anything.
- Session-expired circuit breaker — rely on HTTP error → standard backoff.
- QR login — replaced by static token auth.

If your real backend already has a different shape, only `src/api/client.ts` needs to change.

## Project layout

```
src/
  api/
    client.ts          the HTTP client (getUpdates + sendMessage)
    types.ts           wire types (MyCowlabMessage, GetUpdatesResp, SendMessageReq/Resp)
  auth/
    accounts.ts        single-account persistence (<stateDir>/openclaw-cowlab/accounts/main.json)
    login.ts           static-token config loader
  config/
    config-schema.ts   zod schema for the channel section
  messaging/
    inbound.ts         message-context conversion + context-token store
    outbound-hooks.ts  message_sending / message_sent hook adapters
    process-message.ts inbound dispatcher (framework glue)
    send.ts            sendMessageMyCowlab
    markdown-filter.ts streaming markdown → plain text
    error-notice.ts    user-facing error notice
    slash-commands.ts  /echo, /toggle-debug
  monitor/
    monitor.ts         long-poll loop with backoff + cursor persistence
  storage/
    state-dir.ts       resolveStateDir() — env-var override chain
    sync-buf.ts        get_updates_buf (cursor) load/save
  util/
    logger.ts          JSON-line logger
    redact.ts          redactBody / redactUrl / redactToken
    agent.ts           resolveAgentWorkspaceDir / resolveMatchedAgentId
    random.ts          generateId
  channel.ts           the ChannelPlugin<ResolvedMyCowlabAccount> wiring
index.ts               plugin entry (registered via api.registerChannel)
tests/
  mock-backend/server.ts  in-memory mock backend for manual / integration tests
  integration.test.ts     end-to-end round-trip via the mock
```

## On-disk state

```
<stateDir>/openclaw-cowlab/
  accounts/
    main.json             # { token, apiUrl, savedAt }
    main.sync.json        # long-poll cursor
    main.context-tokens.json  # { "<userId>": "<contextToken>" }
```

`<stateDir>` resolves to `OPENCLAW_STATE_DIR` → `CLAWDBOT_STATE_DIR` → `~/.openclaw`.

## License

MIT
tools

Comments

Sign in to leave a comment

Loading comments...