@openclaw/googlechat 2026.5.2-beta.1 → 2026.5.3-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/api-C67jv0ow.js +627 -0
  2. package/dist/api.js +3 -0
  3. package/dist/channel-DE2hroMn.js +682 -0
  4. package/dist/channel-config-api.js +5 -0
  5. package/dist/channel-plugin-api.js +2 -0
  6. package/dist/channel.runtime-CoSLc-4R.js +920 -0
  7. package/dist/contract-api.js +3 -0
  8. package/dist/doctor-contract-CG1sLToP.js +151 -0
  9. package/dist/doctor-contract-api.js +2 -0
  10. package/dist/index.js +22 -0
  11. package/dist/runtime-api-wkIdfwqY.js +30 -0
  12. package/dist/runtime-api.js +2 -0
  13. package/dist/secret-contract-DtQ_IO7J.js +99 -0
  14. package/dist/secret-contract-api.js +2 -0
  15. package/dist/setup-entry.js +15 -0
  16. package/dist/setup-plugin-api.js +75 -0
  17. package/dist/setup-surface-CofP-Gg9.js +309 -0
  18. package/dist/test-api.js +3 -0
  19. package/package.json +14 -6
  20. package/api.ts +0 -3
  21. package/channel-config-api.ts +0 -1
  22. package/channel-plugin-api.ts +0 -1
  23. package/contract-api.ts +0 -5
  24. package/doctor-contract-api.ts +0 -1
  25. package/index.ts +0 -20
  26. package/runtime-api.ts +0 -60
  27. package/secret-contract-api.ts +0 -5
  28. package/setup-entry.ts +0 -13
  29. package/setup-plugin-api.ts +0 -3
  30. package/src/accounts.ts +0 -169
  31. package/src/actions.test.ts +0 -265
  32. package/src/actions.ts +0 -227
  33. package/src/api.ts +0 -322
  34. package/src/approval-auth.test.ts +0 -24
  35. package/src/approval-auth.ts +0 -32
  36. package/src/auth.ts +0 -209
  37. package/src/channel-config.test.ts +0 -39
  38. package/src/channel.adapters.ts +0 -293
  39. package/src/channel.deps.runtime.ts +0 -28
  40. package/src/channel.runtime.ts +0 -17
  41. package/src/channel.setup.ts +0 -98
  42. package/src/channel.test.ts +0 -695
  43. package/src/channel.ts +0 -256
  44. package/src/config-schema.test.ts +0 -16
  45. package/src/config-schema.ts +0 -3
  46. package/src/doctor-contract.test.ts +0 -75
  47. package/src/doctor-contract.ts +0 -182
  48. package/src/doctor.ts +0 -57
  49. package/src/gateway.ts +0 -63
  50. package/src/google-auth.runtime.test.ts +0 -462
  51. package/src/google-auth.runtime.ts +0 -539
  52. package/src/group-policy.ts +0 -17
  53. package/src/monitor-access.test.ts +0 -443
  54. package/src/monitor-access.ts +0 -405
  55. package/src/monitor-reply-delivery.ts +0 -156
  56. package/src/monitor-routing.ts +0 -55
  57. package/src/monitor-types.ts +0 -33
  58. package/src/monitor-webhook.test.ts +0 -446
  59. package/src/monitor-webhook.ts +0 -285
  60. package/src/monitor.reply-delivery.test.ts +0 -139
  61. package/src/monitor.ts +0 -428
  62. package/src/monitor.webhook-routing.test.ts +0 -252
  63. package/src/runtime.ts +0 -9
  64. package/src/secret-contract.test.ts +0 -60
  65. package/src/secret-contract.ts +0 -161
  66. package/src/sender-allow.ts +0 -46
  67. package/src/setup-core.ts +0 -40
  68. package/src/setup-surface.ts +0 -236
  69. package/src/setup.test.ts +0 -560
  70. package/src/targets.test.ts +0 -421
  71. package/src/targets.ts +0 -66
  72. package/src/types.config.ts +0 -3
  73. package/src/types.ts +0 -73
  74. package/test-api.ts +0 -2
  75. package/tsconfig.json +0 -16
