@openclaw/googlechat 2026.5.2-beta.2 → 2026.5.3-beta.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.
- package/dist/api-C67jv0ow.js +627 -0
- package/dist/api.js +3 -0
- package/dist/channel-DE2hroMn.js +682 -0
- package/dist/channel-config-api.js +5 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-CoSLc-4R.js +920 -0
- package/dist/contract-api.js +3 -0
- package/dist/doctor-contract-CG1sLToP.js +151 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/index.js +22 -0
- package/dist/runtime-api-wkIdfwqY.js +30 -0
- package/dist/runtime-api.js +2 -0
- package/dist/secret-contract-DtQ_IO7J.js +99 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-plugin-api.js +75 -0
- package/dist/setup-surface-CofP-Gg9.js +309 -0
- package/dist/test-api.js +3 -0
- package/package.json +14 -6
- package/api.ts +0 -3
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -1
- package/contract-api.ts +0 -5
- package/doctor-contract-api.ts +0 -1
- package/index.ts +0 -20
- package/runtime-api.ts +0 -60
- package/secret-contract-api.ts +0 -5
- package/setup-entry.ts +0 -13
- package/setup-plugin-api.ts +0 -3
- package/src/accounts.ts +0 -169
- package/src/actions.test.ts +0 -265
- package/src/actions.ts +0 -227
- package/src/api.ts +0 -322
- package/src/approval-auth.test.ts +0 -24
- package/src/approval-auth.ts +0 -32
- package/src/auth.ts +0 -209
- package/src/channel-config.test.ts +0 -39
- package/src/channel.adapters.ts +0 -293
- package/src/channel.deps.runtime.ts +0 -28
- package/src/channel.runtime.ts +0 -17
- package/src/channel.setup.ts +0 -98
- package/src/channel.test.ts +0 -695
- package/src/channel.ts +0 -256
- package/src/config-schema.test.ts +0 -16
- package/src/config-schema.ts +0 -3
- package/src/doctor-contract.test.ts +0 -75
- package/src/doctor-contract.ts +0 -182
- package/src/doctor.ts +0 -57
- package/src/gateway.ts +0 -63
- package/src/google-auth.runtime.test.ts +0 -462
- package/src/google-auth.runtime.ts +0 -539
- package/src/group-policy.ts +0 -17
- package/src/monitor-access.test.ts +0 -443
- package/src/monitor-access.ts +0 -405
- package/src/monitor-reply-delivery.ts +0 -156
- package/src/monitor-routing.ts +0 -55
- package/src/monitor-types.ts +0 -33
- package/src/monitor-webhook.test.ts +0 -446
- package/src/monitor-webhook.ts +0 -285
- package/src/monitor.reply-delivery.test.ts +0 -139
- package/src/monitor.ts +0 -428
- package/src/monitor.webhook-routing.test.ts +0 -252
- package/src/runtime.ts +0 -9
- package/src/secret-contract.test.ts +0 -60
- package/src/secret-contract.ts +0 -161
- package/src/sender-allow.ts +0 -46
- package/src/setup-core.ts +0 -40
- package/src/setup-surface.ts +0 -236
- package/src/setup.test.ts +0 -560
- package/src/targets.test.ts +0 -421
- package/src/targets.ts +0 -66
- package/src/types.config.ts +0 -3
- package/src/types.ts +0 -73
- package/test-api.ts +0 -2
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
import { a as resolveDefaultGoogleChatAccountId, i as listGoogleChatAccountIds, n as googlechatSetupAdapter, o as resolveGoogleChatAccount, r as listEnabledGoogleChatAccounts, s as resolveGoogleChatConfigAccessorAccount, t as googlechatSetupWizard } from "./setup-surface-CofP-Gg9.js";
|
|
2
|
+
import { E as resolveChannelMediaMaxBytes, L as getGoogleChatRuntime, a as buildChannelConfigSchema, i as PAIRING_APPROVED_MESSAGE, m as fetchRemoteMedia, o as chunkTextForOutbound, r as GoogleChatConfigSchema, t as DEFAULT_ACCOUNT_ID, v as loadOutboundMediaFromUrl$1, y as missingTargetError } from "./runtime-api-wkIdfwqY.js";
|
|
3
|
+
import { a as findGoogleChatDirectMessage, c as sendGoogleChatMessage, o as listGoogleChatReactions, r as deleteGoogleChatReaction, t as createGoogleChatReaction, u as uploadGoogleChatAttachment } from "./api-C67jv0ow.js";
|
|
4
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-CG1sLToP.js";
|
|
5
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DtQ_IO7J.js";
|
|
6
|
+
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
7
|
+
import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
|
|
8
|
+
import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
9
|
+
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
10
|
+
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
|
11
|
+
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
|
12
|
+
import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
13
|
+
import { createActionGate, jsonResult, readNumberParam, readReactionParams, readStringParam } from "openclaw/plugin-sdk/channel-actions";
|
|
14
|
+
import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
|
|
15
|
+
import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
|
|
16
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
|
17
|
+
import { createAccountStatusSink, runPassiveAccountLifecycle } from "openclaw/plugin-sdk/channel-lifecycle";
|
|
18
|
+
import { composeAccountWarningCollectors, createAllowlistProviderOpenWarningCollector, createDangerousNameMatchingMutableAllowlistWarningCollector, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
19
|
+
import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
20
|
+
import { createChannelDirectoryAdapter, listResolvedDirectoryGroupEntriesFromMapKeys, listResolvedDirectoryUserEntriesFromAllowFrom } from "openclaw/plugin-sdk/directory-runtime";
|
|
21
|
+
import { sanitizeForPlainText } from "openclaw/plugin-sdk/outbound-runtime";
|
|
22
|
+
//#region extensions/googlechat/src/targets.ts
|
|
23
|
+
function normalizeGoogleChatTarget(raw) {
|
|
24
|
+
const trimmed = raw?.trim();
|
|
25
|
+
if (!trimmed) return;
|
|
26
|
+
const normalized = trimmed.replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:(users\/)?/i, "users/").replace(/^space:(spaces\/)?/i, "spaces/");
|
|
27
|
+
if (isGoogleChatUserTarget(normalized)) {
|
|
28
|
+
const suffix = normalized.slice(6);
|
|
29
|
+
return suffix.includes("@") ? `users/${normalizeLowercaseStringOrEmpty(suffix)}` : normalized;
|
|
30
|
+
}
|
|
31
|
+
if (isGoogleChatSpaceTarget(normalized)) return normalized;
|
|
32
|
+
if (normalized.includes("@")) return `users/${normalizeLowercaseStringOrEmpty(normalized)}`;
|
|
33
|
+
return normalized;
|
|
34
|
+
}
|
|
35
|
+
function isGoogleChatUserTarget(value) {
|
|
36
|
+
return normalizeLowercaseStringOrEmpty(value).startsWith("users/");
|
|
37
|
+
}
|
|
38
|
+
function isGoogleChatSpaceTarget(value) {
|
|
39
|
+
return normalizeLowercaseStringOrEmpty(value).startsWith("spaces/");
|
|
40
|
+
}
|
|
41
|
+
function stripMessageSuffix(target) {
|
|
42
|
+
const index = target.indexOf("/messages/");
|
|
43
|
+
if (index === -1) return target;
|
|
44
|
+
return target.slice(0, index);
|
|
45
|
+
}
|
|
46
|
+
async function resolveGoogleChatOutboundSpace(params) {
|
|
47
|
+
const normalized = normalizeGoogleChatTarget(params.target);
|
|
48
|
+
if (!normalized) throw new Error("Missing Google Chat target.");
|
|
49
|
+
const base = stripMessageSuffix(normalized);
|
|
50
|
+
if (isGoogleChatSpaceTarget(base)) return base;
|
|
51
|
+
if (isGoogleChatUserTarget(base)) {
|
|
52
|
+
const dm = await findGoogleChatDirectMessage({
|
|
53
|
+
account: params.account,
|
|
54
|
+
userName: base
|
|
55
|
+
});
|
|
56
|
+
if (!dm?.name) throw new Error(`No Google Chat DM found for ${base}`);
|
|
57
|
+
return dm.name;
|
|
58
|
+
}
|
|
59
|
+
return base;
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region extensions/googlechat/src/actions.ts
|
|
63
|
+
const providerId = "googlechat";
|
|
64
|
+
function listEnabledAccounts(cfg) {
|
|
65
|
+
return listEnabledGoogleChatAccounts(cfg).filter((account) => account.enabled && account.credentialSource !== "none");
|
|
66
|
+
}
|
|
67
|
+
function isReactionsEnabled(accounts) {
|
|
68
|
+
for (const account of accounts) if (createActionGate(account.config.actions)("reactions")) return true;
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
function resolveAppUserNames(account) {
|
|
72
|
+
return new Set(["users/app", account.config.botUser?.trim()].filter(Boolean));
|
|
73
|
+
}
|
|
74
|
+
async function loadGoogleChatActionMedia(params) {
|
|
75
|
+
const runtime = getGoogleChatRuntime();
|
|
76
|
+
return /^https?:\/\//i.test(params.mediaUrl) ? await runtime.channel.media.fetchRemoteMedia({
|
|
77
|
+
url: params.mediaUrl,
|
|
78
|
+
maxBytes: params.maxBytes
|
|
79
|
+
}) : await loadOutboundMediaFromUrl(params.mediaUrl, {
|
|
80
|
+
maxBytes: params.maxBytes,
|
|
81
|
+
mediaAccess: params.mediaAccess,
|
|
82
|
+
mediaLocalRoots: params.mediaLocalRoots,
|
|
83
|
+
mediaReadFile: params.mediaReadFile
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const googlechatMessageActions = {
|
|
87
|
+
describeMessageTool: ({ cfg, accountId }) => {
|
|
88
|
+
const accounts = accountId ? [resolveGoogleChatAccount({
|
|
89
|
+
cfg,
|
|
90
|
+
accountId
|
|
91
|
+
})].filter((account) => account.enabled && account.credentialSource !== "none") : listEnabledAccounts(cfg);
|
|
92
|
+
if (accounts.length === 0) return null;
|
|
93
|
+
const actions = /* @__PURE__ */ new Set([]);
|
|
94
|
+
actions.add("send");
|
|
95
|
+
actions.add("upload-file");
|
|
96
|
+
if (isReactionsEnabled(accounts)) {
|
|
97
|
+
actions.add("react");
|
|
98
|
+
actions.add("reactions");
|
|
99
|
+
}
|
|
100
|
+
return { actions: Array.from(actions) };
|
|
101
|
+
},
|
|
102
|
+
extractToolSend: ({ args }) => {
|
|
103
|
+
return extractToolSend(args, "sendMessage");
|
|
104
|
+
},
|
|
105
|
+
handleAction: async ({ action, params, cfg, accountId, mediaAccess, mediaLocalRoots, mediaReadFile }) => {
|
|
106
|
+
const account = resolveGoogleChatAccount({
|
|
107
|
+
cfg,
|
|
108
|
+
accountId
|
|
109
|
+
});
|
|
110
|
+
if (account.credentialSource === "none") throw new Error("Google Chat credentials are missing.");
|
|
111
|
+
if (action === "send" || action === "upload-file") {
|
|
112
|
+
const to = readStringParam(params, "to", { required: true });
|
|
113
|
+
const content = readStringParam(params, "message", {
|
|
114
|
+
required: action === "send",
|
|
115
|
+
allowEmpty: true
|
|
116
|
+
}) ?? readStringParam(params, "initialComment", { allowEmpty: true }) ?? "";
|
|
117
|
+
const mediaUrl = readStringParam(params, "media", { trim: false }) ?? readStringParam(params, "filePath", { trim: false }) ?? readStringParam(params, "path", { trim: false });
|
|
118
|
+
const threadId = readStringParam(params, "threadId") ?? readStringParam(params, "replyTo");
|
|
119
|
+
const space = await resolveGoogleChatOutboundSpace({
|
|
120
|
+
account,
|
|
121
|
+
target: to
|
|
122
|
+
});
|
|
123
|
+
if (mediaUrl) {
|
|
124
|
+
const loaded = await loadGoogleChatActionMedia({
|
|
125
|
+
mediaUrl,
|
|
126
|
+
maxBytes: (account.config.mediaMaxMb ?? 20) * 1024 * 1024,
|
|
127
|
+
mediaAccess,
|
|
128
|
+
mediaLocalRoots,
|
|
129
|
+
mediaReadFile
|
|
130
|
+
});
|
|
131
|
+
const uploadFileName = readStringParam(params, "filename") ?? readStringParam(params, "title") ?? loaded.fileName ?? "attachment";
|
|
132
|
+
const upload = await uploadGoogleChatAttachment({
|
|
133
|
+
account,
|
|
134
|
+
space,
|
|
135
|
+
filename: uploadFileName,
|
|
136
|
+
buffer: loaded.buffer,
|
|
137
|
+
contentType: loaded.contentType
|
|
138
|
+
});
|
|
139
|
+
await sendGoogleChatMessage({
|
|
140
|
+
account,
|
|
141
|
+
space,
|
|
142
|
+
text: content,
|
|
143
|
+
thread: threadId ?? void 0,
|
|
144
|
+
attachments: upload.attachmentUploadToken ? [{
|
|
145
|
+
attachmentUploadToken: upload.attachmentUploadToken,
|
|
146
|
+
contentName: uploadFileName
|
|
147
|
+
}] : void 0
|
|
148
|
+
});
|
|
149
|
+
return jsonResult({
|
|
150
|
+
ok: true,
|
|
151
|
+
to: space
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
if (action === "upload-file") throw new Error("upload-file requires media, filePath, or path");
|
|
155
|
+
await sendGoogleChatMessage({
|
|
156
|
+
account,
|
|
157
|
+
space,
|
|
158
|
+
text: content,
|
|
159
|
+
thread: threadId ?? void 0
|
|
160
|
+
});
|
|
161
|
+
return jsonResult({
|
|
162
|
+
ok: true,
|
|
163
|
+
to: space
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
if (action === "react") {
|
|
167
|
+
const messageName = readStringParam(params, "messageId", { required: true });
|
|
168
|
+
const { emoji, remove, isEmpty } = readReactionParams(params, { removeErrorMessage: "Emoji is required to remove a Google Chat reaction." });
|
|
169
|
+
if (remove || isEmpty) {
|
|
170
|
+
const reactions = await listGoogleChatReactions({
|
|
171
|
+
account,
|
|
172
|
+
messageName
|
|
173
|
+
});
|
|
174
|
+
const appUsers = resolveAppUserNames(account);
|
|
175
|
+
const toRemove = reactions.filter((reaction) => {
|
|
176
|
+
const userName = reaction.user?.name?.trim();
|
|
177
|
+
if (appUsers.size > 0 && !appUsers.has(userName ?? "")) return false;
|
|
178
|
+
if (emoji) return reaction.emoji?.unicode === emoji;
|
|
179
|
+
return true;
|
|
180
|
+
});
|
|
181
|
+
for (const reaction of toRemove) {
|
|
182
|
+
if (!reaction.name) continue;
|
|
183
|
+
await deleteGoogleChatReaction({
|
|
184
|
+
account,
|
|
185
|
+
reactionName: reaction.name
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return jsonResult({
|
|
189
|
+
ok: true,
|
|
190
|
+
removed: toRemove.length
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return jsonResult({
|
|
194
|
+
ok: true,
|
|
195
|
+
reaction: await createGoogleChatReaction({
|
|
196
|
+
account,
|
|
197
|
+
messageName,
|
|
198
|
+
emoji
|
|
199
|
+
})
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (action === "reactions") return jsonResult({
|
|
203
|
+
ok: true,
|
|
204
|
+
reactions: await listGoogleChatReactions({
|
|
205
|
+
account,
|
|
206
|
+
messageName: readStringParam(params, "messageId", { required: true }),
|
|
207
|
+
limit: readNumberParam(params, "limit", { integer: true }) ?? void 0
|
|
208
|
+
})
|
|
209
|
+
});
|
|
210
|
+
throw new Error(`Action ${action} is not supported for provider ${providerId}.`);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region extensions/googlechat/src/approval-auth.ts
|
|
215
|
+
function normalizeGoogleChatApproverId(value) {
|
|
216
|
+
const normalized = normalizeGoogleChatTarget(String(value));
|
|
217
|
+
if (!normalized || !isGoogleChatUserTarget(normalized)) return;
|
|
218
|
+
const suffix = normalizeLowercaseStringOrEmpty(normalized.slice(6));
|
|
219
|
+
if (!suffix || suffix.includes("@")) return;
|
|
220
|
+
return `users/${suffix}`;
|
|
221
|
+
}
|
|
222
|
+
const googleChatApprovalAuth = createResolvedApproverActionAuthAdapter({
|
|
223
|
+
channelLabel: "Google Chat",
|
|
224
|
+
resolveApprovers: ({ cfg, accountId }) => {
|
|
225
|
+
const account = resolveGoogleChatAccount({
|
|
226
|
+
cfg,
|
|
227
|
+
accountId
|
|
228
|
+
}).config;
|
|
229
|
+
return resolveApprovalApprovers({
|
|
230
|
+
allowFrom: account.dm?.allowFrom,
|
|
231
|
+
defaultTo: account.defaultTo,
|
|
232
|
+
normalizeApprover: normalizeGoogleChatApproverId
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
normalizeSenderId: (value) => normalizeGoogleChatApproverId(value)
|
|
236
|
+
});
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region extensions/googlechat/src/group-policy.ts
|
|
239
|
+
function resolveGoogleChatGroupRequireMention(params) {
|
|
240
|
+
return resolveChannelGroupRequireMention({
|
|
241
|
+
cfg: params.cfg,
|
|
242
|
+
channel: "googlechat",
|
|
243
|
+
groupId: params.groupId,
|
|
244
|
+
accountId: params.accountId
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region extensions/googlechat/src/channel.adapters.ts
|
|
249
|
+
const loadGoogleChatChannelRuntime$2 = createLazyRuntimeNamedExport(() => import("./channel.runtime-CoSLc-4R.js"), "googleChatChannelRuntime");
|
|
250
|
+
const formatAllowFromEntry = (entry) => normalizeLowercaseStringOrEmpty(entry.trim().replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:/i, "").replace(/^users\//i, ""));
|
|
251
|
+
const collectGoogleChatSecurityWarnings = composeAccountWarningCollectors(createAllowlistProviderOpenWarningCollector({
|
|
252
|
+
providerConfigPresent: (cfg) => cfg.channels?.googlechat !== void 0,
|
|
253
|
+
resolveGroupPolicy: (account) => account.config.groupPolicy,
|
|
254
|
+
buildOpenWarning: {
|
|
255
|
+
surface: "Google Chat spaces",
|
|
256
|
+
openBehavior: "allows any space to trigger (mention-gated)",
|
|
257
|
+
remediation: "Set channels.googlechat.groupPolicy=\"allowlist\" and configure channels.googlechat.groups"
|
|
258
|
+
}
|
|
259
|
+
}), (account) => account.config.dm?.policy === "open" && "- Google Chat DMs are open to anyone. Set channels.googlechat.dm.policy=\"pairing\" or \"allowlist\".");
|
|
260
|
+
const googlechatGroupsAdapter = { resolveRequireMention: resolveGoogleChatGroupRequireMention };
|
|
261
|
+
const googlechatDirectoryAdapter = createChannelDirectoryAdapter({
|
|
262
|
+
listPeers: async (params) => listResolvedDirectoryUserEntriesFromAllowFrom({
|
|
263
|
+
...params,
|
|
264
|
+
resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount),
|
|
265
|
+
resolveAllowFrom: (account) => account.config.dm?.allowFrom,
|
|
266
|
+
normalizeId: (entry) => normalizeGoogleChatTarget(entry) ?? entry
|
|
267
|
+
}),
|
|
268
|
+
listGroups: async (params) => listResolvedDirectoryGroupEntriesFromMapKeys({
|
|
269
|
+
...params,
|
|
270
|
+
resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount),
|
|
271
|
+
resolveGroups: (account) => account.config.groups
|
|
272
|
+
})
|
|
273
|
+
});
|
|
274
|
+
const googlechatSecurityAdapter = {
|
|
275
|
+
dm: {
|
|
276
|
+
channelKey: "googlechat",
|
|
277
|
+
resolvePolicy: (account) => account.config.dm?.policy,
|
|
278
|
+
resolveAllowFrom: (account) => account.config.dm?.allowFrom,
|
|
279
|
+
allowFromPathSuffix: "dm.",
|
|
280
|
+
normalizeEntry: (raw) => formatAllowFromEntry(raw)
|
|
281
|
+
},
|
|
282
|
+
collectWarnings: collectGoogleChatSecurityWarnings
|
|
283
|
+
};
|
|
284
|
+
const googlechatThreadingAdapter = { scopedAccountReplyToMode: {
|
|
285
|
+
resolveAccount: (cfg, accountId) => resolveGoogleChatAccount({
|
|
286
|
+
cfg,
|
|
287
|
+
accountId
|
|
288
|
+
}),
|
|
289
|
+
resolveReplyToMode: (account, _chatType) => account.config.replyToMode,
|
|
290
|
+
fallback: "off"
|
|
291
|
+
} };
|
|
292
|
+
const googlechatPairingTextAdapter = {
|
|
293
|
+
idLabel: "googlechatUserId",
|
|
294
|
+
message: PAIRING_APPROVED_MESSAGE,
|
|
295
|
+
normalizeAllowEntry: (entry) => formatAllowFromEntry(entry),
|
|
296
|
+
notify: async ({ cfg, id, message, accountId }) => {
|
|
297
|
+
const account = resolveGoogleChatAccount({
|
|
298
|
+
cfg,
|
|
299
|
+
accountId
|
|
300
|
+
});
|
|
301
|
+
if (account.credentialSource === "none") return;
|
|
302
|
+
const user = normalizeGoogleChatTarget(id) ?? id;
|
|
303
|
+
const space = await resolveGoogleChatOutboundSpace({
|
|
304
|
+
account,
|
|
305
|
+
target: isGoogleChatUserTarget(user) ? user : `users/${user}`
|
|
306
|
+
});
|
|
307
|
+
const { sendGoogleChatMessage } = await loadGoogleChatChannelRuntime$2();
|
|
308
|
+
await sendGoogleChatMessage({
|
|
309
|
+
account,
|
|
310
|
+
space,
|
|
311
|
+
text: message
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
const googlechatOutboundAdapter = {
|
|
316
|
+
base: {
|
|
317
|
+
deliveryMode: "direct",
|
|
318
|
+
chunker: chunkTextForOutbound,
|
|
319
|
+
chunkerMode: "markdown",
|
|
320
|
+
textChunkLimit: 4e3,
|
|
321
|
+
sanitizeText: ({ text }) => sanitizeForPlainText(text),
|
|
322
|
+
resolveTarget: ({ to }) => {
|
|
323
|
+
const trimmed = normalizeOptionalString(to) ?? "";
|
|
324
|
+
if (trimmed) {
|
|
325
|
+
const normalized = normalizeGoogleChatTarget(trimmed);
|
|
326
|
+
if (!normalized) return {
|
|
327
|
+
ok: false,
|
|
328
|
+
error: missingTargetError("Google Chat", "<spaces/{space}|users/{user}>")
|
|
329
|
+
};
|
|
330
|
+
return {
|
|
331
|
+
ok: true,
|
|
332
|
+
to: normalized
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
ok: false,
|
|
337
|
+
error: missingTargetError("Google Chat", "<spaces/{space}|users/{user}>")
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
attachedResults: {
|
|
342
|
+
channel: "googlechat",
|
|
343
|
+
sendText: async ({ cfg, to, text, accountId, replyToId, threadId }) => {
|
|
344
|
+
const account = resolveGoogleChatAccount({
|
|
345
|
+
cfg,
|
|
346
|
+
accountId
|
|
347
|
+
});
|
|
348
|
+
const space = await resolveGoogleChatOutboundSpace({
|
|
349
|
+
account,
|
|
350
|
+
target: to
|
|
351
|
+
});
|
|
352
|
+
const thread = typeof threadId === "number" ? String(threadId) : threadId ?? replyToId ?? void 0;
|
|
353
|
+
const { sendGoogleChatMessage } = await loadGoogleChatChannelRuntime$2();
|
|
354
|
+
return {
|
|
355
|
+
messageId: (await sendGoogleChatMessage({
|
|
356
|
+
account,
|
|
357
|
+
space,
|
|
358
|
+
text,
|
|
359
|
+
thread
|
|
360
|
+
}))?.messageName ?? "",
|
|
361
|
+
chatId: space
|
|
362
|
+
};
|
|
363
|
+
},
|
|
364
|
+
sendMedia: async ({ cfg, to, text, mediaUrl, mediaAccess, mediaLocalRoots, mediaReadFile, accountId, replyToId, threadId }) => {
|
|
365
|
+
if (!mediaUrl) throw new Error("Google Chat mediaUrl is required.");
|
|
366
|
+
const account = resolveGoogleChatAccount({
|
|
367
|
+
cfg,
|
|
368
|
+
accountId
|
|
369
|
+
});
|
|
370
|
+
const space = await resolveGoogleChatOutboundSpace({
|
|
371
|
+
account,
|
|
372
|
+
target: to
|
|
373
|
+
});
|
|
374
|
+
const thread = typeof threadId === "number" ? String(threadId) : threadId ?? replyToId ?? void 0;
|
|
375
|
+
const effectiveMaxBytes = resolveChannelMediaMaxBytes({
|
|
376
|
+
cfg,
|
|
377
|
+
resolveChannelLimitMb: ({ cfg, accountId }) => (cfg.channels?.googlechat)?.accounts?.[accountId]?.mediaMaxMb ?? (cfg.channels?.googlechat)?.mediaMaxMb,
|
|
378
|
+
accountId
|
|
379
|
+
}) ?? (account.config.mediaMaxMb ?? 20) * 1024 * 1024;
|
|
380
|
+
const loaded = /^https?:\/\//i.test(mediaUrl) ? await fetchRemoteMedia({
|
|
381
|
+
url: mediaUrl,
|
|
382
|
+
maxBytes: effectiveMaxBytes
|
|
383
|
+
}) : await loadOutboundMediaFromUrl$1(mediaUrl, {
|
|
384
|
+
maxBytes: effectiveMaxBytes,
|
|
385
|
+
mediaAccess,
|
|
386
|
+
mediaLocalRoots,
|
|
387
|
+
mediaReadFile
|
|
388
|
+
});
|
|
389
|
+
const { sendGoogleChatMessage, uploadGoogleChatAttachment } = await loadGoogleChatChannelRuntime$2();
|
|
390
|
+
const upload = await uploadGoogleChatAttachment({
|
|
391
|
+
account,
|
|
392
|
+
space,
|
|
393
|
+
filename: loaded.fileName ?? "attachment",
|
|
394
|
+
buffer: loaded.buffer,
|
|
395
|
+
contentType: loaded.contentType
|
|
396
|
+
});
|
|
397
|
+
return {
|
|
398
|
+
messageId: (await sendGoogleChatMessage({
|
|
399
|
+
account,
|
|
400
|
+
space,
|
|
401
|
+
text,
|
|
402
|
+
thread,
|
|
403
|
+
attachments: upload.attachmentUploadToken ? [{
|
|
404
|
+
attachmentUploadToken: upload.attachmentUploadToken,
|
|
405
|
+
contentName: loaded.fileName
|
|
406
|
+
}] : void 0
|
|
407
|
+
}))?.messageName ?? "",
|
|
408
|
+
chatId: space
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region extensions/googlechat/src/doctor.ts
|
|
415
|
+
function asObjectRecord(value) {
|
|
416
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
417
|
+
}
|
|
418
|
+
function isGoogleChatMutableAllowEntry(raw) {
|
|
419
|
+
const text = raw.trim();
|
|
420
|
+
if (!text || text === "*") return false;
|
|
421
|
+
const withoutPrefix = text.replace(/^(googlechat|google-chat|gchat):/i, "").trim();
|
|
422
|
+
if (!withoutPrefix) return false;
|
|
423
|
+
return withoutPrefix.replace(/^users\//i, "").includes("@");
|
|
424
|
+
}
|
|
425
|
+
const collectGoogleChatMutableAllowlistWarnings = createDangerousNameMatchingMutableAllowlistWarningCollector({
|
|
426
|
+
channel: "googlechat",
|
|
427
|
+
detector: isGoogleChatMutableAllowEntry,
|
|
428
|
+
collectLists: (scope) => {
|
|
429
|
+
const lists = [{
|
|
430
|
+
pathLabel: `${scope.prefix}.groupAllowFrom`,
|
|
431
|
+
list: scope.account.groupAllowFrom
|
|
432
|
+
}];
|
|
433
|
+
const dm = asObjectRecord(scope.account.dm);
|
|
434
|
+
if (dm) lists.push({
|
|
435
|
+
pathLabel: `${scope.prefix}.dm.allowFrom`,
|
|
436
|
+
list: dm.allowFrom
|
|
437
|
+
});
|
|
438
|
+
const groups = asObjectRecord(scope.account.groups);
|
|
439
|
+
if (groups) for (const [groupKey, groupRaw] of Object.entries(groups)) {
|
|
440
|
+
const group = asObjectRecord(groupRaw);
|
|
441
|
+
if (!group) continue;
|
|
442
|
+
lists.push({
|
|
443
|
+
pathLabel: `${scope.prefix}.groups.${groupKey}.users`,
|
|
444
|
+
list: group.users
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
return lists;
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
//#endregion
|
|
451
|
+
//#region extensions/googlechat/src/gateway.ts
|
|
452
|
+
const loadGoogleChatChannelRuntime$1 = createLazyRuntimeNamedExport(() => import("./channel.runtime-CoSLc-4R.js"), "googleChatChannelRuntime");
|
|
453
|
+
async function startGoogleChatGatewayAccount(ctx) {
|
|
454
|
+
const account = ctx.account;
|
|
455
|
+
const statusSink = createAccountStatusSink({
|
|
456
|
+
accountId: account.accountId,
|
|
457
|
+
setStatus: ctx.setStatus
|
|
458
|
+
});
|
|
459
|
+
ctx.log?.info?.(`[${account.accountId}] starting Google Chat webhook`);
|
|
460
|
+
const { resolveGoogleChatWebhookPath, startGoogleChatMonitor } = await loadGoogleChatChannelRuntime$1();
|
|
461
|
+
statusSink({
|
|
462
|
+
running: true,
|
|
463
|
+
lastStartAt: Date.now(),
|
|
464
|
+
webhookPath: resolveGoogleChatWebhookPath({ account }),
|
|
465
|
+
audienceType: account.config.audienceType,
|
|
466
|
+
audience: account.config.audience
|
|
467
|
+
});
|
|
468
|
+
await runPassiveAccountLifecycle({
|
|
469
|
+
abortSignal: ctx.abortSignal,
|
|
470
|
+
start: async () => await startGoogleChatMonitor({
|
|
471
|
+
account,
|
|
472
|
+
config: ctx.cfg,
|
|
473
|
+
runtime: ctx.runtime,
|
|
474
|
+
abortSignal: ctx.abortSignal,
|
|
475
|
+
webhookPath: account.config.webhookPath,
|
|
476
|
+
webhookUrl: account.config.webhookUrl,
|
|
477
|
+
statusSink
|
|
478
|
+
}),
|
|
479
|
+
stop: async (unregister) => {
|
|
480
|
+
unregister?.();
|
|
481
|
+
},
|
|
482
|
+
onStop: async () => {
|
|
483
|
+
statusSink({
|
|
484
|
+
running: false,
|
|
485
|
+
lastStopAt: Date.now()
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
//#endregion
|
|
491
|
+
//#region extensions/googlechat/src/channel.ts
|
|
492
|
+
const loadGoogleChatChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-CoSLc-4R.js"), "googleChatChannelRuntime");
|
|
493
|
+
const meta = {
|
|
494
|
+
id: "googlechat",
|
|
495
|
+
label: "Google Chat",
|
|
496
|
+
selectionLabel: "Google Chat (Chat API)",
|
|
497
|
+
docsPath: "/channels/googlechat",
|
|
498
|
+
docsLabel: "googlechat",
|
|
499
|
+
blurb: "Google Workspace Chat app with HTTP webhook.",
|
|
500
|
+
aliases: ["gchat", "google-chat"],
|
|
501
|
+
order: 55,
|
|
502
|
+
detailLabel: "Google Chat",
|
|
503
|
+
systemImage: "message.badge",
|
|
504
|
+
markdownCapable: true
|
|
505
|
+
};
|
|
506
|
+
const googleChatConfigAdapter = createScopedChannelConfigAdapter({
|
|
507
|
+
sectionKey: "googlechat",
|
|
508
|
+
listAccountIds: listGoogleChatAccountIds,
|
|
509
|
+
resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount),
|
|
510
|
+
resolveAccessorAccount: resolveGoogleChatConfigAccessorAccount,
|
|
511
|
+
defaultAccountId: resolveDefaultGoogleChatAccountId,
|
|
512
|
+
clearBaseFields: [
|
|
513
|
+
"serviceAccount",
|
|
514
|
+
"serviceAccountFile",
|
|
515
|
+
"audienceType",
|
|
516
|
+
"audience",
|
|
517
|
+
"webhookPath",
|
|
518
|
+
"webhookUrl",
|
|
519
|
+
"botUser",
|
|
520
|
+
"name"
|
|
521
|
+
],
|
|
522
|
+
resolveAllowFrom: (account) => account.config.dm?.allowFrom,
|
|
523
|
+
formatAllowFrom: (allowFrom) => formatNormalizedAllowFromEntries({
|
|
524
|
+
allowFrom,
|
|
525
|
+
normalizeEntry: formatAllowFromEntry
|
|
526
|
+
}),
|
|
527
|
+
resolveDefaultTo: (account) => account.config.defaultTo
|
|
528
|
+
});
|
|
529
|
+
const googlechatActions = {
|
|
530
|
+
describeMessageTool: (ctx) => googlechatMessageActions.describeMessageTool?.(ctx) ?? null,
|
|
531
|
+
extractToolSend: (ctx) => googlechatMessageActions.extractToolSend?.(ctx) ?? null,
|
|
532
|
+
handleAction: async (ctx) => {
|
|
533
|
+
if (!googlechatMessageActions.handleAction) throw new Error("Google Chat actions are not available.");
|
|
534
|
+
return await googlechatMessageActions.handleAction(ctx);
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
const googlechatPlugin = createChatChannelPlugin({
|
|
538
|
+
base: {
|
|
539
|
+
id: "googlechat",
|
|
540
|
+
meta: { ...meta },
|
|
541
|
+
setup: googlechatSetupAdapter,
|
|
542
|
+
setupWizard: googlechatSetupWizard,
|
|
543
|
+
capabilities: {
|
|
544
|
+
chatTypes: [
|
|
545
|
+
"direct",
|
|
546
|
+
"group",
|
|
547
|
+
"thread"
|
|
548
|
+
],
|
|
549
|
+
reactions: true,
|
|
550
|
+
threads: true,
|
|
551
|
+
media: true,
|
|
552
|
+
nativeCommands: false,
|
|
553
|
+
blockStreaming: true
|
|
554
|
+
},
|
|
555
|
+
streaming: { blockStreamingCoalesceDefaults: {
|
|
556
|
+
minChars: 1500,
|
|
557
|
+
idleMs: 1e3
|
|
558
|
+
} },
|
|
559
|
+
reload: { configPrefixes: ["channels.googlechat"] },
|
|
560
|
+
configSchema: buildChannelConfigSchema(GoogleChatConfigSchema),
|
|
561
|
+
config: {
|
|
562
|
+
...googleChatConfigAdapter,
|
|
563
|
+
isConfigured: (account) => account.credentialSource !== "none",
|
|
564
|
+
describeAccount: (account) => describeAccountSnapshot({
|
|
565
|
+
account,
|
|
566
|
+
configured: account.credentialSource !== "none",
|
|
567
|
+
extra: { credentialSource: account.credentialSource }
|
|
568
|
+
})
|
|
569
|
+
},
|
|
570
|
+
approvalCapability: googleChatApprovalAuth,
|
|
571
|
+
secrets: {
|
|
572
|
+
secretTargetRegistryEntries,
|
|
573
|
+
collectRuntimeConfigAssignments
|
|
574
|
+
},
|
|
575
|
+
groups: googlechatGroupsAdapter,
|
|
576
|
+
messaging: {
|
|
577
|
+
targetPrefixes: [
|
|
578
|
+
"googlechat",
|
|
579
|
+
"google-chat",
|
|
580
|
+
"gchat"
|
|
581
|
+
],
|
|
582
|
+
normalizeTarget: normalizeGoogleChatTarget,
|
|
583
|
+
targetResolver: {
|
|
584
|
+
looksLikeId: (raw, normalized) => {
|
|
585
|
+
const value = normalized ?? raw.trim();
|
|
586
|
+
return isGoogleChatSpaceTarget(value) || isGoogleChatUserTarget(value);
|
|
587
|
+
},
|
|
588
|
+
hint: "<spaces/{space}|users/{user}>"
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
directory: googlechatDirectoryAdapter,
|
|
592
|
+
resolver: { resolveTargets: async ({ inputs, kind }) => {
|
|
593
|
+
return inputs.map((input) => {
|
|
594
|
+
const normalized = normalizeGoogleChatTarget(input);
|
|
595
|
+
if (!normalized) return {
|
|
596
|
+
input,
|
|
597
|
+
resolved: false,
|
|
598
|
+
note: "empty target"
|
|
599
|
+
};
|
|
600
|
+
if (kind === "user" && isGoogleChatUserTarget(normalized)) return {
|
|
601
|
+
input,
|
|
602
|
+
resolved: true,
|
|
603
|
+
id: normalized
|
|
604
|
+
};
|
|
605
|
+
if (kind === "group" && isGoogleChatSpaceTarget(normalized)) return {
|
|
606
|
+
input,
|
|
607
|
+
resolved: true,
|
|
608
|
+
id: normalized
|
|
609
|
+
};
|
|
610
|
+
return {
|
|
611
|
+
input,
|
|
612
|
+
resolved: false,
|
|
613
|
+
note: "use spaces/{space} or users/{user}"
|
|
614
|
+
};
|
|
615
|
+
});
|
|
616
|
+
} },
|
|
617
|
+
actions: googlechatActions,
|
|
618
|
+
doctor: {
|
|
619
|
+
dmAllowFromMode: "nestedOnly",
|
|
620
|
+
groupModel: "route",
|
|
621
|
+
groupAllowFromFallbackToAllowFrom: false,
|
|
622
|
+
warnOnEmptyGroupSenderAllowlist: false,
|
|
623
|
+
legacyConfigRules,
|
|
624
|
+
normalizeCompatibilityConfig,
|
|
625
|
+
collectMutableAllowlistWarnings: collectGoogleChatMutableAllowlistWarnings
|
|
626
|
+
},
|
|
627
|
+
status: createComputedAccountStatusAdapter({
|
|
628
|
+
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
|
629
|
+
collectStatusIssues: (accounts) => accounts.flatMap((entry) => {
|
|
630
|
+
const accountId = entry.accountId ?? DEFAULT_ACCOUNT_ID;
|
|
631
|
+
const enabled = entry.enabled !== false;
|
|
632
|
+
const configured = entry.configured === true;
|
|
633
|
+
if (!enabled || !configured) return [];
|
|
634
|
+
const issues = [];
|
|
635
|
+
if (!entry.audience) issues.push({
|
|
636
|
+
channel: "googlechat",
|
|
637
|
+
accountId,
|
|
638
|
+
kind: "config",
|
|
639
|
+
message: "Google Chat audience is missing (set channels.googlechat.audience).",
|
|
640
|
+
fix: "Set channels.googlechat.audienceType and channels.googlechat.audience."
|
|
641
|
+
});
|
|
642
|
+
if (!entry.audienceType) issues.push({
|
|
643
|
+
channel: "googlechat",
|
|
644
|
+
accountId,
|
|
645
|
+
kind: "config",
|
|
646
|
+
message: "Google Chat audienceType is missing (app-url or project-number).",
|
|
647
|
+
fix: "Set channels.googlechat.audienceType and channels.googlechat.audience."
|
|
648
|
+
});
|
|
649
|
+
return issues;
|
|
650
|
+
}),
|
|
651
|
+
buildChannelSummary: ({ snapshot }) => buildPassiveProbedChannelStatusSummary(snapshot, {
|
|
652
|
+
credentialSource: snapshot.credentialSource ?? "none",
|
|
653
|
+
audienceType: snapshot.audienceType ?? null,
|
|
654
|
+
audience: snapshot.audience ?? null,
|
|
655
|
+
webhookPath: snapshot.webhookPath ?? null,
|
|
656
|
+
webhookUrl: snapshot.webhookUrl ?? null
|
|
657
|
+
}),
|
|
658
|
+
probeAccount: async ({ account }) => (await loadGoogleChatChannelRuntime()).probeGoogleChat(account),
|
|
659
|
+
resolveAccountSnapshot: ({ account }) => ({
|
|
660
|
+
accountId: account.accountId,
|
|
661
|
+
name: account.name,
|
|
662
|
+
enabled: account.enabled,
|
|
663
|
+
configured: account.credentialSource !== "none",
|
|
664
|
+
extra: {
|
|
665
|
+
credentialSource: account.credentialSource,
|
|
666
|
+
audienceType: account.config.audienceType,
|
|
667
|
+
audience: account.config.audience,
|
|
668
|
+
webhookPath: account.config.webhookPath,
|
|
669
|
+
webhookUrl: account.config.webhookUrl,
|
|
670
|
+
dmPolicy: account.config.dm?.policy ?? "pairing"
|
|
671
|
+
}
|
|
672
|
+
})
|
|
673
|
+
}),
|
|
674
|
+
gateway: { startAccount: startGoogleChatGatewayAccount }
|
|
675
|
+
},
|
|
676
|
+
pairing: { text: googlechatPairingTextAdapter },
|
|
677
|
+
security: googlechatSecurityAdapter,
|
|
678
|
+
threading: googlechatThreadingAdapter,
|
|
679
|
+
outbound: googlechatOutboundAdapter
|
|
680
|
+
});
|
|
681
|
+
//#endregion
|
|
682
|
+
export { googlechatPlugin as t };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { a as buildChannelConfigSchema, r as GoogleChatConfigSchema } from "./runtime-api-wkIdfwqY.js";
|
|
2
|
+
//#region extensions/googlechat/src/config-schema.ts
|
|
3
|
+
const GoogleChatChannelConfigSchema = buildChannelConfigSchema(GoogleChatConfigSchema);
|
|
4
|
+
//#endregion
|
|
5
|
+
export { GoogleChatChannelConfigSchema };
|