@openclaw/crabline
v0.1.1
Published
Standalone CLI for deterministic messaging provider E2E tests
Readme
crabline
Deterministic local messaging-channel mocks for OpenClaw QA.
crabline is config-driven, CI-friendly, and deliberately has no openclaw
dependency. It can run fixture-level local mocks, and it can also serve fake
provider APIs that OpenClaw live adapters can target during deterministic QA.
What It Provides
- 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
- fake provider servers for live-adapter smoke tests, starting with Telegram and WhatsApp
- 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 fake servers are not live-provider coverage. They let OpenClaw run its
normal channel adapter code against a local provider-shaped API. Release lanes
still need the live driver and real provider credentials.
Install
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.yamlQuality Gate
pnpm verifyThat enforces formatting, typecheck, type-aware lint, and Vitest coverage.
Config
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.
Built-In Mock Channels
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.
Fake Provider Servers
serve starts provider-shaped HTTP APIs for OpenClaw live adapters. This is the
preferred Smoke CI path because OpenClaw still uses its normal channel adapter,
but the provider endpoint is local and deterministic.
Telegram:
crabline --json serve telegram --ready-file .crabline/telegram-server.jsonThe JSON manifest contains:
endpoints.apiRoot: set OpenClawchannels.telegram.apiRootto this valuebotToken: set OpenClawchannels.telegram.botTokento this valueadminToken: send this as theX-Crabline-Admin-Tokenheader when posting test user messagesendpoints.adminInboundUrl: authenticated POST endpoint for test user messages; OpenClaw reads them through TelegramgetUpdatesrecorderPath: JSONL file of fake provider API/admin traffic
The admin token is generated randomly unless --admin-token <token> is
provided. The inbound endpoint rejects requests without the matching admin
header (or Authorization: Bearer <token>).
Implemented Telegram Bot API endpoints include getMe, sendMessage,
editMessageText, deleteMessage, setMessageReaction, createForumTopic,
editForumTopic, pinChatMessage, unpinChatMessage, getUpdates,
deleteWebhook, setWebhook, setMyCommands, deleteMyCommands,
sendChatAction, and answerCallbackQuery.
WhatsApp:
crabline --json serve whatsapp --ready-file .crabline/whatsapp-server.jsonThe JSON manifest contains:
endpoints.apiRoot: Crabline WhatsApp fake provider API rootaccessToken: bearer token for fake provider requestsadminToken: send this as theX-Crabline-Admin-Tokenheader when posting test user messagesselfJid: fake authenticated WhatsApp user JIDendpoints.adminInboundUrl: authenticated POST endpoint for test user messages; subscribed Baileys mock sockets receive them asmessages.upsertendpoints.messagesUrl: fake text send endpoint used by the Baileys-shaped mockendpoints.presenceUrl: fake presence endpoint used bysendPresenceUpdaterecorderPath: JSONL file of fake provider API/admin traffic
The started WhatsApp fake server also exposes createBaileysMockSocket() so
tests can exercise a Baileys-style WhatsApp sendMessage() /
sendPresenceUpdate() surface while the fake server owns local provider
simulation. The admin token is generated randomly unless --admin-token <token>
is provided.
OpenClaw bridge callers should post injected user messages with the
providerUrl, providerHeaders, and providerBody returned by
createOpenClawCrablineInbound(). For WhatsApp, inbound messages.upsert
delivery is an in-process Baileys mock socket behavior: create the fake server
and Baileys mock socket in the same Node process when testing listener-driven
inbound delivery. If the socket is created outside the started server, pass the
same WhatsAppBaileysMockRegistry to the server and socket helper.
Target IDs
Built-in providers accept native channel identifiers. Crabline does not add
telegram:, discord:, slack:, or other local prefixes.
target:
id: "C1234567890"Thread targets use the platform's native thread identifier:
target:
channelId: "C1234567890"
threadId: "1700000000.000100"Examples:
- Slack conversations:
C1234567890,G1234567890, orD1234567890 - Slack threads:
1700000000.000100 - Telegram chats:
-1001234567890or@channelusername - Telegram topics:
42 - WhatsApp users:
[email protected] - WhatsApp groups:
[email protected] - Discord channels and threads: Discord snowflake ids such as
123456789012345678
Webhooks
Each built-in provider starts a local webhook during probe, waitForInbound,
or watch. Webhook requests can use the provider's native event shape, or this
simple JSON shape with native thread ids:
{
"id": "slack-inbound-1",
"threadId": "C1234567890",
"text": "reply nonce-123",
"author": "assistant"
}Nested message payloads are also accepted:
{
"message": {
"id": "slack-inbound-1",
"threadId": "C1234567890",
"text": "reply nonce-123"
}
}Malformed webhooks return 400, and non-JSON requests return 415.
Evidence Flow
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-123waitForInbound 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.
More Setup Detail
See Channel Setup for the provider matrix, webhook paths, and OpenClaw live-vs-mock guidance.
