@actagent/googlechat 2026.6.2

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 (73) hide show
  1. package/README.md +11 -0
  2. package/actagent.plugin.json +17 -0
  3. package/api.ts +4 -0
  4. package/channel-config-api.ts +2 -0
  5. package/channel-plugin-api.ts +2 -0
  6. package/config-api.ts +3 -0
  7. package/contract-api.ts +6 -0
  8. package/directory-contract-api.ts +7 -0
  9. package/doctor-contract-api.ts +2 -0
  10. package/index.ts +21 -0
  11. package/npm-shrinkwrap.json +314 -0
  12. package/package.json +88 -0
  13. package/runtime-api.ts +61 -0
  14. package/secret-contract-api.ts +6 -0
  15. package/setup-entry.ts +14 -0
  16. package/setup-plugin-api.ts +3 -0
  17. package/src/accounts.ts +185 -0
  18. package/src/actions.test.ts +312 -0
  19. package/src/actions.ts +228 -0
  20. package/src/api.ts +346 -0
  21. package/src/approval-auth.test.ts +25 -0
  22. package/src/approval-auth.ts +38 -0
  23. package/src/approval-card-actions.test.ts +113 -0
  24. package/src/approval-card-actions.ts +307 -0
  25. package/src/approval-card-click.test.ts +279 -0
  26. package/src/approval-card-click.ts +94 -0
  27. package/src/approval-handler.runtime.test.ts +388 -0
  28. package/src/approval-handler.runtime.ts +413 -0
  29. package/src/approval-native.test.ts +399 -0
  30. package/src/approval-native.ts +246 -0
  31. package/src/auth.ts +219 -0
  32. package/src/channel-base.ts +123 -0
  33. package/src/channel-config.test.ts +174 -0
  34. package/src/channel.adapters.ts +363 -0
  35. package/src/channel.deps.runtime.ts +30 -0
  36. package/src/channel.runtime.ts +18 -0
  37. package/src/channel.setup.ts +7 -0
  38. package/src/channel.test.ts +845 -0
  39. package/src/channel.ts +214 -0
  40. package/src/config-schema.test.ts +32 -0
  41. package/src/config-schema.ts +4 -0
  42. package/src/doctor-contract.test.ts +76 -0
  43. package/src/doctor-contract.ts +181 -0
  44. package/src/doctor.ts +58 -0
  45. package/src/gateway.ts +84 -0
  46. package/src/google-auth.runtime.test.ts +571 -0
  47. package/src/google-auth.runtime.ts +570 -0
  48. package/src/group-policy.ts +18 -0
  49. package/src/monitor-access.test.ts +492 -0
  50. package/src/monitor-access.ts +466 -0
  51. package/src/monitor-durable.test.ts +40 -0
  52. package/src/monitor-durable.ts +24 -0
  53. package/src/monitor-reply-delivery.ts +162 -0
  54. package/src/monitor-routing.ts +66 -0
  55. package/src/monitor-types.ts +34 -0
  56. package/src/monitor-webhook.test.ts +670 -0
  57. package/src/monitor-webhook.ts +361 -0
  58. package/src/monitor.reply-delivery.test.ts +145 -0
  59. package/src/monitor.test.ts +389 -0
  60. package/src/monitor.ts +530 -0
  61. package/src/monitor.webhook-routing.test.ts +258 -0
  62. package/src/runtime.ts +10 -0
  63. package/src/secret-contract.test.ts +61 -0
  64. package/src/secret-contract.ts +162 -0
  65. package/src/setup-core.ts +41 -0
  66. package/src/setup-surface.ts +244 -0
  67. package/src/setup.test.ts +620 -0
  68. package/src/targets.test.ts +562 -0
  69. package/src/targets.ts +67 -0
  70. package/src/types.config.ts +4 -0
  71. package/src/types.ts +139 -0
  72. package/test-api.ts +3 -0
  73. package/tsconfig.json +16 -0
