@openclaw/googlechat 2026.6.2-beta.1 → 2026.6.5-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.
- package/dist/{actions-DisAY2Ac.js → actions-DvYC089V.js} +2 -3
- package/dist/api.js +2 -2
- package/dist/approval-auth-C-vWY4bY.js +27 -0
- package/dist/approval-handler.runtime-CWeVJAQF.js +272 -0
- package/dist/{channel-AbgtAg3y.js → channel-BY9hwCh2.js} +158 -28
- package/dist/{channel-base-DpPD0ef1.js → channel-base-Qh87GFW_.js} +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{api-DPvlxpWa.js → channel.adapters-BQoq1F4R.js} +435 -7
- package/dist/{channel.runtime-C4eLDKR3.js → channel.runtime-CbSK_Zox.js} +117 -3
- package/dist/directory-contract-api.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
- package/dist/channel.adapters-LlD3wKl5.js +0 -280
|
@@ -1,12 +1,161 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { l as resolveGoogleChatAccount, r as formatGoogleChatAllowFromEntry } from "./channel-base-Qh87GFW_.js";
|
|
2
|
+
import { T as resolveChannelMediaMaxBytes, _ as missingTargetError, g as loadOutboundMediaFromUrl, i as PAIRING_APPROVED_MESSAGE, o as chunkTextForOutbound, p as fetchWithSsrFGuard$1, x as readRemoteMediaBuffer } from "./runtime-api-BbVoWRxq.js";
|
|
3
|
+
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
|
4
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
5
|
import crypto from "node:crypto";
|
|
4
6
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
5
7
|
import { parseMediaContentLength } from "openclaw/plugin-sdk/media-runtime";
|
|
6
8
|
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
|
7
9
|
import { buildHostnameAllowlistPolicyFromSuffixAllowlist, fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
10
|
+
import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter, sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
|
|
8
11
|
import fs from "node:fs/promises";
|
|
9
12
|
import { resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
13
|
+
import { adaptScopedAccountAccessor } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
14
|
+
import { composeAccountWarningCollectors, createAllowlistProviderOpenWarningCollector, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
15
|
+
import { createChannelDirectoryAdapter, listResolvedDirectoryGroupEntriesFromMapKeys, listResolvedDirectoryUserEntriesFromAllowFrom } from "openclaw/plugin-sdk/directory-runtime";
|
|
16
|
+
//#region extensions/googlechat/src/approval-card-actions.ts
|
|
17
|
+
const GOOGLECHAT_APPROVAL_ACTION = "openclaw.approval";
|
|
18
|
+
const GOOGLECHAT_APPROVAL_ACTION_PARAM = "openclaw_action";
|
|
19
|
+
const GOOGLECHAT_APPROVAL_TOKEN_PARAM = "token";
|
|
20
|
+
const GOOGLECHAT_APPROVAL_ACTION_VALUE = "approval";
|
|
21
|
+
const MANUAL_EXEC_APPROVAL_COMMAND_RE = /(?:^|[\s`])\/approve[ \t]+([^ \t\r\n`|]+)[ \t]+(allow-once|allow-always|deny)(?=$|[\s`|.,;:!?])/giu;
|
|
22
|
+
const approvalCardBindings = /* @__PURE__ */ new Map();
|
|
23
|
+
const approvalCardResolvingTokens = /* @__PURE__ */ new Set();
|
|
24
|
+
const manualApprovalFollowupSuppressions = /* @__PURE__ */ new Map();
|
|
25
|
+
function createGoogleChatApprovalToken() {
|
|
26
|
+
return crypto.randomBytes(18).toString("base64url");
|
|
27
|
+
}
|
|
28
|
+
function buildGoogleChatApprovalActionParameters(token) {
|
|
29
|
+
return [{
|
|
30
|
+
key: GOOGLECHAT_APPROVAL_ACTION_PARAM,
|
|
31
|
+
value: GOOGLECHAT_APPROVAL_ACTION_VALUE
|
|
32
|
+
}, {
|
|
33
|
+
key: GOOGLECHAT_APPROVAL_TOKEN_PARAM,
|
|
34
|
+
value: token
|
|
35
|
+
}];
|
|
36
|
+
}
|
|
37
|
+
function collectEventParameters(event) {
|
|
38
|
+
const params = {};
|
|
39
|
+
for (const [key, value] of Object.entries(event.common?.parameters ?? {})) if (typeof value === "string") params[key] = value;
|
|
40
|
+
for (const [key, value] of Object.entries(event.commonEventObject?.parameters ?? {})) if (typeof value === "string") params[key] = value;
|
|
41
|
+
for (const item of event.action?.parameters ?? []) if (typeof item.key === "string" && typeof item.value === "string") params[item.key] = item.value;
|
|
42
|
+
return params;
|
|
43
|
+
}
|
|
44
|
+
function readGoogleChatApprovalActionToken(event) {
|
|
45
|
+
const params = collectEventParameters(event);
|
|
46
|
+
if (params[GOOGLECHAT_APPROVAL_ACTION_PARAM] !== GOOGLECHAT_APPROVAL_ACTION_VALUE) return null;
|
|
47
|
+
const actionName = normalizeOptionalString(event.action?.actionMethodName) ?? normalizeOptionalString(event.common?.invokedFunction) ?? normalizeOptionalString(event.commonEventObject?.invokedFunction);
|
|
48
|
+
if (actionName && actionName !== "openclaw.approval" && !actionName.startsWith("https://")) return null;
|
|
49
|
+
return normalizeOptionalString(params[GOOGLECHAT_APPROVAL_TOKEN_PARAM]) ?? null;
|
|
50
|
+
}
|
|
51
|
+
function registerGoogleChatApprovalCardBinding(binding) {
|
|
52
|
+
if (binding.expiresAtMs <= Date.now()) return false;
|
|
53
|
+
approvalCardBindings.set(binding.token, binding);
|
|
54
|
+
registerGoogleChatManualApprovalFollowupSuppression({
|
|
55
|
+
approvalId: binding.approvalId,
|
|
56
|
+
approvalKind: binding.approvalKind,
|
|
57
|
+
allowedDecisions: binding.allowedDecisions,
|
|
58
|
+
expiresAtMs: binding.expiresAtMs
|
|
59
|
+
});
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
function getGoogleChatApprovalCardBinding(token) {
|
|
63
|
+
const binding = approvalCardBindings.get(token);
|
|
64
|
+
if (!binding) return null;
|
|
65
|
+
if (binding.expiresAtMs <= Date.now()) {
|
|
66
|
+
approvalCardBindings.delete(token);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return binding;
|
|
70
|
+
}
|
|
71
|
+
function normalizeApprovalRef(value) {
|
|
72
|
+
const normalized = value.trim().toLowerCase();
|
|
73
|
+
return normalized ? normalized : null;
|
|
74
|
+
}
|
|
75
|
+
function manualApprovalFollowupSuppressionKey(approvalId) {
|
|
76
|
+
return normalizeApprovalRef(approvalId);
|
|
77
|
+
}
|
|
78
|
+
function registerGoogleChatManualApprovalFollowupSuppression(suppression) {
|
|
79
|
+
if (suppression.expiresAtMs <= Date.now()) return false;
|
|
80
|
+
const key = manualApprovalFollowupSuppressionKey(suppression.approvalId);
|
|
81
|
+
if (!key) return false;
|
|
82
|
+
manualApprovalFollowupSuppressions.set(key, suppression);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
function unregisterGoogleChatManualApprovalFollowupSuppression(approvalId) {
|
|
86
|
+
const key = manualApprovalFollowupSuppressionKey(approvalId);
|
|
87
|
+
if (key) manualApprovalFollowupSuppressions.delete(key);
|
|
88
|
+
}
|
|
89
|
+
function approvalRefMatches(bindingApprovalId, approvalRef) {
|
|
90
|
+
const normalizedBindingId = normalizeApprovalRef(bindingApprovalId);
|
|
91
|
+
const normalizedRef = normalizeApprovalRef(approvalRef);
|
|
92
|
+
if (!normalizedBindingId || !normalizedRef) return false;
|
|
93
|
+
return normalizedRef === normalizedBindingId || normalizedRef.length >= 8 && normalizedBindingId.startsWith(normalizedRef);
|
|
94
|
+
}
|
|
95
|
+
function pruneExpiredGoogleChatApprovalCardBindings(nowMs) {
|
|
96
|
+
for (const [token, binding] of approvalCardBindings) if (binding.expiresAtMs <= nowMs) {
|
|
97
|
+
approvalCardBindings.delete(token);
|
|
98
|
+
approvalCardResolvingTokens.delete(token);
|
|
99
|
+
}
|
|
100
|
+
for (const [approvalId, suppression] of manualApprovalFollowupSuppressions) if (suppression.expiresAtMs <= nowMs) manualApprovalFollowupSuppressions.delete(approvalId);
|
|
101
|
+
}
|
|
102
|
+
function hasActiveGoogleChatExecApprovalCardForManualCommand(params) {
|
|
103
|
+
pruneExpiredGoogleChatApprovalCardBindings(params.nowMs);
|
|
104
|
+
for (const binding of approvalCardBindings.values()) if (binding.approvalKind === "exec" && binding.allowedDecisions.includes(params.decision) && approvalRefMatches(binding.approvalId, params.approvalRef)) return true;
|
|
105
|
+
for (const suppression of manualApprovalFollowupSuppressions.values()) if (suppression.approvalKind === "exec" && suppression.allowedDecisions.includes(params.decision) && approvalRefMatches(suppression.approvalId, params.approvalRef)) return true;
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
function shouldSuppressGoogleChatManualExecApprovalFollowupText(text, nowMs = Date.now()) {
|
|
109
|
+
for (const match of text.matchAll(MANUAL_EXEC_APPROVAL_COMMAND_RE)) {
|
|
110
|
+
const approvalRef = match[1];
|
|
111
|
+
const decision = match[2]?.toLowerCase();
|
|
112
|
+
if (approvalRef && decision && hasActiveGoogleChatExecApprovalCardForManualCommand({
|
|
113
|
+
approvalRef,
|
|
114
|
+
decision,
|
|
115
|
+
nowMs
|
|
116
|
+
})) return true;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
function hasSendableMedia(payload) {
|
|
121
|
+
return Boolean(payload.mediaUrl?.trim() || payload.mediaUrls?.some((url) => url.trim()));
|
|
122
|
+
}
|
|
123
|
+
function hasStructuredPayloadPart(payload) {
|
|
124
|
+
return Boolean(hasSendableMedia(payload) || payload.presentation || payload.interactive || payload.btw || payload.spokenText || payload.ttsSupplement);
|
|
125
|
+
}
|
|
126
|
+
function shouldSuppressGoogleChatManualExecApprovalFollowupPayload(payload, nowMs = Date.now()) {
|
|
127
|
+
const text = payload.text?.trim();
|
|
128
|
+
if (!text || hasStructuredPayloadPart(payload)) return false;
|
|
129
|
+
return shouldSuppressGoogleChatManualExecApprovalFollowupText(text, nowMs);
|
|
130
|
+
}
|
|
131
|
+
function claimGoogleChatApprovalCardBinding(token) {
|
|
132
|
+
const binding = getGoogleChatApprovalCardBinding(token);
|
|
133
|
+
if (!binding) return { kind: "missing" };
|
|
134
|
+
if (approvalCardResolvingTokens.has(token)) return { kind: "in-flight" };
|
|
135
|
+
approvalCardResolvingTokens.add(token);
|
|
136
|
+
return {
|
|
137
|
+
kind: "claimed",
|
|
138
|
+
binding
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function completeGoogleChatApprovalCardBinding(token) {
|
|
142
|
+
const binding = approvalCardBindings.get(token);
|
|
143
|
+
approvalCardResolvingTokens.delete(token);
|
|
144
|
+
approvalCardBindings.delete(token);
|
|
145
|
+
if (binding) unregisterGoogleChatManualApprovalFollowupSuppression(binding.approvalId);
|
|
146
|
+
}
|
|
147
|
+
function releaseGoogleChatApprovalCardBinding(token) {
|
|
148
|
+
approvalCardResolvingTokens.delete(token);
|
|
149
|
+
}
|
|
150
|
+
function unregisterGoogleChatApprovalCardBindings(tokens) {
|
|
151
|
+
for (const token of tokens) {
|
|
152
|
+
const binding = approvalCardBindings.get(token);
|
|
153
|
+
approvalCardBindings.delete(token);
|
|
154
|
+
approvalCardResolvingTokens.delete(token);
|
|
155
|
+
if (binding) unregisterGoogleChatManualApprovalFollowupSuppression(binding.approvalId);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
10
159
|
//#region extensions/googlechat/src/google-auth.runtime.ts
|
|
11
160
|
const GOOGLE_AUTH_POLICY = buildHostnameAllowlistPolicyFromSuffixAllowlist(["accounts.google.com", "googleapis.com"]);
|
|
12
161
|
const GOOGLE_AUTH_AUDIT_CONTEXT = "googlechat.auth.google-auth";
|
|
@@ -541,9 +690,11 @@ async function fetchBuffer(account, url, init, options) {
|
|
|
541
690
|
});
|
|
542
691
|
}
|
|
543
692
|
async function sendGoogleChatMessage(params) {
|
|
544
|
-
const { account, space, text, thread, attachments } = params;
|
|
693
|
+
const { account, space, text, thread, cardsV2, attachments } = params;
|
|
694
|
+
if (text && (!cardsV2 || cardsV2.length === 0) && (!attachments || attachments.length === 0) && shouldSuppressGoogleChatManualExecApprovalFollowupText(text)) return null;
|
|
545
695
|
const body = {};
|
|
546
696
|
if (text) body.text = text;
|
|
697
|
+
if (cardsV2 && cardsV2.length > 0) body.cardsV2 = cardsV2;
|
|
547
698
|
if (thread) body.thread = { name: thread };
|
|
548
699
|
if (attachments && attachments.length > 0) body.attachment = attachments.map((item) => Object.assign({ attachmentDataRef: { attachmentUploadToken: item.attachmentUploadToken } }, item.contentName ? { contentName: item.contentName } : {}));
|
|
549
700
|
const urlObj = new URL(`${CHAT_API_BASE}/${space}/messages`);
|
|
@@ -555,10 +706,16 @@ async function sendGoogleChatMessage(params) {
|
|
|
555
706
|
return result ? { messageName: result.name } : null;
|
|
556
707
|
}
|
|
557
708
|
async function updateGoogleChatMessage(params) {
|
|
558
|
-
const { account, messageName, text } = params;
|
|
559
|
-
|
|
709
|
+
const { account, messageName, text, cardsV2 } = params;
|
|
710
|
+
const updateMask = [...text !== void 0 ? ["text"] : [], ...cardsV2 !== void 0 ? ["cardsV2"] : []];
|
|
711
|
+
if (updateMask.length === 0) throw new Error("Google Chat message update requires text or cardsV2.");
|
|
712
|
+
const url = `${CHAT_API_BASE}/${messageName}?updateMask=${updateMask.join(",")}`;
|
|
713
|
+
const body = {};
|
|
714
|
+
if (text !== void 0) body.text = text;
|
|
715
|
+
if (cardsV2 !== void 0) body.cardsV2 = cardsV2;
|
|
716
|
+
return { messageName: (await fetchJson(account, url, {
|
|
560
717
|
method: "PATCH",
|
|
561
|
-
body: JSON.stringify(
|
|
718
|
+
body: JSON.stringify(body)
|
|
562
719
|
})).name };
|
|
563
720
|
}
|
|
564
721
|
async function deleteGoogleChatMessage(params) {
|
|
@@ -631,4 +788,275 @@ async function probeGoogleChat(account) {
|
|
|
631
788
|
}
|
|
632
789
|
}
|
|
633
790
|
//#endregion
|
|
634
|
-
|
|
791
|
+
//#region extensions/googlechat/src/targets.ts
|
|
792
|
+
function normalizeGoogleChatTarget(raw) {
|
|
793
|
+
const trimmed = raw?.trim();
|
|
794
|
+
if (!trimmed) return;
|
|
795
|
+
const normalized = trimmed.replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:(users\/)?/i, "users/").replace(/^space:(spaces\/)?/i, "spaces/");
|
|
796
|
+
if (isGoogleChatUserTarget(normalized)) {
|
|
797
|
+
const suffix = normalized.slice(6);
|
|
798
|
+
return suffix.includes("@") ? `users/${normalizeLowercaseStringOrEmpty(suffix)}` : normalized;
|
|
799
|
+
}
|
|
800
|
+
if (isGoogleChatSpaceTarget(normalized)) return normalized;
|
|
801
|
+
if (normalized.includes("@")) return `users/${normalizeLowercaseStringOrEmpty(normalized)}`;
|
|
802
|
+
return normalized;
|
|
803
|
+
}
|
|
804
|
+
function isGoogleChatUserTarget(value) {
|
|
805
|
+
return normalizeLowercaseStringOrEmpty(value).startsWith("users/");
|
|
806
|
+
}
|
|
807
|
+
function isGoogleChatSpaceTarget(value) {
|
|
808
|
+
return normalizeLowercaseStringOrEmpty(value).startsWith("spaces/");
|
|
809
|
+
}
|
|
810
|
+
function stripMessageSuffix(target) {
|
|
811
|
+
const index = target.indexOf("/messages/");
|
|
812
|
+
if (index === -1) return target;
|
|
813
|
+
return target.slice(0, index);
|
|
814
|
+
}
|
|
815
|
+
async function resolveGoogleChatOutboundSpace(params) {
|
|
816
|
+
const normalized = normalizeGoogleChatTarget(params.target);
|
|
817
|
+
if (!normalized) throw new Error("Missing Google Chat target.");
|
|
818
|
+
const base = stripMessageSuffix(normalized);
|
|
819
|
+
if (isGoogleChatSpaceTarget(base)) return base;
|
|
820
|
+
if (isGoogleChatUserTarget(base)) {
|
|
821
|
+
const dm = await findGoogleChatDirectMessage({
|
|
822
|
+
account: params.account,
|
|
823
|
+
userName: base
|
|
824
|
+
});
|
|
825
|
+
if (!dm?.name) throw new Error(`No Google Chat DM found for ${base}`);
|
|
826
|
+
return dm.name;
|
|
827
|
+
}
|
|
828
|
+
return base;
|
|
829
|
+
}
|
|
830
|
+
//#endregion
|
|
831
|
+
//#region extensions/googlechat/src/group-policy.ts
|
|
832
|
+
function resolveGoogleChatGroupRequireMention(params) {
|
|
833
|
+
return resolveChannelGroupRequireMention({
|
|
834
|
+
cfg: params.cfg,
|
|
835
|
+
channel: "googlechat",
|
|
836
|
+
groupId: params.groupId,
|
|
837
|
+
accountId: params.accountId
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
//#endregion
|
|
841
|
+
//#region extensions/googlechat/src/channel.adapters.ts
|
|
842
|
+
const loadGoogleChatChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-CbSK_Zox.js"), "googleChatChannelRuntime");
|
|
843
|
+
function createGoogleChatSendReceipt(params) {
|
|
844
|
+
const messageId = params.messageId?.trim();
|
|
845
|
+
return createMessageReceiptFromOutboundResults({
|
|
846
|
+
results: messageId ? [{
|
|
847
|
+
channel: "googlechat",
|
|
848
|
+
messageId,
|
|
849
|
+
chatId: params.chatId,
|
|
850
|
+
conversationId: params.chatId
|
|
851
|
+
}] : [],
|
|
852
|
+
threadId: params.chatId,
|
|
853
|
+
kind: params.kind
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
const formatAllowFromEntry = formatGoogleChatAllowFromEntry;
|
|
857
|
+
const collectGoogleChatSecurityWarnings = composeAccountWarningCollectors(createAllowlistProviderOpenWarningCollector({
|
|
858
|
+
providerConfigPresent: (cfg) => cfg.channels?.googlechat !== void 0,
|
|
859
|
+
resolveGroupPolicy: (account) => account.config.groupPolicy,
|
|
860
|
+
buildOpenWarning: {
|
|
861
|
+
surface: "Google Chat spaces",
|
|
862
|
+
openBehavior: "allows any space to trigger (mention-gated)",
|
|
863
|
+
remediation: "Set channels.googlechat.groupPolicy=\"allowlist\" and configure channels.googlechat.groups"
|
|
864
|
+
}
|
|
865
|
+
}), (account) => account.config.dm?.policy === "open" && "- Google Chat DMs are open to anyone. Set channels.googlechat.dm.policy=\"pairing\" or \"allowlist\".");
|
|
866
|
+
const googlechatGroupsAdapter = { resolveRequireMention: resolveGoogleChatGroupRequireMention };
|
|
867
|
+
const googlechatDirectoryAdapter = createChannelDirectoryAdapter({
|
|
868
|
+
listPeers: async (params) => listResolvedDirectoryUserEntriesFromAllowFrom({
|
|
869
|
+
...params,
|
|
870
|
+
resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount),
|
|
871
|
+
resolveAllowFrom: (account) => account.config.dm?.allowFrom,
|
|
872
|
+
normalizeId: (entry) => normalizeGoogleChatTarget(entry) ?? entry
|
|
873
|
+
}),
|
|
874
|
+
listGroups: async (params) => listResolvedDirectoryGroupEntriesFromMapKeys({
|
|
875
|
+
...params,
|
|
876
|
+
resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount),
|
|
877
|
+
resolveGroups: (account) => account.config.groups
|
|
878
|
+
})
|
|
879
|
+
});
|
|
880
|
+
const googlechatSecurityAdapter = {
|
|
881
|
+
dm: {
|
|
882
|
+
channelKey: "googlechat",
|
|
883
|
+
resolvePolicy: (account) => account.config.dm?.policy,
|
|
884
|
+
resolveAllowFrom: (account) => account.config.dm?.allowFrom,
|
|
885
|
+
allowFromPathSuffix: "dm.",
|
|
886
|
+
normalizeEntry: (raw) => formatAllowFromEntry(raw)
|
|
887
|
+
},
|
|
888
|
+
collectWarnings: collectGoogleChatSecurityWarnings
|
|
889
|
+
};
|
|
890
|
+
const googlechatThreadingAdapter = {
|
|
891
|
+
scopedAccountReplyToMode: {
|
|
892
|
+
resolveAccount: (cfg, accountId) => resolveGoogleChatAccount({
|
|
893
|
+
cfg,
|
|
894
|
+
accountId
|
|
895
|
+
}),
|
|
896
|
+
resolveReplyToMode: (account, _chatType) => account.config.replyToMode,
|
|
897
|
+
fallback: "off"
|
|
898
|
+
},
|
|
899
|
+
buildToolContext: ({ cfg, accountId, context, hasRepliedRef }) => {
|
|
900
|
+
const currentChannelId = normalizeGoogleChatTarget(context.To);
|
|
901
|
+
const replyToId = normalizeOptionalString(context.ReplyToIdFull) ?? normalizeOptionalString(context.ReplyToId);
|
|
902
|
+
return {
|
|
903
|
+
currentChannelId,
|
|
904
|
+
currentMessageId: replyToId,
|
|
905
|
+
currentThreadTs: replyToId,
|
|
906
|
+
replyToMode: resolveGoogleChatAccount({
|
|
907
|
+
cfg,
|
|
908
|
+
accountId
|
|
909
|
+
}).config.replyToMode,
|
|
910
|
+
hasRepliedRef
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
const googlechatPairingTextAdapter = {
|
|
915
|
+
idLabel: "googlechatUserId",
|
|
916
|
+
message: PAIRING_APPROVED_MESSAGE,
|
|
917
|
+
normalizeAllowEntry: (entry) => formatAllowFromEntry(entry),
|
|
918
|
+
notify: async ({ cfg, id, message, accountId }) => {
|
|
919
|
+
const account = resolveGoogleChatAccount({
|
|
920
|
+
cfg,
|
|
921
|
+
accountId
|
|
922
|
+
});
|
|
923
|
+
if (account.credentialSource === "none") return;
|
|
924
|
+
const user = normalizeGoogleChatTarget(id) ?? id;
|
|
925
|
+
const space = await resolveGoogleChatOutboundSpace({
|
|
926
|
+
account,
|
|
927
|
+
target: isGoogleChatUserTarget(user) ? user : `users/${user}`
|
|
928
|
+
});
|
|
929
|
+
const { sendGoogleChatMessage } = await loadGoogleChatChannelRuntime();
|
|
930
|
+
await sendGoogleChatMessage({
|
|
931
|
+
account,
|
|
932
|
+
space,
|
|
933
|
+
text: message
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
const googlechatOutboundAdapter = {
|
|
938
|
+
base: {
|
|
939
|
+
deliveryMode: "direct",
|
|
940
|
+
chunker: chunkTextForOutbound,
|
|
941
|
+
chunkerMode: "markdown",
|
|
942
|
+
textChunkLimit: 4e3,
|
|
943
|
+
sanitizeText: ({ text }) => sanitizeForPlainText(text),
|
|
944
|
+
normalizePayload: ({ payload }) => shouldSuppressGoogleChatManualExecApprovalFollowupPayload(payload) ? null : payload,
|
|
945
|
+
resolveTarget: ({ to }) => {
|
|
946
|
+
const trimmed = normalizeOptionalString(to) ?? "";
|
|
947
|
+
if (trimmed) {
|
|
948
|
+
const normalized = normalizeGoogleChatTarget(trimmed);
|
|
949
|
+
if (!normalized) return {
|
|
950
|
+
ok: false,
|
|
951
|
+
error: missingTargetError("Google Chat", "<spaces/{space}|users/{user}>")
|
|
952
|
+
};
|
|
953
|
+
return {
|
|
954
|
+
ok: true,
|
|
955
|
+
to: normalized
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
return {
|
|
959
|
+
ok: false,
|
|
960
|
+
error: missingTargetError("Google Chat", "<spaces/{space}|users/{user}>")
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
},
|
|
964
|
+
attachedResults: {
|
|
965
|
+
channel: "googlechat",
|
|
966
|
+
sendText: async ({ cfg, to, text, accountId, replyToId, threadId }) => {
|
|
967
|
+
const account = resolveGoogleChatAccount({
|
|
968
|
+
cfg,
|
|
969
|
+
accountId
|
|
970
|
+
});
|
|
971
|
+
const space = await resolveGoogleChatOutboundSpace({
|
|
972
|
+
account,
|
|
973
|
+
target: to
|
|
974
|
+
});
|
|
975
|
+
const thread = typeof threadId === "number" ? String(threadId) : threadId ?? replyToId ?? void 0;
|
|
976
|
+
const { sendGoogleChatMessage } = await loadGoogleChatChannelRuntime();
|
|
977
|
+
const messageId = (await sendGoogleChatMessage({
|
|
978
|
+
account,
|
|
979
|
+
space,
|
|
980
|
+
text,
|
|
981
|
+
thread
|
|
982
|
+
}))?.messageName ?? "";
|
|
983
|
+
return {
|
|
984
|
+
messageId,
|
|
985
|
+
chatId: space,
|
|
986
|
+
receipt: createGoogleChatSendReceipt({
|
|
987
|
+
messageId,
|
|
988
|
+
chatId: space,
|
|
989
|
+
kind: "text"
|
|
990
|
+
})
|
|
991
|
+
};
|
|
992
|
+
},
|
|
993
|
+
sendMedia: async ({ cfg, to, text, mediaUrl, mediaAccess, mediaLocalRoots, mediaReadFile, accountId, replyToId, threadId }) => {
|
|
994
|
+
if (!mediaUrl) throw new Error("Google Chat mediaUrl is required.");
|
|
995
|
+
const account = resolveGoogleChatAccount({
|
|
996
|
+
cfg,
|
|
997
|
+
accountId
|
|
998
|
+
});
|
|
999
|
+
const space = await resolveGoogleChatOutboundSpace({
|
|
1000
|
+
account,
|
|
1001
|
+
target: to
|
|
1002
|
+
});
|
|
1003
|
+
const thread = typeof threadId === "number" ? String(threadId) : threadId ?? replyToId ?? void 0;
|
|
1004
|
+
const effectiveMaxBytes = resolveChannelMediaMaxBytes({
|
|
1005
|
+
cfg,
|
|
1006
|
+
resolveChannelLimitMb: ({ cfg: cfgLocal, accountId: accountIdLocal }) => (cfgLocal.channels?.googlechat)?.accounts?.[accountIdLocal]?.mediaMaxMb ?? (cfgLocal.channels?.googlechat)?.mediaMaxMb,
|
|
1007
|
+
accountId
|
|
1008
|
+
}) ?? (account.config.mediaMaxMb ?? 20) * 1024 * 1024;
|
|
1009
|
+
const loaded = /^https?:\/\//i.test(mediaUrl) ? await readRemoteMediaBuffer({
|
|
1010
|
+
url: mediaUrl,
|
|
1011
|
+
maxBytes: effectiveMaxBytes
|
|
1012
|
+
}) : await loadOutboundMediaFromUrl(mediaUrl, {
|
|
1013
|
+
maxBytes: effectiveMaxBytes,
|
|
1014
|
+
mediaAccess,
|
|
1015
|
+
mediaLocalRoots,
|
|
1016
|
+
mediaReadFile
|
|
1017
|
+
});
|
|
1018
|
+
const { sendGoogleChatMessage, uploadGoogleChatAttachment } = await loadGoogleChatChannelRuntime();
|
|
1019
|
+
const upload = await uploadGoogleChatAttachment({
|
|
1020
|
+
account,
|
|
1021
|
+
space,
|
|
1022
|
+
filename: loaded.fileName ?? "attachment",
|
|
1023
|
+
buffer: loaded.buffer,
|
|
1024
|
+
contentType: loaded.contentType
|
|
1025
|
+
});
|
|
1026
|
+
const messageId = (await sendGoogleChatMessage({
|
|
1027
|
+
account,
|
|
1028
|
+
space,
|
|
1029
|
+
text,
|
|
1030
|
+
thread,
|
|
1031
|
+
attachments: upload.attachmentUploadToken ? [{
|
|
1032
|
+
attachmentUploadToken: upload.attachmentUploadToken,
|
|
1033
|
+
contentName: loaded.fileName
|
|
1034
|
+
}] : void 0
|
|
1035
|
+
}))?.messageName ?? "";
|
|
1036
|
+
return {
|
|
1037
|
+
messageId,
|
|
1038
|
+
chatId: space,
|
|
1039
|
+
receipt: createGoogleChatSendReceipt({
|
|
1040
|
+
messageId,
|
|
1041
|
+
chatId: space,
|
|
1042
|
+
kind: "media"
|
|
1043
|
+
})
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
const googlechatMessageAdapter = defineChannelMessageAdapter({
|
|
1049
|
+
id: "googlechat",
|
|
1050
|
+
durableFinal: { capabilities: {
|
|
1051
|
+
text: true,
|
|
1052
|
+
media: true,
|
|
1053
|
+
thread: true,
|
|
1054
|
+
messageSendingHooks: true
|
|
1055
|
+
} },
|
|
1056
|
+
send: {
|
|
1057
|
+
text: googlechatOutboundAdapter.attachedResults.sendText,
|
|
1058
|
+
media: googlechatOutboundAdapter.attachedResults.sendMedia
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
//#endregion
|
|
1062
|
+
export { registerGoogleChatManualApprovalFollowupSuppression as A, buildGoogleChatApprovalActionParameters as C, getGoogleChatApprovalCardBinding as D, createGoogleChatApprovalToken as E, unregisterGoogleChatApprovalCardBindings as M, unregisterGoogleChatManualApprovalFollowupSuppression as N, readGoogleChatApprovalActionToken as O, GOOGLECHAT_APPROVAL_ACTION as S, completeGoogleChatApprovalCardBinding as T, probeGoogleChat as _, googlechatPairingTextAdapter as a, uploadGoogleChatAttachment as b, isGoogleChatSpaceTarget as c, resolveGoogleChatOutboundSpace as d, createGoogleChatReaction as f, listGoogleChatReactions as g, downloadGoogleChatMedia as h, googlechatOutboundAdapter as i, releaseGoogleChatApprovalCardBinding as j, registerGoogleChatApprovalCardBinding as k, isGoogleChatUserTarget as l, deleteGoogleChatReaction as m, googlechatGroupsAdapter as n, googlechatSecurityAdapter as o, deleteGoogleChatMessage as p, googlechatMessageAdapter as r, googlechatThreadingAdapter as s, googlechatDirectoryAdapter as t, normalizeGoogleChatTarget as u, sendGoogleChatMessage as v, claimGoogleChatApprovalCardBinding as w, verifyGoogleChatRequest as x, updateGoogleChatMessage as y };
|
|
@@ -1,13 +1,83 @@
|
|
|
1
|
+
import { D as getGoogleChatApprovalCardBinding, O as readGoogleChatApprovalActionToken, T as completeGoogleChatApprovalCardBinding, _ as probeGoogleChat, b as uploadGoogleChatAttachment, h as downloadGoogleChatMedia, j as releaseGoogleChatApprovalCardBinding, p as deleteGoogleChatMessage, v as sendGoogleChatMessage, w as claimGoogleChatApprovalCardBinding, x as verifyGoogleChatRequest, y as updateGoogleChatMessage } from "./channel.adapters-BQoq1F4R.js";
|
|
1
2
|
import { E as resolveDefaultGroupPolicy, M as warnMissingProviderGroupPolicyFallbackOnce, O as resolveInboundRouteEnvelopeBuilderWithRuntime, P as getGoogleChatRuntime, k as resolveWebhookPath, m as isDangerousNameMatchingEnabled, n as GROUP_POLICY_BLOCKED_LABEL, u as createChannelPairingController, w as resolveAllowlistProviderRuntimeGroupPolicy } from "./runtime-api-BbVoWRxq.js";
|
|
2
|
-
import {
|
|
3
|
+
import { n as googleChatApprovalAuth } from "./approval-auth-C-vWY4bY.js";
|
|
3
4
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
5
|
import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
|
|
5
6
|
import { recordChannelBotPairLoopAndCheckSuppression } from "openclaw/plugin-sdk/channel-inbound";
|
|
6
7
|
import { WEBHOOK_RATE_LIMIT_DEFAULTS, createFixedWindowRateLimiter, normalizeWebhookPath, resolveRequestClientIp } from "openclaw/plugin-sdk/webhook-ingress";
|
|
7
8
|
import { registerWebhookTargetWithPluginRoute, resolveWebhookTargetWithAuthOrReject, withResolvedWebhookRequestPipeline } from "openclaw/plugin-sdk/webhook-targets";
|
|
8
9
|
import { createWebhookInFlightLimiter, readJsonWebhookBodyOrReject } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
10
|
+
import { resolveApprovalOverGateway } from "openclaw/plugin-sdk/approval-gateway-runtime";
|
|
9
11
|
import { channelIngressRoutes, createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
10
12
|
import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
13
|
+
//#region extensions/googlechat/src/approval-card-click.ts
|
|
14
|
+
function logIgnored(target, message) {
|
|
15
|
+
target.runtime.log?.(`[${target.account.accountId}] googlechat approval ignored: ${message}`);
|
|
16
|
+
}
|
|
17
|
+
async function maybeHandleGoogleChatApprovalCardClick(params) {
|
|
18
|
+
if ((params.event.type ?? params.event.eventType) !== "CARD_CLICKED") return false;
|
|
19
|
+
const token = readGoogleChatApprovalActionToken(params.event);
|
|
20
|
+
if (!token) return false;
|
|
21
|
+
const binding = getGoogleChatApprovalCardBinding(token);
|
|
22
|
+
if (!binding) {
|
|
23
|
+
logIgnored(params.target, "unknown or expired card token");
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (binding.accountId !== params.target.account.accountId) {
|
|
27
|
+
logIgnored(params.target, "card token account mismatch");
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (params.event.space?.name !== binding.spaceName) {
|
|
31
|
+
logIgnored(params.target, "card token space mismatch");
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (params.event.message?.name && params.event.message.name !== binding.messageName) {
|
|
35
|
+
logIgnored(params.target, "card token message mismatch");
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (!binding.allowedDecisions.includes(binding.decision)) {
|
|
39
|
+
logIgnored(params.target, "card token decision is no longer allowed");
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const actor = params.event.user?.name;
|
|
43
|
+
if (!(googleChatApprovalAuth.authorizeActorAction?.({
|
|
44
|
+
cfg: params.target.config,
|
|
45
|
+
accountId: params.target.account.accountId,
|
|
46
|
+
senderId: actor,
|
|
47
|
+
action: "approve",
|
|
48
|
+
approvalKind: binding.approvalKind
|
|
49
|
+
}))?.authorized) {
|
|
50
|
+
logIgnored(params.target, `unauthorized actor ${actor || "unknown"}`);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
const claim = claimGoogleChatApprovalCardBinding(token);
|
|
54
|
+
if (claim.kind === "missing") {
|
|
55
|
+
logIgnored(params.target, "card token already consumed");
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (claim.kind === "in-flight") {
|
|
59
|
+
logIgnored(params.target, "card token resolve already in flight");
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const consumed = claim.binding;
|
|
63
|
+
try {
|
|
64
|
+
await resolveApprovalOverGateway({
|
|
65
|
+
cfg: params.target.config,
|
|
66
|
+
approvalId: consumed.approvalId,
|
|
67
|
+
decision: consumed.decision,
|
|
68
|
+
senderId: actor,
|
|
69
|
+
allowPluginFallback: consumed.approvalKind === "exec",
|
|
70
|
+
clientDisplayName: `Google Chat approval (${actor?.trim() || "unknown"})`
|
|
71
|
+
});
|
|
72
|
+
} catch (error) {
|
|
73
|
+
releaseGoogleChatApprovalCardBinding(token);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
completeGoogleChatApprovalCardBinding(token);
|
|
77
|
+
params.target.runtime.log?.(`[${params.target.account.accountId}] googlechat approval resolved id=${consumed.approvalId} decision=${consumed.decision} sender=${actor || "unknown"}`);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
11
81
|
//#region extensions/googlechat/src/monitor-access.ts
|
|
12
82
|
function normalizeUserId(raw) {
|
|
13
83
|
const trimmed = normalizeOptionalString(raw) ?? "";
|
|
@@ -419,6 +489,14 @@ function extractBearerToken(header) {
|
|
|
419
489
|
}
|
|
420
490
|
const ADD_ON_PREAUTH_MAX_BYTES = 16 * 1024;
|
|
421
491
|
const ADD_ON_PREAUTH_TIMEOUT_MS = 3e3;
|
|
492
|
+
function recordParamsToActionParameters(params) {
|
|
493
|
+
if (!params) return;
|
|
494
|
+
const entries = Object.entries(params).filter((entry) => typeof entry[1] === "string").map(([key, value]) => ({
|
|
495
|
+
key,
|
|
496
|
+
value
|
|
497
|
+
}));
|
|
498
|
+
return entries.length > 0 ? entries : void 0;
|
|
499
|
+
}
|
|
422
500
|
function parseGoogleChatInboundPayload(raw, res) {
|
|
423
501
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
424
502
|
res.statusCode = 400;
|
|
@@ -428,6 +506,7 @@ function parseGoogleChatInboundPayload(raw, res) {
|
|
|
428
506
|
let eventPayload = raw;
|
|
429
507
|
let addOnBearerToken = "";
|
|
430
508
|
const rawObj = raw;
|
|
509
|
+
if (rawObj.commonEventObject?.hostApp === "CHAT") addOnBearerToken = typeof rawObj.authorizationEventObject?.systemIdToken === "string" ? rawObj.authorizationEventObject.systemIdToken.trim() : "";
|
|
431
510
|
if (rawObj.commonEventObject?.hostApp === "CHAT" && rawObj.chat?.messagePayload) {
|
|
432
511
|
const chat = rawObj.chat;
|
|
433
512
|
const messagePayload = chat.messagePayload;
|
|
@@ -438,7 +517,28 @@ function parseGoogleChatInboundPayload(raw, res) {
|
|
|
438
517
|
user: chat.user,
|
|
439
518
|
eventTime: chat.eventTime
|
|
440
519
|
};
|
|
441
|
-
|
|
520
|
+
} else if (rawObj.commonEventObject?.hostApp === "CHAT") {
|
|
521
|
+
const chat = rawObj.chat;
|
|
522
|
+
const buttonClickedPayload = chat?.buttonClickedPayload;
|
|
523
|
+
if (buttonClickedPayload) {
|
|
524
|
+
const invokedFunction = rawObj.commonEventObject.invokedFunction;
|
|
525
|
+
const actionParameters = recordParamsToActionParameters(rawObj.commonEventObject.parameters);
|
|
526
|
+
eventPayload = {
|
|
527
|
+
type: "CARD_CLICKED",
|
|
528
|
+
space: buttonClickedPayload.space,
|
|
529
|
+
message: buttonClickedPayload.message,
|
|
530
|
+
user: buttonClickedPayload.user ?? chat.user,
|
|
531
|
+
eventTime: chat.eventTime,
|
|
532
|
+
action: buttonClickedPayload.action ?? {
|
|
533
|
+
...typeof invokedFunction === "string" ? { actionMethodName: invokedFunction } : {},
|
|
534
|
+
...actionParameters ? { parameters: actionParameters } : {}
|
|
535
|
+
},
|
|
536
|
+
commonEventObject: {
|
|
537
|
+
...typeof invokedFunction === "string" ? { invokedFunction } : {},
|
|
538
|
+
parameters: rawObj.commonEventObject.parameters
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
}
|
|
442
542
|
}
|
|
443
543
|
const event = eventPayload;
|
|
444
544
|
const eventType = event.type ?? eventPayload.eventType;
|
|
@@ -458,6 +558,12 @@ function parseGoogleChatInboundPayload(raw, res) {
|
|
|
458
558
|
res.end("invalid payload");
|
|
459
559
|
return { ok: false };
|
|
460
560
|
}
|
|
561
|
+
} else if (eventType === "CARD_CLICKED") {
|
|
562
|
+
if (!event.user || typeof event.user !== "object" || Array.isArray(event.user)) {
|
|
563
|
+
res.statusCode = 400;
|
|
564
|
+
res.end("invalid payload");
|
|
565
|
+
return { ok: false };
|
|
566
|
+
}
|
|
461
567
|
}
|
|
462
568
|
return {
|
|
463
569
|
ok: true,
|
|
@@ -677,7 +783,15 @@ function shouldSuppressGoogleChatBotLoop(params) {
|
|
|
677
783
|
return true;
|
|
678
784
|
}
|
|
679
785
|
async function processGoogleChatEvent(event, target) {
|
|
680
|
-
|
|
786
|
+
const eventType = event.type ?? event.eventType;
|
|
787
|
+
if (eventType === "CARD_CLICKED") {
|
|
788
|
+
await maybeHandleGoogleChatApprovalCardClick({
|
|
789
|
+
event,
|
|
790
|
+
target
|
|
791
|
+
});
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (eventType !== "MESSAGE") return;
|
|
681
795
|
if (!event.message || !event.space) return;
|
|
682
796
|
await processMessageWithPipeline({
|
|
683
797
|
event,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as googlechatDirectoryAdapter } from "./channel.adapters-
|
|
1
|
+
import { t as googlechatDirectoryAdapter } from "./channel.adapters-BQoq1F4R.js";
|
|
2
2
|
//#region extensions/googlechat/directory-contract-api.ts
|
|
3
3
|
const googlechatDirectoryContractPlugin = {
|
|
4
4
|
id: "googlechat",
|
package/dist/setup-plugin-api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createGoogleChatPluginBase } from "./channel-base-
|
|
1
|
+
import { n as createGoogleChatPluginBase } from "./channel-base-Qh87GFW_.js";
|
|
2
2
|
//#region extensions/googlechat/src/channel.setup.ts
|
|
3
3
|
const googlechatSetupPlugin = createGoogleChatPluginBase();
|
|
4
4
|
//#endregion
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/googlechat",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.5-beta.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/googlechat",
|
|
9
|
-
"version": "2026.6.
|
|
9
|
+
"version": "2026.6.5-beta.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"gaxios": "7.1.4",
|
|
12
12
|
"google-auth-library": "10.6.2",
|
|
13
13
|
"zod": "4.4.3"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"openclaw": ">=2026.6.
|
|
16
|
+
"openclaw": ">=2026.6.5-beta.1"
|
|
17
17
|
},
|
|
18
18
|
"peerDependenciesMeta": {
|
|
19
19
|
"openclaw": {
|