@openclaw/crabline 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +31 -31
  2. package/dist/src/cli/program.js +15 -14
  3. package/dist/src/cli/program.js.map +1 -1
  4. package/dist/src/index.d.ts +9 -11
  5. package/dist/src/index.js +5 -6
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/openclaw/bridges/slack.d.ts +1 -1
  8. package/dist/src/openclaw/bridges/telegram.d.ts +1 -1
  9. package/dist/src/openclaw/bridges/whatsapp.d.ts +1 -1
  10. package/dist/src/openclaw/bridges/whatsapp.js +1 -2
  11. package/dist/src/openclaw/bridges/whatsapp.js.map +1 -1
  12. package/dist/src/openclaw/shared.d.ts +11 -11
  13. package/dist/src/openclaw/shared.js +2 -2
  14. package/dist/src/openclaw/shared.js.map +1 -1
  15. package/dist/src/openclaw.d.ts +7 -7
  16. package/dist/src/openclaw.js +9 -9
  17. package/dist/src/openclaw.js.map +1 -1
  18. package/dist/src/providers/catalog.d.ts +1 -1
  19. package/dist/src/providers/catalog.js +1 -1
  20. package/dist/src/providers/catalog.js.map +1 -1
  21. package/dist/src/{fake-servers → servers}/http.d.ts +2 -1
  22. package/dist/src/{fake-servers → servers}/http.js +2 -1
  23. package/dist/src/servers/http.js.map +1 -0
  24. package/dist/src/servers/index.d.ts +25 -0
  25. package/dist/src/servers/index.js +22 -0
  26. package/dist/src/servers/index.js.map +1 -0
  27. package/dist/src/{fake-servers → servers}/slack.d.ts +5 -5
  28. package/dist/src/{fake-servers → servers}/slack.js +2 -2
  29. package/dist/src/servers/slack.js.map +1 -0
  30. package/dist/src/{fake-servers → servers}/telegram.d.ts +5 -5
  31. package/dist/src/{fake-servers → servers}/telegram.js +3 -3
  32. package/dist/src/servers/telegram.js.map +1 -0
  33. package/dist/src/servers/whatsapp-baileys-websocket.d.ts +27 -0
  34. package/dist/src/servers/whatsapp-baileys-websocket.js +555 -0
  35. package/dist/src/servers/whatsapp-baileys-websocket.js.map +1 -0
  36. package/dist/src/servers/whatsapp-wire/binary-node.d.ts +9 -0
  37. package/dist/src/servers/whatsapp-wire/binary-node.js +427 -0
  38. package/dist/src/servers/whatsapp-wire/binary-node.js.map +1 -0
  39. package/dist/src/servers/whatsapp-wire/crypto.d.ts +27 -0
  40. package/dist/src/servers/whatsapp-wire/crypto.js +108 -0
  41. package/dist/src/servers/whatsapp-wire/crypto.js.map +1 -0
  42. package/dist/src/servers/whatsapp-wire/handshake.d.ts +17 -0
  43. package/dist/src/servers/whatsapp-wire/handshake.js +154 -0
  44. package/dist/src/servers/whatsapp-wire/handshake.js.map +1 -0
  45. package/dist/src/servers/whatsapp-wire/signal.d.ts +5 -0
  46. package/dist/src/servers/whatsapp-wire/signal.js +24 -0
  47. package/dist/src/servers/whatsapp-wire/signal.js.map +1 -0
  48. package/dist/src/servers/whatsapp.d.ts +39 -0
  49. package/dist/src/{fake-servers → servers}/whatsapp.js +18 -166
  50. package/dist/src/servers/whatsapp.js.map +1 -0
  51. package/docs/channel-setup.md +29 -27
  52. package/package.json +10 -10
  53. package/dist/src/fake-servers/http.js.map +0 -1
  54. package/dist/src/fake-servers/index.d.ts +0 -25
  55. package/dist/src/fake-servers/index.js +0 -26
  56. package/dist/src/fake-servers/index.js.map +0 -1
  57. package/dist/src/fake-servers/slack.js.map +0 -1
  58. package/dist/src/fake-servers/telegram.js.map +0 -1
  59. package/dist/src/fake-servers/whatsapp-socket-factory.d.ts +0 -34
  60. package/dist/src/fake-servers/whatsapp-socket-factory.js +0 -225
  61. package/dist/src/fake-servers/whatsapp-socket-factory.js.map +0 -1
  62. package/dist/src/fake-servers/whatsapp.d.ts +0 -96
  63. package/dist/src/fake-servers/whatsapp.js.map +0 -1
