@openclaw/crabline 0.1.6 → 0.1.8

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 (47) hide show
  1. package/README.md +107 -2
  2. package/dist/src/cli/program.js +63 -4
  3. package/dist/src/cli/program.js.map +1 -1
  4. package/dist/src/index.d.ts +8 -0
  5. package/dist/src/index.js +4 -0
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/openclaw/bridges/matrix.d.ts +1 -0
  8. package/dist/src/openclaw/bridges/matrix.js +134 -0
  9. package/dist/src/openclaw/bridges/matrix.js.map +1 -0
  10. package/dist/src/openclaw/bridges/mattermost.d.ts +1 -0
  11. package/dist/src/openclaw/bridges/mattermost.js +116 -0
  12. package/dist/src/openclaw/bridges/mattermost.js.map +1 -0
  13. package/dist/src/openclaw/bridges/signal.d.ts +1 -0
  14. package/dist/src/openclaw/bridges/signal.js +112 -0
  15. package/dist/src/openclaw/bridges/signal.js.map +1 -0
  16. package/dist/src/openclaw/bridges/telegram.js +11 -0
  17. package/dist/src/openclaw/bridges/telegram.js.map +1 -1
  18. package/dist/src/openclaw/bridges/zalo.d.ts +1 -0
  19. package/dist/src/openclaw/bridges/zalo.js +102 -0
  20. package/dist/src/openclaw/bridges/zalo.js.map +1 -0
  21. package/dist/src/openclaw/shared.d.ts +3 -0
  22. package/dist/src/openclaw/shared.js.map +1 -1
  23. package/dist/src/openclaw.js +8 -0
  24. package/dist/src/openclaw.js.map +1 -1
  25. package/dist/src/providers/catalog.js +1 -1
  26. package/dist/src/providers/catalog.js.map +1 -1
  27. package/dist/src/servers/http.js +1 -1
  28. package/dist/src/servers/http.js.map +1 -1
  29. package/dist/src/servers/index.d.ts +28 -4
  30. package/dist/src/servers/index.js +25 -1
  31. package/dist/src/servers/index.js.map +1 -1
  32. package/dist/src/servers/matrix.d.ts +37 -0
  33. package/dist/src/servers/matrix.js +387 -0
  34. package/dist/src/servers/matrix.js.map +1 -0
  35. package/dist/src/servers/mattermost.d.ts +33 -0
  36. package/dist/src/servers/mattermost.js +361 -0
  37. package/dist/src/servers/mattermost.js.map +1 -0
  38. package/dist/src/servers/signal.d.ts +27 -0
  39. package/dist/src/servers/signal.js +201 -0
  40. package/dist/src/servers/signal.js.map +1 -0
  41. package/dist/src/servers/telegram.js +8 -0
  42. package/dist/src/servers/telegram.js.map +1 -1
  43. package/dist/src/servers/zalo.d.ts +31 -0
  44. package/dist/src/servers/zalo.js +276 -0
  45. package/dist/src/servers/zalo.js.map +1 -0
  46. package/docs/channel-setup.md +129 -3
  47. package/package.json +5 -4