@@ -1,446 +0,0 @@
1
- import type { IncomingMessage, ServerResponse } from "node:http";
2
- import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
3
- import type { WebhookTarget } from "./monitor-types.js";
4
- import type { GoogleChatEvent } from "./types.js";
5
-
6
- const readJsonWebhookBodyOrReject = vi.hoisted(() => vi.fn());
7
- const resolveWebhookTargetWithAuthOrReject = vi.hoisted(() => vi.fn());
8
- const withResolvedWebhookRequestPipeline = vi.hoisted(() => vi.fn());
9
- const verifyGoogleChatRequest = vi.hoisted(() => vi.fn());
10
-
11
- vi.mock("openclaw/plugin-sdk/webhook-request-guards", () => ({
12
- readJsonWebhookBodyOrReject,
13
- }));
14
-
15
- vi.mock("openclaw/plugin-sdk/webhook-targets", () => ({
16
- resolveWebhookTargetWithAuthOrReject,
17
- withResolvedWebhookRequestPipeline,
18
- }));
19
-
20
- vi.mock("./auth.js", () => ({
21
- verifyGoogleChatRequest,
22
- }));
23
-
24
- type ProcessEventFn = (event: GoogleChatEvent, target: WebhookTarget) => Promise<void>;
25
- let createGoogleChatWebhookRequestHandler: typeof import("./monitor-webhook.js").createGoogleChatWebhookRequestHandler;
26
- let warnAppPrincipalMisconfiguration: typeof import("./monitor-webhook.js").warnAppPrincipalMisconfiguration;
27
-
28
- function createRequest(authorization?: string): IncomingMessage {
29
- return {
30
- method: "POST",
31
- url: "/googlechat",
32
- headers: {
33
- authorization: authorization ?? "",
34
- "content-type": "application/json",
35
- },
36
- } as IncomingMessage;
37
- }
38
-
39
- function createResponse() {
40
- const res = {
41
- statusCode: 0,
42
- headers: {} as Record<string, string>,
43
- body: "",
44
- setHeader: (name: string, value: string) => {
45
- res.headers[name] = value;
46
- },
47
- end: (payload?: string) => {
48
- res.body = payload ?? "";
49
- return res;
50
- },
51
- } as ServerResponse & { headers: Record<string, string>; body: string };
52
- return res;
53
- }
54
-
55
- function installSimplePipeline(targets: unknown[]) {
56
- withResolvedWebhookRequestPipeline.mockImplementation(
57
- async ({
58
- handle,
59
- req,
60
- res,
61
- }: {
62
- handle: (input: {
63
- targets: unknown[];
64
- req: IncomingMessage;
65
- res: ServerResponse;
66
- }) => Promise<unknown>;
67
- req: IncomingMessage;
68
- res: ServerResponse;
69
- }) =>
70
- await handle({
71
- targets,
72
- req,
73
- res,
74
- }),
75
- );
76
- }
77
-
78
- async function runWebhookHandler(options?: {
79
- processEvent?: ProcessEventFn;
80
- authorization?: string;
81
- }) {
82
- const processEvent: ProcessEventFn =
83
- options?.processEvent ?? (vi.fn(async () => {}) as ProcessEventFn);
84
- const handler = createGoogleChatWebhookRequestHandler({
85
- webhookTargets: new Map(),
86
- webhookInFlightLimiter: {} as never,
87
- processEvent,
88
- });
89
- const req = createRequest(options?.authorization);
90
- const res = createResponse();
91
- await expect(handler(req, res)).resolves.toBe(true);
92
- return { processEvent, res };
93
- }
94
-
95
- describe("googlechat monitor webhook", () => {
96
- beforeAll(async () => {
97
- ({ createGoogleChatWebhookRequestHandler, warnAppPrincipalMisconfiguration } =
98
- await import("./monitor-webhook.js"));
99
- });
100
-
101
- beforeEach(() => {
102
- vi.clearAllMocks();
103
- });
104
-
105
- it("accepts add-on payloads that carry systemIdToken in the body", async () => {
106
- installSimplePipeline([
107
- {
108
- account: {
109
- accountId: "default",
110
- config: { appPrincipal: "chat-app" },
111
- },
112
- runtime: { error: vi.fn() },
113
- statusSink: vi.fn(),
114
- audienceType: "app-url",
115
- audience: "https://example.com/googlechat",
116
- },
117
- ]);
118
- readJsonWebhookBodyOrReject.mockResolvedValue({
119
- ok: true,
120
- value: {
121
- commonEventObject: { hostApp: "CHAT" },
122
- authorizationEventObject: { systemIdToken: "addon-token" },
123
- chat: {
124
- eventTime: "2026-03-22T00:00:00.000Z",
125
- user: { name: "users/123" },
126
- messagePayload: {
127
- space: { name: "spaces/AAA" },
128
- message: { name: "spaces/AAA/messages/1", text: "hello" },
129
- },
130
- },
131
- },
132
- });
133
- resolveWebhookTargetWithAuthOrReject.mockImplementation(async ({ isMatch, targets }) => {
134
- for (const target of targets) {
135
- if (await isMatch(target)) {
136
- return target;
137
- }
138
- }
139
- return null;
140
- });
141
- verifyGoogleChatRequest.mockResolvedValue({ ok: true });
142
- const { processEvent, res } = await runWebhookHandler();
143
-
144
- expect(verifyGoogleChatRequest).toHaveBeenCalledWith(
145
- expect.objectContaining({
146
- bearer: "addon-token",
147
- expectedAddOnPrincipal: "chat-app",
148
- }),
149
- );
150
- expect(processEvent).toHaveBeenCalledWith(
151
- expect.objectContaining({
152
- type: "MESSAGE",
153
- space: { name: "spaces/AAA" },
154
- }),
155
- expect.anything(),
156
- );
157
- expect(res.statusCode).toBe(200);
158
- expect(res.headers["Content-Type"]).toBe("application/json");
159
- });
160
-
161
- it("logs WARN with reason when verification fails (missing token)", async () => {
162
- const logFn = vi.fn();
163
- installSimplePipeline([
164
- {
165
- account: {
166
- accountId: "acct-1",
167
- config: { appPrincipal: "chat-app" },
168
- },
169
- runtime: { log: logFn, error: vi.fn() },
170
- audienceType: "app-url",
171
- audience: "https://example.com/googlechat",
172
- },
173
- ]);
174
- readJsonWebhookBodyOrReject.mockResolvedValue({
175
- ok: true,
176
- value: {
177
- commonEventObject: { hostApp: "CHAT" },
178
- authorizationEventObject: { systemIdToken: "bad-token" },
179
- chat: {
180
- messagePayload: {
181
- space: { name: "spaces/AAA" },
182
- message: { name: "spaces/AAA/messages/1", text: "hi" },
183
- },
184
- },
185
- },
186
- });
187
- resolveWebhookTargetWithAuthOrReject.mockImplementation(async ({ isMatch, targets, res }) => {
188
- for (const target of targets) {
189
- if (await isMatch(target)) {
190
- return target;
191
- }
192
- }
193
- res.statusCode = 401;
194
- res.end("unauthorized");
195
- return null;
196
- });
197
- verifyGoogleChatRequest.mockResolvedValue({ ok: false, reason: "missing token" });
198
- const { processEvent, res } = await runWebhookHandler();
199
-
200
- expect(logFn).toHaveBeenCalledWith(expect.stringContaining("acct-1"));
201
- expect(logFn).toHaveBeenCalledWith(expect.stringContaining("missing token"));
202
- expect(processEvent).not.toHaveBeenCalled();
203
- expect(res.statusCode).toBe(401);
204
- });
205
-
206
- it("logs WARN with reason when verification fails (unexpected principal)", async () => {
207
- const logFn = vi.fn();
208
- installSimplePipeline([
209
- {
210
- account: {
211
- accountId: "acct-2",
212
- config: { appPrincipal: "chat-app" },
213
- },
214
- runtime: { log: logFn, error: vi.fn() },
215
- audienceType: "app-url",
216
- audience: "https://example.com/googlechat",
217
- },
218
- ]);
219
- readJsonWebhookBodyOrReject.mockResolvedValue({
220
- ok: true,
221
- value: {
222
- commonEventObject: { hostApp: "CHAT" },
223
- authorizationEventObject: { systemIdToken: "bad-token" },
224
- chat: {
225
- messagePayload: {
226
- space: { name: "spaces/AAA" },
227
- message: { name: "spaces/AAA/messages/1", text: "hi" },
228
- },
229
- },
230
- },
231
- });
232
- resolveWebhookTargetWithAuthOrReject.mockImplementation(async ({ isMatch, targets, res }) => {
233
- for (const target of targets) {
234
- if (await isMatch(target)) {
235
- return target;
236
- }
237
- }
238
- res.statusCode = 401;
239
- res.end("unauthorized");
240
- return null;
241
- });
242
- verifyGoogleChatRequest.mockResolvedValue({
243
- ok: false,
244
- reason: "unexpected add-on principal: 999999999999999999999",
245
- });
246
- const { processEvent, res } = await runWebhookHandler();
247
-
248
- expect(logFn).toHaveBeenCalledWith(expect.stringContaining("acct-2"));
249
- expect(logFn).toHaveBeenCalledWith(
250
- expect.stringContaining("unexpected add-on principal: 999999999999999999999"),
251
- );
252
- expect(processEvent).not.toHaveBeenCalled();
253
- expect(res.statusCode).toBe(401);
254
- });
255
-
256
- it("does not log WARN when verification succeeds", async () => {
257
- const logFn = vi.fn();
258
- installSimplePipeline([
259
- {
260
- account: {
261
- accountId: "acct-ok",
262
- config: { appPrincipal: "chat-app" },
263
- },
264
- runtime: { log: logFn, error: vi.fn() },
265
- statusSink: vi.fn(),
266
- audienceType: "app-url",
267
- audience: "https://example.com/googlechat",
268
- },
269
- ]);
270
- readJsonWebhookBodyOrReject.mockResolvedValue({
271
- ok: true,
272
- value: {
273
- commonEventObject: { hostApp: "CHAT" },
274
- authorizationEventObject: { systemIdToken: "good-token" },
275
- chat: {
276
- eventTime: "2026-03-22T00:00:00.000Z",
277
- user: { name: "users/123" },
278
- messagePayload: {
279
- space: { name: "spaces/AAA" },
280
- message: { name: "spaces/AAA/messages/1", text: "hi" },
281
- },
282
- },
283
- },
284
- });
285
- resolveWebhookTargetWithAuthOrReject.mockImplementation(async ({ isMatch, targets }) => {
286
- for (const target of targets) {
287
- if (await isMatch(target)) {
288
- return target;
289
- }
290
- }
291
- return null;
292
- });
293
- verifyGoogleChatRequest.mockResolvedValue({ ok: true });
294
- const { res } = await runWebhookHandler();
295
-
296
- expect(logFn).not.toHaveBeenCalled();
297
- expect(res.statusCode).toBe(200);
298
- });
299
-
300
- it("does not log failed candidate targets when another target verifies", async () => {
301
- const logA = vi.fn();
302
- const logB = vi.fn();
303
- installSimplePipeline([
304
- {
305
- account: {
306
- accountId: "acct-a",
307
- config: { appPrincipal: "chat-app-a" },
308
- },
309
- runtime: { log: logA, error: vi.fn() },
310
- audienceType: "app-url",
311
- audience: "https://example.com/googlechat",
312
- },
313
- {
314
- account: {
315
- accountId: "acct-b",
316
- config: { appPrincipal: "chat-app-b" },
317
- },
318
- runtime: { log: logB, error: vi.fn() },
319
- statusSink: vi.fn(),
320
- audienceType: "app-url",
321
- audience: "https://example.com/googlechat",
322
- },
323
- ]);
324
- readJsonWebhookBodyOrReject.mockResolvedValue({
325
- ok: true,
326
- value: {
327
- commonEventObject: { hostApp: "CHAT" },
328
- authorizationEventObject: { systemIdToken: "shared-path-token" },
329
- chat: {
330
- eventTime: "2026-03-22T00:00:00.000Z",
331
- user: { name: "users/123" },
332
- messagePayload: {
333
- space: { name: "spaces/BBB" },
334
- message: { name: "spaces/BBB/messages/1", text: "hi" },
335
- },
336
- },
337
- },
338
- });
339
- resolveWebhookTargetWithAuthOrReject.mockImplementation(async ({ isMatch, targets }) => {
340
- for (const target of targets) {
341
- if (await isMatch(target)) {
342
- return target;
343
- }
344
- }
345
- return null;
346
- });
347
- verifyGoogleChatRequest
348
- .mockResolvedValueOnce({ ok: false, reason: "unexpected add-on principal: 111" })
349
- .mockResolvedValueOnce({ ok: true });
350
- const { processEvent, res } = await runWebhookHandler();
351
-
352
- expect(logA).not.toHaveBeenCalled();
353
- expect(logB).not.toHaveBeenCalled();
354
- expect(processEvent).toHaveBeenCalledWith(
355
- expect.anything(),
356
- expect.objectContaining({
357
- account: expect.objectContaining({ accountId: "acct-b" }),
358
- }),
359
- );
360
- expect(res.statusCode).toBe(200);
361
- });
362
-
363
- it("rejects missing add-on bearer tokens before dispatch", async () => {
364
- const logFn = vi.fn();
365
- installSimplePipeline([
366
- {
367
- account: {
368
- accountId: "default",
369
- config: { appPrincipal: "chat-app" },
370
- },
371
- runtime: { log: logFn, error: vi.fn() },
372
- },
373
- ]);
374
- readJsonWebhookBodyOrReject.mockResolvedValue({
375
- ok: true,
376
- value: {
377
- commonEventObject: { hostApp: "CHAT" },
378
- chat: {
379
- messagePayload: {
380
- space: { name: "spaces/AAA" },
381
- message: { name: "spaces/AAA/messages/1", text: "hello" },
382
- },
383
- },
384
- },
385
- });
386
- const { processEvent, res } = await runWebhookHandler();
387
-
388
- expect(processEvent).not.toHaveBeenCalled();
389
- expect(logFn).toHaveBeenCalledWith(expect.stringContaining("default"));
390
- expect(logFn).toHaveBeenCalledWith(expect.stringContaining("missing token"));
391
- expect(res.statusCode).toBe(401);
392
- expect(res.body).toBe("unauthorized");
393
- });
394
- });
395
-
396
- describe("warnAppPrincipalMisconfiguration", () => {
397
- it("warns when appPrincipal is missing for app-url audience", () => {
398
- const log = vi.fn();
399
- warnAppPrincipalMisconfiguration({
400
- accountId: "acct-missing",
401
- audienceType: "app-url",
402
- appPrincipal: undefined,
403
- log,
404
- });
405
- expect(log).toHaveBeenCalledOnce();
406
- expect(log).toHaveBeenCalledWith(expect.stringContaining("acct-missing"));
407
- expect(log).toHaveBeenCalledWith(expect.stringContaining("appPrincipal is missing"));
408
- expect(log).toHaveBeenCalledWith(expect.stringContaining("numeric OAuth 2.0 client ID"));
409
- });
410
-
411
- it("warns when appPrincipal contains @ for app-url audience", () => {
412
- const log = vi.fn();
413
- warnAppPrincipalMisconfiguration({
414
- accountId: "acct-email",
415
- audienceType: "app-url",
416
- appPrincipal: "bot@example.iam.gserviceaccount.com",
417
- log,
418
- });
419
- expect(log).toHaveBeenCalledOnce();
420
- expect(log).toHaveBeenCalledWith(expect.stringContaining("acct-email"));
421
- expect(log).toHaveBeenCalledWith(expect.stringContaining("looks like an email"));
422
- expect(log).toHaveBeenCalledWith(expect.stringContaining("numeric OAuth 2.0 client ID"));
423
- });
424
-
425
- it("does not warn for valid numeric appPrincipal with app-url audience", () => {
426
- const log = vi.fn();
427
- warnAppPrincipalMisconfiguration({
428
- accountId: "acct-ok",
429
- audienceType: "app-url",
430
- appPrincipal: "123456789012345678901",
431
- log,
432
- });
433
- expect(log).not.toHaveBeenCalled();
434
- });
435
-
436
- it("does not warn for project-number audience even with missing appPrincipal", () => {
437
- const log = vi.fn();
438
- warnAppPrincipalMisconfiguration({
439
- accountId: "acct-pn",
440
- audienceType: "project-number",
441
- appPrincipal: undefined,
442
- log,
443
- });
444
- expect(log).not.toHaveBeenCalled();
445
- });
446
- });