Deterministic local messaging-channel mocks for OpenClaw QA.
crabline is config-driven, CI-friendly, and deliberately has no openclaw
dependency. It models OpenClaw channel contracts without connecting to live chat
services or installing provider SDKs. A fixture can still say
channel: telegram on the OpenClaw side; Crabline supplies the mock Telegram
execution backend for that channel shape.
- local mock providers for
discord,feishu,googlechat,imessage,loopback,matrix,mattermost,msteams,slack,telegram,whatsapp, andzalo - a
scriptbridge for channels that are still exercised by external commands - per-provider local webhook endpoints for inbound events
- JSONL recorder files for deterministic wait/watch behavior
- nonce-based
send,roundtrip,agent,probe,run,watch, anddoctorcommands - text output by default and stable
--jsonoutput for automation
Crabline is not a live transport runner. Live OpenClaw channel tests use OpenClaw's live channel adapters and credentials directly.
pnpm install
pnpm build
pnpm verifyRun locally:
pnpm dev fixtures --config fixtures/examples/crabline.example.yaml
pnpm dev roundtrip telegram-dm --config fixtures/examples/crabline.example.yamlpnpm verifyThat enforces formatting, typecheck, type-aware lint, and Vitest coverage.
Config file search order:
--config <path>./crabline.yaml./crabline.yml./crabline.json
Top-level shape:
configVersion: 1
userName: crabline
providers:
telegram:
adapter: telegram
telegram:
recorder:
path: ./.crabline/recorders/telegram.jsonl
webhook:
host: 127.0.0.1
port: 8790
path: /telegram/webhook
fixtures:
- id: telegram-dm
provider: telegram
mode: roundtrip
target:
id: "100000001"
behavior: agentProvider ids are local profile names. Fixtures reference them through
provider. Built-in adapters infer their platform from adapter; platform is
required only for adapter: script.
Built-in provider credentials are optional metadata only. doctor checks
explicit env declarations, script command availability, and config shape; it
does not require live Slack, Discord, Telegram, WhatsApp, Matrix, iMessage, or
other platform secrets for local mocks.
All built-in mock providers support:
probesendroundtripagentwatch
The built-in providers are:
discordfeishugooglechatimessageloopbackmatrixmattermostmsteamsslacktelegramwhatsappzalo
The script adapter can bridge any other OpenClaw channel by running local
commands for probe, send, waitForInbound, or watch.
Generic mock providers encode raw target ids with the platform prefix:
target:
id: "123"becomes:
telegram:123
Thread targets use target.channelId plus target.threadId:
target:
channelId: "room-1"
threadId: "thread-1"becomes:
telegram:room-1:thread-1
Discord keeps a Discord-shaped guild/channel/thread encoding:
target:
id: "123456789012345678"
metadata:
guildId: "987654321098765432"becomes:
discord:987654321098765432:123456789012345678
Telegram keeps topic-style thread IDs:
target:
channelId: "-100123"
threadId: "42"becomes:
telegram:-100123:42
Each built-in provider starts a local webhook during probe, waitForInbound,
or watch. Webhook requests must be JSON:
{
"id": "telegram-inbound-1",
"threadId": "telegram:100000001",
"text": "reply nonce-123",
"author": "assistant"
}Nested message payloads are also accepted:
{
"message": {
"id": "telegram-inbound-1",
"threadId": "telegram:100000001",
"text": "reply nonce-123"
}
}Malformed webhooks return 400, and non-JSON requests return 415.
send records an outbound user event in the provider recorder. For roundtrip
and agent modes, the local mock also records a deterministic assistant reply:
[telegram mock] hello nonce-123
waitForInbound reads the recorder until it finds a matching non-user event.
watch streams matching recorder events. This gives CI channel coverage without
live service latency, external credentials, webhooks exposed to the internet, or
provider SDK state.
See Channel Setup for the provider matrix, webhook paths, and OpenClaw live-vs-mock guidance.