@@ -0,0 +1,154 @@
1
+ import { Buffer } from "node:buffer";
2
+ export function decodeHandshakeMessage(data) {
3
+ const reader = new ProtoReader(data);
4
+ const message = {};
5
+ while (!reader.done()) {
6
+ const tag = reader.uint32();
7
+ const field = tag >>> 3;
8
+ if (field === 2) {
9
+ message.clientHello = decodeClientHello(reader.bytes());
10
+ }
11
+ else if (field === 4) {
12
+ message.clientFinish = decodeClientFinish(reader.bytes());
13
+ }
14
+ else {
15
+ reader.skip(tag & 7);
16
+ }
17
+ }
18
+ return message;
19
+ }
20
+ export function encodeHandshakeMessage(message) {
21
+ const writer = new ProtoWriter();
22
+ if (message.serverHello) {
23
+ writer.bytesField(3, encodeServerHello(message.serverHello));
24
+ }
25
+ return writer.finish();
26
+ }
27
+ function decodeClientHello(data) {
28
+ const reader = new ProtoReader(data);
29
+ let ephemeral;
30
+ while (!reader.done()) {
31
+ const tag = reader.uint32();
32
+ if (tag >>> 3 === 1) {
33
+ ephemeral = reader.bytes();
34
+ }
35
+ else {
36
+ reader.skip(tag & 7);
37
+ }
38
+ }
39
+ return ephemeral === undefined ? {} : { ephemeral };
40
+ }
41
+ function decodeClientFinish(data) {
42
+ const reader = new ProtoReader(data);
43
+ let payload;
44
+ let staticKey;
45
+ while (!reader.done()) {
46
+ const tag = reader.uint32();
47
+ const field = tag >>> 3;
48
+ if (field === 1) {
49
+ staticKey = reader.bytes();
50
+ }
51
+ else if (field === 2) {
52
+ payload = reader.bytes();
53
+ }
54
+ else {
55
+ reader.skip(tag & 7);
56
+ }
57
+ }
58
+ return {
59
+ ...(payload === undefined ? {} : { payload }),
60
+ ...(staticKey === undefined ? {} : { staticKey }),
61
+ };
62
+ }
63
+ function encodeServerHello(serverHello) {
64
+ const writer = new ProtoWriter();
65
+ writer.bytesField(1, serverHello.ephemeral);
66
+ writer.bytesField(2, serverHello.staticKey);
67
+ writer.bytesField(3, serverHello.payload);
68
+ return writer.finish();
69
+ }
70
+ class ProtoReader {
71
+ #offset = 0;
72
+ #buffer;
73
+ constructor(data) {
74
+ this.#buffer = Buffer.from(data);
75
+ }
76
+ done() {
77
+ return this.#offset >= this.#buffer.length;
78
+ }
79
+ bytes() {
80
+ const length = this.uint32();
81
+ this.#require(length);
82
+ const value = this.#buffer.subarray(this.#offset, this.#offset + length);
83
+ this.#offset += length;
84
+ return value;
85
+ }
86
+ skip(wireType) {
87
+ if (wireType === 0) {
88
+ this.uint32();
89
+ return;
90
+ }
91
+ if (wireType === 1) {
92
+ this.#require(8);
93
+ this.#offset += 8;
94
+ return;
95
+ }
96
+ if (wireType === 2) {
97
+ const length = this.uint32();
98
+ this.#require(length);
99
+ this.#offset += length;
100
+ return;
101
+ }
102
+ if (wireType === 5) {
103
+ this.#require(4);
104
+ this.#offset += 4;
105
+ return;
106
+ }
107
+ throw new Error(`Unsupported WhatsApp handshake wire type: ${wireType}.`);
108
+ }
109
+ uint32() {
110
+ let value = 0;
111
+ let shift = 0;
112
+ while (shift < 32) {
113
+ this.#require(1);
114
+ const byte = this.#buffer[this.#offset];
115
+ this.#offset += 1;
116
+ if (byte === undefined) {
117
+ throw new Error("Unexpected end of WhatsApp handshake protobuf.");
118
+ }
119
+ value |= (byte & 0x7f) << shift;
120
+ if ((byte & 0x80) === 0) {
121
+ return value >>> 0;
122
+ }
123
+ shift += 7;
124
+ }
125
+ throw new Error("Invalid WhatsApp handshake varint.");
126
+ }
127
+ #require(length) {
128
+ if (this.#offset + length > this.#buffer.length) {
129
+ throw new Error("Unexpected end of WhatsApp handshake protobuf.");
130
+ }
131
+ }
132
+ }
133
+ class ProtoWriter {
134
+ #parts = [];
135
+ bytesField(field, value) {
136
+ this.uint32((field << 3) | 2);
137
+ this.uint32(value.byteLength);
138
+ this.#parts.push(Buffer.from(value));
139
+ }
140
+ finish() {
141
+ return Buffer.concat(this.#parts);
142
+ }
143
+ uint32(value) {
144
+ let remaining = value >>> 0;
145
+ const bytes = [];
146
+ while (remaining > 127) {
147
+ bytes.push((remaining & 0x7f) | 0x80);
148
+ remaining >>>= 7;
149
+ }
150
+ bytes.push(remaining);
151
+ this.#parts.push(Buffer.from(bytes));
152
+ }
153
+ }
154
+ //# sourceMappingURL=handshake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../../../src/servers/whatsapp-wire/handshake.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { BinaryNode } from "./binary-node.js";
2
+ import { KEY_BUNDLE_TYPE, type KeyPair, type SignedKeyPair } from "./crypto.js";
3
+ export declare function xmppPreKey(pair: KeyPair, id: number): BinaryNode;
4
+ export declare function xmppSignedPreKey(key: SignedKeyPair): BinaryNode;
5
+ export { KEY_BUNDLE_TYPE };
@@ -0,0 +1,24 @@
1
+ import { encodeBigEndian, KEY_BUNDLE_TYPE } from "./crypto.js";
2
+ export function xmppPreKey(pair, id) {
3
+ return {
4
+ attrs: {},
5
+ content: [
6
+ { attrs: {}, content: encodeBigEndian(id, 3), tag: "id" },
7
+ { attrs: {}, content: pair.public, tag: "value" },
8
+ ],
9
+ tag: "key",
10
+ };
11
+ }
12
+ export function xmppSignedPreKey(key) {
13
+ return {
14
+ attrs: {},
15
+ content: [
16
+ { attrs: {}, content: encodeBigEndian(key.keyId, 3), tag: "id" },
17
+ { attrs: {}, content: key.keyPair.public, tag: "value" },
18
+ { attrs: {}, content: key.signature, tag: "signature" },
19
+ ],
20
+ tag: "skey",
21
+ };
22
+ }
23
+ export { KEY_BUNDLE_TYPE };
24
+ //# sourceMappingURL=signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal.js","sourceRoot":"","sources":["../../../../src/servers/whatsapp-wire/signal.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAoC,MAAM,aAAa,CAAC;AAEjG,MAAM,UAAU,UAAU,CAAC,IAAa,EAAE,EAAU;IAClD,OAAO;QACL,KAAK,EAAE,EAAE;QACT,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE;YACzD,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE;SAClD;QACD,GAAG,EAAE,KAAK;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,OAAO;QACL,KAAK,EAAE,EAAE;QACT,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE;YAChE,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE;YACxD,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE;SACxD;QACD,GAAG,EAAE,MAAM;KACZ,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { type WhatsAppBaileysInboundMessage } from "./whatsapp-baileys-websocket.js";
2
+ export type WhatsAppBaileysMessage = WhatsAppBaileysInboundMessage;
3
+ export type WhatsAppServerManifest = {
4
+ accessToken: string;
5
+ adminToken: string;
6
+ baseUrl: string;
7
+ endpoints: {
8
+ adminInboundUrl: string;
9
+ apiRoot: string;
10
+ baileysWebSocketUrl: string;
11
+ messagesUrl: string;
12
+ presenceUrl: string;
13
+ };
14
+ env: {
15
+ CRABLINE_WHATSAPP_ADMIN_TOKEN: string;
16
+ CRABLINE_WHATSAPP_ACCESS_TOKEN: string;
17
+ CRABLINE_WHATSAPP_API_ROOT: string;
18
+ CRABLINE_WHATSAPP_BAILEYS_WEB_SOCKET_URL: string;
19
+ CRABLINE_WHATSAPP_RECORDER_PATH: string;
20
+ CRABLINE_WHATSAPP_SELF_JID: string;
21
+ };
22
+ provider: "whatsapp";
23
+ recorderPath: string;
24
+ selfJid: string;
25
+ version: 1;
26
+ };
27
+ export type StartedWhatsAppServer = {
28
+ close(): Promise<void>;
29
+ manifest: WhatsAppServerManifest;
30
+ };
31
+ export type StartWhatsAppServerParams = {
32
+ accessToken?: string | undefined;
33
+ adminToken?: string | undefined;
34
+ host?: string | undefined;
35
+ port?: number | undefined;
36
+ recorderPath?: string | undefined;
37
+ selfJid?: string | undefined;
38
+ };
39
+ export declare function startWhatsAppServer(params?: StartWhatsAppServerParams): Promise<StartedWhatsAppServer>;
@@ -2,6 +2,7 @@ import { randomBytes } from "node:crypto";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { adminAuthError, hasAdminToken, jsonResponse, parseRequestBody, queryRecord, readTrimmedString, startHttpJsonServer, } from "./http.js";
5
+ import { attachWhatsAppBaileysWebSocketServer, } from "./whatsapp-baileys-websocket.js";
5
6
  const WHATSAPP_USER_JID_RE = /^\d{7,15}(?::\d+)?@s\.whatsapp\.net$/iu;
6
7
  const WHATSAPP_LEGACY_USER_JID_RE = /^\d{7,15}@c\.us$/iu;
7
8
  const WHATSAPP_GROUP_JID_RE = /^\d{5,}@g\.us$/iu;
@@ -65,42 +66,6 @@ function nextMessageId(state) {
65
66
  function waIdFromJid(jid) {
66
67
  return jid.split("@", 1)[0]?.split(":", 1)[0] ?? jid;
67
68
  }
68
- function trimTrailingSlashes(value) {
69
- let end = value.length;
70
- while (end > 0 && value.charCodeAt(end - 1) === 47) {
71
- end -= 1;
72
- }
73
- return value.slice(0, end);
74
- }
75
- function trimLeadingSlashes(value) {
76
- let start = 0;
77
- while (start < value.length && value.charCodeAt(start) === 47) {
78
- start += 1;
79
- }
80
- return value.slice(start);
81
- }
82
- function joinUrl(root, pathPart) {
83
- return `${trimTrailingSlashes(root)}/${trimLeadingSlashes(pathPart)}`;
84
- }
85
- function normalizeApiRoot(value) {
86
- return trimTrailingSlashes(value);
87
- }
88
- async function postJson(params) {
89
- const response = await params.fetchImpl(params.url, {
90
- body: JSON.stringify(params.body),
91
- headers: {
92
- authorization: `Bearer ${params.accessToken}`,
93
- "content-type": "application/json",
94
- },
95
- method: "POST",
96
- });
97
- const rawBody = await response.text();
98
- const parsed = rawBody ? JSON.parse(rawBody) : {};
99
- if (!response.ok) {
100
- throw new Error(`WhatsApp API request failed with HTTP ${response.status}.`);
101
- }
102
- return parsed;
103
- }
104
69
  function requireMessagingProduct(body) {
105
70
  const messagingProduct = readTrimmedString(body.messaging_product);
106
71
  if (messagingProduct && messagingProduct !== "whatsapp") {
@@ -142,122 +107,6 @@ function createWhatsAppMessage(params) {
142
107
  pushName: params.pushName ?? (params.fromMe ? "Test Bot" : "Test User"),
143
108
  };
144
109
  }
145
- function readBaileysTextContent(content) {
146
- const text = readTrimmedString(content.text) ?? readTrimmedString(content.conversation);
147
- if (!text) {
148
- throw new Error("WhatsApp Baileys mock supports text sends only.");
149
- }
150
- return text;
151
- }
152
- function readResponseMessage(body, fallbackJid) {
153
- const message = body.message;
154
- if (message && typeof message === "object") {
155
- return message;
156
- }
157
- const messageId = readTrimmedString(body.messages?.[0]?.id) ??
158
- readTrimmedString(body.messageId) ??
159
- "wamid.FAKEUNKNOWN";
160
- return createWhatsAppMessage({
161
- fromMe: true,
162
- id: messageId,
163
- remoteJid: fallbackJid,
164
- text: "",
165
- });
166
- }
167
- function createEventBus() {
168
- const listeners = new Map();
169
- return {
170
- emit(event, payload) {
171
- const handlers = listeners.get(event);
172
- if (!handlers?.size) {
173
- return false;
174
- }
175
- for (const handler of handlers) {
176
- handler(payload);
177
- }
178
- return true;
179
- },
180
- off(event, handler) {
181
- listeners.get(event)?.delete(handler);
182
- },
183
- on(event, handler) {
184
- const handlers = listeners.get(event) ?? new Set();
185
- handlers.add(handler);
186
- listeners.set(event, handlers);
187
- },
188
- };
189
- }
190
- export class WhatsAppBaileysMockRegistry {
191
- #busesByApiRoot = new Map();
192
- clear(apiRoot) {
193
- this.#busesByApiRoot.delete(normalizeApiRoot(apiRoot));
194
- }
195
- createSocket(config) {
196
- const fetchImpl = config.fetch ?? fetch;
197
- const selfJid = config.selfJid ?? "15550000000@s.whatsapp.net";
198
- const ev = createEventBus();
199
- this.register(config.apiRoot, ev);
200
- return {
201
- ev,
202
- async sendMessage(jid, content) {
203
- const text = readBaileysTextContent(content);
204
- const body = await postJson({
205
- accessToken: config.accessToken,
206
- body: {
207
- messaging_product: "whatsapp",
208
- text: { body: text },
209
- to: jid,
210
- type: "text",
211
- },
212
- fetchImpl,
213
- url: joinUrl(config.apiRoot, "messages"),
214
- });
215
- const message = readResponseMessage(body, jid);
216
- ev.emit("messages.upsert", { messages: [message], type: "notify" });
217
- return message;
218
- },
219
- async sendPresenceUpdate(presence, jid) {
220
- await postJson({
221
- accessToken: config.accessToken,
222
- body: { ...(jid ? { jid } : {}), presence },
223
- fetchImpl,
224
- url: joinUrl(config.apiRoot, "presence"),
225
- });
226
- ev.emit("presence.update", { jid, presence });
227
- },
228
- user: {
229
- id: selfJid,
230
- name: "Test Bot",
231
- },
232
- };
233
- }
234
- emitInbound(apiRoot, message) {
235
- const buses = this.#busesByApiRoot.get(normalizeApiRoot(apiRoot));
236
- if (!buses?.size) {
237
- return;
238
- }
239
- const payload = { messages: [message], type: "notify" };
240
- for (const ev of buses) {
241
- ev.emit("messages.upsert", payload);
242
- }
243
- }
244
- register(apiRoot, ev) {
245
- const key = normalizeApiRoot(apiRoot);
246
- const buses = this.#busesByApiRoot.get(key) ?? new Set();
247
- buses.add(ev);
248
- this.#busesByApiRoot.set(key, buses);
249
- }
250
- }
251
- export const DEFAULT_WHATSAPP_BAILEYS_MOCK_REGISTRY = new WhatsAppBaileysMockRegistry();
252
- export function createWhatsAppBaileysMockSocket(config) {
253
- const registry = config.registry ?? DEFAULT_WHATSAPP_BAILEYS_MOCK_REGISTRY;
254
- return registry.createSocket({
255
- accessToken: config.accessToken,
256
- apiRoot: config.apiRoot,
257
- fetch: config.fetch,
258
- selfJid: config.selfJid,
259
- });
260
- }
261
110
  async function handleSendMessage(params) {
262
111
  const productError = requireMessagingProduct(params.body);
263
112
  if (productError) {
@@ -305,7 +154,7 @@ async function handleAdminInbound(params) {
305
154
  const text = readTrimmedString(params.body.text);
306
155
  if (!text) {
307
156
  return {
308
- response: graphParameterError("(#100) Missing required parameter: text", "An inbound WhatsApp fake event requires text."),
157
+ response: graphParameterError("(#100) Missing required parameter: text", "An inbound WhatsApp event requires text."),
309
158
  };
310
159
  }
311
160
  const message = createWhatsAppMessage({
@@ -381,7 +230,7 @@ async function handleRequest(params) {
381
230
  }
382
231
  await appendEvent(params.state, event);
383
232
  if (result.message) {
384
- params.state.baileysRegistry.emitInbound(params.state.apiRoot, result.message);
233
+ params.state.deliverInboundMessage(result.message);
385
234
  }
386
235
  return result.response;
387
236
  }
@@ -412,16 +261,16 @@ async function handleRequest(params) {
412
261
  }
413
262
  return new Response("not found", { status: 404 });
414
263
  }
415
- export async function startWhatsAppFakeServer(params = {}) {
264
+ export async function startWhatsAppServer(params = {}) {
416
265
  const state = {
417
266
  accessToken: params.accessToken ?? "crabline-whatsapp-access-token",
418
267
  adminToken: params.adminToken ?? randomBytes(24).toString("hex"),
419
268
  apiRoot: "",
420
- baileysRegistry: params.baileysRegistry ?? DEFAULT_WHATSAPP_BAILEYS_MOCK_REGISTRY,
269
+ deliverInboundMessage: () => undefined,
421
270
  displayPhoneNumber: "15550000000",
422
271
  nextMessageId: 1,
423
272
  phoneNumberId: "TEST_PHONE_NUMBER_ID",
424
- recorderPath: params.recorderPath ?? path.resolve(".crabline", "fake-servers", "whatsapp.jsonl"),
273
+ recorderPath: params.recorderPath ?? path.resolve(".crabline", "servers", "whatsapp.jsonl"),
425
274
  selfJid: params.selfJid ?? "15550000000@s.whatsapp.net",
426
275
  };
427
276
  const host = params.host ?? "127.0.0.1";
@@ -433,20 +282,21 @@ export async function startWhatsAppFakeServer(params = {}) {
433
282
  });
434
283
  const baseUrl = httpServer.baseUrl;
435
284
  const apiRoot = `${baseUrl}/crabline/whatsapp`;
285
+ const baileysWebSocketUrl = `${baseUrl.replace(/^http/u, "ws")}/crabline/whatsapp/ws/chat?access_token=${encodeURIComponent(state.accessToken)}`;
436
286
  state.apiRoot = apiRoot;
287
+ const baileysWebSocketServer = attachWhatsAppBaileysWebSocketServer({
288
+ accessToken: state.accessToken,
289
+ appendEvent: (event) => appendEvent(state, event),
290
+ httpServer: httpServer.server,
291
+ path: "/crabline/whatsapp/ws/chat",
292
+ selfJid: state.selfJid,
293
+ });
294
+ state.deliverInboundMessage = (message) => baileysWebSocketServer.deliverInboundMessage(message);
437
295
  return {
438
296
  async close() {
439
- state.baileysRegistry.clear(apiRoot);
297
+ await baileysWebSocketServer.close();
440
298
  await httpServer.close();
441
299
  },
442
- createBaileysMockSocket(config = {}) {
443
- return state.baileysRegistry.createSocket({
444
- accessToken: state.accessToken,
445
- apiRoot,
446
- selfJid: state.selfJid,
447
- ...config,
448
- });
449
- },
450
300
  manifest: {
451
301
  accessToken: state.accessToken,
452
302
  adminToken: state.adminToken,
@@ -454,6 +304,7 @@ export async function startWhatsAppFakeServer(params = {}) {
454
304
  endpoints: {
455
305
  adminInboundUrl: `${apiRoot}/inbound`,
456
306
  apiRoot,
307
+ baileysWebSocketUrl,
457
308
  messagesUrl: `${apiRoot}/messages`,
458
309
  presenceUrl: `${apiRoot}/presence`,
459
310
  },
@@ -461,6 +312,7 @@ export async function startWhatsAppFakeServer(params = {}) {
461
312
  CRABLINE_WHATSAPP_ADMIN_TOKEN: state.adminToken,
462
313
  CRABLINE_WHATSAPP_ACCESS_TOKEN: state.accessToken,
463
314
  CRABLINE_WHATSAPP_API_ROOT: apiRoot,
315
+ CRABLINE_WHATSAPP_BAILEYS_WEB_SOCKET_URL: baileysWebSocketUrl,
464
316
  CRABLINE_WHATSAPP_RECORDER_PATH: state.recorderPath,
465
317
  CRABLINE_WHATSAPP_SELF_JID: state.selfJid,
466
318
  },
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp.js","sourceRoot":"","sources":["../../../src/servers/whatsapp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,iBAAiB,EACjB,mBAAmB,GAEpB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,oCAAoC,GAErC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,oBAAoB,GAAG,wCAAwC,CAAC;AACtE,MAAM,2BAA2B,GAAG,oBAAoB,CAAC;AACzD,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AACjD,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAgE3C,SAAS,UAAU,CAAC,KAAK,GAA4B,EAAE;IACrD,OAAO,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,UAAU,CAAC,MAMnB;IACC,OAAO,YAAY,CACjB;QACE,KAAK,EAAE;YACL,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,GAAG;YACxB,GAAG,CAAC,MAAM,CAAC,OAAO;gBAChB,CAAC,CAAC;oBACE,UAAU,EAAE;wBACV,OAAO,EAAE,MAAM,CAAC,OAAO;wBACvB,iBAAiB,EAAE,UAAU;qBAC9B;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,UAAU,EAAE,aAAa;YACzB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,gBAAgB;SACtC;QACD,EAAE,EAAE,KAAK;KACV,EACD,MAAM,CAAC,MAAM,IAAI,GAAG,CACrB,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAe,EAAE,OAAgB;IAC5D,OAAO,UAAU,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,cAAc;IACrB,OAAO,UAAU,CAAC;QAChB,IAAI,EAAE,GAAG;QACT,OAAO,EAAE,6BAA6B;QACtC,MAAM,EAAE,GAAG;KACZ,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,KAA0B,EAAE,KAAyB;IAC9E,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,MAAM,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,CACL,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC;QAChC,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC;QACvC,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC;QACjC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAC5B,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc,EAAE,KAAa;IACvD,MAAM,WAAW,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,CAAC,WAAW,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;QAChD,OAAO,mBAAmB,CACxB,6BAA6B,KAAK,EAAE,EACpC,GAAG,KAAK,wFAAwF,CACjG,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,SAAS,WAAW,CAAC,OAAwB,EAAE,KAA0B;IACvE,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;IACpD,OAAO,CACL,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,CAAC,IAAI,EAAE,KAAK,UAAU,KAAK,CAAC,WAAW,EAAE,CAC5F,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAA0B;IAC/C,OAAO,aAAa,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACvE,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;AACvD,CAAC;AAED,SAAS,uBAAuB,CAAC,IAA6B;IAC5D,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACnE,IAAI,gBAAgB,IAAI,gBAAgB,KAAK,UAAU,EAAE,CAAC;QACxD,OAAO,mBAAmB,CACxB,6CAA6C,EAC7C,uCAAuC,CACxC,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,mBAAmB,CAAC,IAA6B;IACxD,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,mBAAmB,CACxB,iCAAiC,EACjC,+DAA+D,CAChE,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC;IAC9B,MAAM,QAAQ,GACZ,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ;QAC5C,CAAC,CAAC,iBAAiB,CAAE,WAAuC,CAAC,IAAI,CAAC;QAClE,CAAC,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAC7B,MAAM,WAAW,GACf,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;QACpC,CAAC,CAAC,iBAAiB,CAAE,OAAmC,CAAC,IAAI,CAAC;QAC9D,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,IAAI,GAAG,QAAQ,IAAI,WAAW,CAAC;IACrC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,mBAAmB,CACxB,8CAA8C,EAC9C,0CAA0C,CAC3C,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,qBAAqB,CAAC,MAO9B;IACC,OAAO;QACL,GAAG,EAAE;YACH,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B;QACD,OAAO,EAAE;YACP,YAAY,EAAE,MAAM,CAAC,IAAI;SAC1B;QACD,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAC/C,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC;KACxE,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,MAGhC;IACC,MAAM,YAAY,GAAG,uBAAuB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1D,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,MAAM,EAAE,GAAG,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACvE,IAAI,EAAE,YAAY,QAAQ,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,IAAI,YAAY,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,qBAAqB,CAAC;QACpC,MAAM,EAAE,IAAI;QACZ,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC;QAC/B,SAAS,EAAE,EAAE;QACb,IAAI;KACL,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,OAAO,UAAU,CAAC;QAChB,QAAQ,EAAE;YACR;gBACE,KAAK,EAAE,EAAE;gBACT,KAAK,EAAE,IAAI;aACZ;SACF;QACD,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,OAAO;QACP,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE;QACzB,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QAClC,iBAAiB,EAAE,UAAU;QAC7B,KAAK,EAAE,EAAE;KACV,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,MAGjC;IACC,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACzF,IAAI,OAAO,YAAY,QAAQ,EAAE,CAAC;QAChC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC/B,CAAC;IACD,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC7F,IAAI,SAAS,YAAY,QAAQ,EAAE,CAAC;QAClC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;IACD,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO;YACL,QAAQ,EAAE,mBAAmB,CAC3B,yCAAyC,EACzC,0CAA0C,CAC3C;SACF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,qBAAqB,CAAC;QACpC,MAAM,EAAE,KAAK;QACb,EAAE,EAAE,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC;QAC3E,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,WAAW;QAChE,SAAS,EAAE,OAAO;QAClB,SAAS;QACT,IAAI;KACL,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG;QACd,KAAK,EAAE;YACL;gBACE,OAAO,EAAE;oBACP;wBACE,KAAK,EAAE,UAAU;wBACjB,KAAK,EAAE;4BACL,QAAQ,EAAE;gCACR;oCACE,OAAO,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,WAAW,EAAE;oCACzE,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC;iCAC9B;6BACF;4BACD,QAAQ,EAAE;gCACR;oCACE,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC;oCAC5B,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE;oCAClB,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;oCACpB,SAAS;oCACT,IAAI,EAAE,MAAM;iCACb;6BACF;4BACD,iBAAiB,EAAE,UAAU;4BAC7B,QAAQ,EAAE;gCACR,oBAAoB,EAAE,MAAM,CAAC,KAAK,CAAC,kBAAkB;gCACrD,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa;6BAC5C;yBACF;qBACF;iBACF;gBACD,EAAE,EAAE,WAAW;aAChB;SACF;QACD,MAAM,EAAE,2BAA2B;KACpC,CAAC;IACF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,MAAgE;IAC3F,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;IAEnE,IAAI,GAAG,CAAC,QAAQ,KAAK,2BAA2B,EAAE,CAAC;QACjD,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,KAAK,4BAA4B,EAAE,CAAC;QAClD,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACrC,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5D,OAAO,cAAc,EAAE,CAAC;QAC1B,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QACvE,MAAM,KAAK,GAA0B;YACnC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,IAAI;YACJ,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM;YAC7B,IAAI,EAAE,GAAG,CAAC,QAAQ;YAClB,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;YACvB,IAAI,EAAE,OAAO;SACd,CAAC;QACF,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QACjC,CAAC;QACD,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACvC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,MAAM,CAAC,QAAQ,CAAC;IACzB,CAAC;IAED,MAAM,IAAI,GACR,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9F,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE;QAC9B,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,IAAI;QACJ,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,KAAK;QACtC,IAAI,EAAE,GAAG,CAAC,QAAQ;QAClB,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;QACvB,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;IAEH,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,cAAc,EAAE,CAAC;IAC1B,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,6BAA6B,EAAE,CAAC;QACnD,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACrC,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,MAAM,iBAAiB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,6BAA6B,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC;QACjE,IAAI,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5E,OAAO,mBAAmB,CACxB,oCAAoC,EACpC,gEAAgE,CACjE,CAAC;QACJ,CAAC;QACD,OAAO,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAM,GAA8B,EAAE;IAEtC,MAAM,KAAK,GAAwB;QACjC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,gCAAgC;QACnE,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;QAChE,OAAO,EAAE,EAAE;QACX,qBAAqB,EAAE,GAAG,EAAE,CAAC,SAAS;QACtC,kBAAkB,EAAE,aAAa;QACjC,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,sBAAsB;QACrC,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC;QAC3F,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,4BAA4B;KACxD,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,WAAW,CAAC;IACxC,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC;QAC3C,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACtD,IAAI;QACJ,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC;QACtB,UAAU,EAAE,UAAU;KACvB,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;IACnC,MAAM,OAAO,GAAG,GAAG,OAAO,oBAAoB,CAAC;IAC/C,MAAM,mBAAmB,GAAG,GAAG,OAAO,CAAC,OAAO,CAC5C,QAAQ,EACR,IAAI,CACL,2CAA2C,kBAAkB,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;IACpF,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,MAAM,sBAAsB,GAAG,oCAAoC,CAAC;QAClE,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC;QACjD,UAAU,EAAE,UAAU,CAAC,MAAM;QAC7B,IAAI,EAAE,4BAA4B;QAClC,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC;IACH,KAAK,CAAC,qBAAqB,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,sBAAsB,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACjG,OAAO;QACL,KAAK,CAAC,KAAK;YACT,MAAM,sBAAsB,CAAC,KAAK,EAAE,CAAC;YACrC,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;QAC3B,CAAC;QACD,QAAQ,EAAE;YACR,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,OAAO;YACP,SAAS,EAAE;gBACT,eAAe,EAAE,GAAG,OAAO,UAAU;gBACrC,OAAO;gBACP,mBAAmB;gBACnB,WAAW,EAAE,GAAG,OAAO,WAAW;gBAClC,WAAW,EAAE,GAAG,OAAO,WAAW;aACnC;YACD,GAAG,EAAE;gBACH,6BAA6B,EAAE,KAAK,CAAC,UAAU;gBAC/C,8BAA8B,EAAE,KAAK,CAAC,WAAW;gBACjD,0BAA0B,EAAE,OAAO;gBACnC,wCAAwC,EAAE,mBAAmB;gBAC7D,+BAA+B,EAAE,KAAK,CAAC,YAAY;gBACnD,0BAA0B,EAAE,KAAK,CAAC,OAAO;aAC1C;YACD,QAAQ,EAAE,UAAU;YACpB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,OAAO,EAAE,CAAC;SACX;KACF,CAAC;AACJ,CAAC"}
@@ -4,7 +4,7 @@ Crabline is a local mock service for OpenClaw channel contracts. It has two
4
4
  surfaces:
5
5
 
6
6
  - fixture-level local mocks used directly by the Crabline CLI
7
- - fake provider servers that OpenClaw live adapters can target
7
+ - local provider servers that OpenClaw live adapters can target
8
8
 
9
9
  Live channel testing belongs in OpenClaw's live channel adapters. Crabline
10
10
  belongs in deterministic smoke CI and local QA where the test needs
@@ -70,11 +70,11 @@ Provider credential fields such as `botToken`, `accessToken`, `baseURL`, or
70
70
  `serverUrl` are optional mock metadata. They are not required for local mock
71
71
  execution.
72
72
 
73
- ## Fake Provider Servers
73
+ ## Local Provider Servers
74
74
 
75
- Fake provider servers sit below OpenClaw's normal channel adapters. QA starts the
75
+ Local provider servers sit below OpenClaw's normal channel adapters. QA starts the
76
76
  server, writes the emitted runtime manifest into OpenClaw config/env, and then
77
- OpenClaw talks to the local fake provider instead of the public provider.
77
+ OpenClaw talks to the local provider instead of the public provider.
78
78
 
79
79
  Slack:
80
80
 
@@ -145,34 +145,36 @@ crabline --json serve whatsapp --ready-file .crabline/whatsapp-server.json
145
145
 
146
146
  Manifest fields:
147
147
 
148
- - `endpoints.apiRoot`: Crabline WhatsApp fake provider API root
149
- - `accessToken`: bearer token for fake provider requests
148
+ - `endpoints.apiRoot`: Crabline WhatsApp local provider API root
149
+ - `endpoints.baileysWebSocketUrl`: Baileys-compatible WebSocket URL for
150
+ `waWebSocketUrl`, including the local provider access token query parameter
151
+ - `accessToken`: bearer token for local provider requests
150
152
  - `adminToken`: value for the `X-Crabline-Admin-Token` header on admin ingress
151
- - `selfJid`: fake authenticated WhatsApp user JID
152
- - `env.CRABLINE_WHATSAPP_ACCESS_TOKEN`: access token for the runtime socket
153
- factory
154
- - `env.CRABLINE_WHATSAPP_API_ROOT`: API root for the runtime socket factory
155
- - `env.CRABLINE_WHATSAPP_RECORDER_PATH`: recorder file followed by the runtime
156
- socket factory for cross-process inbound delivery
157
- - `env.CRABLINE_WHATSAPP_SELF_JID`: fake authenticated WhatsApp user JID for
158
- the runtime socket factory
153
+ - `selfJid`: local authenticated WhatsApp user JID
154
+ - `env.CRABLINE_WHATSAPP_ACCESS_TOKEN`: same value as `accessToken`
155
+ - `env.CRABLINE_WHATSAPP_API_ROOT`: same value as `endpoints.apiRoot`
156
+ - `env.CRABLINE_WHATSAPP_BAILEYS_WEB_SOCKET_URL`: same value as
157
+ `endpoints.baileysWebSocketUrl`
158
+ - `env.CRABLINE_WHATSAPP_RECORDER_PATH`: recorder file for HTTP traffic and
159
+ Baileys WebSocket stanzas
160
+ - `env.CRABLINE_WHATSAPP_SELF_JID`: local authenticated WhatsApp user JID
159
161
  - `endpoints.adminInboundUrl`: authenticated admin ingress for test user
160
- messages; subscribed Baileys mock sockets receive them as `messages.upsert`
161
- - `endpoints.messagesUrl`: text send endpoint used by the Baileys-shaped mock
162
- - `endpoints.presenceUrl`: presence endpoint used by `sendPresenceUpdate`
163
- - `recorderPath`: JSONL provider traffic recorder
164
-
165
- Use the package subpath `@openclaw/crabline/whatsapp-socket-factory` when a
166
- runtime needs a Baileys-style module with `createWhatsAppSocket()`. The module
167
- reads the `CRABLINE_WHATSAPP_*` env vars emitted by the manifest, sends outbound
168
- traffic to the fake provider API, and follows the recorder for inbound
169
- `messages.upsert` events. The admin token is generated randomly unless
170
- `--admin-token <token>` is provided.
162
+ messages using the WhatsApp Business webhook payload shape
163
+ - `endpoints.messagesUrl`: text send endpoint for Graph-style callers
164
+ - `endpoints.presenceUrl`: presence endpoint for Graph-style callers
165
+ - `recorderPath`: JSONL provider traffic recorder for HTTP traffic and Baileys
166
+ WebSocket stanzas
167
+
168
+ Pass `endpoints.baileysWebSocketUrl` to Baileys as `waWebSocketUrl` when a
169
+ runtime needs to connect through the local provider. The local server completes
170
+ the Baileys Noise handshake, serves bootstrap/device/prekey queries, and records
171
+ encrypted outbound WebSocket stanzas. The WebSocket endpoint rejects clients
172
+ that do not present the access token embedded in the manifest URL. The admin
173
+ token is generated randomly unless `--admin-token <token>` is provided.
171
174
 
172
175
  OpenClaw bridge callers should post injected user messages with the
173
176
  `providerUrl`, `providerHeaders`, and `providerBody` returned by
174
- `createOpenClawCrablineInbound()`. For direct in-process tests, the started fake
175
- server also exposes `createBaileysMockSocket()`.
177
+ `createOpenClawCrablineInbound()`.
176
178
  The admin ingress accepts JSON like:
177
179
 
178
180
  ```json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/crabline",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Standalone CLI for deterministic messaging provider E2E tests",
5
5
  "homepage": "https://github.com/openclaw/crabline#readme",
6
6
  "bugs": {
@@ -27,10 +27,6 @@
27
27
  ".": {
28
28
  "types": "./dist/src/index.d.ts",
29
29
  "import": "./dist/src/index.js"
30
- },
31
- "./whatsapp-socket-factory": {
32
- "types": "./dist/src/fake-servers/whatsapp-socket-factory.d.ts",
33
- "import": "./dist/src/fake-servers/whatsapp-socket-factory.js"
34
30
  }
35
31
  },
36
32
  "scripts": {
@@ -53,18 +49,22 @@
53
49
  "check": "pnpm verify"
54
50
  },
55
51
  "dependencies": {
56
- "commander": "^14.0.3",
52
+ "commander": "^15.0.0",
53
+ "curve25519-js": "^0.0.4",
57
54
  "picocolors": "^1.1.1",
55
+ "ws": "^8.21.0",
58
56
  "yaml": "^2.9.0",
59
57
  "zod": "^4.4.3"
60
58
  },
61
59
  "devDependencies": {
62
- "@types/node": "^25.9.2",
63
- "@typescript/native-preview": "7.0.0-dev.20260503.1",
60
+ "@types/node": "^26.0.1",
61
+ "@types/ws": "^8.18.1",
62
+ "@typescript/native-preview": "7.0.0-dev.20260629.1",
64
63
  "@vitest/coverage-v8": "^4.1.8",
65
- "oxfmt": "^0.47.0",
64
+ "baileys": "7.0.0-rc13",
65
+ "oxfmt": "^0.56.0",
66
66
  "oxlint": "^1.69.0",
67
- "oxlint-tsgolint": "^0.22.1",
67
+ "oxlint-tsgolint": "^0.23.0",
68
68
  "tsx": "^4.22.4",
69
69
  "typescript": "^6.0.3",
70
70
  "vitest": "^4.1.8"