@openclaw/crabline 0.1.11 → 0.1.12
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.
- package/README.md +99 -12
- package/dist/src/bin/crabline.js +3 -1
- package/dist/src/bin/crabline.js.map +1 -1
- package/dist/src/cli/program.d.ts +5 -1
- package/dist/src/cli/program.js +264 -50
- package/dist/src/cli/program.js.map +1 -1
- package/dist/src/config/load.js +39 -3
- package/dist/src/config/load.js.map +1 -1
- package/dist/src/config/schema.d.ts +20 -0
- package/dist/src/config/schema.js +163 -35
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/core/errors.d.ts +1 -0
- package/dist/src/core/errors.js +6 -3
- package/dist/src/core/errors.js.map +1 -1
- package/dist/src/core/http-path.d.ts +1 -0
- package/dist/src/core/http-path.js +14 -0
- package/dist/src/core/http-path.js.map +1 -0
- package/dist/src/core/matcher.d.ts +3 -1
- package/dist/src/core/matcher.js +58 -2
- package/dist/src/core/matcher.js.map +1 -1
- package/dist/src/core/nonces.d.ts +2 -0
- package/dist/src/core/nonces.js +11 -1
- package/dist/src/core/nonces.js.map +1 -1
- package/dist/src/core/reporters.d.ts +5 -0
- package/dist/src/core/reporters.js +26 -2
- package/dist/src/core/reporters.js.map +1 -1
- package/dist/src/core/run.d.ts +3 -1
- package/dist/src/core/run.js +178 -66
- package/dist/src/core/run.js.map +1 -1
- package/dist/src/matrix-ids.d.ts +5 -0
- package/dist/src/matrix-ids.js +83 -0
- package/dist/src/matrix-ids.js.map +1 -0
- package/dist/src/openclaw/artifact-generation.d.ts +7 -2
- package/dist/src/openclaw/artifact-generation.js +399 -27
- package/dist/src/openclaw/artifact-generation.js.map +1 -1
- package/dist/src/openclaw/bridges/matrix.js +21 -4
- package/dist/src/openclaw/bridges/matrix.js.map +1 -1
- package/dist/src/openclaw/bridges/mattermost.js +13 -8
- package/dist/src/openclaw/bridges/mattermost.js.map +1 -1
- package/dist/src/openclaw/bridges/probe-response.d.ts +1 -0
- package/dist/src/openclaw/bridges/probe-response.js +5 -0
- package/dist/src/openclaw/bridges/probe-response.js.map +1 -0
- package/dist/src/openclaw/bridges/signal.js +15 -6
- package/dist/src/openclaw/bridges/signal.js.map +1 -1
- package/dist/src/openclaw/bridges/slack.js +116 -23
- package/dist/src/openclaw/bridges/slack.js.map +1 -1
- package/dist/src/openclaw/bridges/telegram.js +45 -19
- package/dist/src/openclaw/bridges/telegram.js.map +1 -1
- package/dist/src/openclaw/bridges/whatsapp.js +32 -16
- package/dist/src/openclaw/bridges/whatsapp.js.map +1 -1
- package/dist/src/openclaw/bridges/zalo.js +8 -3
- package/dist/src/openclaw/bridges/zalo.js.map +1 -1
- package/dist/src/openclaw/private-file.d.ts +38 -3
- package/dist/src/openclaw/private-file.js +2008 -87
- package/dist/src/openclaw/private-file.js.map +1 -1
- package/dist/src/openclaw/shared.d.ts +1 -0
- package/dist/src/openclaw/shared.js +48 -13
- package/dist/src/openclaw/shared.js.map +1 -1
- package/dist/src/openclaw/smoke-lock.d.ts +9 -0
- package/dist/src/openclaw/smoke-lock.js +626 -172
- package/dist/src/openclaw/smoke-lock.js.map +1 -1
- package/dist/src/openclaw.js +225 -42
- package/dist/src/openclaw.js.map +1 -1
- package/dist/src/platform/process-owned-lock.d.ts +11 -0
- package/dist/src/platform/process-owned-lock.js +1698 -0
- package/dist/src/platform/process-owned-lock.js.map +1 -0
- package/dist/src/platform/recorder-directory.d.ts +1 -0
- package/dist/src/platform/recorder-directory.js +14 -0
- package/dist/src/platform/recorder-directory.js.map +1 -0
- package/dist/src/platform/windows-acl.d.ts +18 -0
- package/dist/src/platform/windows-acl.js +1367 -0
- package/dist/src/platform/windows-acl.js.map +1 -0
- package/dist/src/platform/windows-lock-root.d.ts +13 -0
- package/dist/src/platform/windows-lock-root.js +98 -0
- package/dist/src/platform/windows-lock-root.js.map +1 -0
- package/dist/src/providers/builtin/discord.d.ts +4 -0
- package/dist/src/providers/builtin/discord.js +52 -9
- package/dist/src/providers/builtin/discord.js.map +1 -1
- package/dist/src/providers/builtin/external-webhook-auth.d.ts +1 -0
- package/dist/src/providers/builtin/external-webhook-auth.js +38 -1
- package/dist/src/providers/builtin/external-webhook-auth.js.map +1 -1
- package/dist/src/providers/builtin/feishu.d.ts +1 -0
- package/dist/src/providers/builtin/feishu.js +96 -26
- package/dist/src/providers/builtin/feishu.js.map +1 -1
- package/dist/src/providers/builtin/googlechat.d.ts +1 -0
- package/dist/src/providers/builtin/googlechat.js +95 -26
- package/dist/src/providers/builtin/googlechat.js.map +1 -1
- package/dist/src/providers/builtin/imessage.js +60 -17
- package/dist/src/providers/builtin/imessage.js.map +1 -1
- package/dist/src/providers/builtin/loopback.js +88 -35
- package/dist/src/providers/builtin/loopback.js.map +1 -1
- package/dist/src/providers/builtin/matrix.d.ts +2 -2
- package/dist/src/providers/builtin/matrix.js +78 -21
- package/dist/src/providers/builtin/matrix.js.map +1 -1
- package/dist/src/providers/builtin/mattermost.d.ts +11 -7
- package/dist/src/providers/builtin/mattermost.js +138 -24
- package/dist/src/providers/builtin/mattermost.js.map +1 -1
- package/dist/src/providers/builtin/msteams.d.ts +1 -0
- package/dist/src/providers/builtin/msteams.js +118 -38
- package/dist/src/providers/builtin/msteams.js.map +1 -1
- package/dist/src/providers/builtin/native-local-mock.js +4 -4
- package/dist/src/providers/builtin/native-local-mock.js.map +1 -1
- package/dist/src/providers/builtin/script.js +1057 -146
- package/dist/src/providers/builtin/script.js.map +1 -1
- package/dist/src/providers/builtin/slack.d.ts +8 -1
- package/dist/src/providers/builtin/slack.js +327 -24
- package/dist/src/providers/builtin/slack.js.map +1 -1
- package/dist/src/providers/builtin/telegram.d.ts +1 -1
- package/dist/src/providers/builtin/telegram.js +55 -14
- package/dist/src/providers/builtin/telegram.js.map +1 -1
- package/dist/src/providers/builtin/whatsapp.d.ts +4 -3
- package/dist/src/providers/builtin/whatsapp.js +157 -29
- package/dist/src/providers/builtin/whatsapp.js.map +1 -1
- package/dist/src/providers/builtin/zalo.d.ts +5 -2
- package/dist/src/providers/builtin/zalo.js +31 -7
- package/dist/src/providers/builtin/zalo.js.map +1 -1
- package/dist/src/providers/catalog.d.ts +1 -1
- package/dist/src/providers/catalog.js +1 -1
- package/dist/src/providers/catalog.js.map +1 -1
- package/dist/src/providers/local-mock.d.ts +4 -0
- package/dist/src/providers/local-mock.js +162 -35
- package/dist/src/providers/local-mock.js.map +1 -1
- package/dist/src/providers/native-ids.d.ts +2 -0
- package/dist/src/providers/native-ids.js +3 -0
- package/dist/src/providers/native-ids.js.map +1 -1
- package/dist/src/providers/recorder.d.ts +14 -3
- package/dist/src/providers/recorder.js +521 -41
- package/dist/src/providers/recorder.js.map +1 -1
- package/dist/src/providers/registry.d.ts +1 -0
- package/dist/src/providers/registry.js +65 -20
- package/dist/src/providers/registry.js.map +1 -1
- package/dist/src/providers/signed-jwt.d.ts +6 -1
- package/dist/src/providers/signed-jwt.js +241 -50
- package/dist/src/providers/signed-jwt.js.map +1 -1
- package/dist/src/providers/slack-ids.d.ts +1 -0
- package/dist/src/providers/slack-ids.js +5 -0
- package/dist/src/providers/slack-ids.js.map +1 -1
- package/dist/src/providers/target-normalizers.d.ts +9 -1
- package/dist/src/providers/target-normalizers.js +177 -58
- package/dist/src/providers/target-normalizers.js.map +1 -1
- package/dist/src/providers/webhook-server.d.ts +3 -0
- package/dist/src/providers/webhook-server.js +268 -47
- package/dist/src/providers/webhook-server.js.map +1 -1
- package/dist/src/servers/http.d.ts +14 -2
- package/dist/src/servers/http.js +229 -58
- package/dist/src/servers/http.js.map +1 -1
- package/dist/src/servers/matrix.d.ts +3 -0
- package/dist/src/servers/matrix.js +268 -110
- package/dist/src/servers/matrix.js.map +1 -1
- package/dist/src/servers/mattermost.d.ts +5 -0
- package/dist/src/servers/mattermost.js +389 -91
- package/dist/src/servers/mattermost.js.map +1 -1
- package/dist/src/servers/recorder.d.ts +11 -0
- package/dist/src/servers/recorder.js +1072 -69
- package/dist/src/servers/recorder.js.map +1 -1
- package/dist/src/servers/signal.d.ts +1 -0
- package/dist/src/servers/signal.js +279 -44
- package/dist/src/servers/signal.js.map +1 -1
- package/dist/src/servers/slack.d.ts +15 -0
- package/dist/src/servers/slack.js +259 -51
- package/dist/src/servers/slack.js.map +1 -1
- package/dist/src/servers/telegram-identity.d.ts +6 -0
- package/dist/src/servers/telegram-identity.js +27 -0
- package/dist/src/servers/telegram-identity.js.map +1 -0
- package/dist/src/servers/telegram.d.ts +17 -6
- package/dist/src/servers/telegram.js +1169 -107
- package/dist/src/servers/telegram.js.map +1 -1
- package/dist/src/servers/webhook-target.d.ts +12 -4
- package/dist/src/servers/webhook-target.js +226 -28
- package/dist/src/servers/webhook-target.js.map +1 -1
- package/dist/src/servers/whatsapp-baileys-websocket.d.ts +60 -1
- package/dist/src/servers/whatsapp-baileys-websocket.js +782 -112
- package/dist/src/servers/whatsapp-baileys-websocket.js.map +1 -1
- package/dist/src/servers/whatsapp-jid.d.ts +1 -0
- package/dist/src/servers/whatsapp-jid.js +9 -5
- package/dist/src/servers/whatsapp-jid.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/binary-node.d.ts +1 -0
- package/dist/src/servers/whatsapp-wire/binary-node.js +37 -13
- package/dist/src/servers/whatsapp-wire/binary-node.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/crypto.d.ts +3 -0
- package/dist/src/servers/whatsapp-wire/crypto.js +23 -8
- package/dist/src/servers/whatsapp-wire/crypto.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/handshake.d.ts +9 -3
- package/dist/src/servers/whatsapp-wire/handshake.js +173 -12
- package/dist/src/servers/whatsapp-wire/handshake.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/signal.js +3 -3
- package/dist/src/servers/whatsapp-wire/signal.js.map +1 -1
- package/dist/src/servers/whatsapp.d.ts +7 -0
- package/dist/src/servers/whatsapp.js +149 -24
- package/dist/src/servers/whatsapp.js.map +1 -1
- package/dist/src/servers/zalo.d.ts +1 -0
- package/dist/src/servers/zalo.js +193 -82
- package/dist/src/servers/zalo.js.map +1 -1
- package/docs/channel-setup.md +185 -43
- package/fixtures/examples/crabline.example.yaml +3 -2
- package/package.json +18 -10
- package/fixtures/examples/openclaw-bridge.yaml +0 -125
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import path from "node:path";
|
|
3
2
|
import { CrablineError } from "../../core/errors.js";
|
|
4
|
-
import { LocalMockProviderAdapter } from "../local-mock.js";
|
|
3
|
+
import { LocalMockProviderAdapter, resolveGeneratedLocalMockRecorderPath } from "../local-mock.js";
|
|
5
4
|
import { getBuiltinTargetCodec } from "../target-normalizers.js";
|
|
5
|
+
const LOOPBACK_V2_PREFIX = "loopback+v2:";
|
|
6
6
|
function createMessageId() {
|
|
7
7
|
return `loopback-mock-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
8
8
|
}
|
|
@@ -33,12 +33,45 @@ function toPostableText(message) {
|
|
|
33
33
|
}
|
|
34
34
|
return message.fallbackText ?? "[card]";
|
|
35
35
|
}
|
|
36
|
+
function malformedThreadAddress(cause) {
|
|
37
|
+
return new CrablineError("Loopback v2 thread address is malformed.", {
|
|
38
|
+
...(cause === undefined ? {} : { cause }),
|
|
39
|
+
kind: "inbound",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function encodeThreadAddressComponent(value) {
|
|
43
|
+
if (!value) {
|
|
44
|
+
throw malformedThreadAddress();
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return encodeURIComponent(value);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw malformedThreadAddress(error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function decodeThreadAddressComponent(value) {
|
|
54
|
+
let decoded;
|
|
55
|
+
let canonical;
|
|
56
|
+
try {
|
|
57
|
+
decoded = decodeURIComponent(value);
|
|
58
|
+
canonical = encodeURIComponent(decoded);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw malformedThreadAddress(error);
|
|
62
|
+
}
|
|
63
|
+
if (!decoded || canonical !== value) {
|
|
64
|
+
throw malformedThreadAddress();
|
|
65
|
+
}
|
|
66
|
+
return decoded;
|
|
67
|
+
}
|
|
36
68
|
export class LoopbackChatAdapter {
|
|
37
69
|
name = "loopback";
|
|
38
70
|
persistMessageHistory = true;
|
|
39
71
|
userName;
|
|
40
72
|
#messages = new Map();
|
|
41
73
|
#nextSequence = new Map();
|
|
74
|
+
#lastMessageTimestamp = -1;
|
|
42
75
|
constructor(userName) {
|
|
43
76
|
this.userName = userName;
|
|
44
77
|
}
|
|
@@ -46,34 +79,42 @@ export class LoopbackChatAdapter {
|
|
|
46
79
|
return Promise.resolve();
|
|
47
80
|
}
|
|
48
81
|
channelIdFromThreadId(threadId) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
return address;
|
|
52
|
-
}
|
|
53
|
-
return this.decodeThreadId(threadId).channelId ?? address;
|
|
82
|
+
const address = this.decodeThreadId(threadId);
|
|
83
|
+
return address.channelId ?? address.id;
|
|
54
84
|
}
|
|
55
85
|
decodeThreadId(threadId) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
86
|
+
if (threadId.startsWith(LOOPBACK_V2_PREFIX)) {
|
|
87
|
+
const threadParts = threadId.split("::");
|
|
88
|
+
if (threadParts.length > 2) {
|
|
89
|
+
throw malformedThreadAddress();
|
|
90
|
+
}
|
|
91
|
+
const [address = "", rawThreadId] = threadParts;
|
|
92
|
+
const addressParts = address.slice(LOOPBACK_V2_PREFIX.length).split(":");
|
|
93
|
+
if ((addressParts.length !== 1 && addressParts.length !== 2) ||
|
|
94
|
+
addressParts.some((part) => part.length === 0) ||
|
|
95
|
+
rawThreadId === "") {
|
|
96
|
+
throw malformedThreadAddress();
|
|
97
|
+
}
|
|
98
|
+
const [rawChannelOrId = "", rawId] = addressParts;
|
|
99
|
+
const decoded = {
|
|
100
|
+
id: decodeThreadAddressComponent(rawId ?? rawChannelOrId),
|
|
101
|
+
};
|
|
102
|
+
if (rawId !== undefined) {
|
|
103
|
+
decoded.channelId = decodeThreadAddressComponent(rawChannelOrId);
|
|
63
104
|
}
|
|
64
|
-
if (rawThreadId) {
|
|
65
|
-
decoded.threadId = rawThreadId;
|
|
105
|
+
if (rawThreadId !== undefined) {
|
|
106
|
+
decoded.threadId = decodeThreadAddressComponent(rawThreadId);
|
|
66
107
|
}
|
|
67
108
|
return decoded;
|
|
68
109
|
}
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
};
|
|
72
|
-
if (
|
|
73
|
-
decoded.channelId =
|
|
110
|
+
const [address = threadId, rawThreadId] = threadId.split("::");
|
|
111
|
+
const [, channelId = address, id = address] = address.split(":");
|
|
112
|
+
const decoded = { id };
|
|
113
|
+
if (channelId) {
|
|
114
|
+
decoded.channelId = channelId;
|
|
74
115
|
}
|
|
75
116
|
if (rawThreadId) {
|
|
76
|
-
decoded.threadId =
|
|
117
|
+
decoded.threadId = rawThreadId;
|
|
77
118
|
}
|
|
78
119
|
return decoded;
|
|
79
120
|
}
|
|
@@ -93,17 +134,18 @@ export class LoopbackChatAdapter {
|
|
|
93
134
|
existing.text = text;
|
|
94
135
|
existing.formatted = text;
|
|
95
136
|
existing.metadata.edited = true;
|
|
96
|
-
existing.metadata.editedAt = new Date();
|
|
137
|
+
existing.metadata.editedAt = new Date(this.#nextMessageTimestamp());
|
|
97
138
|
existing.raw.text = text;
|
|
98
139
|
return Promise.resolve({ id: existing.id, raw: cloneRawMessage(existing.raw), threadId });
|
|
99
140
|
}
|
|
100
141
|
encodeThreadId(platformData) {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
142
|
+
const id = encodeThreadAddressComponent(platformData.id);
|
|
143
|
+
const address = platformData.channelId === undefined
|
|
144
|
+
? `${LOOPBACK_V2_PREFIX}${id}`
|
|
145
|
+
: `${LOOPBACK_V2_PREFIX}${encodeThreadAddressComponent(platformData.channelId)}:${id}`;
|
|
146
|
+
return platformData.threadId === undefined
|
|
147
|
+
? address
|
|
148
|
+
: `${address}::${encodeThreadAddressComponent(platformData.threadId)}`;
|
|
107
149
|
}
|
|
108
150
|
fetchMessages(threadId, options) {
|
|
109
151
|
const storedMessages = [...(this.#messages.get(threadId) ?? [])];
|
|
@@ -115,7 +157,7 @@ export class LoopbackChatAdapter {
|
|
|
115
157
|
}
|
|
116
158
|
const limit = options?.limit ?? storedMessages.length;
|
|
117
159
|
let cursor;
|
|
118
|
-
if (options?.cursor) {
|
|
160
|
+
if (options?.cursor !== undefined) {
|
|
119
161
|
if (!/^[1-9]\d*$/u.test(options.cursor)) {
|
|
120
162
|
throw new CrablineError("Loopback message cursor must be a positive safe integer.", {
|
|
121
163
|
kind: "config",
|
|
@@ -139,10 +181,11 @@ export class LoopbackChatAdapter {
|
|
|
139
181
|
return Promise.resolve(result);
|
|
140
182
|
}
|
|
141
183
|
fetchThread(threadId) {
|
|
184
|
+
const address = this.decodeThreadId(threadId);
|
|
142
185
|
return Promise.resolve({
|
|
143
|
-
channelId:
|
|
186
|
+
channelId: address.channelId ?? address.id,
|
|
144
187
|
id: threadId,
|
|
145
|
-
isDM:
|
|
188
|
+
isDM: address.channelId === undefined,
|
|
146
189
|
metadata: {},
|
|
147
190
|
});
|
|
148
191
|
}
|
|
@@ -176,7 +219,7 @@ export class LoopbackChatAdapter {
|
|
|
176
219
|
id: createMessageId(),
|
|
177
220
|
text,
|
|
178
221
|
threadId,
|
|
179
|
-
timestamp:
|
|
222
|
+
timestamp: this.#nextMessageTimestamp(),
|
|
180
223
|
};
|
|
181
224
|
const parsed = this.parseMessage(raw);
|
|
182
225
|
this.#append(threadId, parsed);
|
|
@@ -197,7 +240,7 @@ export class LoopbackChatAdapter {
|
|
|
197
240
|
id: createMessageId(),
|
|
198
241
|
text,
|
|
199
242
|
threadId,
|
|
200
|
-
timestamp:
|
|
243
|
+
timestamp: this.#nextMessageTimestamp(),
|
|
201
244
|
};
|
|
202
245
|
const parsed = this.parseMessage(raw);
|
|
203
246
|
this.#append(threadId, parsed);
|
|
@@ -205,6 +248,11 @@ export class LoopbackChatAdapter {
|
|
|
205
248
|
}
|
|
206
249
|
listSince(threadId, since) {
|
|
207
250
|
const sinceTime = new Date(since).getTime();
|
|
251
|
+
if (!Number.isFinite(sinceTime)) {
|
|
252
|
+
throw new CrablineError("Loopback since timestamp must be a valid date.", {
|
|
253
|
+
kind: "config",
|
|
254
|
+
});
|
|
255
|
+
}
|
|
208
256
|
return (this.#messages.get(threadId) ?? [])
|
|
209
257
|
.map((entry) => entry.message)
|
|
210
258
|
.filter((message) => message.metadata.dateSent.getTime() >= sinceTime)
|
|
@@ -217,6 +265,11 @@ export class LoopbackChatAdapter {
|
|
|
217
265
|
this.#messages.set(threadId, bucket);
|
|
218
266
|
this.#nextSequence.set(threadId, sequence);
|
|
219
267
|
}
|
|
268
|
+
#nextMessageTimestamp() {
|
|
269
|
+
const timestamp = Math.max(Date.now(), this.#lastMessageTimestamp + 1);
|
|
270
|
+
this.#lastMessageTimestamp = timestamp;
|
|
271
|
+
return new Date(timestamp).toISOString();
|
|
272
|
+
}
|
|
220
273
|
}
|
|
221
274
|
export class LoopbackProviderAdapter extends LocalMockProviderAdapter {
|
|
222
275
|
constructor(id, config, _userName) {
|
|
@@ -228,7 +281,7 @@ export class LoopbackProviderAdapter extends LocalMockProviderAdapter {
|
|
|
228
281
|
defaultWebhook: { host: "127.0.0.1", path: "/loopback/webhook", port: 0 },
|
|
229
282
|
endpointLabel: "webhook endpoint",
|
|
230
283
|
platform: "loopback",
|
|
231
|
-
recorderPath:
|
|
284
|
+
recorderPath: resolveGeneratedLocalMockRecorderPath(id, `-${randomUUID()}`),
|
|
232
285
|
},
|
|
233
286
|
});
|
|
234
287
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loopback.js","sourceRoot":"","sources":["../../../../src/providers/builtin/loopback.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport path from \"node:path\";\nimport { CrablineError } from \"../../core/errors.js\";\nimport type { ProviderConfig } from \"../../config/schema.js\";\nimport { LocalMockProviderAdapter } from \"../local-mock.js\";\nimport { getBuiltinTargetCodec } from \"../target-normalizers.js\";\nimport type { LoopbackMessage, LoopbackRawMessage, ProviderAdapter } from \"../types.js\";\n\ntype ThreadAddress = {\n channelId?: string | undefined;\n id: string;\n threadId?: string | undefined;\n};\n\ntype PostableMessage =\n | string\n | { card: unknown; fallbackText?: string }\n | { markdown: string }\n | { raw: string };\n\ntype StoredLoopbackMessage = {\n message: LoopbackMessage;\n sequence: number;\n};\n\nfunction createMessageId(): string {\n return `loopback-mock-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction cloneRawMessage(raw: LoopbackRawMessage): LoopbackRawMessage {\n return { ...raw };\n}\n\nfunction cloneMessage(message: LoopbackMessage): LoopbackMessage {\n return {\n ...message,\n author: { ...message.author },\n metadata: {\n dateSent: new Date(message.metadata.dateSent),\n edited: message.metadata.edited,\n ...(message.metadata.editedAt ? { editedAt: new Date(message.metadata.editedAt) } : {}),\n },\n raw: cloneRawMessage(message.raw),\n };\n}\n\nfunction toPostableText(message: PostableMessage): string {\n if (typeof message === \"string\") {\n return message;\n }\n if (\"raw\" in message) {\n return message.raw;\n }\n if (\"markdown\" in message) {\n return message.markdown;\n }\n return message.fallbackText ?? \"[card]\";\n}\n\nexport class LoopbackChatAdapter {\n readonly name = \"loopback\";\n readonly persistMessageHistory = true;\n readonly userName;\n\n readonly #messages = new Map<string, StoredLoopbackMessage[]>();\n readonly #nextSequence = new Map<string, number>();\n\n constructor(userName: string) {\n this.userName = userName;\n }\n\n addReaction(): Promise<void> {\n return Promise.resolve();\n }\n\n channelIdFromThreadId(threadId: string): string {\n const [address = threadId] = threadId.split(\"::\");\n if (!address.startsWith(\"loopback+v2:\")) {\n return address;\n }\n return this.decodeThreadId(threadId).channelId ?? address;\n }\n\n decodeThreadId(threadId: string): ThreadAddress {\n const [address = threadId, rawThreadId] = threadId.split(\"::\");\n const [platform, rawChannelOrId, rawId] = address.split(\":\");\n if (platform !== \"loopback+v2\" || !rawChannelOrId) {\n const [, channelId = address, id = address] = address.split(\":\");\n const decoded: ThreadAddress = { id };\n if (channelId) {\n decoded.channelId = channelId;\n }\n if (rawThreadId) {\n decoded.threadId = rawThreadId;\n }\n return decoded;\n }\n\n const decoded: ThreadAddress = {\n id: decodeURIComponent(rawId ?? rawChannelOrId),\n };\n if (rawId) {\n decoded.channelId = decodeURIComponent(rawChannelOrId);\n }\n if (rawThreadId) {\n decoded.threadId = decodeURIComponent(rawThreadId);\n }\n return decoded;\n }\n\n deleteMessage(threadId: string, messageId: string): Promise<void> {\n const messages = this.#messages.get(threadId) ?? [];\n this.#messages.set(\n threadId,\n messages.filter((entry) => entry.message.id !== messageId),\n );\n return Promise.resolve();\n }\n\n editMessage(\n threadId: string,\n messageId: string,\n message: PostableMessage,\n ): Promise<{ id: string; raw: LoopbackRawMessage; threadId: string }> {\n const messages = this.#messages.get(threadId) ?? [];\n const stored = messages.find((entry) => entry.message.id === messageId);\n if (!stored) {\n throw new CrablineError(`Loopback message not found: ${messageId}`, { kind: \"inbound\" });\n }\n\n const existing = stored.message;\n const text = toPostableText(message);\n existing.text = text;\n existing.formatted = text;\n existing.metadata.edited = true;\n existing.metadata.editedAt = new Date();\n existing.raw.text = text;\n return Promise.resolve({ id: existing.id, raw: cloneRawMessage(existing.raw), threadId });\n }\n\n encodeThreadId(platformData: ThreadAddress): string {\n const address = platformData.channelId\n ? `loopback+v2:${encodeURIComponent(platformData.channelId)}:${encodeURIComponent(platformData.id)}`\n : `loopback+v2:${encodeURIComponent(platformData.id)}`;\n return platformData.threadId\n ? `${address}::${encodeURIComponent(platformData.threadId)}`\n : address;\n }\n\n fetchMessages(\n threadId: string,\n options?: { cursor?: string; limit?: number },\n ): Promise<{ messages: LoopbackMessage[]; nextCursor?: string }> {\n const storedMessages = [...(this.#messages.get(threadId) ?? [])];\n if (\n options?.limit !== undefined &&\n (!Number.isSafeInteger(options.limit) || options.limit <= 0)\n ) {\n throw new CrablineError(\"Loopback message limit must be a positive safe integer.\", {\n kind: \"config\",\n });\n }\n const limit = options?.limit ?? storedMessages.length;\n let cursor: number | undefined;\n if (options?.cursor) {\n if (!/^[1-9]\\d*$/u.test(options.cursor)) {\n throw new CrablineError(\"Loopback message cursor must be a positive safe integer.\", {\n kind: \"config\",\n });\n }\n cursor = Number(options.cursor);\n if (!Number.isSafeInteger(cursor) || cursor > (this.#nextSequence.get(threadId) ?? 0)) {\n throw new CrablineError(\n \"Loopback message cursor must be a positive safe integer within message history.\",\n { kind: \"config\" },\n );\n }\n }\n\n const eligibleMessages =\n cursor === undefined\n ? storedMessages\n : storedMessages.filter((entry) => entry.sequence < cursor);\n const page = eligibleMessages.slice(-limit);\n const result: { messages: LoopbackMessage[]; nextCursor?: string } = {\n messages: page.map((entry) => cloneMessage(entry.message)),\n };\n if (eligibleMessages.length > page.length && page[0]) {\n result.nextCursor = String(page[0].sequence);\n }\n return Promise.resolve(result);\n }\n\n fetchThread(threadId: string) {\n return Promise.resolve({\n channelId: this.channelIdFromThreadId(threadId),\n id: threadId,\n isDM: true,\n metadata: {},\n });\n }\n\n handleWebhook(_request?: Request): Promise<Response> {\n return Promise.resolve(\n new Response(\"loopback adapter has no webhook surface\", { status: 501 }),\n );\n }\n\n isDM(): boolean {\n return true;\n }\n\n parseMessage(raw: LoopbackRawMessage): LoopbackMessage {\n return {\n author: {\n isMe: raw.author === \"assistant\",\n userName: raw.author === \"assistant\" ? this.userName : \"loopback\",\n },\n formatted: raw.text,\n id: raw.id,\n metadata: {\n dateSent: new Date(raw.timestamp),\n edited: false,\n },\n raw: cloneRawMessage(raw),\n text: raw.text,\n threadId: raw.threadId,\n };\n }\n\n postMessage(\n threadId: string,\n message: PostableMessage,\n ): Promise<{ id: string; raw: LoopbackRawMessage; threadId: string }> {\n const text = toPostableText(message);\n const raw = {\n author: \"assistant\",\n id: createMessageId(),\n text,\n threadId,\n timestamp: new Date().toISOString(),\n } satisfies LoopbackRawMessage;\n const parsed = this.parseMessage(raw);\n this.#append(threadId, parsed);\n return Promise.resolve({ id: raw.id, raw: cloneRawMessage(raw), threadId });\n }\n\n removeReaction(): Promise<void> {\n return Promise.resolve();\n }\n\n renderFormatted(content: string): string {\n return content;\n }\n\n startTyping(): Promise<void> {\n return Promise.resolve();\n }\n\n ingestUserMessage(threadId: string, text: string): LoopbackMessage {\n const raw = {\n author: \"user\",\n id: createMessageId(),\n text,\n threadId,\n timestamp: new Date().toISOString(),\n } satisfies LoopbackRawMessage;\n const parsed = this.parseMessage(raw);\n this.#append(threadId, parsed);\n return cloneMessage(parsed);\n }\n\n listSince(threadId: string, since: string): LoopbackMessage[] {\n const sinceTime = new Date(since).getTime();\n return (this.#messages.get(threadId) ?? [])\n .map((entry) => entry.message)\n .filter((message) => message.metadata.dateSent.getTime() >= sinceTime)\n .map(cloneMessage);\n }\n\n #append(threadId: string, message: LoopbackMessage): void {\n const bucket = this.#messages.get(threadId) ?? [];\n const sequence = (this.#nextSequence.get(threadId) ?? 0) + 1;\n bucket.push({ message: cloneMessage(message), sequence });\n this.#messages.set(threadId, bucket);\n this.#nextSequence.set(threadId, sequence);\n }\n}\n\nexport class LoopbackProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, _userName: string) {\n super({\n codec: getBuiltinTargetCodec(\"loopback\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/loopback/webhook\", port: 0 },\n endpointLabel: \"webhook endpoint\",\n platform: \"loopback\",\n recorderPath: path.resolve(\".crabline\", \"recorders\", `${id}-${randomUUID()}.jsonl`),\n },\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"loopback.js","sourceRoot":"","sources":["../../../../src/providers/builtin/loopback.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { CrablineError } from \"../../core/errors.js\";\nimport type { ProviderConfig } from \"../../config/schema.js\";\nimport { LocalMockProviderAdapter, resolveGeneratedLocalMockRecorderPath } from \"../local-mock.js\";\nimport { getBuiltinTargetCodec } from \"../target-normalizers.js\";\nimport type { LoopbackMessage, LoopbackRawMessage, ProviderAdapter } from \"../types.js\";\n\ntype ThreadAddress = {\n channelId?: string | undefined;\n id: string;\n threadId?: string | undefined;\n};\n\ntype PostableMessage =\n | string\n | { card: unknown; fallbackText?: string }\n | { markdown: string }\n | { raw: string };\n\ntype StoredLoopbackMessage = {\n message: LoopbackMessage;\n sequence: number;\n};\n\nconst LOOPBACK_V2_PREFIX = \"loopback+v2:\";\n\nfunction createMessageId(): string {\n return `loopback-mock-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction cloneRawMessage(raw: LoopbackRawMessage): LoopbackRawMessage {\n return { ...raw };\n}\n\nfunction cloneMessage(message: LoopbackMessage): LoopbackMessage {\n return {\n ...message,\n author: { ...message.author },\n metadata: {\n dateSent: new Date(message.metadata.dateSent),\n edited: message.metadata.edited,\n ...(message.metadata.editedAt ? { editedAt: new Date(message.metadata.editedAt) } : {}),\n },\n raw: cloneRawMessage(message.raw),\n };\n}\n\nfunction toPostableText(message: PostableMessage): string {\n if (typeof message === \"string\") {\n return message;\n }\n if (\"raw\" in message) {\n return message.raw;\n }\n if (\"markdown\" in message) {\n return message.markdown;\n }\n return message.fallbackText ?? \"[card]\";\n}\n\nfunction malformedThreadAddress(cause?: unknown): CrablineError {\n return new CrablineError(\"Loopback v2 thread address is malformed.\", {\n ...(cause === undefined ? {} : { cause }),\n kind: \"inbound\",\n });\n}\n\nfunction encodeThreadAddressComponent(value: string): string {\n if (!value) {\n throw malformedThreadAddress();\n }\n try {\n return encodeURIComponent(value);\n } catch (error) {\n throw malformedThreadAddress(error);\n }\n}\n\nfunction decodeThreadAddressComponent(value: string): string {\n let decoded: string;\n let canonical: string;\n try {\n decoded = decodeURIComponent(value);\n canonical = encodeURIComponent(decoded);\n } catch (error) {\n throw malformedThreadAddress(error);\n }\n if (!decoded || canonical !== value) {\n throw malformedThreadAddress();\n }\n return decoded;\n}\n\nexport class LoopbackChatAdapter {\n readonly name = \"loopback\";\n readonly persistMessageHistory = true;\n readonly userName;\n\n readonly #messages = new Map<string, StoredLoopbackMessage[]>();\n readonly #nextSequence = new Map<string, number>();\n #lastMessageTimestamp = -1;\n\n constructor(userName: string) {\n this.userName = userName;\n }\n\n addReaction(): Promise<void> {\n return Promise.resolve();\n }\n\n channelIdFromThreadId(threadId: string): string {\n const address = this.decodeThreadId(threadId);\n return address.channelId ?? address.id;\n }\n\n decodeThreadId(threadId: string): ThreadAddress {\n if (threadId.startsWith(LOOPBACK_V2_PREFIX)) {\n const threadParts = threadId.split(\"::\");\n if (threadParts.length > 2) {\n throw malformedThreadAddress();\n }\n const [address = \"\", rawThreadId] = threadParts;\n const addressParts = address.slice(LOOPBACK_V2_PREFIX.length).split(\":\");\n if (\n (addressParts.length !== 1 && addressParts.length !== 2) ||\n addressParts.some((part) => part.length === 0) ||\n rawThreadId === \"\"\n ) {\n throw malformedThreadAddress();\n }\n const [rawChannelOrId = \"\", rawId] = addressParts;\n const decoded: ThreadAddress = {\n id: decodeThreadAddressComponent(rawId ?? rawChannelOrId),\n };\n if (rawId !== undefined) {\n decoded.channelId = decodeThreadAddressComponent(rawChannelOrId);\n }\n if (rawThreadId !== undefined) {\n decoded.threadId = decodeThreadAddressComponent(rawThreadId);\n }\n return decoded;\n }\n\n const [address = threadId, rawThreadId] = threadId.split(\"::\");\n const [, channelId = address, id = address] = address.split(\":\");\n const decoded: ThreadAddress = { id };\n if (channelId) {\n decoded.channelId = channelId;\n }\n if (rawThreadId) {\n decoded.threadId = rawThreadId;\n }\n return decoded;\n }\n\n deleteMessage(threadId: string, messageId: string): Promise<void> {\n const messages = this.#messages.get(threadId) ?? [];\n this.#messages.set(\n threadId,\n messages.filter((entry) => entry.message.id !== messageId),\n );\n return Promise.resolve();\n }\n\n editMessage(\n threadId: string,\n messageId: string,\n message: PostableMessage,\n ): Promise<{ id: string; raw: LoopbackRawMessage; threadId: string }> {\n const messages = this.#messages.get(threadId) ?? [];\n const stored = messages.find((entry) => entry.message.id === messageId);\n if (!stored) {\n throw new CrablineError(`Loopback message not found: ${messageId}`, { kind: \"inbound\" });\n }\n\n const existing = stored.message;\n const text = toPostableText(message);\n existing.text = text;\n existing.formatted = text;\n existing.metadata.edited = true;\n existing.metadata.editedAt = new Date(this.#nextMessageTimestamp());\n existing.raw.text = text;\n return Promise.resolve({ id: existing.id, raw: cloneRawMessage(existing.raw), threadId });\n }\n\n encodeThreadId(platformData: ThreadAddress): string {\n const id = encodeThreadAddressComponent(platformData.id);\n const address =\n platformData.channelId === undefined\n ? `${LOOPBACK_V2_PREFIX}${id}`\n : `${LOOPBACK_V2_PREFIX}${encodeThreadAddressComponent(platformData.channelId)}:${id}`;\n return platformData.threadId === undefined\n ? address\n : `${address}::${encodeThreadAddressComponent(platformData.threadId)}`;\n }\n\n fetchMessages(\n threadId: string,\n options?: { cursor?: string; limit?: number },\n ): Promise<{ messages: LoopbackMessage[]; nextCursor?: string }> {\n const storedMessages = [...(this.#messages.get(threadId) ?? [])];\n if (\n options?.limit !== undefined &&\n (!Number.isSafeInteger(options.limit) || options.limit <= 0)\n ) {\n throw new CrablineError(\"Loopback message limit must be a positive safe integer.\", {\n kind: \"config\",\n });\n }\n const limit = options?.limit ?? storedMessages.length;\n let cursor: number | undefined;\n if (options?.cursor !== undefined) {\n if (!/^[1-9]\\d*$/u.test(options.cursor)) {\n throw new CrablineError(\"Loopback message cursor must be a positive safe integer.\", {\n kind: \"config\",\n });\n }\n cursor = Number(options.cursor);\n if (!Number.isSafeInteger(cursor) || cursor > (this.#nextSequence.get(threadId) ?? 0)) {\n throw new CrablineError(\n \"Loopback message cursor must be a positive safe integer within message history.\",\n { kind: \"config\" },\n );\n }\n }\n\n const eligibleMessages =\n cursor === undefined\n ? storedMessages\n : storedMessages.filter((entry) => entry.sequence < cursor);\n const page = eligibleMessages.slice(-limit);\n const result: { messages: LoopbackMessage[]; nextCursor?: string } = {\n messages: page.map((entry) => cloneMessage(entry.message)),\n };\n if (eligibleMessages.length > page.length && page[0]) {\n result.nextCursor = String(page[0].sequence);\n }\n return Promise.resolve(result);\n }\n\n fetchThread(threadId: string) {\n const address = this.decodeThreadId(threadId);\n return Promise.resolve({\n channelId: address.channelId ?? address.id,\n id: threadId,\n isDM: address.channelId === undefined,\n metadata: {},\n });\n }\n\n handleWebhook(_request?: Request): Promise<Response> {\n return Promise.resolve(\n new Response(\"loopback adapter has no webhook surface\", { status: 501 }),\n );\n }\n\n isDM(): boolean {\n return true;\n }\n\n parseMessage(raw: LoopbackRawMessage): LoopbackMessage {\n return {\n author: {\n isMe: raw.author === \"assistant\",\n userName: raw.author === \"assistant\" ? this.userName : \"loopback\",\n },\n formatted: raw.text,\n id: raw.id,\n metadata: {\n dateSent: new Date(raw.timestamp),\n edited: false,\n },\n raw: cloneRawMessage(raw),\n text: raw.text,\n threadId: raw.threadId,\n };\n }\n\n postMessage(\n threadId: string,\n message: PostableMessage,\n ): Promise<{ id: string; raw: LoopbackRawMessage; threadId: string }> {\n const text = toPostableText(message);\n const raw = {\n author: \"assistant\",\n id: createMessageId(),\n text,\n threadId,\n timestamp: this.#nextMessageTimestamp(),\n } satisfies LoopbackRawMessage;\n const parsed = this.parseMessage(raw);\n this.#append(threadId, parsed);\n return Promise.resolve({ id: raw.id, raw: cloneRawMessage(raw), threadId });\n }\n\n removeReaction(): Promise<void> {\n return Promise.resolve();\n }\n\n renderFormatted(content: string): string {\n return content;\n }\n\n startTyping(): Promise<void> {\n return Promise.resolve();\n }\n\n ingestUserMessage(threadId: string, text: string): LoopbackMessage {\n const raw = {\n author: \"user\",\n id: createMessageId(),\n text,\n threadId,\n timestamp: this.#nextMessageTimestamp(),\n } satisfies LoopbackRawMessage;\n const parsed = this.parseMessage(raw);\n this.#append(threadId, parsed);\n return cloneMessage(parsed);\n }\n\n listSince(threadId: string, since: string): LoopbackMessage[] {\n const sinceTime = new Date(since).getTime();\n if (!Number.isFinite(sinceTime)) {\n throw new CrablineError(\"Loopback since timestamp must be a valid date.\", {\n kind: \"config\",\n });\n }\n return (this.#messages.get(threadId) ?? [])\n .map((entry) => entry.message)\n .filter((message) => message.metadata.dateSent.getTime() >= sinceTime)\n .map(cloneMessage);\n }\n\n #append(threadId: string, message: LoopbackMessage): void {\n const bucket = this.#messages.get(threadId) ?? [];\n const sequence = (this.#nextSequence.get(threadId) ?? 0) + 1;\n bucket.push({ message: cloneMessage(message), sequence });\n this.#messages.set(threadId, bucket);\n this.#nextSequence.set(threadId, sequence);\n }\n\n #nextMessageTimestamp(): string {\n const timestamp = Math.max(Date.now(), this.#lastMessageTimestamp + 1);\n this.#lastMessageTimestamp = timestamp;\n return new Date(timestamp).toISOString();\n }\n}\n\nexport class LoopbackProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, _userName: string) {\n super({\n codec: getBuiltinTargetCodec(\"loopback\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/loopback/webhook\", port: 0 },\n endpointLabel: \"webhook endpoint\",\n platform: \"loopback\",\n recorderPath: resolveGeneratedLocalMockRecorderPath(id, `-${randomUUID()}`),\n },\n });\n }\n}\n"]}
|
|
@@ -5,12 +5,12 @@ export declare function resolveMatrixAdapterConfig(config: ProviderConfig, userN
|
|
|
5
5
|
auth: {
|
|
6
6
|
accessToken: string;
|
|
7
7
|
type: "accessToken";
|
|
8
|
-
userID
|
|
8
|
+
userID: string;
|
|
9
9
|
} | {
|
|
10
10
|
password: string;
|
|
11
11
|
type: "password";
|
|
12
|
-
userID?: string | undefined;
|
|
13
12
|
username: string;
|
|
13
|
+
userID: string;
|
|
14
14
|
};
|
|
15
15
|
baseURL: string;
|
|
16
16
|
commandPrefix: string | undefined;
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { CrablineError } from "../../core/errors.js";
|
|
3
|
+
import { isMatrixEventId, isMatrixRoomId, isMatrixUserId } from "../../matrix-ids.js";
|
|
3
4
|
import { LocalMockProviderAdapter } from "../local-mock.js";
|
|
4
5
|
import { getBuiltinTargetCodec, MATRIX_EVENT_ID_RULE, MATRIX_ROOM_ID_RULE, } from "../target-normalizers.js";
|
|
5
|
-
import { authorFromBotFlag, genericMockPayloadWithNativeThread, isRecord, optionalRecord, optionalString,
|
|
6
|
+
import { authorFromBotFlag, genericMockPayloadWithNativeThread, isRecord, optionalRecord, optionalString, } from "./native-local-mock.js";
|
|
6
7
|
import { requireExternalWebhookAuthentication } from "./external-webhook-auth.js";
|
|
7
8
|
export function resolveMatrixAdapterConfig(config, userName, env = process.env) {
|
|
9
|
+
const fallbackUserId = env.MATRIX_USER_ID?.trim() || `@${userName}:matrix.local`;
|
|
10
|
+
const configuredAuth = config.matrix?.auth
|
|
11
|
+
? {
|
|
12
|
+
...config.matrix.auth,
|
|
13
|
+
userID: config.matrix.auth.userID?.trim() || fallbackUserId,
|
|
14
|
+
}
|
|
15
|
+
: undefined;
|
|
16
|
+
if (!isMatrixUserId(configuredAuth?.userID ?? fallbackUserId)) {
|
|
17
|
+
throw new CrablineError("Matrix auth.userID must be a canonical Matrix user ID.", {
|
|
18
|
+
kind: "config",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
8
21
|
return {
|
|
9
|
-
auth:
|
|
22
|
+
auth: configuredAuth ?? {
|
|
10
23
|
accessToken: env.MATRIX_ACCESS_TOKEN ?? "local-mock-matrix-token",
|
|
11
24
|
type: "accessToken",
|
|
12
|
-
userID:
|
|
25
|
+
userID: fallbackUserId,
|
|
13
26
|
},
|
|
14
27
|
baseURL: config.matrix?.baseURL ?? env.MATRIX_BASE_URL ?? "http://matrix.local",
|
|
15
28
|
commandPrefix: config.matrix?.commandPrefix,
|
|
@@ -52,40 +65,72 @@ export function matchesMatrixThread(candidateThreadId, expectedThreadId, target)
|
|
|
52
65
|
if (!expectedThreadId) {
|
|
53
66
|
return true;
|
|
54
67
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
if (target.channelId && isMatrixEventId(expectedThreadId)) {
|
|
69
|
+
return candidateThreadId === matrixThreadKey(target.channelId, expectedThreadId);
|
|
70
|
+
}
|
|
58
71
|
return (candidateThreadId === expectedThreadId ||
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
candidateThreadId.startsWith(`${scopedExpected}:thread:`)));
|
|
72
|
+
(isMatrixRoomId(expectedThreadId) &&
|
|
73
|
+
candidateThreadId.startsWith(`${expectedThreadId}:thread:`)));
|
|
62
74
|
}
|
|
63
75
|
export function normalizeMatrixWebhookPayload(payload, botUserId) {
|
|
64
76
|
if (!isRecord(payload)) {
|
|
65
77
|
throw new CrablineError("Matrix webhook payload must be an object", { kind: "inbound" });
|
|
66
78
|
}
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
const message = optionalRecord(payload, "message");
|
|
80
|
+
if ((message && ("text" in message || "threadId" in message)) ||
|
|
81
|
+
"text" in payload ||
|
|
82
|
+
"threadId" in payload) {
|
|
83
|
+
const normalized = genericMockPayloadWithNativeThread({
|
|
69
84
|
channelRule: MATRIX_ROOM_ID_RULE,
|
|
70
85
|
payload,
|
|
71
86
|
threadRule: MATRIX_EVENT_ID_RULE,
|
|
72
87
|
});
|
|
88
|
+
const threadId = (message ? optionalString(message, "threadId") : undefined) ??
|
|
89
|
+
optionalString(payload, "threadId");
|
|
90
|
+
const roomId = (message ? optionalString(message, "channelId") : undefined) ??
|
|
91
|
+
optionalString(payload, "channelId") ??
|
|
92
|
+
optionalString(payload, "roomId") ??
|
|
93
|
+
optionalString(payload, "room_id");
|
|
94
|
+
if ("threadId" in normalized &&
|
|
95
|
+
!isMatrixRoomId(normalized.threadId) &&
|
|
96
|
+
!isMatrixEventId(normalized.threadId)) {
|
|
97
|
+
throw new CrablineError(`mock webhook threadId must be a native ${MATRIX_EVENT_ID_RULE.name} or ${MATRIX_ROOM_ID_RULE.name}.`, { kind: "inbound" });
|
|
98
|
+
}
|
|
99
|
+
if (threadId && isMatrixEventId(threadId)) {
|
|
100
|
+
if (!roomId || !isMatrixRoomId(roomId)) {
|
|
101
|
+
throw new CrablineError(`Matrix generic event threadId requires a native ${MATRIX_ROOM_ID_RULE.name} channelId.`, { kind: "inbound" });
|
|
102
|
+
}
|
|
103
|
+
const scopedThreadId = matrixThreadKey(roomId, threadId);
|
|
104
|
+
return {
|
|
105
|
+
...normalized,
|
|
106
|
+
...("message" in normalized && isRecord(normalized.message)
|
|
107
|
+
? { message: { ...normalized.message, threadId: scopedThreadId } }
|
|
108
|
+
: {}),
|
|
109
|
+
threadId: scopedThreadId,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (threadId && isMatrixRoomId(threadId) && roomId && roomId !== threadId) {
|
|
113
|
+
throw new CrablineError("Matrix generic room threadId must match channelId.", {
|
|
114
|
+
kind: "inbound",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return normalized;
|
|
73
118
|
}
|
|
74
119
|
const content = optionalRecord(payload, "content");
|
|
75
120
|
const roomId = optionalString(payload, "room_id");
|
|
76
121
|
const eventId = optionalString(payload, "event_id");
|
|
77
122
|
const eventType = optionalString(payload, "type");
|
|
78
|
-
const
|
|
79
|
-
const
|
|
123
|
+
const sender = payload.sender;
|
|
124
|
+
const senderId = typeof sender === "string" ? sender : undefined;
|
|
125
|
+
const msgtype = content?.msgtype;
|
|
126
|
+
const text = content?.body;
|
|
80
127
|
if (eventType !== "m.room.message") {
|
|
81
128
|
throw new CrablineError("Matrix event payload requires type=m.room.message", {
|
|
82
129
|
kind: "inbound",
|
|
83
130
|
});
|
|
84
131
|
}
|
|
85
|
-
if (!roomId || !eventId ||
|
|
86
|
-
throw new CrablineError("Matrix event payload requires room_id, event_id, and content.body", {
|
|
87
|
-
kind: "inbound",
|
|
88
|
-
});
|
|
132
|
+
if (!roomId || !eventId || typeof msgtype !== "string" || typeof text !== "string") {
|
|
133
|
+
throw new CrablineError("Matrix event payload requires room_id, event_id, string content.msgtype, and string content.body", { kind: "inbound" });
|
|
89
134
|
}
|
|
90
135
|
const relation = content ? optionalRecord(content, "m.relates_to") : undefined;
|
|
91
136
|
const isThread = relation && optionalString(relation, "rel_type") === "m.thread";
|
|
@@ -95,14 +140,26 @@ export function normalizeMatrixWebhookPayload(payload, botUserId) {
|
|
|
95
140
|
kind: "inbound",
|
|
96
141
|
});
|
|
97
142
|
}
|
|
143
|
+
if (!isMatrixRoomId(roomId)) {
|
|
144
|
+
throw new CrablineError(`Matrix room_id must be a native ${MATRIX_ROOM_ID_RULE.name} such as ${MATRIX_ROOM_ID_RULE.example}.`, { kind: "inbound" });
|
|
145
|
+
}
|
|
146
|
+
if (!isMatrixEventId(eventId)) {
|
|
147
|
+
throw new CrablineError(`Matrix event_id must be a native ${MATRIX_EVENT_ID_RULE.name} such as ${MATRIX_EVENT_ID_RULE.example}.`, { kind: "inbound" });
|
|
148
|
+
}
|
|
149
|
+
if (threadRootId && !isMatrixEventId(threadRootId)) {
|
|
150
|
+
throw new CrablineError(`Matrix thread root event_id must be a native ${MATRIX_EVENT_ID_RULE.name} such as ${MATRIX_EVENT_ID_RULE.example}.`, { kind: "inbound" });
|
|
151
|
+
}
|
|
152
|
+
if (!senderId || !isMatrixUserId(senderId)) {
|
|
153
|
+
throw new CrablineError("Matrix sender must be a canonical Matrix user ID.", {
|
|
154
|
+
kind: "inbound",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
98
157
|
return {
|
|
99
158
|
author: authorFromBotFlag(senderId !== undefined && senderId === botUserId),
|
|
100
|
-
id:
|
|
159
|
+
id: eventId,
|
|
101
160
|
raw: payload,
|
|
102
161
|
text,
|
|
103
|
-
threadId: threadRootId
|
|
104
|
-
? matrixThreadKey(requireNativeInboundId(roomId, MATRIX_ROOM_ID_RULE, "Matrix room_id"), requireNativeInboundId(threadRootId, MATRIX_EVENT_ID_RULE, "Matrix thread root event_id"))
|
|
105
|
-
: requireNativeInboundId(roomId, MATRIX_ROOM_ID_RULE, "Matrix room_id"),
|
|
162
|
+
threadId: threadRootId ? matrixThreadKey(roomId, threadRootId) : roomId,
|
|
106
163
|
};
|
|
107
164
|
}
|
|
108
165
|
//# sourceMappingURL=matrix.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matrix.js","sourceRoot":"","sources":["../../../../src/providers/builtin/matrix.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport { CrablineError } from \"../../core/errors.js\";\nimport type { ProviderConfig } from \"../../config/schema.js\";\nimport { LocalMockProviderAdapter } from \"../local-mock.js\";\nimport type { ProviderAdapter } from \"../types.js\";\nimport {\n getBuiltinTargetCodec,\n MATRIX_EVENT_ID_RULE,\n MATRIX_ROOM_ID_RULE,\n} from \"../target-normalizers.js\";\nimport {\n authorFromBotFlag,\n genericMockPayloadWithNativeThread,\n isRecord,\n optionalRecord,\n optionalString,\n requireNativeInboundId,\n} from \"./native-local-mock.js\";\nimport { requireExternalWebhookAuthentication } from \"./external-webhook-auth.js\";\n\nexport function resolveMatrixAdapterConfig(\n config: ProviderConfig,\n userName: string,\n env: NodeJS.ProcessEnv = process.env,\n) {\n return {\n auth: config.matrix?.auth ?? {\n accessToken: env.MATRIX_ACCESS_TOKEN ?? \"local-mock-matrix-token\",\n type: \"accessToken\" as const,\n userID: env.MATRIX_USER_ID?.trim() || `@${userName}:matrix.local`,\n },\n baseURL: config.matrix?.baseURL ?? env.MATRIX_BASE_URL ?? \"http://matrix.local\",\n commandPrefix: config.matrix?.commandPrefix,\n recoveryKey: config.matrix?.recoveryKey ?? env.MATRIX_RECOVERY_KEY,\n };\n}\n\nexport class MatrixProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, userName: string, _runtime?: unknown) {\n const resolvedConfig = resolveMatrixAdapterConfig(config, userName);\n const botUserId = resolvedConfig.auth.userID;\n requireExternalWebhookAuthentication({\n authenticated: false,\n provider: \"Matrix\",\n requirement:\n \"a provider-native authenticated ingress mode, which this adapter does not support\",\n webhook: config.matrix?.webhook,\n });\n super({\n codec: getBuiltinTargetCodec(\"matrix\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/matrix/webhook\", port: 8797 },\n endpointLabel: \"webhook endpoint\",\n matchesThread: matchesMatrixThread,\n normalizeWebhookPayload: (payload) => normalizeMatrixWebhookPayload(payload, botUserId),\n platform: \"matrix\",\n publicUrl: config.matrix?.webhook.publicUrl,\n recorderPath: config.matrix?.recorder.path\n ? path.resolve(config.matrix.recorder.path)\n : undefined,\n webhook: config.matrix?.webhook,\n },\n });\n }\n}\n\nfunction matrixThreadKey(roomId: string, threadId: string): string {\n return `${roomId}:thread:${threadId}`;\n}\n\nexport function matchesMatrixThread(\n candidateThreadId: string,\n expectedThreadId: string | undefined,\n target: { channelId?: string | undefined },\n): boolean {\n if (!expectedThreadId) {\n return true;\n }\n const scopedExpected =\n target.channelId && MATRIX_EVENT_ID_RULE.pattern.test(expectedThreadId)\n ? matrixThreadKey(target.channelId, expectedThreadId)\n : expectedThreadId;\n return (\n candidateThreadId === expectedThreadId ||\n candidateThreadId === scopedExpected ||\n (MATRIX_ROOM_ID_RULE.pattern.test(scopedExpected) &&\n candidateThreadId.startsWith(`${scopedExpected}:thread:`))\n );\n}\n\nexport function normalizeMatrixWebhookPayload(payload: unknown, botUserId?: string) {\n if (!isRecord(payload)) {\n throw new CrablineError(\"Matrix webhook payload must be an object\", { kind: \"inbound\" });\n }\n\n if (optionalRecord(payload, \"message\")) {\n return genericMockPayloadWithNativeThread({\n channelRule: MATRIX_ROOM_ID_RULE,\n payload,\n threadRule: MATRIX_EVENT_ID_RULE,\n });\n }\n\n const content = optionalRecord(payload, \"content\");\n const roomId = optionalString(payload, \"room_id\");\n const eventId = optionalString(payload, \"event_id\");\n const eventType = optionalString(payload, \"type\");\n const senderId = optionalString(payload, \"sender\");\n const text = content ? optionalString(content, \"body\") : undefined;\n if (eventType !== \"m.room.message\") {\n throw new CrablineError(\"Matrix event payload requires type=m.room.message\", {\n kind: \"inbound\",\n });\n }\n if (!roomId || !eventId || !text) {\n throw new CrablineError(\"Matrix event payload requires room_id, event_id, and content.body\", {\n kind: \"inbound\",\n });\n }\n\n const relation = content ? optionalRecord(content, \"m.relates_to\") : undefined;\n const isThread = relation && optionalString(relation, \"rel_type\") === \"m.thread\";\n const threadRootId = isThread ? optionalString(relation, \"event_id\") : undefined;\n if (isThread && !threadRootId) {\n throw new CrablineError(\"Matrix m.thread relation requires event_id\", {\n kind: \"inbound\",\n });\n }\n\n return {\n author: authorFromBotFlag(senderId !== undefined && senderId === botUserId),\n id: requireNativeInboundId(eventId, MATRIX_EVENT_ID_RULE, \"Matrix event_id\"),\n raw: payload,\n text,\n threadId: threadRootId\n ? matrixThreadKey(\n requireNativeInboundId(roomId, MATRIX_ROOM_ID_RULE, \"Matrix room_id\"),\n requireNativeInboundId(threadRootId, MATRIX_EVENT_ID_RULE, \"Matrix thread root event_id\"),\n )\n : requireNativeInboundId(roomId, MATRIX_ROOM_ID_RULE, \"Matrix room_id\"),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"matrix.js","sourceRoot":"","sources":["../../../../src/providers/builtin/matrix.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport { CrablineError } from \"../../core/errors.js\";\nimport type { ProviderConfig } from \"../../config/schema.js\";\nimport { isMatrixEventId, isMatrixRoomId, isMatrixUserId } from \"../../matrix-ids.js\";\nimport { LocalMockProviderAdapter } from \"../local-mock.js\";\nimport type { ProviderAdapter } from \"../types.js\";\nimport {\n getBuiltinTargetCodec,\n MATRIX_EVENT_ID_RULE,\n MATRIX_ROOM_ID_RULE,\n} from \"../target-normalizers.js\";\nimport {\n authorFromBotFlag,\n genericMockPayloadWithNativeThread,\n isRecord,\n optionalRecord,\n optionalString,\n} from \"./native-local-mock.js\";\nimport { requireExternalWebhookAuthentication } from \"./external-webhook-auth.js\";\n\nexport function resolveMatrixAdapterConfig(\n config: ProviderConfig,\n userName: string,\n env: NodeJS.ProcessEnv = process.env,\n) {\n const fallbackUserId = env.MATRIX_USER_ID?.trim() || `@${userName}:matrix.local`;\n const configuredAuth = config.matrix?.auth\n ? {\n ...config.matrix.auth,\n userID: config.matrix.auth.userID?.trim() || fallbackUserId,\n }\n : undefined;\n if (!isMatrixUserId(configuredAuth?.userID ?? fallbackUserId)) {\n throw new CrablineError(\"Matrix auth.userID must be a canonical Matrix user ID.\", {\n kind: \"config\",\n });\n }\n return {\n auth: configuredAuth ?? {\n accessToken: env.MATRIX_ACCESS_TOKEN ?? \"local-mock-matrix-token\",\n type: \"accessToken\" as const,\n userID: fallbackUserId,\n },\n baseURL: config.matrix?.baseURL ?? env.MATRIX_BASE_URL ?? \"http://matrix.local\",\n commandPrefix: config.matrix?.commandPrefix,\n recoveryKey: config.matrix?.recoveryKey ?? env.MATRIX_RECOVERY_KEY,\n };\n}\n\nexport class MatrixProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, userName: string, _runtime?: unknown) {\n const resolvedConfig = resolveMatrixAdapterConfig(config, userName);\n const botUserId = resolvedConfig.auth.userID;\n requireExternalWebhookAuthentication({\n authenticated: false,\n provider: \"Matrix\",\n requirement:\n \"a provider-native authenticated ingress mode, which this adapter does not support\",\n webhook: config.matrix?.webhook,\n });\n super({\n codec: getBuiltinTargetCodec(\"matrix\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/matrix/webhook\", port: 8797 },\n endpointLabel: \"webhook endpoint\",\n matchesThread: matchesMatrixThread,\n normalizeWebhookPayload: (payload) => normalizeMatrixWebhookPayload(payload, botUserId),\n platform: \"matrix\",\n publicUrl: config.matrix?.webhook.publicUrl,\n recorderPath: config.matrix?.recorder.path\n ? path.resolve(config.matrix.recorder.path)\n : undefined,\n webhook: config.matrix?.webhook,\n },\n });\n }\n}\n\nfunction matrixThreadKey(roomId: string, threadId: string): string {\n return `${roomId}:thread:${threadId}`;\n}\n\nexport function matchesMatrixThread(\n candidateThreadId: string,\n expectedThreadId: string | undefined,\n target: { channelId?: string | undefined },\n): boolean {\n if (!expectedThreadId) {\n return true;\n }\n if (target.channelId && isMatrixEventId(expectedThreadId)) {\n return candidateThreadId === matrixThreadKey(target.channelId, expectedThreadId);\n }\n return (\n candidateThreadId === expectedThreadId ||\n (isMatrixRoomId(expectedThreadId) &&\n candidateThreadId.startsWith(`${expectedThreadId}:thread:`))\n );\n}\n\nexport function normalizeMatrixWebhookPayload(payload: unknown, botUserId?: string) {\n if (!isRecord(payload)) {\n throw new CrablineError(\"Matrix webhook payload must be an object\", { kind: \"inbound\" });\n }\n\n const message = optionalRecord(payload, \"message\");\n if (\n (message && (\"text\" in message || \"threadId\" in message)) ||\n \"text\" in payload ||\n \"threadId\" in payload\n ) {\n const normalized = genericMockPayloadWithNativeThread({\n channelRule: MATRIX_ROOM_ID_RULE,\n payload,\n threadRule: MATRIX_EVENT_ID_RULE,\n });\n const threadId =\n (message ? optionalString(message, \"threadId\") : undefined) ??\n optionalString(payload, \"threadId\");\n const roomId =\n (message ? optionalString(message, \"channelId\") : undefined) ??\n optionalString(payload, \"channelId\") ??\n optionalString(payload, \"roomId\") ??\n optionalString(payload, \"room_id\");\n if (\n \"threadId\" in normalized &&\n !isMatrixRoomId(normalized.threadId) &&\n !isMatrixEventId(normalized.threadId)\n ) {\n throw new CrablineError(\n `mock webhook threadId must be a native ${MATRIX_EVENT_ID_RULE.name} or ${MATRIX_ROOM_ID_RULE.name}.`,\n { kind: \"inbound\" },\n );\n }\n if (threadId && isMatrixEventId(threadId)) {\n if (!roomId || !isMatrixRoomId(roomId)) {\n throw new CrablineError(\n `Matrix generic event threadId requires a native ${MATRIX_ROOM_ID_RULE.name} channelId.`,\n { kind: \"inbound\" },\n );\n }\n const scopedThreadId = matrixThreadKey(roomId, threadId);\n return {\n ...normalized,\n ...(\"message\" in normalized && isRecord(normalized.message)\n ? { message: { ...normalized.message, threadId: scopedThreadId } }\n : {}),\n threadId: scopedThreadId,\n };\n }\n if (threadId && isMatrixRoomId(threadId) && roomId && roomId !== threadId) {\n throw new CrablineError(\"Matrix generic room threadId must match channelId.\", {\n kind: \"inbound\",\n });\n }\n return normalized;\n }\n\n const content = optionalRecord(payload, \"content\");\n const roomId = optionalString(payload, \"room_id\");\n const eventId = optionalString(payload, \"event_id\");\n const eventType = optionalString(payload, \"type\");\n const sender = payload.sender;\n const senderId = typeof sender === \"string\" ? sender : undefined;\n const msgtype = content?.msgtype;\n const text = content?.body;\n if (eventType !== \"m.room.message\") {\n throw new CrablineError(\"Matrix event payload requires type=m.room.message\", {\n kind: \"inbound\",\n });\n }\n if (!roomId || !eventId || typeof msgtype !== \"string\" || typeof text !== \"string\") {\n throw new CrablineError(\n \"Matrix event payload requires room_id, event_id, string content.msgtype, and string content.body\",\n { kind: \"inbound\" },\n );\n }\n\n const relation = content ? optionalRecord(content, \"m.relates_to\") : undefined;\n const isThread = relation && optionalString(relation, \"rel_type\") === \"m.thread\";\n const threadRootId = isThread ? optionalString(relation, \"event_id\") : undefined;\n if (isThread && !threadRootId) {\n throw new CrablineError(\"Matrix m.thread relation requires event_id\", {\n kind: \"inbound\",\n });\n }\n if (!isMatrixRoomId(roomId)) {\n throw new CrablineError(\n `Matrix room_id must be a native ${MATRIX_ROOM_ID_RULE.name} such as ${MATRIX_ROOM_ID_RULE.example}.`,\n { kind: \"inbound\" },\n );\n }\n if (!isMatrixEventId(eventId)) {\n throw new CrablineError(\n `Matrix event_id must be a native ${MATRIX_EVENT_ID_RULE.name} such as ${MATRIX_EVENT_ID_RULE.example}.`,\n { kind: \"inbound\" },\n );\n }\n if (threadRootId && !isMatrixEventId(threadRootId)) {\n throw new CrablineError(\n `Matrix thread root event_id must be a native ${MATRIX_EVENT_ID_RULE.name} such as ${MATRIX_EVENT_ID_RULE.example}.`,\n { kind: \"inbound\" },\n );\n }\n if (!senderId || !isMatrixUserId(senderId)) {\n throw new CrablineError(\"Matrix sender must be a canonical Matrix user ID.\", {\n kind: \"inbound\",\n });\n }\n\n return {\n author: authorFromBotFlag(senderId !== undefined && senderId === botUserId),\n id: eventId,\n raw: payload,\n text,\n threadId: threadRootId ? matrixThreadKey(roomId, threadRootId) : roomId,\n };\n}\n"]}
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import type { ProviderConfig } from "../../config/schema.js";
|
|
2
2
|
import { LocalMockProviderAdapter } from "../local-mock.js";
|
|
3
3
|
import type { ProviderAdapter } from "../types.js";
|
|
4
|
-
|
|
4
|
+
type MattermostEnvironment = Partial<Pick<NodeJS.ProcessEnv, "MATTERMOST_BASE_URL" | "MATTERMOST_BOT_TOKEN" | "MATTERMOST_TOKEN" | "MATTERMOST_URL">>;
|
|
5
|
+
export declare function resolveMattermostAdapterConfig(config: ProviderConfig, env?: MattermostEnvironment): {
|
|
5
6
|
baseUrl: string;
|
|
6
7
|
botToken: string;
|
|
7
8
|
userName: string | undefined;
|
|
9
|
+
webhookToken: string | undefined;
|
|
8
10
|
};
|
|
9
11
|
export declare class MattermostProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {
|
|
10
|
-
constructor(id: string, config: ProviderConfig, _userName: string,
|
|
12
|
+
constructor(id: string, config: ProviderConfig, _userName: string, runtime?: unknown);
|
|
11
13
|
}
|
|
12
14
|
export declare function matchesMattermostThread(candidateThreadId: string, expectedThreadId: string | undefined, target: {
|
|
13
15
|
channelId?: string | undefined;
|
|
14
16
|
}): boolean;
|
|
15
|
-
|
|
16
|
-
author
|
|
17
|
-
authorIsBot?: boolean;
|
|
17
|
+
type NormalizedMattermostWebhookPayload = {
|
|
18
|
+
author: "assistant" | "system" | "user";
|
|
18
19
|
id?: string | undefined;
|
|
19
|
-
raw:
|
|
20
|
-
text
|
|
20
|
+
raw: unknown;
|
|
21
|
+
text: string;
|
|
22
|
+
threadId: string;
|
|
21
23
|
};
|
|
24
|
+
export declare function normalizeMattermostWebhookPayload(payload: unknown): NormalizedMattermostWebhookPayload;
|
|
25
|
+
export {};
|