@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
package/src/setup.test.ts DELETED
@@ -1,560 +0,0 @@
1
- import {
2
- expectLifecyclePatch,
3
- expectPendingUntilAbort,
4
- startAccountAndTrackLifecycle,
5
- } from "openclaw/plugin-sdk/channel-test-helpers";
6
- import {
7
- createPluginSetupWizardConfigure,
8
- createPluginSetupWizardStatus,
9
- createTestWizardPrompter,
10
- runSetupWizardConfigure,
11
- } from "openclaw/plugin-sdk/plugin-test-runtime";
12
- import type { WizardPrompter } from "openclaw/plugin-sdk/plugin-test-runtime";
13
- import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
14
- import { afterEach, describe, expect, it, vi } from "vitest";
15
- import type { OpenClawConfig } from "../runtime-api.js";
16
- import {
17
- listGoogleChatAccountIds,
18
- resolveGoogleChatAccount,
19
- resolveDefaultGoogleChatAccountId,
20
- type ResolvedGoogleChatAccount,
21
- } from "./accounts.js";
22
- import { startGoogleChatGatewayAccount } from "./gateway.js";
23
- import { googlechatSetupAdapter } from "./setup-core.js";
24
- import { googlechatSetupWizard } from "./setup-surface.js";
25
-
26
- const hoisted = vi.hoisted(() => ({
27
- startGoogleChatMonitor: vi.fn(),
28
- }));
29
-
30
- vi.mock("./channel.runtime.js", () => ({
31
- googleChatChannelRuntime: {
32
- resolveGoogleChatWebhookPath: ({
33
- account,
34
- }: {
35
- account: { config: { webhookPath?: string } };
36
- }) => account.config.webhookPath ?? "/googlechat",
37
- startGoogleChatMonitor: hoisted.startGoogleChatMonitor,
38
- },
39
- }));
40
-
41
- const googlechatSetupPlugin = {
42
- id: "googlechat",
43
- meta: {
44
- label: "Google Chat",
45
- },
46
- config: {
47
- defaultAccountId: resolveDefaultGoogleChatAccountId,
48
- listAccountIds: listGoogleChatAccountIds,
49
- },
50
- setupWizard: googlechatSetupWizard,
51
- } as never;
52
-
53
- const googlechatConfigure = createPluginSetupWizardConfigure(googlechatSetupPlugin);
54
- const googlechatStatus = createPluginSetupWizardStatus(googlechatSetupPlugin);
55
-
56
- function buildAccount(): ResolvedGoogleChatAccount {
57
- return {
58
- accountId: "default",
59
- enabled: true,
60
- credentialSource: "inline",
61
- credentials: {},
62
- config: {
63
- webhookPath: "/googlechat",
64
- webhookUrl: "https://example.com/googlechat",
65
- audienceType: "app-url",
66
- audience: "https://example.com/googlechat",
67
- },
68
- };
69
- }
70
-
71
- async function waitForGoogleChatMonitorStarted() {
72
- for (let attempt = 0; attempt < 10; attempt += 1) {
73
- if (hoisted.startGoogleChatMonitor.mock.calls.length === 1) {
74
- return;
75
- }
76
- await new Promise<void>((resolve) => setImmediate(resolve));
77
- }
78
- expect(hoisted.startGoogleChatMonitor).toHaveBeenCalledOnce();
79
- }
80
-
81
- describe("googlechat setup", () => {
82
- afterEach(() => {
83
- vi.clearAllMocks();
84
- vi.unstubAllEnvs();
85
- });
86
-
87
- it("rejects env auth for non-default accounts", () => {
88
- if (!googlechatSetupAdapter.validateInput) {
89
- throw new Error("Expected googlechatSetupAdapter.validateInput to be defined");
90
- }
91
- expect(
92
- googlechatSetupAdapter.validateInput({
93
- accountId: "secondary",
94
- input: { useEnv: true },
95
- } as never),
96
- ).toBe("GOOGLE_CHAT_SERVICE_ACCOUNT env vars can only be used for the default account.");
97
- });
98
-
99
- it("requires inline or file credentials when env auth is not used", () => {
100
- if (!googlechatSetupAdapter.validateInput) {
101
- throw new Error("Expected googlechatSetupAdapter.validateInput to be defined");
102
- }
103
- expect(
104
- googlechatSetupAdapter.validateInput({
105
- accountId: DEFAULT_ACCOUNT_ID,
106
- input: { useEnv: false, token: "", tokenFile: "" },
107
- } as never),
108
- ).toBe("Google Chat requires --token (service account JSON) or --token-file.");
109
- });
110
-
111
- it("builds a patch from token-file and trims optional webhook fields", () => {
112
- if (!googlechatSetupAdapter.applyAccountConfig) {
113
- throw new Error("Expected googlechatSetupAdapter.applyAccountConfig to be defined");
114
- }
115
- expect(
116
- googlechatSetupAdapter.applyAccountConfig({
117
- cfg: { channels: { googlechat: {} } },
118
- accountId: DEFAULT_ACCOUNT_ID,
119
- input: {
120
- name: "Default",
121
- tokenFile: "/tmp/googlechat.json",
122
- audienceType: " app-url ",
123
- audience: " https://example.com/googlechat ",
124
- webhookPath: " /googlechat ",
125
- webhookUrl: " https://example.com/googlechat/hook ",
126
- },
127
- } as never),
128
- ).toEqual({
129
- channels: {
130
- googlechat: {
131
- enabled: true,
132
- name: "Default",
133
- serviceAccountFile: "/tmp/googlechat.json",
134
- audienceType: "app-url",
135
- audience: "https://example.com/googlechat",
136
- webhookPath: "/googlechat",
137
- webhookUrl: "https://example.com/googlechat/hook",
138
- },
139
- },
140
- });
141
- });
142
-
143
- it("prefers inline token patch when token-file is absent", () => {
144
- if (!googlechatSetupAdapter.applyAccountConfig) {
145
- throw new Error("Expected googlechatSetupAdapter.applyAccountConfig to be defined");
146
- }
147
- expect(
148
- googlechatSetupAdapter.applyAccountConfig({
149
- cfg: { channels: { googlechat: {} } },
150
- accountId: DEFAULT_ACCOUNT_ID,
151
- input: {
152
- name: "Default",
153
- token: { client_email: "bot@example.com" },
154
- },
155
- } as never),
156
- ).toEqual({
157
- channels: {
158
- googlechat: {
159
- enabled: true,
160
- name: "Default",
161
- serviceAccount: { client_email: "bot@example.com" },
162
- },
163
- },
164
- });
165
- });
166
-
167
- it("configures service-account auth and webhook audience", async () => {
168
- const prompter = createTestWizardPrompter({
169
- text: vi.fn(async ({ message }: { message: string }) => {
170
- if (message === "Service account JSON path") {
171
- return "/tmp/googlechat-service-account.json";
172
- }
173
- if (message === "App URL") {
174
- return "https://example.com/googlechat";
175
- }
176
- throw new Error(`Unexpected prompt: ${message}`);
177
- }) as WizardPrompter["text"],
178
- });
179
-
180
- const result = await runSetupWizardConfigure({
181
- configure: googlechatConfigure,
182
- cfg: {} as OpenClawConfig,
183
- prompter,
184
- options: {},
185
- });
186
-
187
- expect(result.accountId).toBe("default");
188
- expect(result.cfg.channels?.googlechat?.enabled).toBe(true);
189
- expect(result.cfg.channels?.googlechat?.serviceAccountFile).toBe(
190
- "/tmp/googlechat-service-account.json",
191
- );
192
- expect(result.cfg.channels?.googlechat?.audienceType).toBe("app-url");
193
- expect(result.cfg.channels?.googlechat?.audience).toBe("https://example.com/googlechat");
194
- });
195
-
196
- it("reads the named-account DM policy instead of the channel root", () => {
197
- expect(
198
- googlechatSetupWizard.dmPolicy?.getCurrent(
199
- {
200
- channels: {
201
- googlechat: {
202
- dm: {
203
- policy: "disabled",
204
- },
205
- accounts: {
206
- alerts: {
207
- serviceAccount: { client_email: "bot@example.com" },
208
- dm: {
209
- policy: "allowlist",
210
- },
211
- },
212
- },
213
- },
214
- },
215
- } as OpenClawConfig,
216
- "alerts",
217
- ),
218
- ).toBe("allowlist");
219
- });
220
-
221
- it("reports configured state for the selected account instead of any account", async () => {
222
- const status = await googlechatStatus({
223
- cfg: {
224
- channels: {
225
- googlechat: {
226
- accounts: {
227
- default: {
228
- serviceAccount: { client_email: "default@example.com" },
229
- },
230
- alerts: {},
231
- },
232
- },
233
- },
234
- } as OpenClawConfig,
235
- accountOverrides: {
236
- googlechat: "alerts",
237
- },
238
- options: {},
239
- });
240
-
241
- expect(status.configured).toBe(false);
242
- });
243
-
244
- it("reports configured state for the configured defaultAccount instead of any account", async () => {
245
- const status = await googlechatStatus({
246
- cfg: {
247
- channels: {
248
- googlechat: {
249
- defaultAccount: "alerts",
250
- accounts: {
251
- default: {
252
- serviceAccount: { client_email: "default@example.com" },
253
- },
254
- alerts: {},
255
- },
256
- },
257
- },
258
- } as OpenClawConfig,
259
- accountOverrides: {},
260
- options: {},
261
- });
262
-
263
- expect(status.configured).toBe(false);
264
- });
265
-
266
- it("reports account-scoped config keys for named accounts", () => {
267
- expect(googlechatSetupWizard.dmPolicy?.resolveConfigKeys?.({}, "alerts")).toEqual({
268
- policyKey: "channels.googlechat.accounts.alerts.dm.policy",
269
- allowFromKey: "channels.googlechat.accounts.alerts.dm.allowFrom",
270
- });
271
- });
272
-
273
- it("uses configured defaultAccount for omitted DM policy account context", () => {
274
- const cfg = {
275
- channels: {
276
- googlechat: {
277
- defaultAccount: "alerts",
278
- dm: {
279
- policy: "disabled",
280
- },
281
- accounts: {
282
- alerts: {
283
- serviceAccount: { client_email: "bot@example.com" },
284
- dm: {
285
- policy: "allowlist",
286
- },
287
- },
288
- },
289
- },
290
- },
291
- } as OpenClawConfig;
292
-
293
- expect(googlechatSetupWizard.dmPolicy?.getCurrent(cfg)).toBe("allowlist");
294
- expect(googlechatSetupWizard.dmPolicy?.resolveConfigKeys?.(cfg)).toEqual({
295
- policyKey: "channels.googlechat.accounts.alerts.dm.policy",
296
- allowFromKey: "channels.googlechat.accounts.alerts.dm.allowFrom",
297
- });
298
-
299
- const next = googlechatSetupWizard.dmPolicy?.setPolicy(cfg, "open");
300
- expect(next?.channels?.googlechat?.dm?.policy).toBe("disabled");
301
- expect(next?.channels?.googlechat?.accounts?.alerts?.dm?.policy).toBe("open");
302
- });
303
-
304
- it("uses configured defaultAccount for omitted allowFrom prompt context", async () => {
305
- const prompter = {
306
- note: vi.fn(async () => {}),
307
- text: vi.fn(async () => "users/123456789"),
308
- };
309
-
310
- const next = await googlechatSetupWizard.dmPolicy?.promptAllowFrom?.({
311
- cfg: {
312
- channels: {
313
- googlechat: {
314
- defaultAccount: "alerts",
315
- dm: {
316
- allowFrom: ["users/root"],
317
- },
318
- accounts: {
319
- alerts: {
320
- serviceAccount: { client_email: "bot@example.com" },
321
- dm: {
322
- allowFrom: ["users/alerts"],
323
- },
324
- },
325
- },
326
- },
327
- },
328
- } as OpenClawConfig,
329
- prompter: prompter as any,
330
- });
331
-
332
- expect(next?.channels?.googlechat?.dm?.allowFrom).toEqual(["users/root"]);
333
- expect(next?.channels?.googlechat?.accounts?.alerts?.dm?.allowFrom).toEqual([
334
- "users/123456789",
335
- ]);
336
- });
337
-
338
- it('writes open DM policy to the named account and preserves inherited allowFrom with "*"', () => {
339
- const next = googlechatSetupWizard.dmPolicy?.setPolicy(
340
- {
341
- channels: {
342
- googlechat: {
343
- dm: {
344
- allowFrom: ["users/123"],
345
- },
346
- accounts: {
347
- alerts: {
348
- serviceAccount: { client_email: "bot@example.com" },
349
- },
350
- },
351
- },
352
- },
353
- } as OpenClawConfig,
354
- "open",
355
- "alerts",
356
- );
357
-
358
- expect(next?.channels?.googlechat?.dm?.policy).toBeUndefined();
359
- expect(next?.channels?.googlechat?.accounts?.alerts?.dm?.policy).toBe("open");
360
- expect(next?.channels?.googlechat?.accounts?.alerts?.dm?.allowFrom).toEqual(["users/123", "*"]);
361
- });
362
-
363
- it("keeps startAccount pending until abort, then unregisters", async () => {
364
- const unregister = vi.fn();
365
- hoisted.startGoogleChatMonitor.mockResolvedValue(unregister);
366
-
367
- const { abort, patches, task, isSettled } = startAccountAndTrackLifecycle({
368
- startAccount: startGoogleChatGatewayAccount,
369
- account: buildAccount(),
370
- });
371
- await expectPendingUntilAbort({
372
- waitForStarted: waitForGoogleChatMonitorStarted,
373
- isSettled,
374
- abort,
375
- task,
376
- assertBeforeAbort: () => {
377
- expect(unregister).not.toHaveBeenCalled();
378
- },
379
- assertAfterAbort: () => {
380
- expect(unregister).toHaveBeenCalledOnce();
381
- },
382
- });
383
- expectLifecyclePatch(patches, { running: true });
384
- expectLifecyclePatch(patches, { running: false });
385
- });
386
- });
387
-
388
- describe("resolveGoogleChatAccount", () => {
389
- it("parses default-account env JSON credentials only when they decode to an object", () => {
390
- vi.stubEnv("GOOGLE_CHAT_SERVICE_ACCOUNT", '{"client_email":"bot@example.com"}');
391
-
392
- const resolved = resolveGoogleChatAccount({
393
- cfg: { channels: { googlechat: {} } },
394
- accountId: "default",
395
- });
396
-
397
- expect(resolved.credentialSource).toBe("env");
398
- expect(resolved.credentials).toEqual({ client_email: "bot@example.com" });
399
- });
400
-
401
- it("ignores env JSON credentials when they decode to a non-object value", () => {
402
- vi.stubEnv("GOOGLE_CHAT_SERVICE_ACCOUNT", '["not","an","object"]');
403
- vi.stubEnv("GOOGLE_CHAT_SERVICE_ACCOUNT_FILE", "/tmp/googlechat.json");
404
-
405
- const resolved = resolveGoogleChatAccount({
406
- cfg: { channels: { googlechat: {} } },
407
- accountId: "default",
408
- });
409
-
410
- expect(resolved.credentialSource).toBe("env");
411
- expect(resolved.credentials).toBeUndefined();
412
- expect(resolved.credentialsFile).toBe("/tmp/googlechat.json");
413
- });
414
-
415
- it("inherits shared defaults from accounts.default for named accounts", () => {
416
- const cfg: OpenClawConfig = {
417
- channels: {
418
- googlechat: {
419
- accounts: {
420
- default: {
421
- audienceType: "app-url",
422
- audience: "https://example.com/googlechat",
423
- webhookPath: "/googlechat",
424
- },
425
- andy: {
426
- serviceAccountFile: "/tmp/andy-sa.json",
427
- },
428
- },
429
- },
430
- },
431
- };
432
-
433
- const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
434
- expect(resolved.config.audienceType).toBe("app-url");
435
- expect(resolved.config.audience).toBe("https://example.com/googlechat");
436
- expect(resolved.config.webhookPath).toBe("/googlechat");
437
- expect(resolved.config.serviceAccountFile).toBe("/tmp/andy-sa.json");
438
- });
439
-
440
- it("prefers top-level and account overrides over accounts.default", () => {
441
- const cfg: OpenClawConfig = {
442
- channels: {
443
- googlechat: {
444
- audienceType: "project-number",
445
- audience: "1234567890",
446
- accounts: {
447
- default: {
448
- audienceType: "app-url",
449
- audience: "https://default.example.com/googlechat",
450
- webhookPath: "/googlechat-default",
451
- },
452
- april: {
453
- webhookPath: "/googlechat-april",
454
- },
455
- },
456
- },
457
- },
458
- };
459
-
460
- const resolved = resolveGoogleChatAccount({ cfg, accountId: "april" });
461
- expect(resolved.config.audienceType).toBe("project-number");
462
- expect(resolved.config.audience).toBe("1234567890");
463
- expect(resolved.config.webhookPath).toBe("/googlechat-april");
464
- });
465
-
466
- it("does not inherit disabled state from accounts.default for named accounts", () => {
467
- const cfg: OpenClawConfig = {
468
- channels: {
469
- googlechat: {
470
- accounts: {
471
- default: {
472
- enabled: false,
473
- audienceType: "app-url",
474
- audience: "https://example.com/googlechat",
475
- },
476
- andy: {
477
- serviceAccountFile: "/tmp/andy-sa.json",
478
- },
479
- },
480
- },
481
- },
482
- };
483
-
484
- const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
485
- expect(resolved.enabled).toBe(true);
486
- expect(resolved.config.enabled).toBeUndefined();
487
- expect(resolved.config.audienceType).toBe("app-url");
488
- });
489
-
490
- it("does not inherit default-account credentials into named accounts", () => {
491
- const cfg: OpenClawConfig = {
492
- channels: {
493
- googlechat: {
494
- accounts: {
495
- default: {
496
- serviceAccountRef: {
497
- source: "env",
498
- provider: "test",
499
- id: "default-sa",
500
- },
501
- audienceType: "app-url",
502
- audience: "https://example.com/googlechat",
503
- },
504
- andy: {
505
- serviceAccountFile: "/tmp/andy-sa.json",
506
- },
507
- },
508
- },
509
- },
510
- };
511
-
512
- const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
513
- expect(resolved.credentialSource).toBe("file");
514
- expect(resolved.credentialsFile).toBe("/tmp/andy-sa.json");
515
- expect(resolved.config.audienceType).toBe("app-url");
516
- });
517
-
518
- it("does not inherit dangerous name matching from accounts.default", () => {
519
- const cfg: OpenClawConfig = {
520
- channels: {
521
- googlechat: {
522
- accounts: {
523
- default: {
524
- dangerouslyAllowNameMatching: true,
525
- audienceType: "app-url",
526
- audience: "https://example.com/googlechat",
527
- },
528
- andy: {
529
- serviceAccountFile: "/tmp/andy-sa.json",
530
- },
531
- },
532
- },
533
- },
534
- };
535
-
536
- const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
537
- expect(resolved.config.dangerouslyAllowNameMatching).toBeUndefined();
538
- expect(resolved.config.audienceType).toBe("app-url");
539
- });
540
-
541
- it("uses configured defaultAccount when accountId is omitted", () => {
542
- const cfg: OpenClawConfig = {
543
- channels: {
544
- googlechat: {
545
- defaultAccount: "alerts",
546
- accounts: {
547
- alerts: {
548
- serviceAccountFile: "/tmp/alerts-sa.json",
549
- },
550
- },
551
- },
552
- },
553
- };
554
-
555
- const resolved = resolveGoogleChatAccount({ cfg });
556
- expect(resolved.accountId).toBe("alerts");
557
- expect(resolved.credentialSource).toBe("file");
558
- expect(resolved.credentialsFile).toBe("/tmp/alerts-sa.json");
559
- });
560
- });