@openclaw/crabline 0.1.10 → 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 +12 -2
- package/dist/src/openclaw/artifact-generation.js +436 -15
- 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 +40 -3
- package/dist/src/openclaw/private-file.js +2070 -95
- 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 +696 -187
- package/dist/src/openclaw/smoke-lock.js.map +1 -1
- package/dist/src/openclaw.js +352 -29
- 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 +532 -42
- 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 +1241 -22
- 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 +1193 -118
- 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
|
-
import { createPublicKey } from "node:crypto";
|
|
1
|
+
import { createPublicKey, X509Certificate } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { CrablineError } from "../../core/errors.js";
|
|
4
4
|
import { LocalMockProviderAdapter } from "../local-mock.js";
|
|
5
|
-
import { createCachedJwtKeyResolver, readBearerToken, resolveHttpCacheExpiry, verifySignedJwt, } from "../signed-jwt.js";
|
|
5
|
+
import { createCachedJwtKeyResolver, JwtKeyInfrastructureError, readBearerToken, resolveHttpCacheExpiry, verifySignedJwt, } from "../signed-jwt.js";
|
|
6
6
|
import { getBuiltinTargetCodec, GOOGLE_CHAT_SPACE_RULE, GOOGLE_CHAT_THREAD_RULE, } from "../target-normalizers.js";
|
|
7
7
|
import { authorFromBotFlag, genericMockPayloadWithNativeThread, isRecord, optionalRecord, optionalString, requireNativeInboundId, } from "./native-local-mock.js";
|
|
8
8
|
import { requireExternalWebhookAuthentication } from "./external-webhook-auth.js";
|
|
@@ -17,6 +17,7 @@ export function resolveGoogleChatAdapterConfig(config, _env = process.env) {
|
|
|
17
17
|
const GOOGLE_CHAT_SERVICE_ACCOUNT = "chat@system.gserviceaccount.com";
|
|
18
18
|
const GOOGLE_OAUTH_CERTS_URL = "https://www.googleapis.com/oauth2/v1/certs";
|
|
19
19
|
const GOOGLE_CHAT_CERTS_URL = "https://www.googleapis.com/service_accounts/v1/metadata/x509/chat@system.gserviceaccount.com";
|
|
20
|
+
const GOOGLE_CHAT_MESSAGE_NAME_PATTERN = /^spaces\/[A-Za-z0-9_-]+\/messages\/[^/\s]+$/u;
|
|
20
21
|
export function createGoogleChatWebhookAuthenticator(config, runtime = {}) {
|
|
21
22
|
if (config.googlechat?.disableSignatureVerification) {
|
|
22
23
|
return undefined;
|
|
@@ -32,20 +33,42 @@ export function createGoogleChatWebhookAuthenticator(config, runtime = {}) {
|
|
|
32
33
|
const createCertificateResolver = (certificateUrl) => {
|
|
33
34
|
return createCachedJwtKeyResolver({
|
|
34
35
|
async fetchKeys(signal) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
try {
|
|
37
|
+
const requestedAt = runtime.now?.() ?? Date.now();
|
|
38
|
+
const response = await fetchImpl(certificateUrl, { signal });
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
throw new Error(`Google certificate fetch failed with HTTP ${response.status}.`);
|
|
41
|
+
}
|
|
42
|
+
const body = await response.json();
|
|
43
|
+
if (!isRecord(body)) {
|
|
44
|
+
throw new Error("Google certificate response must be an object.");
|
|
45
|
+
}
|
|
46
|
+
const values = Object.entries(body).map(([kid, certificate]) => {
|
|
47
|
+
if (typeof certificate !== "string") {
|
|
48
|
+
throw new Error("Google certificate response values must be strings.");
|
|
49
|
+
}
|
|
50
|
+
const certificateLabel = ["-----BEGIN", "CERTIFICATE-----"].join(" ");
|
|
51
|
+
const key = certificate.includes(certificateLabel)
|
|
52
|
+
? new X509Certificate(certificate).publicKey
|
|
53
|
+
: createPublicKey(certificate);
|
|
54
|
+
if (key.asymmetricKeyType !== "rsa") {
|
|
55
|
+
throw new Error("Google certificate response keys must use RSA.");
|
|
56
|
+
}
|
|
57
|
+
return { kid, key };
|
|
58
|
+
});
|
|
59
|
+
if (values.length === 0) {
|
|
60
|
+
throw new Error("Google certificate response must include signing keys.");
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
expiresAt: resolveHttpCacheExpiry(response, requestedAt),
|
|
64
|
+
values,
|
|
65
|
+
};
|
|
39
66
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw error instanceof JwtKeyInfrastructureError
|
|
69
|
+
? error
|
|
70
|
+
: new JwtKeyInfrastructureError(error instanceof Error ? error.message : "Google certificate fetch failed.", { cause: error });
|
|
43
71
|
}
|
|
44
|
-
const values = Object.entries(body).flatMap(([kid, certificate]) => typeof certificate === "string" ? [{ certificate, kid }] : []);
|
|
45
|
-
return {
|
|
46
|
-
expiresAt: resolveHttpCacheExpiry(response, fetchedAt),
|
|
47
|
-
values,
|
|
48
|
-
};
|
|
49
72
|
},
|
|
50
73
|
keyId: (value) => value.kid,
|
|
51
74
|
now: runtime.now,
|
|
@@ -70,7 +93,10 @@ export function createGoogleChatWebhookAuthenticator(config, runtime = {}) {
|
|
|
70
93
|
payload = JSON.parse(rawBody);
|
|
71
94
|
}
|
|
72
95
|
catch {
|
|
73
|
-
return
|
|
96
|
+
return new Response("unauthorized", {
|
|
97
|
+
headers: { "www-authenticate": "Bearer" },
|
|
98
|
+
status: 401,
|
|
99
|
+
});
|
|
74
100
|
}
|
|
75
101
|
const isPubsub = isRecord(payload) &&
|
|
76
102
|
isRecord(payload.message) &&
|
|
@@ -94,7 +120,7 @@ export function createGoogleChatWebhookAuthenticator(config, runtime = {}) {
|
|
|
94
120
|
const certificate = await (certificateUrl === GOOGLE_OAUTH_CERTS_URL
|
|
95
121
|
? resolveGoogleIdentityCertificate(header)
|
|
96
122
|
: resolveGoogleChatCertificate(header));
|
|
97
|
-
return
|
|
123
|
+
return certificate.key;
|
|
98
124
|
},
|
|
99
125
|
token,
|
|
100
126
|
});
|
|
@@ -111,14 +137,20 @@ export function createGoogleChatWebhookAuthenticator(config, runtime = {}) {
|
|
|
111
137
|
}
|
|
112
138
|
return undefined;
|
|
113
139
|
}
|
|
114
|
-
catch {
|
|
115
|
-
return
|
|
116
|
-
headers: { "www-authenticate": "Bearer" },
|
|
117
|
-
status: 401,
|
|
118
|
-
});
|
|
140
|
+
catch (error) {
|
|
141
|
+
return googleAuthenticationResponse(error instanceof JwtKeyInfrastructureError ? 503 : 401);
|
|
119
142
|
}
|
|
120
143
|
};
|
|
121
144
|
}
|
|
145
|
+
function googleAuthenticationResponse(status) {
|
|
146
|
+
return new Response(status === 401 ? "unauthorized" : "service unavailable", {
|
|
147
|
+
headers: {
|
|
148
|
+
"cache-control": "no-store",
|
|
149
|
+
...(status === 401 ? { "www-authenticate": "Bearer" } : {}),
|
|
150
|
+
},
|
|
151
|
+
status,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
122
154
|
export function matchesGoogleChatThread(candidateThreadId, expectedThreadId, target = {}) {
|
|
123
155
|
const candidateSpace = GOOGLE_CHAT_THREAD_RULE.pattern.test(candidateThreadId)
|
|
124
156
|
? candidateThreadId.slice(0, candidateThreadId.indexOf("/threads/"))
|
|
@@ -137,12 +169,18 @@ export class GoogleChatProviderAdapter extends LocalMockProviderAdapter {
|
|
|
137
169
|
constructor(id, config, _userName, runtime) {
|
|
138
170
|
const endpointAudience = config.googlechat?.endpointUrl ?? config.googlechat?.webhook.publicUrl;
|
|
139
171
|
const pubsubServiceAccount = config.googlechat?.pubsubServiceAccountEmail ?? config.googlechat?.credentials?.client_email;
|
|
172
|
+
if (config.googlechat?.pubsubAudience && !pubsubServiceAccount) {
|
|
173
|
+
throw new CrablineError("Google Chat pubsubAudience requires a Pub/Sub service-account identity via pubsubServiceAccountEmail or credentials.client_email.", { kind: "config" });
|
|
174
|
+
}
|
|
140
175
|
const authenticationConfigured = !config.googlechat?.disableSignatureVerification &&
|
|
141
176
|
Boolean(endpointAudience ||
|
|
142
177
|
config.googlechat?.googleChatProjectNumber ||
|
|
143
178
|
(config.googlechat?.pubsubAudience && pubsubServiceAccount));
|
|
144
179
|
requireExternalWebhookAuthentication({
|
|
145
180
|
authenticated: authenticationConfigured,
|
|
181
|
+
authenticatedIngressUrls: config.googlechat?.endpointUrl
|
|
182
|
+
? [config.googlechat.endpointUrl]
|
|
183
|
+
: undefined,
|
|
146
184
|
provider: "Google Chat",
|
|
147
185
|
requirement: "googlechat.endpointUrl, googlechat.googleChatProjectNumber, or googlechat.pubsubAudience with a Pub/Sub service-account identity and signature verification enabled",
|
|
148
186
|
webhook: config.googlechat?.webhook,
|
|
@@ -154,8 +192,10 @@ export class GoogleChatProviderAdapter extends LocalMockProviderAdapter {
|
|
|
154
192
|
id,
|
|
155
193
|
options: {
|
|
156
194
|
...(authenticateWebhookRequest ? { authenticateWebhookRequest } : {}),
|
|
195
|
+
createWebhookSuccessResponse: () => new Response(null, { status: 200 }),
|
|
157
196
|
defaultWebhook: { host: "127.0.0.1", path: "/googlechat/webhook", port: 8792 },
|
|
158
197
|
endpointLabel: "webhook endpoint",
|
|
198
|
+
handleWebhookPayload: handleGoogleChatWebhookPayload,
|
|
159
199
|
matchesThread: matchesGoogleChatThread,
|
|
160
200
|
normalizeWebhookPayload: normalizeGoogleChatWebhookPayload,
|
|
161
201
|
platform: "googlechat",
|
|
@@ -164,6 +204,7 @@ export class GoogleChatProviderAdapter extends LocalMockProviderAdapter {
|
|
|
164
204
|
? path.resolve(config.googlechat.recorder.path)
|
|
165
205
|
: undefined,
|
|
166
206
|
webhook: config.googlechat?.webhook,
|
|
207
|
+
webhookMethods: ["POST"],
|
|
167
208
|
},
|
|
168
209
|
});
|
|
169
210
|
}
|
|
@@ -193,27 +234,55 @@ export function normalizeGoogleChatWebhookPayload(payload) {
|
|
|
193
234
|
const sender = optionalRecord(message, "sender");
|
|
194
235
|
const spaceName = space ? optionalString(space, "name") : undefined;
|
|
195
236
|
const threadName = thread ? optionalString(thread, "name") : undefined;
|
|
237
|
+
const messageName = optionalString(message, "name");
|
|
196
238
|
const text = optionalString(message, "text") ?? optionalString(message, "argumentText");
|
|
197
239
|
if (!spaceName || !text) {
|
|
198
240
|
throw new CrablineError("Google Chat message payload requires space.name and text", {
|
|
199
241
|
kind: "inbound",
|
|
200
242
|
});
|
|
201
243
|
}
|
|
202
|
-
|
|
244
|
+
const validatedSpaceName = requireNativeInboundId(spaceName, GOOGLE_CHAT_SPACE_RULE, "Google Chat space.name");
|
|
245
|
+
if (messageName) {
|
|
246
|
+
if (!GOOGLE_CHAT_MESSAGE_NAME_PATTERN.test(messageName)) {
|
|
247
|
+
throw new CrablineError("Google Chat message.name must be a valid message resource name", {
|
|
248
|
+
kind: "inbound",
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (!messageName.startsWith(`${validatedSpaceName}/messages/`)) {
|
|
252
|
+
throw new CrablineError("Google Chat message.name must belong to message.space.name", {
|
|
253
|
+
kind: "inbound",
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const validatedThreadName = threadName
|
|
258
|
+
? requireNativeInboundId(threadName, GOOGLE_CHAT_THREAD_RULE, "Google Chat thread.name")
|
|
259
|
+
: undefined;
|
|
260
|
+
if (validatedThreadName && !validatedThreadName.startsWith(`${validatedSpaceName}/threads/`)) {
|
|
203
261
|
throw new CrablineError("Google Chat thread.name must belong to message.space.name", {
|
|
204
262
|
kind: "inbound",
|
|
205
263
|
});
|
|
206
264
|
}
|
|
207
265
|
return {
|
|
208
266
|
author: authorFromBotFlag(optionalString(sender ?? {}, "type") === "BOT"),
|
|
209
|
-
...(
|
|
267
|
+
...(messageName ? { id: messageName } : {}),
|
|
210
268
|
raw: payload,
|
|
211
269
|
text,
|
|
212
|
-
threadId:
|
|
213
|
-
? requireNativeInboundId(threadName, GOOGLE_CHAT_THREAD_RULE, "Google Chat thread.name")
|
|
214
|
-
: requireNativeInboundId(spaceName, GOOGLE_CHAT_SPACE_RULE, "Google Chat space.name"),
|
|
270
|
+
threadId: validatedThreadName ?? validatedSpaceName,
|
|
215
271
|
};
|
|
216
272
|
}
|
|
273
|
+
export function handleGoogleChatWebhookPayload(payload) {
|
|
274
|
+
let event;
|
|
275
|
+
try {
|
|
276
|
+
event = unwrapGoogleChatPubsubPayload(payload);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
if (isRecord(event) && typeof event.type === "string" && event.type !== "MESSAGE") {
|
|
282
|
+
return new Response(null, { status: 200 });
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
217
286
|
function unwrapGoogleChatPubsubPayload(payload) {
|
|
218
287
|
if (!isRecord(payload)) {
|
|
219
288
|
return payload;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"googlechat.js","sourceRoot":"","sources":["../../../../src/providers/builtin/googlechat.ts"],"names":[],"mappings":"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","sourcesContent":["import { createPublicKey } 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 type { ProviderAdapter } from \"../types.js\";\nimport {\n createCachedJwtKeyResolver,\n readBearerToken,\n resolveHttpCacheExpiry,\n verifySignedJwt,\n} from \"../signed-jwt.js\";\nimport {\n getBuiltinTargetCodec,\n GOOGLE_CHAT_SPACE_RULE,\n GOOGLE_CHAT_THREAD_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 resolveGoogleChatAdapterConfig(\n config: ProviderConfig,\n _env: NodeJS.ProcessEnv = process.env,\n) {\n return {\n endpointUrl: config.googlechat?.endpointUrl,\n projectNumber: config.googlechat?.googleChatProjectNumber ?? \"local-mock-googlechat\",\n pubsubServiceAccountEmail:\n config.googlechat?.pubsubServiceAccountEmail ?? config.googlechat?.credentials?.client_email,\n userName: config.googlechat?.userName,\n };\n}\n\nconst GOOGLE_CHAT_SERVICE_ACCOUNT = \"chat@system.gserviceaccount.com\";\nconst GOOGLE_OAUTH_CERTS_URL = \"https://www.googleapis.com/oauth2/v1/certs\";\nconst GOOGLE_CHAT_CERTS_URL =\n \"https://www.googleapis.com/service_accounts/v1/metadata/x509/chat@system.gserviceaccount.com\";\n\ntype GoogleChatAuthRuntime = {\n fetch?: typeof fetch | undefined;\n keyFetchTimeoutMs?: number | undefined;\n now?: (() => number) | undefined;\n unknownKeyCooldownMs?: number | undefined;\n};\n\ntype GoogleCertificate = {\n certificate: string;\n kid: string;\n};\n\nexport function createGoogleChatWebhookAuthenticator(\n config: ProviderConfig,\n runtime: GoogleChatAuthRuntime = {},\n) {\n if (config.googlechat?.disableSignatureVerification) {\n return undefined;\n }\n const endpointAudience = config.googlechat?.endpointUrl ?? config.googlechat?.webhook.publicUrl;\n const projectAudience = config.googlechat?.googleChatProjectNumber;\n const pubsubAudience = config.googlechat?.pubsubAudience ?? endpointAudience;\n const pubsubServiceAccount =\n config.googlechat?.pubsubServiceAccountEmail ?? config.googlechat?.credentials?.client_email;\n if (!(endpointAudience || projectAudience || pubsubAudience)) {\n return undefined;\n }\n\n const fetchImpl = runtime.fetch ?? fetch;\n const createCertificateResolver = (certificateUrl: string) => {\n return createCachedJwtKeyResolver<GoogleCertificate>({\n async fetchKeys(signal) {\n const fetchedAt = runtime.now?.() ?? Date.now();\n const response = await fetchImpl(certificateUrl, { signal });\n if (!response.ok) {\n throw new Error(`Google certificate fetch failed with HTTP ${response.status}.`);\n }\n const body: unknown = await response.json();\n if (!isRecord(body)) {\n throw new Error(\"Google certificate response must be an object.\");\n }\n const values = Object.entries(body).flatMap(([kid, certificate]) =>\n typeof certificate === \"string\" ? [{ certificate, kid }] : [],\n );\n return {\n expiresAt: resolveHttpCacheExpiry(response, fetchedAt),\n values,\n };\n },\n keyId: (value) => value.kid,\n now: runtime.now,\n refreshCooldownMs: runtime.unknownKeyCooldownMs,\n timeoutMs: runtime.keyFetchTimeoutMs,\n unknownKeyMessage: \"Google JWT signing key is unknown.\",\n });\n };\n const resolveGoogleIdentityCertificate = createCertificateResolver(GOOGLE_OAUTH_CERTS_URL);\n const resolveGoogleChatCertificate = createCertificateResolver(GOOGLE_CHAT_CERTS_URL);\n return async (request: Request, rawBody: string): Promise<Response | undefined> => {\n const token = readBearerToken(request);\n if (!token) {\n return new Response(\"unauthorized\", {\n headers: { \"www-authenticate\": \"Bearer\" },\n status: 401,\n });\n }\n try {\n let payload: unknown;\n try {\n payload = JSON.parse(rawBody) as unknown;\n } catch {\n return undefined;\n }\n const isPubsub =\n isRecord(payload) &&\n isRecord(payload.message) &&\n typeof payload.message.data === \"string\" &&\n typeof payload.subscription === \"string\";\n const audience = isPubsub ? pubsubAudience : (projectAudience ?? endpointAudience);\n if (!audience) {\n throw new Error(\"Google Chat verification is not configured for this transport.\");\n }\n const usesGoogleIdentityToken =\n isPubsub || (projectAudience === undefined && endpointAudience !== undefined);\n const certificateUrl = usesGoogleIdentityToken\n ? GOOGLE_OAUTH_CERTS_URL\n : GOOGLE_CHAT_CERTS_URL;\n const claims = await verifySignedJwt({\n audience,\n issuers: usesGoogleIdentityToken\n ? [\"accounts.google.com\", \"https://accounts.google.com\"]\n : [GOOGLE_CHAT_SERVICE_ACCOUNT],\n now: runtime.now,\n async resolveKey(header) {\n const certificate = await (certificateUrl === GOOGLE_OAUTH_CERTS_URL\n ? resolveGoogleIdentityCertificate(header)\n : resolveGoogleChatCertificate(header));\n return createPublicKey(certificate.certificate);\n },\n token,\n });\n if (\n isPubsub &&\n (!pubsubServiceAccount ||\n claims.email !== pubsubServiceAccount ||\n claims.email_verified !== true)\n ) {\n throw new Error(\"Google Pub/Sub token identity is invalid.\");\n }\n if (\n !isPubsub &&\n usesGoogleIdentityToken &&\n (claims.email !== GOOGLE_CHAT_SERVICE_ACCOUNT || claims.email_verified !== true)\n ) {\n throw new Error(\"Google Chat ID token identity is invalid.\");\n }\n return undefined;\n } catch {\n return new Response(\"unauthorized\", {\n headers: { \"www-authenticate\": \"Bearer\" },\n status: 401,\n });\n }\n };\n}\n\nexport function matchesGoogleChatThread(\n candidateThreadId: string,\n expectedThreadId: string | undefined,\n target: { channelId?: string | undefined } = {},\n): boolean {\n const candidateSpace = GOOGLE_CHAT_THREAD_RULE.pattern.test(candidateThreadId)\n ? candidateThreadId.slice(0, candidateThreadId.indexOf(\"/threads/\"))\n : candidateThreadId;\n if (target.channelId && candidateSpace !== target.channelId) {\n return false;\n }\n if (!expectedThreadId) {\n return true;\n }\n return (\n candidateThreadId === expectedThreadId ||\n (GOOGLE_CHAT_SPACE_RULE.pattern.test(expectedThreadId) &&\n candidateThreadId.startsWith(`${expectedThreadId}/threads/`))\n );\n}\n\nexport class GoogleChatProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, _userName: string, runtime?: unknown) {\n const endpointAudience = config.googlechat?.endpointUrl ?? config.googlechat?.webhook.publicUrl;\n const pubsubServiceAccount =\n config.googlechat?.pubsubServiceAccountEmail ?? config.googlechat?.credentials?.client_email;\n const authenticationConfigured =\n !config.googlechat?.disableSignatureVerification &&\n Boolean(\n endpointAudience ||\n config.googlechat?.googleChatProjectNumber ||\n (config.googlechat?.pubsubAudience && pubsubServiceAccount),\n );\n requireExternalWebhookAuthentication({\n authenticated: authenticationConfigured,\n provider: \"Google Chat\",\n requirement:\n \"googlechat.endpointUrl, googlechat.googleChatProjectNumber, or googlechat.pubsubAudience with a Pub/Sub service-account identity and signature verification enabled\",\n webhook: config.googlechat?.webhook,\n });\n const authenticateWebhookRequest = createGoogleChatWebhookAuthenticator(\n config,\n (runtime as GoogleChatAuthRuntime | undefined) ?? {},\n );\n super({\n codec: getBuiltinTargetCodec(\"googlechat\"),\n config,\n id,\n options: {\n ...(authenticateWebhookRequest ? { authenticateWebhookRequest } : {}),\n defaultWebhook: { host: \"127.0.0.1\", path: \"/googlechat/webhook\", port: 8792 },\n endpointLabel: \"webhook endpoint\",\n matchesThread: matchesGoogleChatThread,\n normalizeWebhookPayload: normalizeGoogleChatWebhookPayload,\n platform: \"googlechat\",\n publicUrl: config.googlechat?.webhook.publicUrl,\n recorderPath: config.googlechat?.recorder.path\n ? path.resolve(config.googlechat.recorder.path)\n : undefined,\n webhook: config.googlechat?.webhook,\n },\n });\n }\n}\n\nexport function normalizeGoogleChatWebhookPayload(payload: unknown) {\n payload = unwrapGoogleChatPubsubPayload(payload);\n if (!isRecord(payload)) {\n throw new CrablineError(\"Google Chat webhook payload must be an object\", {\n kind: \"inbound\",\n });\n }\n\n const chat = optionalRecord(payload, \"chat\");\n if (chat && \"messagePayload\" in chat) {\n throw new CrablineError(\n \"Google Workspace add-on chat.messagePayload events are unsupported without a configured deployment identity\",\n { kind: \"inbound\" },\n );\n }\n const payloadMessage = optionalRecord(payload, \"message\");\n const message = payloadMessage ?? payload;\n if (payloadMessage && optionalString(payloadMessage, \"threadId\")) {\n return genericMockPayloadWithNativeThread({\n channelRule: GOOGLE_CHAT_SPACE_RULE,\n payload,\n threadRule: GOOGLE_CHAT_THREAD_RULE,\n });\n }\n\n const space = optionalRecord(message, \"space\");\n const thread = optionalRecord(message, \"thread\");\n const sender = optionalRecord(message, \"sender\");\n const spaceName = space ? optionalString(space, \"name\") : undefined;\n const threadName = thread ? optionalString(thread, \"name\") : undefined;\n const text = optionalString(message, \"text\") ?? optionalString(message, \"argumentText\");\n if (!spaceName || !text) {\n throw new CrablineError(\"Google Chat message payload requires space.name and text\", {\n kind: \"inbound\",\n });\n }\n if (threadName && !threadName.startsWith(`${spaceName}/threads/`)) {\n throw new CrablineError(\"Google Chat thread.name must belong to message.space.name\", {\n kind: \"inbound\",\n });\n }\n\n return {\n author: authorFromBotFlag(optionalString(sender ?? {}, \"type\") === \"BOT\"),\n ...(optionalString(message, \"name\") ? { id: optionalString(message, \"name\") } : {}),\n raw: payload,\n text,\n threadId: threadName\n ? requireNativeInboundId(threadName, GOOGLE_CHAT_THREAD_RULE, \"Google Chat thread.name\")\n : requireNativeInboundId(spaceName, GOOGLE_CHAT_SPACE_RULE, \"Google Chat space.name\"),\n };\n}\n\nfunction unwrapGoogleChatPubsubPayload(payload: unknown): unknown {\n if (!isRecord(payload)) {\n return payload;\n }\n const message = optionalRecord(payload, \"message\");\n const data = message ? optionalString(message, \"data\") : undefined;\n if (!data || typeof payload.subscription !== \"string\") {\n return payload;\n }\n\n try {\n if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(data)) {\n throw new Error(\"invalid base64\");\n }\n const decoded = Buffer.from(data, \"base64\");\n if (decoded.toString(\"base64\") !== data) {\n throw new Error(\"non-canonical base64\");\n }\n return JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(decoded)) as unknown;\n } catch {\n throw new CrablineError(\"Google Pub/Sub message.data must contain base64-encoded JSON.\", {\n kind: \"inbound\",\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"googlechat.js","sourceRoot":"","sources":["../../../../src/providers/builtin/googlechat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,eAAe,EACf,sBAAsB,EACtB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iBAAiB,EACjB,kCAAkC,EAClC,QAAQ,EACR,cAAc,EACd,cAAc,EACd,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,oCAAoC,EAAE,MAAM,4BAA4B,CAAC;AAElF,MAAM,UAAU,8BAA8B,CAC5C,MAAsB,EACtB,IAAI,GAAsB,OAAO,CAAC,GAAG;IAErC,OAAO;QACL,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,WAAW;QAC3C,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,uBAAuB,IAAI,uBAAuB;QACpF,yBAAyB,EACvB,MAAM,CAAC,UAAU,EAAE,yBAAyB,IAAI,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,YAAY;QAC9F,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,2BAA2B,GAAG,iCAAiC,CAAC;AACtE,MAAM,sBAAsB,GAAG,4CAA4C,CAAC;AAC5E,MAAM,qBAAqB,GACzB,8FAA8F,CAAC;AACjG,MAAM,gCAAgC,GAAG,8CAA8C,CAAC;AAcxF,MAAM,UAAU,oCAAoC,CAClD,MAAsB,EACtB,OAAO,GAA0B,EAAE;IAEnC,IAAI,MAAM,CAAC,UAAU,EAAE,4BAA4B,EAAE,CAAC;QACpD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,gBAAgB,GAAG,MAAM,CAAC,UAAU,EAAE,WAAW,IAAI,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAC;IAChG,MAAM,eAAe,GAAG,MAAM,CAAC,UAAU,EAAE,uBAAuB,CAAC;IACnE,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,EAAE,cAAc,IAAI,gBAAgB,CAAC;IAC7E,MAAM,oBAAoB,GACxB,MAAM,CAAC,UAAU,EAAE,yBAAyB,IAAI,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,YAAY,CAAC;IAC/F,IAAI,CAAC,CAAC,gBAAgB,IAAI,eAAe,IAAI,cAAc,CAAC,EAAE,CAAC;QAC7D,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;IACzC,MAAM,yBAAyB,GAAG,CAAC,cAAsB,EAAE,EAAE;QAC3D,OAAO,0BAA0B,CAAoB;YACnD,KAAK,CAAC,SAAS,CAAC,MAAM;gBACpB,IAAI,CAAC;oBACH,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;oBAClD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;oBAC7D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;wBACjB,MAAM,IAAI,KAAK,CAAC,6CAA6C,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;oBACnF,CAAC;oBACD,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;oBAC5C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;wBACpB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;oBACpE,CAAC;oBACD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,EAAE;wBAC7D,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;4BACpC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;wBACzE,CAAC;wBACD,MAAM,gBAAgB,GAAG,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;wBACtE,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,gBAAgB,CAAC;4BAChD,CAAC,CAAC,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC,SAAS;4BAC5C,CAAC,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;wBACjC,IAAI,GAAG,CAAC,iBAAiB,KAAK,KAAK,EAAE,CAAC;4BACpC,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;wBACpE,CAAC;wBACD,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;oBACtB,CAAC,CAAC,CAAC;oBACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBACxB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;oBAC5E,CAAC;oBACD,OAAO;wBACL,SAAS,EAAE,sBAAsB,CAAC,QAAQ,EAAE,WAAW,CAAC;wBACxD,MAAM;qBACP,CAAC;gBACJ,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,KAAK,YAAY,yBAAyB;wBAC9C,CAAC,CAAC,KAAK;wBACP,CAAC,CAAC,IAAI,yBAAyB,CAC3B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,kCAAkC,EAC3E,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;gBACR,CAAC;YACH,CAAC;YACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG;YAC3B,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,iBAAiB,EAAE,OAAO,CAAC,oBAAoB;YAC/C,SAAS,EAAE,OAAO,CAAC,iBAAiB;YACpC,iBAAiB,EAAE,oCAAoC;SACxD,CAAC,CAAC;IACL,CAAC,CAAC;IACF,MAAM,gCAAgC,GAAG,yBAAyB,CAAC,sBAAsB,CAAC,CAAC;IAC3F,MAAM,4BAA4B,GAAG,yBAAyB,CAAC,qBAAqB,CAAC,CAAC;IACtF,OAAO,KAAK,EAAE,OAAgB,EAAE,OAAe,EAAiC,EAAE;QAChF,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE;gBAClC,OAAO,EAAE,EAAE,kBAAkB,EAAE,QAAQ,EAAE;gBACzC,MAAM,EAAE,GAAG;aACZ,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC;YACH,IAAI,OAAgB,CAAC;YACrB,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC;YAC3C,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE;oBAClC,OAAO,EAAE,EAAE,kBAAkB,EAAE,QAAQ,EAAE;oBACzC,MAAM,EAAE,GAAG;iBACZ,CAAC,CAAC;YACL,CAAC;YACD,MAAM,QAAQ,GACZ,QAAQ,CAAC,OAAO,CAAC;gBACjB,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC;gBACzB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ;gBACxC,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,CAAC;YAC3C,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,eAAe,IAAI,gBAAgB,CAAC,CAAC;YACnF,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;YACpF,CAAC;YACD,MAAM,uBAAuB,GAC3B,QAAQ,IAAI,CAAC,eAAe,KAAK,SAAS,IAAI,gBAAgB,KAAK,SAAS,CAAC,CAAC;YAChF,MAAM,cAAc,GAAG,uBAAuB;gBAC5C,CAAC,CAAC,sBAAsB;gBACxB,CAAC,CAAC,qBAAqB,CAAC;YAC1B,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC;gBACnC,QAAQ;gBACR,OAAO,EAAE,uBAAuB;oBAC9B,CAAC,CAAC,CAAC,qBAAqB,EAAE,6BAA6B,CAAC;oBACxD,CAAC,CAAC,CAAC,2BAA2B,CAAC;gBACjC,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,KAAK,CAAC,UAAU,CAAC,MAAM;oBACrB,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,KAAK,sBAAsB;wBAClE,CAAC,CAAC,gCAAgC,CAAC,MAAM,CAAC;wBAC1C,CAAC,CAAC,4BAA4B,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC1C,OAAO,WAAW,CAAC,GAAG,CAAC;gBACzB,CAAC;gBACD,KAAK;aACN,CAAC,CAAC;YACH,IACE,QAAQ;gBACR,CAAC,CAAC,oBAAoB;oBACpB,MAAM,CAAC,KAAK,KAAK,oBAAoB;oBACrC,MAAM,CAAC,cAAc,KAAK,IAAI,CAAC,EACjC,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC/D,CAAC;YACD,IACE,CAAC,QAAQ;gBACT,uBAAuB;gBACvB,CAAC,MAAM,CAAC,KAAK,KAAK,2BAA2B,IAAI,MAAM,CAAC,cAAc,KAAK,IAAI,CAAC,EAChF,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC/D,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,4BAA4B,CAAC,KAAK,YAAY,yBAAyB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CAAC,MAAiB;IACrD,OAAO,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,qBAAqB,EAAE;QAC3E,OAAO,EAAE;YACP,eAAe,EAAE,UAAU;YAC3B,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5D;QACD,MAAM;KACP,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,iBAAyB,EACzB,gBAAoC,EACpC,MAAM,GAAuC,EAAE;IAE/C,MAAM,cAAc,GAAG,uBAAuB,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC;QAC5E,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACpE,CAAC,CAAC,iBAAiB,CAAC;IACtB,IAAI,MAAM,CAAC,SAAS,IAAI,cAAc,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QAC5D,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CACL,iBAAiB,KAAK,gBAAgB;QACtC,CAAC,sBAAsB,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC;YACpD,iBAAiB,CAAC,UAAU,CAAC,GAAG,gBAAgB,WAAW,CAAC,CAAC,CAChE,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,yBAA0B,SAAQ,wBAAwB;IACrE,YAAY,EAAU,EAAE,MAAsB,EAAE,SAAiB,EAAE,OAAiB;QAClF,MAAM,gBAAgB,GAAG,MAAM,CAAC,UAAU,EAAE,WAAW,IAAI,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAC;QAChG,MAAM,oBAAoB,GACxB,MAAM,CAAC,UAAU,EAAE,yBAAyB,IAAI,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,YAAY,CAAC;QAC/F,IAAI,MAAM,CAAC,UAAU,EAAE,cAAc,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC/D,MAAM,IAAI,aAAa,CACrB,mIAAmI,EACnI,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;QACJ,CAAC;QACD,MAAM,wBAAwB,GAC5B,CAAC,MAAM,CAAC,UAAU,EAAE,4BAA4B;YAChD,OAAO,CACL,gBAAgB;gBAChB,MAAM,CAAC,UAAU,EAAE,uBAAuB;gBAC1C,CAAC,MAAM,CAAC,UAAU,EAAE,cAAc,IAAI,oBAAoB,CAAC,CAC5D,CAAC;QACJ,oCAAoC,CAAC;YACnC,aAAa,EAAE,wBAAwB;YACvC,wBAAwB,EAAE,MAAM,CAAC,UAAU,EAAE,WAAW;gBACtD,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC;gBACjC,CAAC,CAAC,SAAS;YACb,QAAQ,EAAE,aAAa;YACvB,WAAW,EACT,qKAAqK;YACvK,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO;SACpC,CAAC,CAAC;QACH,MAAM,0BAA0B,GAAG,oCAAoC,CACrE,MAAM,EACL,OAA6C,IAAI,EAAE,CACrD,CAAC;QACF,KAAK,CAAC;YACJ,KAAK,EAAE,qBAAqB,CAAC,YAAY,CAAC;YAC1C,MAAM;YACN,EAAE;YACF,OAAO,EAAE;gBACP,GAAG,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAAE,0BAA0B,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrE,4BAA4B,EAAE,GAAG,EAAE,CAAC,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;gBACvE,cAAc,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,IAAI,EAAE;gBAC9E,aAAa,EAAE,kBAAkB;gBACjC,oBAAoB,EAAE,8BAA8B;gBACpD,aAAa,EAAE,uBAAuB;gBACtC,uBAAuB,EAAE,iCAAiC;gBAC1D,QAAQ,EAAE,YAAY;gBACtB,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,SAAS;gBAC/C,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI;oBAC5C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAC/C,CAAC,CAAC,SAAS;gBACb,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO;gBACnC,cAAc,EAAE,CAAC,MAAM,CAAC;aACzB;SACF,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,UAAU,iCAAiC,CAAC,OAAgB;IAChE,OAAO,GAAG,6BAA6B,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,aAAa,CAAC,+CAA+C,EAAE;YACvE,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,IAAI,IAAI,gBAAgB,IAAI,IAAI,EAAE,CAAC;QACrC,MAAM,IAAI,aAAa,CACrB,6GAA6G,EAC7G,EAAE,IAAI,EAAE,SAAS,EAAE,CACpB,CAAC;IACJ,CAAC;IACD,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,cAAc,IAAI,OAAO,CAAC;IAC1C,IAAI,cAAc,IAAI,cAAc,CAAC,cAAc,EAAE,UAAU,CAAC,EAAE,CAAC;QACjE,OAAO,kCAAkC,CAAC;YACxC,WAAW,EAAE,sBAAsB;YACnC,OAAO;YACP,UAAU,EAAE,uBAAuB;SACpC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,cAAc,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IACxF,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,EAAE,CAAC;QACxB,MAAM,IAAI,aAAa,CAAC,0DAA0D,EAAE;YAClF,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC;IACL,CAAC;IACD,MAAM,kBAAkB,GAAG,sBAAsB,CAC/C,SAAS,EACT,sBAAsB,EACtB,wBAAwB,CACzB,CAAC;IACF,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,CAAC,gCAAgC,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,aAAa,CAAC,gEAAgE,EAAE;gBACxF,IAAI,EAAE,SAAS;aAChB,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,kBAAkB,YAAY,CAAC,EAAE,CAAC;YAC/D,MAAM,IAAI,aAAa,CAAC,4DAA4D,EAAE;gBACpF,IAAI,EAAE,SAAS;aAChB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,MAAM,mBAAmB,GAAG,UAAU;QACpC,CAAC,CAAC,sBAAsB,CAAC,UAAU,EAAE,uBAAuB,EAAE,yBAAyB,CAAC;QACxF,CAAC,CAAC,SAAS,CAAC;IACd,IAAI,mBAAmB,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,GAAG,kBAAkB,WAAW,CAAC,EAAE,CAAC;QAC7F,MAAM,IAAI,aAAa,CAAC,2DAA2D,EAAE;YACnF,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,MAAM,EAAE,iBAAiB,CAAC,cAAc,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,CAAC,KAAK,KAAK,CAAC;QACzE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,GAAG,EAAE,OAAO;QACZ,IAAI;QACJ,QAAQ,EAAE,mBAAmB,IAAI,kBAAkB;KACpD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B,CAAC,OAAgB;IAC7D,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,6BAA6B,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAClF,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,6BAA6B,CAAC,OAAgB;IACrD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnE,IAAI,CAAC,IAAI,IAAI,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,CAAC;QACH,IAAI,CAAC,mEAAmE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpF,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAY,CAAC;IAC1F,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,aAAa,CAAC,+DAA+D,EAAE;YACvF,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["import { createPublicKey, type KeyObject, X509Certificate } 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 type { ProviderAdapter } from \"../types.js\";\nimport {\n createCachedJwtKeyResolver,\n JwtKeyInfrastructureError,\n readBearerToken,\n resolveHttpCacheExpiry,\n verifySignedJwt,\n} from \"../signed-jwt.js\";\nimport {\n getBuiltinTargetCodec,\n GOOGLE_CHAT_SPACE_RULE,\n GOOGLE_CHAT_THREAD_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 resolveGoogleChatAdapterConfig(\n config: ProviderConfig,\n _env: NodeJS.ProcessEnv = process.env,\n) {\n return {\n endpointUrl: config.googlechat?.endpointUrl,\n projectNumber: config.googlechat?.googleChatProjectNumber ?? \"local-mock-googlechat\",\n pubsubServiceAccountEmail:\n config.googlechat?.pubsubServiceAccountEmail ?? config.googlechat?.credentials?.client_email,\n userName: config.googlechat?.userName,\n };\n}\n\nconst GOOGLE_CHAT_SERVICE_ACCOUNT = \"chat@system.gserviceaccount.com\";\nconst GOOGLE_OAUTH_CERTS_URL = \"https://www.googleapis.com/oauth2/v1/certs\";\nconst GOOGLE_CHAT_CERTS_URL =\n \"https://www.googleapis.com/service_accounts/v1/metadata/x509/chat@system.gserviceaccount.com\";\nconst GOOGLE_CHAT_MESSAGE_NAME_PATTERN = /^spaces\\/[A-Za-z0-9_-]+\\/messages\\/[^/\\s]+$/u;\n\ntype GoogleChatAuthRuntime = {\n fetch?: typeof fetch | undefined;\n keyFetchTimeoutMs?: number | undefined;\n now?: (() => number) | undefined;\n unknownKeyCooldownMs?: number | undefined;\n};\n\ntype GoogleCertificate = {\n kid: string;\n key: KeyObject;\n};\n\nexport function createGoogleChatWebhookAuthenticator(\n config: ProviderConfig,\n runtime: GoogleChatAuthRuntime = {},\n) {\n if (config.googlechat?.disableSignatureVerification) {\n return undefined;\n }\n const endpointAudience = config.googlechat?.endpointUrl ?? config.googlechat?.webhook.publicUrl;\n const projectAudience = config.googlechat?.googleChatProjectNumber;\n const pubsubAudience = config.googlechat?.pubsubAudience ?? endpointAudience;\n const pubsubServiceAccount =\n config.googlechat?.pubsubServiceAccountEmail ?? config.googlechat?.credentials?.client_email;\n if (!(endpointAudience || projectAudience || pubsubAudience)) {\n return undefined;\n }\n\n const fetchImpl = runtime.fetch ?? fetch;\n const createCertificateResolver = (certificateUrl: string) => {\n return createCachedJwtKeyResolver<GoogleCertificate>({\n async fetchKeys(signal) {\n try {\n const requestedAt = runtime.now?.() ?? Date.now();\n const response = await fetchImpl(certificateUrl, { signal });\n if (!response.ok) {\n throw new Error(`Google certificate fetch failed with HTTP ${response.status}.`);\n }\n const body: unknown = await response.json();\n if (!isRecord(body)) {\n throw new Error(\"Google certificate response must be an object.\");\n }\n const values = Object.entries(body).map(([kid, certificate]) => {\n if (typeof certificate !== \"string\") {\n throw new Error(\"Google certificate response values must be strings.\");\n }\n const certificateLabel = [\"-----BEGIN\", \"CERTIFICATE-----\"].join(\" \");\n const key = certificate.includes(certificateLabel)\n ? new X509Certificate(certificate).publicKey\n : createPublicKey(certificate);\n if (key.asymmetricKeyType !== \"rsa\") {\n throw new Error(\"Google certificate response keys must use RSA.\");\n }\n return { kid, key };\n });\n if (values.length === 0) {\n throw new Error(\"Google certificate response must include signing keys.\");\n }\n return {\n expiresAt: resolveHttpCacheExpiry(response, requestedAt),\n values,\n };\n } catch (error) {\n throw error instanceof JwtKeyInfrastructureError\n ? error\n : new JwtKeyInfrastructureError(\n error instanceof Error ? error.message : \"Google certificate fetch failed.\",\n { cause: error },\n );\n }\n },\n keyId: (value) => value.kid,\n now: runtime.now,\n refreshCooldownMs: runtime.unknownKeyCooldownMs,\n timeoutMs: runtime.keyFetchTimeoutMs,\n unknownKeyMessage: \"Google JWT signing key is unknown.\",\n });\n };\n const resolveGoogleIdentityCertificate = createCertificateResolver(GOOGLE_OAUTH_CERTS_URL);\n const resolveGoogleChatCertificate = createCertificateResolver(GOOGLE_CHAT_CERTS_URL);\n return async (request: Request, rawBody: string): Promise<Response | undefined> => {\n const token = readBearerToken(request);\n if (!token) {\n return new Response(\"unauthorized\", {\n headers: { \"www-authenticate\": \"Bearer\" },\n status: 401,\n });\n }\n try {\n let payload: unknown;\n try {\n payload = JSON.parse(rawBody) as unknown;\n } catch {\n return new Response(\"unauthorized\", {\n headers: { \"www-authenticate\": \"Bearer\" },\n status: 401,\n });\n }\n const isPubsub =\n isRecord(payload) &&\n isRecord(payload.message) &&\n typeof payload.message.data === \"string\" &&\n typeof payload.subscription === \"string\";\n const audience = isPubsub ? pubsubAudience : (projectAudience ?? endpointAudience);\n if (!audience) {\n throw new Error(\"Google Chat verification is not configured for this transport.\");\n }\n const usesGoogleIdentityToken =\n isPubsub || (projectAudience === undefined && endpointAudience !== undefined);\n const certificateUrl = usesGoogleIdentityToken\n ? GOOGLE_OAUTH_CERTS_URL\n : GOOGLE_CHAT_CERTS_URL;\n const claims = await verifySignedJwt({\n audience,\n issuers: usesGoogleIdentityToken\n ? [\"accounts.google.com\", \"https://accounts.google.com\"]\n : [GOOGLE_CHAT_SERVICE_ACCOUNT],\n now: runtime.now,\n async resolveKey(header) {\n const certificate = await (certificateUrl === GOOGLE_OAUTH_CERTS_URL\n ? resolveGoogleIdentityCertificate(header)\n : resolveGoogleChatCertificate(header));\n return certificate.key;\n },\n token,\n });\n if (\n isPubsub &&\n (!pubsubServiceAccount ||\n claims.email !== pubsubServiceAccount ||\n claims.email_verified !== true)\n ) {\n throw new Error(\"Google Pub/Sub token identity is invalid.\");\n }\n if (\n !isPubsub &&\n usesGoogleIdentityToken &&\n (claims.email !== GOOGLE_CHAT_SERVICE_ACCOUNT || claims.email_verified !== true)\n ) {\n throw new Error(\"Google Chat ID token identity is invalid.\");\n }\n return undefined;\n } catch (error) {\n return googleAuthenticationResponse(error instanceof JwtKeyInfrastructureError ? 503 : 401);\n }\n };\n}\n\nfunction googleAuthenticationResponse(status: 401 | 503): Response {\n return new Response(status === 401 ? \"unauthorized\" : \"service unavailable\", {\n headers: {\n \"cache-control\": \"no-store\",\n ...(status === 401 ? { \"www-authenticate\": \"Bearer\" } : {}),\n },\n status,\n });\n}\n\nexport function matchesGoogleChatThread(\n candidateThreadId: string,\n expectedThreadId: string | undefined,\n target: { channelId?: string | undefined } = {},\n): boolean {\n const candidateSpace = GOOGLE_CHAT_THREAD_RULE.pattern.test(candidateThreadId)\n ? candidateThreadId.slice(0, candidateThreadId.indexOf(\"/threads/\"))\n : candidateThreadId;\n if (target.channelId && candidateSpace !== target.channelId) {\n return false;\n }\n if (!expectedThreadId) {\n return true;\n }\n return (\n candidateThreadId === expectedThreadId ||\n (GOOGLE_CHAT_SPACE_RULE.pattern.test(expectedThreadId) &&\n candidateThreadId.startsWith(`${expectedThreadId}/threads/`))\n );\n}\n\nexport class GoogleChatProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, _userName: string, runtime?: unknown) {\n const endpointAudience = config.googlechat?.endpointUrl ?? config.googlechat?.webhook.publicUrl;\n const pubsubServiceAccount =\n config.googlechat?.pubsubServiceAccountEmail ?? config.googlechat?.credentials?.client_email;\n if (config.googlechat?.pubsubAudience && !pubsubServiceAccount) {\n throw new CrablineError(\n \"Google Chat pubsubAudience requires a Pub/Sub service-account identity via pubsubServiceAccountEmail or credentials.client_email.\",\n { kind: \"config\" },\n );\n }\n const authenticationConfigured =\n !config.googlechat?.disableSignatureVerification &&\n Boolean(\n endpointAudience ||\n config.googlechat?.googleChatProjectNumber ||\n (config.googlechat?.pubsubAudience && pubsubServiceAccount),\n );\n requireExternalWebhookAuthentication({\n authenticated: authenticationConfigured,\n authenticatedIngressUrls: config.googlechat?.endpointUrl\n ? [config.googlechat.endpointUrl]\n : undefined,\n provider: \"Google Chat\",\n requirement:\n \"googlechat.endpointUrl, googlechat.googleChatProjectNumber, or googlechat.pubsubAudience with a Pub/Sub service-account identity and signature verification enabled\",\n webhook: config.googlechat?.webhook,\n });\n const authenticateWebhookRequest = createGoogleChatWebhookAuthenticator(\n config,\n (runtime as GoogleChatAuthRuntime | undefined) ?? {},\n );\n super({\n codec: getBuiltinTargetCodec(\"googlechat\"),\n config,\n id,\n options: {\n ...(authenticateWebhookRequest ? { authenticateWebhookRequest } : {}),\n createWebhookSuccessResponse: () => new Response(null, { status: 200 }),\n defaultWebhook: { host: \"127.0.0.1\", path: \"/googlechat/webhook\", port: 8792 },\n endpointLabel: \"webhook endpoint\",\n handleWebhookPayload: handleGoogleChatWebhookPayload,\n matchesThread: matchesGoogleChatThread,\n normalizeWebhookPayload: normalizeGoogleChatWebhookPayload,\n platform: \"googlechat\",\n publicUrl: config.googlechat?.webhook.publicUrl,\n recorderPath: config.googlechat?.recorder.path\n ? path.resolve(config.googlechat.recorder.path)\n : undefined,\n webhook: config.googlechat?.webhook,\n webhookMethods: [\"POST\"],\n },\n });\n }\n}\n\nexport function normalizeGoogleChatWebhookPayload(payload: unknown) {\n payload = unwrapGoogleChatPubsubPayload(payload);\n if (!isRecord(payload)) {\n throw new CrablineError(\"Google Chat webhook payload must be an object\", {\n kind: \"inbound\",\n });\n }\n\n const chat = optionalRecord(payload, \"chat\");\n if (chat && \"messagePayload\" in chat) {\n throw new CrablineError(\n \"Google Workspace add-on chat.messagePayload events are unsupported without a configured deployment identity\",\n { kind: \"inbound\" },\n );\n }\n const payloadMessage = optionalRecord(payload, \"message\");\n const message = payloadMessage ?? payload;\n if (payloadMessage && optionalString(payloadMessage, \"threadId\")) {\n return genericMockPayloadWithNativeThread({\n channelRule: GOOGLE_CHAT_SPACE_RULE,\n payload,\n threadRule: GOOGLE_CHAT_THREAD_RULE,\n });\n }\n\n const space = optionalRecord(message, \"space\");\n const thread = optionalRecord(message, \"thread\");\n const sender = optionalRecord(message, \"sender\");\n const spaceName = space ? optionalString(space, \"name\") : undefined;\n const threadName = thread ? optionalString(thread, \"name\") : undefined;\n const messageName = optionalString(message, \"name\");\n const text = optionalString(message, \"text\") ?? optionalString(message, \"argumentText\");\n if (!spaceName || !text) {\n throw new CrablineError(\"Google Chat message payload requires space.name and text\", {\n kind: \"inbound\",\n });\n }\n const validatedSpaceName = requireNativeInboundId(\n spaceName,\n GOOGLE_CHAT_SPACE_RULE,\n \"Google Chat space.name\",\n );\n if (messageName) {\n if (!GOOGLE_CHAT_MESSAGE_NAME_PATTERN.test(messageName)) {\n throw new CrablineError(\"Google Chat message.name must be a valid message resource name\", {\n kind: \"inbound\",\n });\n }\n if (!messageName.startsWith(`${validatedSpaceName}/messages/`)) {\n throw new CrablineError(\"Google Chat message.name must belong to message.space.name\", {\n kind: \"inbound\",\n });\n }\n }\n const validatedThreadName = threadName\n ? requireNativeInboundId(threadName, GOOGLE_CHAT_THREAD_RULE, \"Google Chat thread.name\")\n : undefined;\n if (validatedThreadName && !validatedThreadName.startsWith(`${validatedSpaceName}/threads/`)) {\n throw new CrablineError(\"Google Chat thread.name must belong to message.space.name\", {\n kind: \"inbound\",\n });\n }\n\n return {\n author: authorFromBotFlag(optionalString(sender ?? {}, \"type\") === \"BOT\"),\n ...(messageName ? { id: messageName } : {}),\n raw: payload,\n text,\n threadId: validatedThreadName ?? validatedSpaceName,\n };\n}\n\nexport function handleGoogleChatWebhookPayload(payload: unknown): Response | undefined {\n let event: unknown;\n try {\n event = unwrapGoogleChatPubsubPayload(payload);\n } catch {\n return undefined;\n }\n if (isRecord(event) && typeof event.type === \"string\" && event.type !== \"MESSAGE\") {\n return new Response(null, { status: 200 });\n }\n return undefined;\n}\n\nfunction unwrapGoogleChatPubsubPayload(payload: unknown): unknown {\n if (!isRecord(payload)) {\n return payload;\n }\n const message = optionalRecord(payload, \"message\");\n const data = message ? optionalString(message, \"data\") : undefined;\n if (!data || typeof payload.subscription !== \"string\") {\n return payload;\n }\n\n try {\n if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(data)) {\n throw new Error(\"invalid base64\");\n }\n const decoded = Buffer.from(data, \"base64\");\n if (decoded.toString(\"base64\") !== data) {\n throw new Error(\"non-canonical base64\");\n }\n return JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(decoded)) as unknown;\n } catch {\n throw new CrablineError(\"Google Pub/Sub message.data must contain base64-encoded JSON.\", {\n kind: \"inbound\",\n });\n }\n}\n"]}
|
|
@@ -1,16 +1,60 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { CrablineError } from "../../core/errors.js";
|
|
3
3
|
import { LocalMockProviderAdapter } from "../local-mock.js";
|
|
4
|
+
import { matchesNativeId } from "../native-ids.js";
|
|
4
5
|
import { getBuiltinTargetCodec, IMESSAGE_THREAD_RULE } from "../target-normalizers.js";
|
|
5
6
|
import { authorFromBotFlag, genericMockPayloadWithNativeThread, isRecord, optionalRecord, optionalString, requireNativeInboundId, } from "./native-local-mock.js";
|
|
6
7
|
import { requireExternalWebhookAuthentication } from "./external-webhook-auth.js";
|
|
7
|
-
export function resolveIMessageAdapterConfig(config, env
|
|
8
|
+
export function resolveIMessageAdapterConfig(config, env) {
|
|
9
|
+
const local = config.imessage?.local ?? true;
|
|
10
|
+
env ??= local ? {} : process.env;
|
|
11
|
+
const configuredApiKey = config.imessage?.apiKey;
|
|
12
|
+
const environmentApiKey = env.IMESSAGE_API_KEY;
|
|
13
|
+
let apiKey = configuredApiKey?.trim()
|
|
14
|
+
? configuredApiKey
|
|
15
|
+
: environmentApiKey?.trim()
|
|
16
|
+
? environmentApiKey
|
|
17
|
+
: undefined;
|
|
18
|
+
if (!apiKey) {
|
|
19
|
+
if (!local) {
|
|
20
|
+
throw new CrablineError("Remote iMessage mode requires imessage.apiKey or IMESSAGE_API_KEY to be non-empty.", { kind: "config" });
|
|
21
|
+
}
|
|
22
|
+
apiKey = "local-mock-imessage-api-key";
|
|
23
|
+
}
|
|
8
24
|
return {
|
|
9
|
-
apiKey
|
|
10
|
-
local
|
|
25
|
+
apiKey,
|
|
26
|
+
local,
|
|
11
27
|
serverUrl: config.imessage?.serverUrl ?? env.IMESSAGE_SERVER_URL,
|
|
12
28
|
};
|
|
13
29
|
}
|
|
30
|
+
function isNativeIMessageData(data) {
|
|
31
|
+
return [
|
|
32
|
+
"chat_guid",
|
|
33
|
+
"chat_identifier",
|
|
34
|
+
"chatGuid",
|
|
35
|
+
"chatIdentifier",
|
|
36
|
+
"guid",
|
|
37
|
+
"is_from_me",
|
|
38
|
+
"isFromMe",
|
|
39
|
+
].some((key) => key in data);
|
|
40
|
+
}
|
|
41
|
+
function iMessageNativeData(payload) {
|
|
42
|
+
const data = optionalRecord(payload, "data") ?? payload;
|
|
43
|
+
const params = optionalRecord(data, "params");
|
|
44
|
+
const message = optionalRecord(params ?? {}, "message") ?? optionalRecord(data, "message");
|
|
45
|
+
if (message && isNativeIMessageData(message)) {
|
|
46
|
+
return message;
|
|
47
|
+
}
|
|
48
|
+
return data;
|
|
49
|
+
}
|
|
50
|
+
function iMessageThreadIdentifiers(data) {
|
|
51
|
+
return [
|
|
52
|
+
optionalString(data, "chatGuid"),
|
|
53
|
+
optionalString(data, "chatIdentifier"),
|
|
54
|
+
optionalString(data, "chat_guid"),
|
|
55
|
+
optionalString(data, "chat_identifier"),
|
|
56
|
+
].filter((value) => value !== undefined);
|
|
57
|
+
}
|
|
14
58
|
export class IMessageProviderAdapter extends LocalMockProviderAdapter {
|
|
15
59
|
constructor(id, config, _userName, _runtime) {
|
|
16
60
|
requireExternalWebhookAuthentication({
|
|
@@ -40,39 +84,38 @@ export class IMessageProviderAdapter extends LocalMockProviderAdapter {
|
|
|
40
84
|
}
|
|
41
85
|
export function matchesIMessageThread(candidateThreadId, expectedThreadId, target, raw) {
|
|
42
86
|
const rawPayload = isRecord(raw) ? raw : undefined;
|
|
43
|
-
const data = rawPayload ? (
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const expectedIdentifiers = new Set([expectedThreadId, target.id]);
|
|
49
|
-
if (target.channelId) {
|
|
50
|
-
expectedIdentifiers.add(target.channelId);
|
|
87
|
+
const data = rawPayload ? iMessageNativeData(rawPayload) : undefined;
|
|
88
|
+
const aliases = data ? iMessageThreadIdentifiers(data) : [];
|
|
89
|
+
const expectedIdentifiers = new Set([target.channelId ?? target.id]);
|
|
90
|
+
if (expectedThreadId !== undefined) {
|
|
91
|
+
expectedIdentifiers.add(expectedThreadId);
|
|
51
92
|
}
|
|
52
93
|
return (expectedIdentifiers.has(candidateThreadId) ||
|
|
53
|
-
(
|
|
94
|
+
aliases.some((alias) => expectedIdentifiers.has(alias)));
|
|
54
95
|
}
|
|
55
96
|
function normalizeIMessageWebhookPayload(payload) {
|
|
56
97
|
if (!isRecord(payload)) {
|
|
57
98
|
throw new CrablineError("iMessage webhook payload must be an object", { kind: "inbound" });
|
|
58
99
|
}
|
|
59
|
-
|
|
100
|
+
const message = optionalRecord(payload, "message");
|
|
101
|
+
if ((message && !isNativeIMessageData(message)) ||
|
|
102
|
+
(!message && !isNativeIMessageData(payload) && optionalString(payload, "threadId"))) {
|
|
60
103
|
return genericMockPayloadWithNativeThread({
|
|
61
104
|
channelRule: IMESSAGE_THREAD_RULE,
|
|
62
105
|
payload,
|
|
63
106
|
threadRule: IMESSAGE_THREAD_RULE,
|
|
64
107
|
});
|
|
65
108
|
}
|
|
66
|
-
const data =
|
|
67
|
-
const threadId =
|
|
109
|
+
const data = iMessageNativeData(payload);
|
|
110
|
+
const threadId = iMessageThreadIdentifiers(data).find((candidate) => matchesNativeId(candidate, IMESSAGE_THREAD_RULE));
|
|
68
111
|
const text = optionalString(data, "text") ?? optionalString(data, "message");
|
|
69
112
|
if (!threadId || !text) {
|
|
70
|
-
throw new CrablineError("iMessage webhook payload requires chatGuid or chatIdentifier and text", {
|
|
113
|
+
throw new CrablineError("iMessage webhook payload requires chatGuid or chatIdentifier (including native snake_case aliases) and text", {
|
|
71
114
|
kind: "inbound",
|
|
72
115
|
});
|
|
73
116
|
}
|
|
74
117
|
return {
|
|
75
|
-
author: authorFromBotFlag(data.isFromMe === true),
|
|
118
|
+
author: authorFromBotFlag(data.isFromMe === true || data.is_from_me === true),
|
|
76
119
|
...(optionalString(data, "guid") ? { id: optionalString(data, "guid") } : {}),
|
|
77
120
|
raw: payload,
|
|
78
121
|
text,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imessage.js","sourceRoot":"","sources":["../../../../src/providers/builtin/imessage.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 { getBuiltinTargetCodec, IMESSAGE_THREAD_RULE } 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 resolveIMessageAdapterConfig(\n config: ProviderConfig,\n env: NodeJS.ProcessEnv = process.env,\n) {\n return {\n apiKey: config.imessage?.apiKey ?? env.IMESSAGE_API_KEY ?? \"local-mock-imessage-api-key\",\n local: config.imessage?.local ?? true,\n serverUrl: config.imessage?.serverUrl ?? env.IMESSAGE_SERVER_URL,\n };\n}\n\nexport class IMessageProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, _userName: string, _runtime?: unknown) {\n requireExternalWebhookAuthentication({\n authenticated: false,\n provider: \"iMessage\",\n requirement:\n \"a provider-native authenticated ingress mode, which this adapter does not support\",\n webhook: config.imessage?.webhook,\n });\n super({\n codec: getBuiltinTargetCodec(\"imessage\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/imessage/webhook\", port: 8796 },\n endpointLabel: \"webhook endpoint\",\n matchesThread: matchesIMessageThread,\n normalizeWebhookPayload: normalizeIMessageWebhookPayload,\n platform: \"imessage\",\n publicUrl: config.imessage?.webhook.publicUrl,\n recorderPath: config.imessage?.recorder.path\n ? path.resolve(config.imessage.recorder.path)\n : undefined,\n webhook: config.imessage?.webhook,\n },\n });\n }\n}\n\nexport function matchesIMessageThread(\n candidateThreadId: string,\n expectedThreadId: string | undefined,\n target: { channelId?: string | undefined; id: string },\n raw?: unknown,\n): boolean {\n const rawPayload = isRecord(raw) ? raw : undefined;\n const data = rawPayload ? (optionalRecord(rawPayload, \"data\") ?? rawPayload) : undefined;\n const recipientAlias = data ? optionalString(data, \"chatIdentifier\") : undefined;\n if (expectedThreadId === undefined) {\n return true;\n }\n const expectedIdentifiers = new Set([expectedThreadId, target.id]);\n if (target.channelId) {\n expectedIdentifiers.add(target.channelId);\n }\n return (\n expectedIdentifiers.has(candidateThreadId) ||\n (recipientAlias !== undefined && expectedIdentifiers.has(recipientAlias))\n );\n}\n\nfunction normalizeIMessageWebhookPayload(payload: unknown) {\n if (!isRecord(payload)) {\n throw new CrablineError(\"iMessage webhook payload must be an object\", { kind: \"inbound\" });\n }\n\n if (optionalRecord(payload, \"message\")) {\n return genericMockPayloadWithNativeThread({\n channelRule: IMESSAGE_THREAD_RULE,\n payload,\n threadRule: IMESSAGE_THREAD_RULE,\n });\n }\n\n const data = optionalRecord(payload, \"data\") ?? payload;\n const threadId = optionalString(data, \"chatGuid\") ?? optionalString(data, \"chatIdentifier\");\n const text = optionalString(data, \"text\") ?? optionalString(data, \"message\");\n if (!threadId || !text) {\n throw new CrablineError(\n \"iMessage webhook payload requires chatGuid or chatIdentifier and text\",\n {\n kind: \"inbound\",\n },\n );\n }\n\n return {\n author: authorFromBotFlag(data.isFromMe === true),\n ...(optionalString(data, \"guid\") ? { id: optionalString(data, \"guid\") } : {}),\n raw: payload,\n text,\n threadId: requireNativeInboundId(threadId, IMESSAGE_THREAD_RULE, \"iMessage chatGuid\"),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"imessage.js","sourceRoot":"","sources":["../../../../src/providers/builtin/imessage.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 { matchesNativeId } from \"../native-ids.js\";\nimport type { ProviderAdapter } from \"../types.js\";\nimport { getBuiltinTargetCodec, IMESSAGE_THREAD_RULE } 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 resolveIMessageAdapterConfig(config: ProviderConfig, env?: NodeJS.ProcessEnv) {\n const local = config.imessage?.local ?? true;\n env ??= local ? {} : process.env;\n const configuredApiKey = config.imessage?.apiKey;\n const environmentApiKey = env.IMESSAGE_API_KEY;\n let apiKey = configuredApiKey?.trim()\n ? configuredApiKey\n : environmentApiKey?.trim()\n ? environmentApiKey\n : undefined;\n if (!apiKey) {\n if (!local) {\n throw new CrablineError(\n \"Remote iMessage mode requires imessage.apiKey or IMESSAGE_API_KEY to be non-empty.\",\n { kind: \"config\" },\n );\n }\n apiKey = \"local-mock-imessage-api-key\";\n }\n return {\n apiKey,\n local,\n serverUrl: config.imessage?.serverUrl ?? env.IMESSAGE_SERVER_URL,\n };\n}\n\nfunction isNativeIMessageData(data: Record<string, unknown>): boolean {\n return [\n \"chat_guid\",\n \"chat_identifier\",\n \"chatGuid\",\n \"chatIdentifier\",\n \"guid\",\n \"is_from_me\",\n \"isFromMe\",\n ].some((key) => key in data);\n}\n\nfunction iMessageNativeData(payload: Record<string, unknown>): Record<string, unknown> {\n const data = optionalRecord(payload, \"data\") ?? payload;\n const params = optionalRecord(data, \"params\");\n const message = optionalRecord(params ?? {}, \"message\") ?? optionalRecord(data, \"message\");\n if (message && isNativeIMessageData(message)) {\n return message;\n }\n return data;\n}\n\nfunction iMessageThreadIdentifiers(data: Record<string, unknown>): string[] {\n return [\n optionalString(data, \"chatGuid\"),\n optionalString(data, \"chatIdentifier\"),\n optionalString(data, \"chat_guid\"),\n optionalString(data, \"chat_identifier\"),\n ].filter((value): value is string => value !== undefined);\n}\n\nexport class IMessageProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, _userName: string, _runtime?: unknown) {\n requireExternalWebhookAuthentication({\n authenticated: false,\n provider: \"iMessage\",\n requirement:\n \"a provider-native authenticated ingress mode, which this adapter does not support\",\n webhook: config.imessage?.webhook,\n });\n super({\n codec: getBuiltinTargetCodec(\"imessage\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/imessage/webhook\", port: 8796 },\n endpointLabel: \"webhook endpoint\",\n matchesThread: matchesIMessageThread,\n normalizeWebhookPayload: normalizeIMessageWebhookPayload,\n platform: \"imessage\",\n publicUrl: config.imessage?.webhook.publicUrl,\n recorderPath: config.imessage?.recorder.path\n ? path.resolve(config.imessage.recorder.path)\n : undefined,\n webhook: config.imessage?.webhook,\n },\n });\n }\n}\n\nexport function matchesIMessageThread(\n candidateThreadId: string,\n expectedThreadId: string | undefined,\n target: { channelId?: string | undefined; id: string },\n raw?: unknown,\n): boolean {\n const rawPayload = isRecord(raw) ? raw : undefined;\n const data = rawPayload ? iMessageNativeData(rawPayload) : undefined;\n const aliases = data ? iMessageThreadIdentifiers(data) : [];\n const expectedIdentifiers = new Set([target.channelId ?? target.id]);\n if (expectedThreadId !== undefined) {\n expectedIdentifiers.add(expectedThreadId);\n }\n return (\n expectedIdentifiers.has(candidateThreadId) ||\n aliases.some((alias) => expectedIdentifiers.has(alias))\n );\n}\n\nfunction normalizeIMessageWebhookPayload(payload: unknown) {\n if (!isRecord(payload)) {\n throw new CrablineError(\"iMessage webhook payload must be an object\", { kind: \"inbound\" });\n }\n\n const message = optionalRecord(payload, \"message\");\n if (\n (message && !isNativeIMessageData(message)) ||\n (!message && !isNativeIMessageData(payload) && optionalString(payload, \"threadId\"))\n ) {\n return genericMockPayloadWithNativeThread({\n channelRule: IMESSAGE_THREAD_RULE,\n payload,\n threadRule: IMESSAGE_THREAD_RULE,\n });\n }\n\n const data = iMessageNativeData(payload);\n const threadId = iMessageThreadIdentifiers(data).find((candidate) =>\n matchesNativeId(candidate, IMESSAGE_THREAD_RULE),\n );\n const text = optionalString(data, \"text\") ?? optionalString(data, \"message\");\n if (!threadId || !text) {\n throw new CrablineError(\n \"iMessage webhook payload requires chatGuid or chatIdentifier (including native snake_case aliases) and text\",\n {\n kind: \"inbound\",\n },\n );\n }\n\n return {\n author: authorFromBotFlag(data.isFromMe === true || data.is_from_me === true),\n ...(optionalString(data, \"guid\") ? { id: optionalString(data, \"guid\") } : {}),\n raw: payload,\n text,\n threadId: requireNativeInboundId(threadId, IMESSAGE_THREAD_RULE, \"iMessage chatGuid\"),\n };\n}\n"]}
|