@openclaw/msteams 2026.5.12 → 2026.5.14-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.js +2 -2
- package/dist/{channel-BApPsQGS.js → channel-C5CVTygn.js} +138 -8
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-CnDHBWml.js → channel.runtime-fqvE_yLN.js} +2 -2
- package/dist/contract-api.js +1 -1
- package/dist/{graph-users-BQJvcsX8.js → graph-users-ChPPxUzD.js} +66 -29
- package/dist/{oauth-BWJyilR1.js → oauth-DsVj42gA.js} +1 -1
- package/dist/{oauth.token-xxpoLWy5.js → oauth.token-mUfXUE0j.js} +2 -1
- package/dist/{probe-4kXMWuAw.js → probe-I2DM0U-s.js} +2 -2
- package/dist/secret-contract-api.js +1 -1
- package/dist/setup-plugin-api.js +2 -2
- package/dist/{setup-surface-Ce463w3t.js → setup-surface-CQrMX-nJ.js} +220 -4
- package/dist/{src-BEhrsfns.js → src-CYZq-lQ4.js} +83 -94
- package/dist/test-api.js +1 -1
- package/package.json +6 -6
- package/dist/policy-bM71GXRd.js +0 -134
- package/dist/resolve-allowlist-DPCTpYxi.js +0 -219
- /package/dist/{secret-contract-BuoEXmPS.js → secret-contract-DDPQm4fE.js} +0 -0
|
@@ -1,7 +1,223 @@
|
|
|
1
|
-
import { O as formatUnknownError, g as saveDelegatedTokens, h as resolveMSTeamsCredentials, p as hasConfiguredMSTeamsCredentials, v as normalizeSecretInputString } from "./graph-users-
|
|
2
|
-
import {
|
|
1
|
+
import { O as formatUnknownError, c as normalizeQuery, f as resolveGraphToken, g as saveDelegatedTokens, h as resolveMSTeamsCredentials, o as listChannelsForTeam, p as hasConfiguredMSTeamsCredentials, s as listTeamsByName, t as searchGraphUsers, v as normalizeSecretInputString } from "./graph-users-ChPPxUzD.js";
|
|
2
|
+
import { mapAllowlistResolutionInputs } from "openclaw/plugin-sdk/allow-from";
|
|
3
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
4
|
import { DEFAULT_ACCOUNT_ID, createStandardChannelSetupStatus, createTopLevelChannelAllowFromSetter, createTopLevelChannelDmPolicy, createTopLevelChannelGroupPolicySetter, mergeAllowFromEntries, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
4
5
|
import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
|
6
|
+
//#region extensions/msteams/src/resolve-allowlist.ts
|
|
7
|
+
function stripProviderPrefix(raw) {
|
|
8
|
+
return raw.replace(/^(msteams|teams):/i, "");
|
|
9
|
+
}
|
|
10
|
+
function normalizeMSTeamsMessagingTarget(raw) {
|
|
11
|
+
let trimmed = raw.trim();
|
|
12
|
+
if (!trimmed) return;
|
|
13
|
+
trimmed = stripProviderPrefix(trimmed).trim();
|
|
14
|
+
if (/^conversation:/i.test(trimmed)) {
|
|
15
|
+
const id = trimmed.slice(13).trim();
|
|
16
|
+
return id ? `conversation:${id}` : void 0;
|
|
17
|
+
}
|
|
18
|
+
if (/^user:/i.test(trimmed)) {
|
|
19
|
+
const id = trimmed.slice(5).trim();
|
|
20
|
+
return id ? `user:${id}` : void 0;
|
|
21
|
+
}
|
|
22
|
+
return trimmed || void 0;
|
|
23
|
+
}
|
|
24
|
+
function normalizeMSTeamsUserInput(raw) {
|
|
25
|
+
return stripProviderPrefix(raw).replace(/^(user|conversation):/i, "").trim();
|
|
26
|
+
}
|
|
27
|
+
function parseMSTeamsConversationId(raw) {
|
|
28
|
+
const trimmed = stripProviderPrefix(raw).trim();
|
|
29
|
+
if (!/^conversation:/i.test(trimmed)) return null;
|
|
30
|
+
return trimmed.slice(13).trim();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detect whether a raw target string looks like a Microsoft Teams conversation
|
|
34
|
+
* or user id that cron announce delivery and other explicit-target paths can
|
|
35
|
+
* forward verbatim to the channel adapter.
|
|
36
|
+
*
|
|
37
|
+
* Accepts both prefixed and bare formats:
|
|
38
|
+
* - `conversation:<id>` — explicit conversation prefix
|
|
39
|
+
* - `user:<aad-guid>` — user id (16+ hex chars, UUID-like)
|
|
40
|
+
* - `19:abc@thread.tacv2` / `19:abc@thread.skype` — channel / legacy group
|
|
41
|
+
* - `19:{userId}_{appId}@unq.gbl.spaces` — Graph 1:1 chat thread format
|
|
42
|
+
* - `a:1xxx` — Bot Framework personal (1:1) chat id
|
|
43
|
+
* - `8:orgid:xxx` — Bot Framework org-scoped personal chat id
|
|
44
|
+
* - `29:xxx` — Bot Framework user id
|
|
45
|
+
*
|
|
46
|
+
* Display-name user targets such as `user:John Smith` intentionally return
|
|
47
|
+
* false so that the Graph API directory lookup still runs for them.
|
|
48
|
+
*/
|
|
49
|
+
function looksLikeMSTeamsTargetId(raw) {
|
|
50
|
+
const trimmed = raw.trim();
|
|
51
|
+
if (!trimmed) return false;
|
|
52
|
+
if (/^conversation:/i.test(trimmed)) return true;
|
|
53
|
+
if (/^user:/i.test(trimmed)) {
|
|
54
|
+
const id = trimmed.slice(5).trim();
|
|
55
|
+
return /^[0-9a-fA-F-]{16,}$/.test(id);
|
|
56
|
+
}
|
|
57
|
+
if (/^19:.+@thread\.(tacv2|skype)$/i.test(trimmed)) return true;
|
|
58
|
+
if (/^19:.+@unq\.gbl\.spaces$/i.test(trimmed)) return true;
|
|
59
|
+
if (/^a:1[A-Za-z0-9_-]+$/i.test(trimmed)) return true;
|
|
60
|
+
if (/^8:orgid:[A-Za-z0-9-]+$/i.test(trimmed)) return true;
|
|
61
|
+
if (/^29:[A-Za-z0-9_-]+$/i.test(trimmed)) return true;
|
|
62
|
+
return /@thread\b/i.test(trimmed);
|
|
63
|
+
}
|
|
64
|
+
function normalizeMSTeamsTeamKey(raw) {
|
|
65
|
+
return stripProviderPrefix(raw).replace(/^team:/i, "").trim() || void 0;
|
|
66
|
+
}
|
|
67
|
+
function normalizeMSTeamsChannelKey(raw) {
|
|
68
|
+
return (raw?.trim().replace(/^#/, "").trim() ?? "") || void 0;
|
|
69
|
+
}
|
|
70
|
+
function normalizeMSTeamsConversationTargetId(raw) {
|
|
71
|
+
const trimmed = stripProviderPrefix(raw).trim();
|
|
72
|
+
return parseMSTeamsConversationId(trimmed) ?? trimmed;
|
|
73
|
+
}
|
|
74
|
+
function looksLikeMSTeamsThreadConversationId(raw) {
|
|
75
|
+
const normalized = normalizeMSTeamsConversationTargetId(raw);
|
|
76
|
+
return /^19:.+@thread\./i.test(normalized);
|
|
77
|
+
}
|
|
78
|
+
function parseMSTeamsTeamChannelInput(raw) {
|
|
79
|
+
const trimmed = stripProviderPrefix(raw).trim();
|
|
80
|
+
if (!trimmed) return {};
|
|
81
|
+
const parts = trimmed.split("/");
|
|
82
|
+
const team = normalizeMSTeamsTeamKey(parts[0] ?? "");
|
|
83
|
+
const channel = parts.length > 1 ? normalizeMSTeamsChannelKey(parts.slice(1).join("/")) : void 0;
|
|
84
|
+
return {
|
|
85
|
+
...team ? { team } : {},
|
|
86
|
+
...channel ? { channel } : {}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function parseMSTeamsTeamEntry(raw) {
|
|
90
|
+
const { team, channel } = parseMSTeamsTeamChannelInput(raw);
|
|
91
|
+
if (!team) return null;
|
|
92
|
+
return {
|
|
93
|
+
teamKey: team,
|
|
94
|
+
...channel ? { channelKey: channel } : {}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function resolveMSTeamsChannelAllowlist(params) {
|
|
98
|
+
let tokenPromise;
|
|
99
|
+
const getToken = () => {
|
|
100
|
+
tokenPromise ??= resolveGraphToken(params.cfg);
|
|
101
|
+
return tokenPromise;
|
|
102
|
+
};
|
|
103
|
+
return await mapAllowlistResolutionInputs({
|
|
104
|
+
inputs: params.entries,
|
|
105
|
+
mapInput: async (input) => {
|
|
106
|
+
const { team, channel } = parseMSTeamsTeamChannelInput(input);
|
|
107
|
+
if (!team) return {
|
|
108
|
+
input,
|
|
109
|
+
resolved: false
|
|
110
|
+
};
|
|
111
|
+
if (looksLikeMSTeamsThreadConversationId(team)) {
|
|
112
|
+
const teamId = normalizeMSTeamsConversationTargetId(team);
|
|
113
|
+
if (!channel) return {
|
|
114
|
+
input,
|
|
115
|
+
resolved: true,
|
|
116
|
+
teamId,
|
|
117
|
+
teamName: teamId
|
|
118
|
+
};
|
|
119
|
+
if (!looksLikeMSTeamsThreadConversationId(channel)) return {
|
|
120
|
+
input,
|
|
121
|
+
resolved: false,
|
|
122
|
+
teamId,
|
|
123
|
+
teamName: teamId,
|
|
124
|
+
note: "channel id required for conversation-id team"
|
|
125
|
+
};
|
|
126
|
+
const channelId = normalizeMSTeamsConversationTargetId(channel);
|
|
127
|
+
return {
|
|
128
|
+
input,
|
|
129
|
+
resolved: true,
|
|
130
|
+
teamId,
|
|
131
|
+
teamName: teamId,
|
|
132
|
+
channelId,
|
|
133
|
+
channelName: channelId
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const token = await getToken();
|
|
137
|
+
const teams = /^[0-9a-fA-F-]{16,}$/.test(team) ? [{
|
|
138
|
+
id: team,
|
|
139
|
+
displayName: team
|
|
140
|
+
}] : await listTeamsByName(token, team);
|
|
141
|
+
if (teams.length === 0) return {
|
|
142
|
+
input,
|
|
143
|
+
resolved: false,
|
|
144
|
+
note: "team not found"
|
|
145
|
+
};
|
|
146
|
+
const teamMatch = teams[0];
|
|
147
|
+
const graphTeamId = teamMatch.id?.trim();
|
|
148
|
+
const teamName = teamMatch.displayName?.trim() || team;
|
|
149
|
+
if (!graphTeamId) return {
|
|
150
|
+
input,
|
|
151
|
+
resolved: false,
|
|
152
|
+
note: "team id missing"
|
|
153
|
+
};
|
|
154
|
+
let teamChannels = [];
|
|
155
|
+
try {
|
|
156
|
+
teamChannels = await listChannelsForTeam(token, graphTeamId);
|
|
157
|
+
} catch {}
|
|
158
|
+
const teamId = teamChannels.find((ch) => normalizeOptionalLowercaseString(ch.displayName) === "general")?.id?.trim() || graphTeamId;
|
|
159
|
+
if (!channel) return {
|
|
160
|
+
input,
|
|
161
|
+
resolved: true,
|
|
162
|
+
teamId,
|
|
163
|
+
teamName,
|
|
164
|
+
note: teams.length > 1 ? "multiple teams; chose first" : void 0
|
|
165
|
+
};
|
|
166
|
+
const normalizedChannel = normalizeOptionalLowercaseString(channel);
|
|
167
|
+
const channelMatch = teamChannels.find((item) => item.id === channel) ?? teamChannels.find((item) => normalizeOptionalLowercaseString(item.displayName) === normalizedChannel) ?? teamChannels.find((item) => normalizeLowercaseStringOrEmpty(item.displayName ?? "").includes(normalizedChannel ?? ""));
|
|
168
|
+
if (!channelMatch?.id) return {
|
|
169
|
+
input,
|
|
170
|
+
resolved: false,
|
|
171
|
+
note: "channel not found"
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
input,
|
|
175
|
+
resolved: true,
|
|
176
|
+
teamId,
|
|
177
|
+
teamName,
|
|
178
|
+
channelId: channelMatch.id,
|
|
179
|
+
channelName: channelMatch.displayName ?? channel,
|
|
180
|
+
note: teamChannels.length > 1 ? "multiple channels; chose first" : void 0
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
async function resolveMSTeamsUserAllowlist(params) {
|
|
186
|
+
const token = await resolveGraphToken(params.cfg);
|
|
187
|
+
return await mapAllowlistResolutionInputs({
|
|
188
|
+
inputs: params.entries,
|
|
189
|
+
mapInput: async (input) => {
|
|
190
|
+
const query = normalizeQuery(normalizeMSTeamsUserInput(input));
|
|
191
|
+
if (!query) return {
|
|
192
|
+
input,
|
|
193
|
+
resolved: false
|
|
194
|
+
};
|
|
195
|
+
if (/^[0-9a-fA-F-]{16,}$/.test(query)) return {
|
|
196
|
+
input,
|
|
197
|
+
resolved: true,
|
|
198
|
+
id: query
|
|
199
|
+
};
|
|
200
|
+
const users = await searchGraphUsers({
|
|
201
|
+
token,
|
|
202
|
+
query,
|
|
203
|
+
top: 10
|
|
204
|
+
});
|
|
205
|
+
const match = users[0];
|
|
206
|
+
if (!match?.id) return {
|
|
207
|
+
input,
|
|
208
|
+
resolved: false
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
input,
|
|
212
|
+
resolved: true,
|
|
213
|
+
id: match.id,
|
|
214
|
+
name: match.displayName ?? void 0,
|
|
215
|
+
note: users.length > 1 ? "multiple matches; chose first" : void 0
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
5
221
|
//#region extensions/msteams/src/setup-core.ts
|
|
6
222
|
const msteamsSetupAdapter = {
|
|
7
223
|
resolveAccountId: () => DEFAULT_ACCOUNT_ID,
|
|
@@ -271,7 +487,7 @@ const msteamsSetupWizard = {
|
|
|
271
487
|
}
|
|
272
488
|
};
|
|
273
489
|
try {
|
|
274
|
-
const { loginMSTeamsDelegated } = await import("./oauth-
|
|
490
|
+
const { loginMSTeamsDelegated } = await import("./oauth-DsVj42gA.js");
|
|
275
491
|
const progress = params.prompter.progress("MSTeams Delegated OAuth");
|
|
276
492
|
saveDelegatedTokens(await loginMSTeamsDelegated({
|
|
277
493
|
isRemote: true,
|
|
@@ -310,4 +526,4 @@ const msteamsSetupWizard = {
|
|
|
310
526
|
})
|
|
311
527
|
};
|
|
312
528
|
//#endregion
|
|
313
|
-
export { msteamsSetupAdapter as i, openDelegatedOAuthUrl as n, createMSTeamsSetupWizardBase as r, msteamsSetupWizard as t };
|
|
529
|
+
export { looksLikeMSTeamsTargetId as a, parseMSTeamsConversationId as c, resolveMSTeamsUserAllowlist as d, msteamsSetupAdapter as i, parseMSTeamsTeamChannelInput as l, openDelegatedOAuthUrl as n, normalizeMSTeamsMessagingTarget as o, createMSTeamsSetupWizardBase as r, normalizeMSTeamsUserInput as s, msteamsSetupWizard as t, resolveMSTeamsChannelAllowlist as u };
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { A as summarizeMapping, D as resolveDefaultGroupPolicy, E as resolveChannelMediaMaxBytes, M as getMSTeamsRuntime, N as getOptionalMSTeamsRuntime, _ as isDangerousNameMatchingEnabled, a as buildMediaPayload, b as logTypingFailure, c as createChannelMessageReplyPipeline, f as dispatchReplyFromConfigWithSettledDispatcher$1, l as createChannelPairingController, n as DEFAULT_WEBHOOK_MAX_BODY_BYTES, t as DEFAULT_ACCOUNT_ID, v as keepHttpServerTaskAlive, x as mergeAllowlist } from "./runtime-api-C3EIaIpt.js";
|
|
2
|
-
import { A as ATTACHMENT_TAG_RE, B as isLikelyImageAttachment, C as loadMSTeamsSdkWithAuth, D as formatMSTeamsSendErrorHint, E as classifyMSTeamsSendError, F as estimateBase64DecodedBytes, G as resolveAttachmentFetchPolicy, H as isUrlAllowed, I as extractHtmlFromAttachment, J as safeFetchWithPolicy, K as resolveMediaSsrfPolicy, L as extractInlineImageCandidates, M as IMG_SRC_RE, N as applyAuthorizationHeaderForUrl, O as formatUnknownError, P as encodeGraphShareId, R as inferPlaceholder, S as createMSTeamsTokenProvider, T as ensureUserAgentHeader, U as normalizeContentType, V as isRecord$1, W as readNestedString, X as tryBuildGraphSharesUrlForSharedLink, Y as safeHostForUrl, _ as resolveMSTeamsStorePath, a as fetchGraphJson, b as createBotFrameworkJwtValidator, h as resolveMSTeamsCredentials, j as GRAPH_ROOT, q as resolveRequestUrl, w as buildUserAgent, x as createMSTeamsAdapter, z as isDownloadableAttachment } from "./graph-users-
|
|
3
|
-
import {
|
|
4
|
-
import { i as resolveMSTeamsRouteConfig,
|
|
5
|
-
import { C as readJsonFile, S as createMSTeamsConversationStoreFs, T as writeJsonFile, _ as buildFileInfoCard, b as createMSTeamsPollStoreFs, c as renderReplyPayloadsToMessages, d as withRevokedProxyFallback, f as resolveGraphChatId, g as removePendingUploadFs, h as getPendingUploadFs, l as sendMSTeamsMessages, m as removePendingUpload, p as getPendingUpload, s as buildConversationReference, u as AI_GENERATED_ENTITY, v as parseFileConsentInvoke, w as withFileLock, x as extractMSTeamsPollVote, y as uploadToConsentUrl } from "./probe-
|
|
2
|
+
import { A as ATTACHMENT_TAG_RE, B as isLikelyImageAttachment, C as loadMSTeamsSdkWithAuth, D as formatMSTeamsSendErrorHint, E as classifyMSTeamsSendError, F as estimateBase64DecodedBytes, G as resolveAttachmentFetchPolicy, H as isUrlAllowed, I as extractHtmlFromAttachment, J as safeFetchWithPolicy, K as resolveMediaSsrfPolicy, L as extractInlineImageCandidates, M as IMG_SRC_RE, N as applyAuthorizationHeaderForUrl, O as formatUnknownError, P as encodeGraphShareId, R as inferPlaceholder, S as createMSTeamsTokenProvider, T as ensureUserAgentHeader, U as normalizeContentType, V as isRecord$1, W as readNestedString, X as tryBuildGraphSharesUrlForSharedLink, Y as safeHostForUrl, _ as resolveMSTeamsStorePath, a as fetchGraphJson, b as createBotFrameworkJwtValidator, h as resolveMSTeamsCredentials, j as GRAPH_ROOT, q as resolveRequestUrl, w as buildUserAgent, x as createMSTeamsAdapter, z as isDownloadableAttachment } from "./graph-users-ChPPxUzD.js";
|
|
3
|
+
import { d as resolveMSTeamsUserAllowlist, u as resolveMSTeamsChannelAllowlist } from "./setup-surface-CQrMX-nJ.js";
|
|
4
|
+
import { i as resolveMSTeamsRouteConfig, n as resolveMSTeamsAllowlistMatch, r as resolveMSTeamsReplyPolicy } from "./channel-C5CVTygn.js";
|
|
5
|
+
import { C as readJsonFile, S as createMSTeamsConversationStoreFs, T as writeJsonFile, _ as buildFileInfoCard, b as createMSTeamsPollStoreFs, c as renderReplyPayloadsToMessages, d as withRevokedProxyFallback, f as resolveGraphChatId, g as removePendingUploadFs, h as getPendingUploadFs, l as sendMSTeamsMessages, m as removePendingUpload, p as getPendingUpload, s as buildConversationReference, u as AI_GENERATED_ENTITY, v as parseFileConsentInvoke, w as withFileLock, x as extractMSTeamsPollVote, y as uploadToConsentUrl } from "./probe-I2DM0U-s.js";
|
|
6
6
|
import { formatAllowlistMatchMeta } from "openclaw/plugin-sdk/allow-from";
|
|
7
7
|
import { createLiveMessageState, createPreviewMessageReceipt, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, markLiveMessageFinalized } from "openclaw/plugin-sdk/channel-message";
|
|
8
8
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
9
|
import { createDraftStreamLoop } from "openclaw/plugin-sdk/channel-lifecycle";
|
|
10
|
-
import {
|
|
10
|
+
import { saveResponseMedia } from "openclaw/plugin-sdk/media-runtime";
|
|
11
11
|
import { dispatchReplyFromConfigWithSettledDispatcher, hasFinalInboundReplyDispatch, resolveInboundReplyDispatchCounts } from "openclaw/plugin-sdk/inbound-reply-dispatch";
|
|
12
12
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
13
|
-
import { Buffer as Buffer$1 } from "node:buffer";
|
|
14
13
|
import path from "node:path";
|
|
15
14
|
import { appendRegularFile } from "openclaw/plugin-sdk/security-runtime";
|
|
16
15
|
import { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
|
|
@@ -20,7 +19,7 @@ import { channelIngressRoutes, resolveStableChannelMessageIngress } from "opencl
|
|
|
20
19
|
import { logInboundDrop, resolveInboundMentionDecision, resolveInboundSessionEnvelopeContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
21
20
|
import { filterSupplementalContextItems, resolveChannelContextVisibilityMode, shouldIncludeSupplementalContext } from "openclaw/plugin-sdk/context-visibility-runtime";
|
|
22
21
|
import { DEFAULT_GROUP_HISTORY_LIMIT, buildPendingHistoryContextFromMap, recordPendingHistoryEntryIfEnabled } from "openclaw/plugin-sdk/reply-history";
|
|
23
|
-
import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, resolveChannelPreviewStreamMode, resolveChannelProgressDraftMaxLines, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress } from "openclaw/plugin-sdk/channel-streaming";
|
|
22
|
+
import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, normalizeChannelProgressDraftLineIdentity, resolveChannelPreviewStreamMode, resolveChannelProgressDraftMaxLines, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress } from "openclaw/plugin-sdk/channel-streaming";
|
|
24
23
|
//#region extensions/msteams/src/feedback-reflection-prompt.ts
|
|
25
24
|
/** Max chars of the thumbed-down response to include in the reflection prompt. */
|
|
26
25
|
const MAX_RESPONSE_CHARS = 500;
|
|
@@ -624,7 +623,7 @@ async function fetchBotFrameworkAttachmentInfo(params) {
|
|
|
624
623
|
return;
|
|
625
624
|
}
|
|
626
625
|
}
|
|
627
|
-
async function
|
|
626
|
+
async function saveBotFrameworkAttachmentView(params) {
|
|
628
627
|
const url = `${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}/views/${encodeURIComponent(params.viewId)}`;
|
|
629
628
|
let response;
|
|
630
629
|
try {
|
|
@@ -646,12 +645,16 @@ async function fetchBotFrameworkAttachmentView(params) {
|
|
|
646
645
|
const contentLength = response.headers.get("content-length");
|
|
647
646
|
if (contentLength && Number(contentLength) > params.maxBytes) return;
|
|
648
647
|
try {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
648
|
+
return await getMSTeamsRuntime().channel.media.saveResponseMedia(response, {
|
|
649
|
+
sourceUrl: url,
|
|
650
|
+
filePathHint: params.fileNameHint,
|
|
651
|
+
maxBytes: params.maxBytes,
|
|
652
|
+
fallbackContentType: params.contentTypeHint,
|
|
653
|
+
subdir: "inbound",
|
|
654
|
+
originalFilename: params.preserveFilenames ? params.fileNameHint : void 0
|
|
655
|
+
});
|
|
653
656
|
} catch (err) {
|
|
654
|
-
params.logger?.warn?.("msteams botFramework attachmentView
|
|
657
|
+
params.logger?.warn?.("msteams botFramework attachmentView save failed", { error: err instanceof Error ? err.message : String(err) });
|
|
655
658
|
return;
|
|
656
659
|
}
|
|
657
660
|
}
|
|
@@ -691,40 +694,31 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
|
691
694
|
const viewId = typeof candidateView?.viewId === "string" && candidateView.viewId ? candidateView.viewId : void 0;
|
|
692
695
|
if (!viewId) return;
|
|
693
696
|
if (typeof candidateView?.size === "number" && candidateView.size > 0 && candidateView.size > params.maxBytes) return;
|
|
694
|
-
const
|
|
697
|
+
const fileNameHint = typeof params.fileNameHint === "string" && params.fileNameHint || typeof info.name === "string" && info.name || void 0;
|
|
698
|
+
const contentTypeHint = typeof params.contentTypeHint === "string" && params.contentTypeHint || typeof info.type === "string" && info.type || void 0;
|
|
699
|
+
const saved = await saveBotFrameworkAttachmentView({
|
|
695
700
|
serviceUrl: params.serviceUrl,
|
|
696
701
|
attachmentId: params.attachmentId,
|
|
697
702
|
viewId,
|
|
698
703
|
accessToken,
|
|
699
704
|
maxBytes: params.maxBytes,
|
|
705
|
+
fileNameHint,
|
|
706
|
+
contentTypeHint,
|
|
707
|
+
preserveFilenames: params.preserveFilenames,
|
|
700
708
|
policy,
|
|
701
709
|
fetchFn: params.fetchFn,
|
|
702
710
|
resolveFn: params.resolveFn,
|
|
703
711
|
logger: params.logger
|
|
704
712
|
});
|
|
705
|
-
if (!
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
headerMime: contentTypeHint,
|
|
711
|
-
filePath: fileNameHint
|
|
712
|
-
});
|
|
713
|
-
try {
|
|
714
|
-
const originalFilename = params.preserveFilenames ? fileNameHint : void 0;
|
|
715
|
-
const saved = await getMSTeamsRuntime().channel.media.saveMediaBuffer(buffer, mime ?? contentTypeHint, "inbound", params.maxBytes, originalFilename);
|
|
716
|
-
return {
|
|
717
|
-
path: saved.path,
|
|
713
|
+
if (!saved) return;
|
|
714
|
+
return {
|
|
715
|
+
path: saved.path,
|
|
716
|
+
contentType: saved.contentType,
|
|
717
|
+
placeholder: inferPlaceholder({
|
|
718
718
|
contentType: saved.contentType,
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
})
|
|
723
|
-
};
|
|
724
|
-
} catch (err) {
|
|
725
|
-
params.logger?.warn?.("msteams botFramework save failed", { error: err instanceof Error ? err.message : String(err) });
|
|
726
|
-
return;
|
|
727
|
-
}
|
|
719
|
+
fileName: fileNameHint
|
|
720
|
+
})
|
|
721
|
+
};
|
|
728
722
|
}
|
|
729
723
|
/**
|
|
730
724
|
* Download media for every attachment referenced by a Bot Framework personal
|
|
@@ -780,8 +774,8 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
|
|
|
780
774
|
* Direct fetch path used when the caller's `fetchImpl` has already validated
|
|
781
775
|
* the URL against a hostname allowlist (for example `safeFetchWithPolicy`).
|
|
782
776
|
*
|
|
783
|
-
* Bypasses the strict SSRF dispatcher on `
|
|
784
|
-
* 1. The pinned undici dispatcher used by `
|
|
777
|
+
* Bypasses the strict SSRF dispatcher on `readRemoteMediaBuffer` because:
|
|
778
|
+
* 1. The pinned undici dispatcher used by `readRemoteMediaBuffer` is incompatible
|
|
785
779
|
* with Node 24+'s built-in undici v7 (fails with "invalid onRequestStart
|
|
786
780
|
* method"), which silently breaks SharePoint/OneDrive downloads. See
|
|
787
781
|
* issue #63396.
|
|
@@ -789,43 +783,35 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
|
|
|
789
783
|
* (`safeFetch` validates every redirect hop against the hostname
|
|
790
784
|
* allowlist before following).
|
|
791
785
|
*/
|
|
792
|
-
async function
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
const length = Number(contentLength);
|
|
801
|
-
if (Number.isFinite(length) && length > params.maxBytes) throw new Error(`content length ${length} exceeds maxBytes ${params.maxBytes}`);
|
|
802
|
-
}
|
|
803
|
-
return {
|
|
804
|
-
buffer: await readResponseWithLimit(response, params.maxBytes, { onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`payload size ${size} exceeds maxBytes ${maxBytes}`) }),
|
|
805
|
-
contentType: response.headers.get("content-type") ?? void 0
|
|
806
|
-
};
|
|
786
|
+
async function saveRemoteMediaDirect(params) {
|
|
787
|
+
return await saveResponseMedia(await params.fetchImpl(params.url, { redirect: "follow" }), {
|
|
788
|
+
sourceUrl: params.url,
|
|
789
|
+
filePathHint: params.filePathHint,
|
|
790
|
+
maxBytes: params.maxBytes,
|
|
791
|
+
fallbackContentType: params.contentTypeHint,
|
|
792
|
+
originalFilename: params.originalFilename
|
|
793
|
+
});
|
|
807
794
|
}
|
|
808
795
|
async function downloadAndStoreMSTeamsRemoteMedia(params) {
|
|
809
|
-
|
|
810
|
-
|
|
796
|
+
const originalFilename = params.preserveFilenames ? params.filePathHint : void 0;
|
|
797
|
+
let saved;
|
|
798
|
+
if (params.useDirectFetch && params.fetchImpl) saved = await saveRemoteMediaDirect({
|
|
811
799
|
url: params.url,
|
|
800
|
+
filePathHint: params.filePathHint,
|
|
812
801
|
fetchImpl: params.fetchImpl,
|
|
813
|
-
maxBytes: params.maxBytes
|
|
802
|
+
maxBytes: params.maxBytes,
|
|
803
|
+
contentTypeHint: params.contentTypeHint,
|
|
804
|
+
originalFilename
|
|
814
805
|
});
|
|
815
|
-
else
|
|
806
|
+
else saved = await getMSTeamsRuntime().channel.media.saveRemoteMedia({
|
|
816
807
|
url: params.url,
|
|
817
808
|
fetchImpl: params.fetchImpl,
|
|
818
809
|
filePathHint: params.filePathHint,
|
|
819
810
|
maxBytes: params.maxBytes,
|
|
820
|
-
ssrfPolicy: params.ssrfPolicy
|
|
811
|
+
ssrfPolicy: params.ssrfPolicy,
|
|
812
|
+
fallbackContentType: params.contentTypeHint,
|
|
813
|
+
originalFilename
|
|
821
814
|
});
|
|
822
|
-
const mime = await getMSTeamsRuntime().media.detectMime({
|
|
823
|
-
buffer: fetched.buffer,
|
|
824
|
-
headerMime: fetched.contentType ?? params.contentTypeHint,
|
|
825
|
-
filePath: params.filePathHint
|
|
826
|
-
});
|
|
827
|
-
const originalFilename = params.preserveFilenames ? params.filePathHint : void 0;
|
|
828
|
-
const saved = await getMSTeamsRuntime().channel.media.saveMediaBuffer(fetched.buffer, mime ?? params.contentTypeHint, "inbound", params.maxBytes, originalFilename);
|
|
829
815
|
return {
|
|
830
816
|
path: saved.path,
|
|
831
817
|
contentType: saved.contentType,
|
|
@@ -1106,28 +1092,38 @@ async function downloadGraphHostedContent(params) {
|
|
|
1106
1092
|
params.logger?.warn?.("msteams graph hostedContent base64 decode failed", { error: err instanceof Error ? err.message : String(err) });
|
|
1107
1093
|
continue;
|
|
1108
1094
|
}
|
|
1109
|
-
} else if (item.id)
|
|
1110
|
-
const { response: valRes, release } = await fetchWithSsrFGuard({
|
|
1111
|
-
url: `${params.messageUrl}/hostedContents/${encodeURIComponent(item.id)}/$value`,
|
|
1112
|
-
fetchImpl: params.fetchFn ?? fetch,
|
|
1113
|
-
init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
|
|
1114
|
-
policy: params.ssrfPolicy,
|
|
1115
|
-
auditContext: "msteams.graph.hostedContent.value"
|
|
1116
|
-
});
|
|
1095
|
+
} else if (item.id) {
|
|
1117
1096
|
try {
|
|
1118
|
-
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1097
|
+
const valueUrl = `${params.messageUrl}/hostedContents/${encodeURIComponent(item.id)}/$value`;
|
|
1098
|
+
const { response: valRes, release } = await fetchWithSsrFGuard({
|
|
1099
|
+
url: valueUrl,
|
|
1100
|
+
fetchImpl: params.fetchFn ?? fetch,
|
|
1101
|
+
init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
|
|
1102
|
+
policy: params.ssrfPolicy,
|
|
1103
|
+
auditContext: "msteams.graph.hostedContent.value"
|
|
1104
|
+
});
|
|
1105
|
+
try {
|
|
1106
|
+
if (!valRes.ok) continue;
|
|
1107
|
+
const saved = await getMSTeamsRuntime().channel.media.saveResponseMedia(valRes, {
|
|
1108
|
+
sourceUrl: valueUrl,
|
|
1109
|
+
maxBytes: params.maxBytes,
|
|
1110
|
+
fallbackContentType: item.contentType ?? void 0,
|
|
1111
|
+
subdir: "inbound"
|
|
1112
|
+
});
|
|
1113
|
+
out.push({
|
|
1114
|
+
path: saved.path,
|
|
1115
|
+
contentType: saved.contentType,
|
|
1116
|
+
placeholder: inferPlaceholder({ contentType: saved.contentType })
|
|
1117
|
+
});
|
|
1118
|
+
} finally {
|
|
1119
|
+
await release();
|
|
1120
|
+
}
|
|
1121
|
+
} catch (err) {
|
|
1122
|
+
params.logger?.warn?.("msteams graph hostedContent value fetch failed", { error: err instanceof Error ? err.message : String(err) });
|
|
1123
|
+
continue;
|
|
1125
1124
|
}
|
|
1126
|
-
} catch (err) {
|
|
1127
|
-
params.logger?.warn?.("msteams graph hostedContent value fetch failed", { error: err instanceof Error ? err.message : String(err) });
|
|
1128
1125
|
continue;
|
|
1129
|
-
}
|
|
1130
|
-
else continue;
|
|
1126
|
+
} else continue;
|
|
1131
1127
|
if (buffer.byteLength > params.maxBytes) continue;
|
|
1132
1128
|
const mime = await getMSTeamsRuntime().media.detectMime({
|
|
1133
1129
|
buffer,
|
|
@@ -1824,13 +1820,8 @@ function createTeamsReplyStreamController(params) {
|
|
|
1824
1820
|
if (!stream || streamMode !== "progress") return;
|
|
1825
1821
|
if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
|
|
1826
1822
|
if (shouldStreamPreviewToolProgress) {
|
|
1827
|
-
const normalized =
|
|
1828
|
-
if (normalized) {
|
|
1829
|
-
if (normalizeProgressLineIdentity(progressLines.at(-1)) !== normalized) {
|
|
1830
|
-
const progressLine = typeof line === "object" && line !== void 0 ? line : normalized;
|
|
1831
|
-
progressLines = [...progressLines, progressLine].slice(-resolveChannelProgressDraftMaxLines(params.msteamsConfig));
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1823
|
+
const normalized = normalizeChannelProgressDraftLineIdentity(line);
|
|
1824
|
+
if (normalized) progressLines = mergeChannelProgressDraftLine(progressLines, typeof line === "object" && line !== void 0 ? line : normalized, { maxLines: resolveChannelProgressDraftMaxLines(params.msteamsConfig) });
|
|
1834
1825
|
}
|
|
1835
1826
|
await noteProgressWork();
|
|
1836
1827
|
};
|
|
@@ -1961,9 +1952,6 @@ function createTeamsReplyStreamController(params) {
|
|
|
1961
1952
|
}
|
|
1962
1953
|
};
|
|
1963
1954
|
}
|
|
1964
|
-
function normalizeProgressLineIdentity(line) {
|
|
1965
|
-
return (typeof line === "string" ? line : line?.text)?.replace(/\s+/g, " ").trim() ?? "";
|
|
1966
|
-
}
|
|
1967
1955
|
//#endregion
|
|
1968
1956
|
//#region extensions/msteams/src/reply-dispatcher.ts
|
|
1969
1957
|
function createMSTeamsReplyDispatcher(params) {
|
|
@@ -2214,6 +2202,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2214
2202
|
onItemEvent: async (payload) => {
|
|
2215
2203
|
await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
|
|
2216
2204
|
event: "item",
|
|
2205
|
+
itemId: payload.itemId,
|
|
2217
2206
|
itemKind: payload.kind,
|
|
2218
2207
|
title: payload.title,
|
|
2219
2208
|
name: payload.name,
|
package/dist/test-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as msteamsPlugin } from "./channel-
|
|
1
|
+
import { t as msteamsPlugin } from "./channel-C5CVTygn.js";
|
|
2
2
|
export { msteamsPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/msteams",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.14-beta.2",
|
|
4
4
|
"description": "OpenClaw Microsoft Teams channel plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"type": "module",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@azure/identity": "4.13.1",
|
|
12
|
-
"@microsoft/teams.api": "2.0.
|
|
13
|
-
"@microsoft/teams.apps": "2.0.
|
|
12
|
+
"@microsoft/teams.api": "2.0.11",
|
|
13
|
+
"@microsoft/teams.apps": "2.0.11",
|
|
14
14
|
"express": "5.2.1",
|
|
15
15
|
"jsonwebtoken": "9.0.3",
|
|
16
16
|
"jwks-rsa": "4.0.1",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"openclaw": "workspace:*"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"openclaw": ">=2026.5.
|
|
25
|
+
"openclaw": ">=2026.5.14-beta.2"
|
|
26
26
|
},
|
|
27
27
|
"peerDependenciesMeta": {
|
|
28
28
|
"openclaw": {
|
|
@@ -58,10 +58,10 @@
|
|
|
58
58
|
"minHostVersion": ">=2026.4.10"
|
|
59
59
|
},
|
|
60
60
|
"compat": {
|
|
61
|
-
"pluginApi": ">=2026.5.
|
|
61
|
+
"pluginApi": ">=2026.5.14-beta.2"
|
|
62
62
|
},
|
|
63
63
|
"build": {
|
|
64
|
-
"openclawVersion": "2026.5.
|
|
64
|
+
"openclawVersion": "2026.5.14-beta.2"
|
|
65
65
|
},
|
|
66
66
|
"release": {
|
|
67
67
|
"publishToClawHub": true,
|