@@ -0,0 +1,244 @@
1
+ // Googlechat plugin module implements setup surface behavior.
2
+ import {
3
+ addWildcardAllowFrom,
4
+ applySetupAccountConfigPatch,
5
+ createPromptParsedAllowFromForAccount,
6
+ createStandardChannelSetupStatus,
7
+ DEFAULT_ACCOUNT_ID,
8
+ formatDocsLink,
9
+ mergeAllowFromEntries,
10
+ migrateBaseNameToDefaultAccount,
11
+ splitSetupEntries,
12
+ createSetupTranslator,
13
+ type ChannelSetupDmPolicy,
14
+ type ChannelSetupWizard,
15
+ } from "actagent/plugin-sdk/setup";
16
+ import {
17
+ normalizeOptionalString,
18
+ normalizeStringifiedOptionalString,
19
+ } from "actagent/plugin-sdk/string-coerce-runtime";
20
+ import { resolveDefaultGoogleChatAccountId, resolveGoogleChatAccount } from "./accounts.js";
21
+
22
+ const t = createSetupTranslator();
23
+
24
+ const channel = "googlechat" as const;
25
+ const ENV_SERVICE_ACCOUNT = "GOOGLE_CHAT_SERVICE_ACCOUNT";
26
+ const ENV_SERVICE_ACCOUNT_FILE = "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE";
27
+ const USE_ENV_FLAG = "__googlechatUseEnv";
28
+ const AUTH_METHOD_FLAG = "__googlechatAuthMethod";
29
+
30
+ type GoogleChatTextInput = NonNullable<ChannelSetupWizard["textInputs"]>[number];
31
+ type GoogleChatTextInputKey = GoogleChatTextInput["inputKey"];
32
+
33
+ const promptAllowFrom = createPromptParsedAllowFromForAccount({
34
+ defaultAccountId: resolveDefaultGoogleChatAccountId,
35
+ message: t("wizard.googlechat.allowFromPrompt"),
36
+ placeholder: "users/123456789, name@example.com",
37
+ parseEntries: (raw) => ({
38
+ entries: mergeAllowFromEntries(undefined, splitSetupEntries(raw)),
39
+ }),
40
+ getExistingAllowFrom: ({ cfg, accountId }) =>
41
+ resolveGoogleChatAccount({ cfg, accountId }).config.dm?.allowFrom ?? [],
42
+ applyAllowFrom: ({ cfg, accountId, allowFrom }) =>
43
+ applySetupAccountConfigPatch({
44
+ cfg,
45
+ channelKey: channel,
46
+ accountId,
47
+ patch: {
48
+ dm: {
49
+ ...resolveGoogleChatAccount({ cfg, accountId }).config.dm,
50
+ allowFrom,
51
+ },
52
+ },
53
+ }),
54
+ });
55
+
56
+ const googlechatDmPolicy: ChannelSetupDmPolicy = {
57
+ label: "Google Chat",
58
+ channel,
59
+ policyKey: "channels.googlechat.dm.policy",
60
+ allowFromKey: "channels.googlechat.dm.allowFrom",
61
+ resolveConfigKeys: (cfg, accountId) =>
62
+ (accountId ?? resolveDefaultGoogleChatAccountId(cfg)) !== DEFAULT_ACCOUNT_ID
63
+ ? {
64
+ policyKey: `channels.googlechat.accounts.${accountId ?? resolveDefaultGoogleChatAccountId(cfg)}.dm.policy`,
65
+ allowFromKey: `channels.googlechat.accounts.${accountId ?? resolveDefaultGoogleChatAccountId(cfg)}.dm.allowFrom`,
66
+ }
67
+ : {
68
+ policyKey: "channels.googlechat.dm.policy",
69
+ allowFromKey: "channels.googlechat.dm.allowFrom",
70
+ },
71
+ getCurrent: (cfg, accountId) =>
72
+ resolveGoogleChatAccount({
73
+ cfg,
74
+ accountId: accountId ?? resolveDefaultGoogleChatAccountId(cfg),
75
+ }).config.dm?.policy ?? "pairing",
76
+ setPolicy: (cfg, policy, accountId) => {
77
+ const resolvedAccountId = accountId ?? resolveDefaultGoogleChatAccountId(cfg);
78
+ const currentDm = resolveGoogleChatAccount({
79
+ cfg,
80
+ accountId: resolvedAccountId,
81
+ }).config.dm;
82
+ return applySetupAccountConfigPatch({
83
+ cfg,
84
+ channelKey: channel,
85
+ accountId: resolvedAccountId,
86
+ patch: {
87
+ dm: {
88
+ ...currentDm,
89
+ policy,
90
+ ...(policy === "open" ? { allowFrom: addWildcardAllowFrom(currentDm?.allowFrom) } : {}),
91
+ },
92
+ },
93
+ });
94
+ },
95
+ promptAllowFrom,
96
+ };
97
+
98
+ function createServiceAccountTextInput(params: {
99
+ inputKey: GoogleChatTextInputKey;
100
+ message: string;
101
+ placeholder: string;
102
+ authMethod: "file" | "inline";
103
+ patchKey: "serviceAccountFile" | "serviceAccount";
104
+ }): GoogleChatTextInput {
105
+ return {
106
+ inputKey: params.inputKey,
107
+ message: params.message,
108
+ placeholder: params.placeholder,
109
+ shouldPrompt: ({ credentialValues }) =>
110
+ credentialValues[USE_ENV_FLAG] !== "1" &&
111
+ credentialValues[AUTH_METHOD_FLAG] === params.authMethod,
112
+ validate: ({ value }) => (normalizeStringifiedOptionalString(value) ? undefined : "Required"),
113
+ normalizeValue: ({ value }) => normalizeStringifiedOptionalString(value) ?? "",
114
+ applySet: async ({ cfg, accountId, value }) =>
115
+ applySetupAccountConfigPatch({
116
+ cfg,
117
+ channelKey: channel,
118
+ accountId,
119
+ patch: { [params.patchKey]: value },
120
+ }),
121
+ };
122
+ }
123
+
124
+ export const googlechatSetupWizard: ChannelSetupWizard = {
125
+ channel,
126
+ status: createStandardChannelSetupStatus({
127
+ channelLabel: "Google Chat",
128
+ configuredLabel: t("wizard.channels.statusConfigured"),
129
+ unconfiguredLabel: t("wizard.channels.statusNeedsServiceAccount"),
130
+ configuredHint: t("wizard.channels.statusConfigured"),
131
+ unconfiguredHint: t("wizard.channels.statusNeedsAuth"),
132
+ includeStatusLine: true,
133
+ resolveConfigured: ({ cfg, accountId }) =>
134
+ resolveGoogleChatAccount({ cfg, accountId }).credentialSource !== "none",
135
+ }),
136
+ introNote: {
137
+ title: t("wizard.googlechat.setupTitle"),
138
+ lines: [
139
+ t("wizard.googlechat.setupServiceAccount"),
140
+ t("wizard.googlechat.setupScopes"),
141
+ t("wizard.googlechat.setupAudience"),
142
+ t("wizard.channels.docs", { link: formatDocsLink("/channels/googlechat", "googlechat") }),
143
+ ],
144
+ },
145
+ prepare: async ({ cfg, accountId, credentialValues, prompter }) => {
146
+ const envReady =
147
+ accountId === DEFAULT_ACCOUNT_ID &&
148
+ (Boolean(process.env[ENV_SERVICE_ACCOUNT]) || Boolean(process.env[ENV_SERVICE_ACCOUNT_FILE]));
149
+ if (envReady) {
150
+ const useEnv = await prompter.confirm({
151
+ message: t("wizard.googlechat.useEnvPrompt"),
152
+ initialValue: true,
153
+ });
154
+ if (useEnv) {
155
+ return {
156
+ cfg: applySetupAccountConfigPatch({
157
+ cfg,
158
+ channelKey: channel,
159
+ accountId,
160
+ patch: {},
161
+ }),
162
+ credentialValues: {
163
+ ...credentialValues,
164
+ [USE_ENV_FLAG]: "1",
165
+ },
166
+ };
167
+ }
168
+ }
169
+
170
+ const method = await prompter.select({
171
+ message: t("wizard.googlechat.authMethod"),
172
+ options: [
173
+ { value: "file", label: t("wizard.googlechat.serviceAccountFile") },
174
+ { value: "inline", label: t("wizard.googlechat.serviceAccountInline") },
175
+ ],
176
+ initialValue: "file",
177
+ });
178
+
179
+ return {
180
+ credentialValues: {
181
+ ...credentialValues,
182
+ [USE_ENV_FLAG]: "0",
183
+ [AUTH_METHOD_FLAG]: method,
184
+ },
185
+ };
186
+ },
187
+ credentials: [],
188
+ textInputs: [
189
+ createServiceAccountTextInput({
190
+ inputKey: "tokenFile",
191
+ message: t("wizard.googlechat.serviceAccountPath"),
192
+ placeholder: "/path/to/service-account.json",
193
+ authMethod: "file",
194
+ patchKey: "serviceAccountFile",
195
+ }),
196
+ createServiceAccountTextInput({
197
+ inputKey: "token",
198
+ message: t("wizard.googlechat.serviceAccountJson"),
199
+ placeholder: '{"type":"service_account", ... }',
200
+ authMethod: "inline",
201
+ patchKey: "serviceAccount",
202
+ }),
203
+ ],
204
+ finalize: async ({ cfg, accountId, prompter }) => {
205
+ const account = resolveGoogleChatAccount({
206
+ cfg,
207
+ accountId,
208
+ });
209
+ const audienceType = await prompter.select({
210
+ message: t("wizard.googlechat.webhookAudienceType"),
211
+ options: [
212
+ { value: "app-url", label: t("wizard.googlechat.appUrlRecommended") },
213
+ { value: "project-number", label: t("wizard.googlechat.projectNumber") },
214
+ ],
215
+ initialValue: account.config.audienceType === "project-number" ? "project-number" : "app-url",
216
+ });
217
+ const audience = await prompter.text({
218
+ message:
219
+ audienceType === "project-number"
220
+ ? t("wizard.googlechat.projectNumber")
221
+ : t("wizard.googlechat.appUrl"),
222
+ placeholder:
223
+ audienceType === "project-number" ? "1234567890" : "https://your.host/googlechat",
224
+ initialValue: account.config.audience || undefined,
225
+ validate: (value) =>
226
+ normalizeStringifiedOptionalString(value) ? undefined : t("common.required"),
227
+ });
228
+ return {
229
+ cfg: migrateBaseNameToDefaultAccount({
230
+ cfg: applySetupAccountConfigPatch({
231
+ cfg,
232
+ channelKey: channel,
233
+ accountId,
234
+ patch: {
235
+ audienceType,
236
+ audience: normalizeOptionalString(audience) ?? "",
237
+ },
238
+ }),
239
+ channelKey: channel,
240
+ }),
241
+ };
242
+ },
243
+ dmPolicy: googlechatDmPolicy,
244
+ };