@@ -0,0 +1,361 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { WebSocket, WebSocketServer } from "ws";
5
+ import { adminAuthError, hasAdminToken, jsonResponse, parseRequestBody, queryRecord, readTrimmedString, startHttpJsonServer, } from "./http.js";
6
+ export function mattermostId(value) {
7
+ return createHash("sha256").update(value).digest("hex").slice(0, 26);
8
+ }
9
+ async function appendEvent(state, event) {
10
+ await fs.mkdir(path.dirname(state.recorderPath), { recursive: true });
11
+ await fs.appendFile(state.recorderPath, `${JSON.stringify(event)}\n`, "utf8");
12
+ }
13
+ function authorized(request, token) {
14
+ const [scheme, value] = request.headers.authorization?.trim().split(/\s+/, 2) ?? [];
15
+ return scheme?.toLowerCase() === "bearer" && value === token;
16
+ }
17
+ function mattermostError(message, status) {
18
+ return jsonResponse({
19
+ id: `api.context.${status}`,
20
+ message,
21
+ request_id: "",
22
+ status_code: status,
23
+ }, status);
24
+ }
25
+ function eventBroadcast(params) {
26
+ return {
27
+ channel_id: params.channelId ?? "",
28
+ omit_users: params.omitUsers ?? null,
29
+ team_id: "",
30
+ user_id: params.userId ?? "",
31
+ };
32
+ }
33
+ function postEvent(event, post, senderName, channelType) {
34
+ return {
35
+ event,
36
+ data: {
37
+ channel_display_name: post.channel_id,
38
+ channel_id: post.channel_id,
39
+ channel_name: post.channel_id,
40
+ channel_type: channelType,
41
+ post: JSON.stringify(post),
42
+ sender_name: senderName,
43
+ },
44
+ broadcast: eventBroadcast({ channelId: post.channel_id, userId: post.user_id }),
45
+ };
46
+ }
47
+ function sendEvent(state, client, event) {
48
+ const seq = state.websocketClients.get(client);
49
+ if (seq === undefined || client.readyState !== WebSocket.OPEN) {
50
+ return;
51
+ }
52
+ client.send(JSON.stringify({ ...event, seq }));
53
+ state.websocketClients.set(client, seq + 1);
54
+ }
55
+ function broadcast(state, event) {
56
+ if (state.websocketClients.size === 0) {
57
+ state.pendingEvents.push(event);
58
+ return;
59
+ }
60
+ for (const client of state.websocketClients.keys()) {
61
+ sendEvent(state, client, event);
62
+ }
63
+ }
64
+ function createPost(params) {
65
+ const id = mattermostId(`post-${params.state.nextPost++}`);
66
+ const post = {
67
+ channel_id: params.channelId,
68
+ create_at: Date.now(),
69
+ id,
70
+ message: params.message,
71
+ root_id: params.rootId ?? "",
72
+ type: "",
73
+ user_id: params.userId,
74
+ };
75
+ params.state.posts.set(id, post);
76
+ return post;
77
+ }
78
+ async function handleAdminInbound(params) {
79
+ const channelId = readTrimmedString(params.body.channelId ?? params.body.channel_id);
80
+ const senderId = readTrimmedString(params.body.senderId ?? params.body.user_id);
81
+ const text = readTrimmedString(params.body.text ?? params.body.message);
82
+ if (!channelId || !senderId || !text) {
83
+ return jsonResponse({ error: "channelId, senderId, and text are required", ok: false }, 400);
84
+ }
85
+ const senderName = readTrimmedString(params.body.senderName) ?? senderId;
86
+ const channelType = readTrimmedString(params.body.channelType) ?? "D";
87
+ params.state.users.set(senderId, { id: senderId, update_at: Date.now(), username: senderName });
88
+ params.state.channels.set(channelId, {
89
+ display_name: channelId,
90
+ id: channelId,
91
+ name: channelId,
92
+ type: channelType,
93
+ });
94
+ const post = createPost({
95
+ channelId,
96
+ message: text,
97
+ rootId: readTrimmedString(params.body.rootId ?? params.body.root_id),
98
+ state: params.state,
99
+ userId: senderId,
100
+ });
101
+ broadcast(params.state, postEvent("posted", post, senderName, channelType));
102
+ return jsonResponse({ ok: true, post });
103
+ }
104
+ async function handleApi(params) {
105
+ const { body, method, path: apiPath, state } = params;
106
+ if (method === "GET" && apiPath === "/users/me") {
107
+ return jsonResponse(state.users.get(state.botUserId));
108
+ }
109
+ const usernameMatch = /^\/users\/username\/([^/]+)$/u.exec(apiPath);
110
+ if (method === "GET" && usernameMatch?.[1]) {
111
+ const username = decodeURIComponent(usernameMatch[1]);
112
+ const user = [...state.users.values()].find((entry) => entry.username === username);
113
+ return user ? jsonResponse(user) : mattermostError("User not found", 404);
114
+ }
115
+ const userMatch = /^\/users\/([^/]+)$/u.exec(apiPath);
116
+ if (method === "GET" && userMatch?.[1]) {
117
+ const user = state.users.get(userMatch[1]);
118
+ return user ? jsonResponse(user) : mattermostError("User not found", 404);
119
+ }
120
+ const channelMatch = /^\/channels\/([^/]+)$/u.exec(apiPath);
121
+ if (method === "GET" && channelMatch?.[1]) {
122
+ const channel = state.channels.get(channelMatch[1]);
123
+ return channel ? jsonResponse(channel) : mattermostError("Channel not found", 404);
124
+ }
125
+ if (method === "POST" && apiPath === "/channels/direct") {
126
+ const userIds = Array.isArray(body) ? body : [];
127
+ if (userIds.length !== 2 || userIds.some((value) => typeof value !== "string" || !value)) {
128
+ return mattermostError("Two user IDs are required", 400);
129
+ }
130
+ const channelId = mattermostId(`dm:${userIds.map(String).sort().join(":")}`);
131
+ const channel = { display_name: "", id: channelId, name: channelId, type: "D" };
132
+ state.channels.set(channelId, channel);
133
+ return jsonResponse(channel, 201);
134
+ }
135
+ if (method === "POST" && apiPath === "/users/me/typing") {
136
+ const channelId = readTrimmedString(body.channel_id);
137
+ if (!channelId) {
138
+ return mattermostError("channel_id is required", 400);
139
+ }
140
+ broadcast(state, {
141
+ broadcast: eventBroadcast({
142
+ channelId,
143
+ omitUsers: { [state.botUserId]: true },
144
+ }),
145
+ data: {
146
+ channel_id: channelId,
147
+ ...(readTrimmedString(body.parent_id)
148
+ ? { parent_id: readTrimmedString(body.parent_id) }
149
+ : {}),
150
+ user_id: state.botUserId,
151
+ },
152
+ event: "typing",
153
+ });
154
+ return jsonResponse({});
155
+ }
156
+ if (method === "POST" && apiPath === "/posts") {
157
+ const channelId = readTrimmedString(body.channel_id);
158
+ const message = readTrimmedString(body.message);
159
+ if (!channelId || !message) {
160
+ return mattermostError("channel_id and message are required", 400);
161
+ }
162
+ const post = createPost({
163
+ channelId,
164
+ message,
165
+ rootId: readTrimmedString(body.root_id),
166
+ state,
167
+ userId: state.botUserId,
168
+ });
169
+ const channelType = state.channels.get(channelId)?.type ?? "O";
170
+ broadcast(state, postEvent("posted", post, state.botUsername, channelType));
171
+ return jsonResponse(post, 201);
172
+ }
173
+ const postMatch = /^\/posts\/([^/]+)$/u.exec(apiPath);
174
+ if (postMatch?.[1] && method === "PUT") {
175
+ const post = state.posts.get(postMatch[1]);
176
+ if (!post) {
177
+ return mattermostError("Post not found", 404);
178
+ }
179
+ const updated = { ...post, message: readTrimmedString(body.message) ?? post.message };
180
+ state.posts.set(updated.id, updated);
181
+ broadcast(state, postEvent("post_edited", updated, state.botUsername, state.channels.get(updated.channel_id)?.type ?? "O"));
182
+ return jsonResponse(updated);
183
+ }
184
+ if (postMatch?.[1] && method === "DELETE") {
185
+ const post = state.posts.get(postMatch[1]);
186
+ if (!post) {
187
+ return mattermostError("Post not found", 404);
188
+ }
189
+ state.posts.delete(post.id);
190
+ broadcast(state, postEvent("post_deleted", post, state.botUsername, state.channels.get(post.channel_id)?.type ?? "O"));
191
+ return new Response(null, { status: 204 });
192
+ }
193
+ return mattermostError("Not found", 404);
194
+ }
195
+ async function handleRequest(request, state) {
196
+ const url = new URL(request.url ?? "/", "http://localhost");
197
+ const method = request.method ?? "GET";
198
+ if (url.pathname === "/crabline/mattermost/inbound" && method === "POST") {
199
+ if (!hasAdminToken(request, state.adminToken)) {
200
+ return adminAuthError();
201
+ }
202
+ const body = await parseRequestBody(request);
203
+ await appendEvent(state, {
204
+ at: new Date().toISOString(),
205
+ body,
206
+ method,
207
+ path: url.pathname,
208
+ query: queryRecord(url),
209
+ type: "admin",
210
+ });
211
+ return await handleAdminInbound({ body, state });
212
+ }
213
+ if (!url.pathname.startsWith("/api/v4/")) {
214
+ return new Response("not found", { status: 404 });
215
+ }
216
+ if (!authorized(request, state.botToken)) {
217
+ return mattermostError("Invalid or missing token", 401);
218
+ }
219
+ const body = method === "GET" || method === "DELETE" ? {} : await parseRequestBody(request);
220
+ await appendEvent(state, {
221
+ at: new Date().toISOString(),
222
+ ...(Object.keys(body).length > 0 ? { body } : {}),
223
+ method,
224
+ path: url.pathname,
225
+ query: queryRecord(url),
226
+ type: "api",
227
+ });
228
+ return await handleApi({ body, method, path: url.pathname.slice("/api/v4".length), state });
229
+ }
230
+ function attachWebSocketServer(params) {
231
+ const websocketServer = new WebSocketServer({ noServer: true });
232
+ const onUpgrade = (request, socket, head) => {
233
+ if (new URL(request.url ?? "/", "http://localhost").pathname !== "/api/v4/websocket") {
234
+ socket.destroy();
235
+ return;
236
+ }
237
+ websocketServer.handleUpgrade(request, socket, head, (client) => {
238
+ websocketServer.emit("connection", client, request);
239
+ });
240
+ };
241
+ params.server.on("upgrade", onUpgrade);
242
+ websocketServer.on("connection", (client) => {
243
+ client.on("message", (raw) => {
244
+ let message;
245
+ try {
246
+ message = JSON.parse(raw.toString());
247
+ }
248
+ catch {
249
+ client.close(1003, "invalid json");
250
+ return;
251
+ }
252
+ const seq = message.seq ?? 0;
253
+ if (!params.state.websocketClients.has(client)) {
254
+ if (message.action !== "authentication_challenge" ||
255
+ message.data?.token !== params.state.botToken) {
256
+ client.send(JSON.stringify({
257
+ error: { id: "api.context.unauthorized", message: "Authentication failed" },
258
+ seq_reply: seq,
259
+ status: "FAIL",
260
+ }));
261
+ return;
262
+ }
263
+ params.state.websocketClients.set(client, 0);
264
+ client.send(JSON.stringify({ seq_reply: seq, status: "OK" }));
265
+ sendEvent(params.state, client, {
266
+ broadcast: eventBroadcast({ userId: params.state.botUserId }),
267
+ data: {
268
+ connection_id: mattermostId(`connection-${Date.now()}-${Math.random()}`),
269
+ server_version: "crabline-mattermost.1",
270
+ },
271
+ event: "hello",
272
+ });
273
+ for (const event of params.state.pendingEvents.splice(0)) {
274
+ sendEvent(params.state, client, event);
275
+ }
276
+ return;
277
+ }
278
+ if (message.action === "user_typing" && message.data?.channel_id) {
279
+ broadcast(params.state, {
280
+ broadcast: eventBroadcast({
281
+ channelId: message.data.channel_id,
282
+ omitUsers: { [params.state.botUserId]: true },
283
+ }),
284
+ data: {
285
+ parent_id: message.data.parent_id ?? "",
286
+ user_id: params.state.botUserId,
287
+ },
288
+ event: "typing",
289
+ });
290
+ client.send(JSON.stringify({ seq_reply: seq, status: "OK" }));
291
+ return;
292
+ }
293
+ if (message.action === "ping") {
294
+ client.send(JSON.stringify({ data: { text: "pong" }, seq_reply: seq, status: "OK" }));
295
+ return;
296
+ }
297
+ client.send(JSON.stringify({
298
+ error: { id: "api.websocket.invalid_action", message: "Unsupported action" },
299
+ seq_reply: seq,
300
+ status: "FAIL",
301
+ }));
302
+ });
303
+ client.once("close", () => params.state.websocketClients.delete(client));
304
+ });
305
+ return async () => {
306
+ params.server.off("upgrade", onUpgrade);
307
+ for (const client of websocketServer.clients) {
308
+ client.close();
309
+ }
310
+ await new Promise((resolve, reject) => websocketServer.close((error) => (error ? reject(error) : resolve())));
311
+ };
312
+ }
313
+ export async function startMattermostServer(params = {}) {
314
+ const botUserId = params.botUserId ?? mattermostId("crabline-mattermost-bot");
315
+ const botUsername = params.botUsername ?? "crabline_bot";
316
+ const state = {
317
+ adminToken: params.adminToken ?? randomBytes(24).toString("base64url"),
318
+ botToken: params.botToken ?? "crabline-mattermost-token",
319
+ botUserId,
320
+ botUsername,
321
+ channels: new Map(),
322
+ nextPost: 1,
323
+ pendingEvents: [],
324
+ posts: new Map(),
325
+ recorderPath: params.recorderPath ?? path.resolve(".crabline", "servers", "mattermost.jsonl"),
326
+ users: new Map([[botUserId, { id: botUserId, update_at: Date.now(), username: botUsername }]]),
327
+ websocketClients: new Map(),
328
+ };
329
+ const httpServer = await startHttpJsonServer({
330
+ handle: (request) => handleRequest(request, state),
331
+ host: params.host ?? "127.0.0.1",
332
+ port: params.port ?? 0,
333
+ serverName: "Mattermost",
334
+ });
335
+ const closeWebSocketServer = attachWebSocketServer({ server: httpServer.server, state });
336
+ return {
337
+ async close() {
338
+ await closeWebSocketServer();
339
+ await httpServer.close();
340
+ },
341
+ manifest: {
342
+ adminToken: state.adminToken,
343
+ baseUrl: httpServer.baseUrl,
344
+ botToken: state.botToken,
345
+ botUserId,
346
+ endpoints: {
347
+ adminInboundUrl: `${httpServer.baseUrl}/crabline/mattermost/inbound`,
348
+ apiRoot: `${httpServer.baseUrl}/api/v4`,
349
+ websocketUrl: `${httpServer.baseUrl.replace(/^http/u, "ws")}/api/v4/websocket`,
350
+ },
351
+ env: {
352
+ MATTERMOST_BOT_TOKEN: state.botToken,
353
+ MATTERMOST_URL: httpServer.baseUrl,
354
+ },
355
+ provider: "mattermost",
356
+ recorderPath: state.recorderPath,
357
+ version: 1,
358
+ },
359
+ };
360
+ }
361
+ //# sourceMappingURL=mattermost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mattermost.js","sourceRoot":"","sources":["../../../src/servers/mattermost.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export type SignalServerManifest = {
2
+ account: string;
3
+ adminToken: string;
4
+ baseUrl: string;
5
+ endpoints: {
6
+ adminInboundUrl: string;
7
+ apiRoot: string;
8
+ eventsUrl: string;
9
+ rpcUrl: string;
10
+ };
11
+ env: Record<string, never>;
12
+ provider: "signal";
13
+ recorderPath: string;
14
+ version: 1;
15
+ };
16
+ export type StartedSignalServer = {
17
+ close(): Promise<void>;
18
+ manifest: SignalServerManifest;
19
+ };
20
+ export type StartSignalServerParams = {
21
+ account?: string | undefined;
22
+ adminToken?: string | undefined;
23
+ host?: string | undefined;
24
+ port?: number | undefined;
25
+ recorderPath?: string | undefined;
26
+ };
27
+ export declare function startSignalServer(params?: StartSignalServerParams): Promise<StartedSignalServer>;
@@ -0,0 +1,201 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import { createServer } from "node:http";
4
+ import path from "node:path";
5
+ import { adminAuthError, closeServer, hasAdminToken, jsonResponse, parseRequestBody, queryRecord, readInteger, readTrimmedString, writeResponse, } from "./http.js";
6
+ const SIGNAL_CLI_SSE_KEEPALIVE_MS = 15_000;
7
+ async function appendEvent(state, event) {
8
+ await fs.mkdir(path.dirname(state.recorderPath), { recursive: true });
9
+ await fs.appendFile(state.recorderPath, `${JSON.stringify(event)}\n`, "utf8");
10
+ }
11
+ function rpcResponse(id, result) {
12
+ return jsonResponse({ id: id ?? null, jsonrpc: "2.0", result });
13
+ }
14
+ function emitSignalEvent(state, payload) {
15
+ const event = `event:receive\ndata:${JSON.stringify(payload)}\n\n`;
16
+ if (state.clients.size === 0) {
17
+ state.pendingEvents.push(event);
18
+ return;
19
+ }
20
+ for (const client of state.clients) {
21
+ client.write(event);
22
+ }
23
+ }
24
+ async function handleAdminInbound(params) {
25
+ const text = readTrimmedString(params.body.text);
26
+ const sourceNumber = readTrimmedString(params.body.sourceNumber ?? params.body.senderId);
27
+ if (!text || !sourceNumber) {
28
+ return jsonResponse({ error: "sourceNumber and text are required", ok: false }, 400);
29
+ }
30
+ const timestamp = readInteger(params.body.timestamp) ?? params.state.nextTimestamp++;
31
+ const groupId = readTrimmedString(params.body.groupId);
32
+ const payload = {
33
+ envelope: {
34
+ sourceName: readTrimmedString(params.body.sourceName ?? params.body.senderName),
35
+ sourceNumber,
36
+ timestamp,
37
+ dataMessage: {
38
+ message: text,
39
+ timestamp,
40
+ ...(groupId ? { groupInfo: { groupId } } : {}),
41
+ },
42
+ },
43
+ };
44
+ emitSignalEvent(params.state, payload);
45
+ return jsonResponse({ event: payload, ok: true });
46
+ }
47
+ async function handleRpc(params) {
48
+ const method = readTrimmedString(params.body.method);
49
+ if (!method) {
50
+ return jsonResponse({
51
+ error: { code: -32600, message: "Invalid Request" },
52
+ id: params.body.id ?? null,
53
+ jsonrpc: "2.0",
54
+ }, 400);
55
+ }
56
+ if (method === "version") {
57
+ return rpcResponse(params.body.id, { version: "crabline-signal-1" });
58
+ }
59
+ if (["send", "sendReaction", "sendReceipt", "sendTyping"].includes(method)) {
60
+ const timestamp = params.state.nextTimestamp++;
61
+ return rpcResponse(params.body.id, method === "sendTyping" ? {} : { timestamp });
62
+ }
63
+ return jsonResponse({
64
+ error: { code: -32601, message: `Method not found: ${method}` },
65
+ id: params.body.id ?? null,
66
+ jsonrpc: "2.0",
67
+ });
68
+ }
69
+ async function handleRequest(params) {
70
+ const url = new URL(params.request.url ?? "/", "http://localhost");
71
+ if (url.pathname === "/api/v1/events" && params.request.method === "GET") {
72
+ await appendEvent(params.state, {
73
+ at: new Date().toISOString(),
74
+ method: "GET",
75
+ path: url.pathname,
76
+ query: queryRecord(url),
77
+ type: "api",
78
+ });
79
+ params.response.writeHead(200, {
80
+ "cache-control": "no-cache",
81
+ connection: "keep-alive",
82
+ "content-type": "text/event-stream",
83
+ });
84
+ params.response.flushHeaders();
85
+ params.state.clients.add(params.response);
86
+ for (const event of params.state.pendingEvents.splice(0)) {
87
+ params.response.write(event);
88
+ }
89
+ params.response.once("close", () => params.state.clients.delete(params.response));
90
+ return;
91
+ }
92
+ let fetchResponse;
93
+ if (url.pathname === "/crabline/signal/inbound" && params.request.method === "POST") {
94
+ if (!hasAdminToken(params.request, params.state.adminToken)) {
95
+ fetchResponse = adminAuthError();
96
+ }
97
+ else {
98
+ const body = await parseRequestBody(params.request);
99
+ await appendEvent(params.state, {
100
+ at: new Date().toISOString(),
101
+ body,
102
+ method: "POST",
103
+ path: url.pathname,
104
+ query: queryRecord(url),
105
+ type: "admin",
106
+ });
107
+ fetchResponse = await handleAdminInbound({ body, state: params.state });
108
+ }
109
+ }
110
+ else if (url.pathname === "/api/v1/check" && params.request.method === "GET") {
111
+ await appendEvent(params.state, {
112
+ at: new Date().toISOString(),
113
+ method: "GET",
114
+ path: url.pathname,
115
+ query: queryRecord(url),
116
+ type: "api",
117
+ });
118
+ fetchResponse = new Response(null, { status: 200 });
119
+ }
120
+ else if (url.pathname === "/api/v1/rpc" && params.request.method === "POST") {
121
+ const body = await parseRequestBody(params.request);
122
+ await appendEvent(params.state, {
123
+ at: new Date().toISOString(),
124
+ body,
125
+ method: "POST",
126
+ path: url.pathname,
127
+ query: queryRecord(url),
128
+ type: "api",
129
+ });
130
+ fetchResponse = await handleRpc({ body, state: params.state });
131
+ }
132
+ else {
133
+ fetchResponse = new Response("not found", { status: 404 });
134
+ }
135
+ await writeResponse(params.response, fetchResponse);
136
+ }
137
+ export async function startSignalServer(params = {}) {
138
+ const state = {
139
+ account: params.account ?? "+15550000000",
140
+ adminToken: params.adminToken ?? randomBytes(24).toString("base64url"),
141
+ clients: new Set(),
142
+ nextTimestamp: Date.now(),
143
+ pendingEvents: [],
144
+ recorderPath: params.recorderPath ?? path.resolve(".crabline", "servers", "signal.jsonl"),
145
+ };
146
+ const host = params.host ?? "127.0.0.1";
147
+ const server = createServer((request, response) => {
148
+ void handleRequest({ request, response, state }).catch(async (error) => {
149
+ if (!response.headersSent) {
150
+ await writeResponse(response, jsonResponse({ error: error instanceof Error ? error.message : String(error), ok: false }, 500));
151
+ }
152
+ else {
153
+ response.destroy(error instanceof Error ? error : new Error(String(error)));
154
+ }
155
+ });
156
+ });
157
+ const keepalive = setInterval(() => {
158
+ for (const client of state.clients) {
159
+ client.write(":\n");
160
+ }
161
+ }, SIGNAL_CLI_SSE_KEEPALIVE_MS);
162
+ keepalive.unref();
163
+ await new Promise((resolve, reject) => {
164
+ server.once("error", reject);
165
+ server.listen(params.port ?? 0, host, () => {
166
+ server.off("error", reject);
167
+ resolve();
168
+ });
169
+ });
170
+ const address = server.address();
171
+ if (!address || typeof address === "string") {
172
+ await closeServer(server);
173
+ throw new Error("Unable to resolve Signal local server address.");
174
+ }
175
+ const baseUrl = `http://${host.includes(":") ? `[${host}]` : host}:${address.port}`;
176
+ return {
177
+ async close() {
178
+ clearInterval(keepalive);
179
+ for (const client of state.clients) {
180
+ client.end();
181
+ }
182
+ await closeServer(server);
183
+ },
184
+ manifest: {
185
+ account: state.account,
186
+ adminToken: state.adminToken,
187
+ baseUrl,
188
+ endpoints: {
189
+ adminInboundUrl: `${baseUrl}/crabline/signal/inbound`,
190
+ apiRoot: baseUrl,
191
+ eventsUrl: `${baseUrl}/api/v1/events`,
192
+ rpcUrl: `${baseUrl}/api/v1/rpc`,
193
+ },
194
+ env: {},
195
+ provider: "signal",
196
+ recorderPath: state.recorderPath,
197
+ version: 1,
198
+ },
199
+ };
200
+ }
201
+ //# sourceMappingURL=signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal.js","sourceRoot":"","sources":["../../../src/servers/signal.ts"],"names":[],"mappings":"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"}
@@ -210,6 +210,13 @@ function createInboundUpdate(state, body) {
210
210
  const fromUsername = toStringValue(body.fromUsername ?? body.from_username);
211
211
  const messageId = toIntegerValue(body.messageId ?? body.message_id);
212
212
  const updateId = toIntegerValue(body.updateId ?? body.update_id);
213
+ const entities = Array.isArray(body.entities)
214
+ ? body.entities.filter((entry) => typeof entry === "object" &&
215
+ entry !== null &&
216
+ typeof entry.length === "number" &&
217
+ typeof entry.offset === "number" &&
218
+ typeof entry.type === "string")
219
+ : undefined;
213
220
  if (messageId !== undefined) {
214
221
  state.nextMessageId = Math.max(state.nextMessageId, messageId + 1);
215
222
  }
@@ -227,6 +234,7 @@ function createInboundUpdate(state, body) {
227
234
  ...(fromUsername ? { username: fromUsername } : {}),
228
235
  },
229
236
  message_id: messageId ?? state.nextMessageId++,
237
+ ...(entities && entities.length > 0 ? { entities } : {}),
230
238
  ...(threadId !== undefined ? { message_thread_id: threadId } : {}),
231
239
  text,
232
240
  },