@openclaw/msteams 2026.8.1-beta.3 → 2026.8.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/api.cjs +2 -2
- package/dist/approval-card-B6ZIMO83.cjs +303 -0
- package/dist/approval-handler.runtime-B-ZA6Vjp.cjs +107 -0
- package/dist/{channel-KpWENcgK.cjs → channel-ChmCvkuA.cjs} +157 -46
- package/dist/channel-config-api.cjs +1 -1
- package/dist/channel-plugin-api.cjs +1 -1
- package/dist/{channel.runtime-Bkzj8jZ0.cjs → channel.runtime-D3qqruU3.cjs} +17 -590
- package/dist/{channel.setup-mOFtsfYz.cjs → channel.setup-CKb8aKCV.cjs} +11 -4
- package/dist/{config-schema-C4CszdDi.cjs → config-schema-MyN2lamA.cjs} +12 -11
- package/dist/configured-state.cjs +20 -0
- package/dist/directory-contract-api.cjs +1 -1
- package/dist/doctor-contract-api.cjs +76 -39
- package/dist/{messenger-IISPSOvj.cjs → messenger-Ctb9_CO7.cjs} +10 -158
- package/dist/{oauth-C9So7uld.cjs → oauth-BV7HWGe0.cjs} +1 -1
- package/dist/reaction-types-DtWOWuDN.cjs +149 -0
- package/dist/{resolve-allowlist-CIfWUwuc.cjs → resolve-allowlist-CWbvAW4v.cjs} +21 -47
- package/dist/runtime-api.cjs +2 -1
- package/dist/secret-input-C65t6kjM.cjs +1 -0
- package/dist/send-Bym7IH-v.cjs +627 -0
- package/dist/setup-plugin-api.cjs +1 -1
- package/dist/{src-C127-DcQ.cjs → src-BGhvpEOP.cjs} +146 -18
- package/dist/thread-session-Br3R0ovy.cjs +63 -0
- package/package.json +13 -11
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const require_resolve_allowlist = require("./resolve-allowlist-CWbvAW4v.cjs");
|
|
2
|
+
let openclaw_plugin_sdk_html_entity_runtime = require("openclaw/plugin-sdk/html-entity-runtime");
|
|
3
|
+
//#region extensions/msteams/src/graph-thread.ts
|
|
4
|
+
/**
|
|
5
|
+
* Strip HTML tags from Teams message content, preserving @mention display names.
|
|
6
|
+
* Teams wraps mentions in <at>Name</at> tags.
|
|
7
|
+
*/
|
|
8
|
+
function stripHtmlFromTeamsMessage(html) {
|
|
9
|
+
let text = html.replace(/<at[^>]*>(.*?)<\/at>/gi, "@$1");
|
|
10
|
+
text = text.replace(/<[^>]*>/g, " ");
|
|
11
|
+
text = (0, openclaw_plugin_sdk_html_entity_runtime.decodeHtmlEntities)(text).replaceAll("\xA0", " ");
|
|
12
|
+
return text.replace(/\s+/g, " ").trim();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Fetch a single channel message (the parent/root of a thread).
|
|
16
|
+
* Returns undefined on error so callers can degrade gracefully.
|
|
17
|
+
*/
|
|
18
|
+
async function fetchChannelMessage(token, groupId, channelId, messageId, deadline) {
|
|
19
|
+
const path = `/teams/${encodeURIComponent(groupId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(messageId)}`;
|
|
20
|
+
try {
|
|
21
|
+
return await require_resolve_allowlist.fetchGraphJson({
|
|
22
|
+
token,
|
|
23
|
+
path,
|
|
24
|
+
...deadline ? { deadline } : {}
|
|
25
|
+
});
|
|
26
|
+
} catch {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fetch a single chat message's full text via Graph and return plain text.
|
|
32
|
+
*
|
|
33
|
+
* Used to recover the complete quoted message for Teams quote replies: the
|
|
34
|
+
* inbound blockquote only carries a Teams-truncated `preview` snippet. The
|
|
35
|
+
* app-only `GET /chats/{chatId}/messages/{messageId}` endpoint IS permitted
|
|
36
|
+
* with the `Chat.Read.All` application permission.
|
|
37
|
+
*
|
|
38
|
+
* Returns undefined on any failure so callers degrade to the truncated preview.
|
|
39
|
+
*/
|
|
40
|
+
async function fetchChatMessageText(token, chatId, messageId, deadline) {
|
|
41
|
+
const path = `/chats/${encodeURIComponent(chatId)}/messages/${encodeURIComponent(messageId)}`;
|
|
42
|
+
try {
|
|
43
|
+
const msg = await require_resolve_allowlist.fetchGraphJson({
|
|
44
|
+
token,
|
|
45
|
+
path,
|
|
46
|
+
...deadline ? { deadline } : {}
|
|
47
|
+
});
|
|
48
|
+
const raw = msg.body?.content ?? "";
|
|
49
|
+
return (msg.body?.contentType === "html" ? stripHtmlFromTeamsMessage(raw) : raw.trim()) || void 0;
|
|
50
|
+
} catch {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Fetch thread replies for a channel message, ordered chronologically.
|
|
56
|
+
*
|
|
57
|
+
* **Limitation:** The Graph API replies endpoint (`/messages/{id}/replies`) does not
|
|
58
|
+
* support `$orderby`, so results are always returned in ascending (oldest-first) order.
|
|
59
|
+
* Combined with the `$top` cap of 50, this means only the **oldest 50 replies** are
|
|
60
|
+
* returned for long threads — newer replies are silently omitted. There is currently no
|
|
61
|
+
* Graph API workaround for this; pagination via `@odata.nextLink` can retrieve more
|
|
62
|
+
* replies but still in ascending order only.
|
|
63
|
+
*/
|
|
64
|
+
async function fetchThreadReplies(token, groupId, channelId, messageId, limit = 50, deadline) {
|
|
65
|
+
return (await require_resolve_allowlist.fetchGraphJson({
|
|
66
|
+
token,
|
|
67
|
+
path: `/teams/${encodeURIComponent(groupId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(messageId)}/replies?$top=${Math.min(Math.max(limit, 1), 50)}`,
|
|
68
|
+
...deadline ? { deadline } : {}
|
|
69
|
+
})).value ?? [];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Format thread messages into a context string for the agent.
|
|
73
|
+
* Skips the current message (by id) and blank messages.
|
|
74
|
+
*/
|
|
75
|
+
function formatThreadContext(messages, currentMessageId) {
|
|
76
|
+
const lines = [];
|
|
77
|
+
for (const msg of messages) {
|
|
78
|
+
if (msg.id && msg.id === currentMessageId) continue;
|
|
79
|
+
const sender = msg.from?.user?.displayName ?? msg.from?.application?.displayName ?? "unknown";
|
|
80
|
+
const contentType = msg.body?.contentType ?? "text";
|
|
81
|
+
const rawContent = msg.body?.content ?? "";
|
|
82
|
+
const content = contentType === "html" ? stripHtmlFromTeamsMessage(rawContent) : rawContent.trim();
|
|
83
|
+
if (!content) continue;
|
|
84
|
+
lines.push(`${sender}: ${content}`);
|
|
85
|
+
}
|
|
86
|
+
return lines.join("\n");
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region extensions/msteams/src/reaction-types.ts
|
|
90
|
+
const TEAMS_REACTION_EMOJI = {
|
|
91
|
+
like: "👍",
|
|
92
|
+
heart: "❤️",
|
|
93
|
+
laugh: "😆",
|
|
94
|
+
surprised: "😮",
|
|
95
|
+
sad: "😢",
|
|
96
|
+
angry: "😡"
|
|
97
|
+
};
|
|
98
|
+
const TEAMS_REACTION_TYPES = Object.keys(TEAMS_REACTION_EMOJI);
|
|
99
|
+
function getMSTeamsReactionEmoji(raw) {
|
|
100
|
+
return TEAMS_REACTION_EMOJI[raw.trim().toLowerCase()];
|
|
101
|
+
}
|
|
102
|
+
function resolveMSTeamsReactionEmoji(raw) {
|
|
103
|
+
const normalized = raw.trim();
|
|
104
|
+
if (!normalized) throw new Error(`Reaction type is required. Common types: ${TEAMS_REACTION_TYPES.join(", ")}`);
|
|
105
|
+
return getMSTeamsReactionEmoji(normalized) ?? normalized;
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
Object.defineProperty(exports, "fetchChannelMessage", {
|
|
109
|
+
enumerable: true,
|
|
110
|
+
get: function() {
|
|
111
|
+
return fetchChannelMessage;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
Object.defineProperty(exports, "fetchChatMessageText", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
get: function() {
|
|
117
|
+
return fetchChatMessageText;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
Object.defineProperty(exports, "fetchThreadReplies", {
|
|
121
|
+
enumerable: true,
|
|
122
|
+
get: function() {
|
|
123
|
+
return fetchThreadReplies;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
Object.defineProperty(exports, "formatThreadContext", {
|
|
127
|
+
enumerable: true,
|
|
128
|
+
get: function() {
|
|
129
|
+
return formatThreadContext;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
Object.defineProperty(exports, "getMSTeamsReactionEmoji", {
|
|
133
|
+
enumerable: true,
|
|
134
|
+
get: function() {
|
|
135
|
+
return getMSTeamsReactionEmoji;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
Object.defineProperty(exports, "resolveMSTeamsReactionEmoji", {
|
|
139
|
+
enumerable: true,
|
|
140
|
+
get: function() {
|
|
141
|
+
return resolveMSTeamsReactionEmoji;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
Object.defineProperty(exports, "stripHtmlFromTeamsMessage", {
|
|
145
|
+
enumerable: true,
|
|
146
|
+
get: function() {
|
|
147
|
+
return stripHtmlFromTeamsMessage;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const require_runtime = require("./runtime-gZTAuFux.cjs");
|
|
2
2
|
require("./runtime-api.cjs");
|
|
3
3
|
const require_delegated_state = require("./delegated-state-DiEz9JIa.cjs");
|
|
4
|
+
require("./secret-input-C65t6kjM.cjs");
|
|
4
5
|
let openclaw_plugin_sdk_lazy_runtime = require("openclaw/plugin-sdk/lazy-runtime");
|
|
5
6
|
let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
|
|
6
7
|
let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
|
|
@@ -14,8 +15,8 @@ let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-run
|
|
|
14
15
|
let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
|
|
15
16
|
let openclaw_plugin_sdk_secret_file = require("openclaw/plugin-sdk/secret-file");
|
|
16
17
|
let node_module = require("node:module");
|
|
17
|
-
let openclaw_plugin_sdk_secret_input = require("openclaw/plugin-sdk/secret-input");
|
|
18
18
|
let openclaw_plugin_sdk_html_entity_runtime = require("openclaw/plugin-sdk/html-entity-runtime");
|
|
19
|
+
let openclaw_plugin_sdk_secret_input = require("openclaw/plugin-sdk/secret-input");
|
|
19
20
|
//#region extensions/msteams/src/request-timeout.ts
|
|
20
21
|
const MSTEAMS_REQUEST_TIMEOUT_MS = 3e4;
|
|
21
22
|
const MSTEAMS_SHAREPOINT_UPLOAD_BASE_TIMEOUT_MS = 5 * 6e4;
|
|
@@ -110,25 +111,6 @@ const DEFAULT_MEDIA_AUTH_HOST_ALLOWLIST = [
|
|
|
110
111
|
"graph.microsoft.cn"
|
|
111
112
|
];
|
|
112
113
|
const GRAPH_ROOT = "https://graph.microsoft.com/v1.0";
|
|
113
|
-
function estimateBase64DecodedBytes(base64) {
|
|
114
|
-
let effectiveLen = 0;
|
|
115
|
-
for (let i = 0; i < base64.length; i += 1) {
|
|
116
|
-
if (base64.charCodeAt(i) <= 32) continue;
|
|
117
|
-
effectiveLen += 1;
|
|
118
|
-
}
|
|
119
|
-
if (effectiveLen === 0) return 0;
|
|
120
|
-
let padding = 0;
|
|
121
|
-
let end = base64.length - 1;
|
|
122
|
-
while (end >= 0 && base64.charCodeAt(end) <= 32) end -= 1;
|
|
123
|
-
if (end >= 0 && base64[end] === "=") {
|
|
124
|
-
padding = 1;
|
|
125
|
-
end -= 1;
|
|
126
|
-
while (end >= 0 && base64.charCodeAt(end) <= 32) end -= 1;
|
|
127
|
-
if (end >= 0 && base64[end] === "=") padding = 2;
|
|
128
|
-
}
|
|
129
|
-
const estimated = Math.floor(effectiveLen * 3 / 4) - padding;
|
|
130
|
-
return Math.max(0, estimated);
|
|
131
|
-
}
|
|
132
114
|
/**
|
|
133
115
|
* Host suffixes for SharePoint/OneDrive shared links that must be fetched via
|
|
134
116
|
* the Graph `/shares/{shareId}/driveItem/content` endpoint instead of directly.
|
|
@@ -275,7 +257,7 @@ function decodeDataImageWithLimits(src, opts) {
|
|
|
275
257
|
candidate: null,
|
|
276
258
|
estimatedBytes: 0
|
|
277
259
|
};
|
|
278
|
-
const estimatedBytes =
|
|
260
|
+
const estimatedBytes = node_buffer.Buffer.byteLength(canonicalPayload, "base64");
|
|
279
261
|
if (estimatedBytes <= 0) return {
|
|
280
262
|
candidate: null,
|
|
281
263
|
estimatedBytes: 0
|
|
@@ -448,7 +430,7 @@ async function safeFetch(params) {
|
|
|
448
430
|
const resolveFn = params.resolveFn ?? node_dns_promises.lookup;
|
|
449
431
|
const hasDispatcher = Boolean(params.requestInit && typeof params.requestInit === "object" && "dispatcher" in params.requestInit);
|
|
450
432
|
const currentHeaders = new Headers(params.requestInit?.headers);
|
|
451
|
-
|
|
433
|
+
const currentUrl = params.url;
|
|
452
434
|
if (!isUrlAllowed(currentUrl, params.allowHosts)) throw new Error(`Initial download URL blocked: ${currentUrl}`);
|
|
453
435
|
if (currentHeaders.has("authorization") && params.authorizationAllowHosts && !isUrlAllowed(currentUrl, params.authorizationAllowHosts)) currentHeaders.delete("authorization");
|
|
454
436
|
if (!hasDispatcher) {
|
|
@@ -479,37 +461,29 @@ async function safeFetch(params) {
|
|
|
479
461
|
});
|
|
480
462
|
return (0, openclaw_plugin_sdk_fetch_runtime.responseWithRelease)(guarded.response, guarded.release);
|
|
481
463
|
}
|
|
482
|
-
|
|
464
|
+
try {
|
|
483
465
|
const initialHost = new URL(currentUrl).hostname;
|
|
484
466
|
await resolveAndValidateIP(initialHost, resolveFn);
|
|
485
467
|
} catch {
|
|
486
468
|
throw new Error(`Initial download URL blocked: ${currentUrl}`);
|
|
487
469
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
throw new Error(`Invalid redirect URL: ${location}`);
|
|
502
|
-
}
|
|
503
|
-
if (!isUrlAllowed(redirectUrl, params.allowHosts)) throw new Error(`Media redirect target blocked by allowlist: ${redirectUrl}`);
|
|
504
|
-
if (currentHeaders.has("authorization") && params.authorizationAllowHosts && !isUrlAllowed(redirectUrl, params.authorizationAllowHosts)) currentHeaders.delete("authorization");
|
|
505
|
-
if (hasDispatcher) return res;
|
|
506
|
-
if (resolveFn) {
|
|
507
|
-
const redirectHost = new URL(redirectUrl).hostname;
|
|
508
|
-
await resolveAndValidateIP(redirectHost, resolveFn);
|
|
509
|
-
}
|
|
510
|
-
currentUrl = redirectUrl;
|
|
470
|
+
const res = await (params.fetchFn ?? fetch)(currentUrl, {
|
|
471
|
+
...params.requestInit,
|
|
472
|
+
headers: currentHeaders,
|
|
473
|
+
redirect: "manual"
|
|
474
|
+
});
|
|
475
|
+
if (!isRedirectStatus(res.status)) return res;
|
|
476
|
+
const location = res.headers.get("location");
|
|
477
|
+
if (!location) return res;
|
|
478
|
+
let redirectUrl;
|
|
479
|
+
try {
|
|
480
|
+
redirectUrl = new URL(location, currentUrl).toString();
|
|
481
|
+
} catch {
|
|
482
|
+
throw new Error(`Invalid redirect URL: ${location}`);
|
|
511
483
|
}
|
|
512
|
-
throw new Error(`
|
|
484
|
+
if (!isUrlAllowed(redirectUrl, params.allowHosts)) throw new Error(`Media redirect target blocked by allowlist: ${redirectUrl}`);
|
|
485
|
+
if (currentHeaders.has("authorization") && params.authorizationAllowHosts && !isUrlAllowed(redirectUrl, params.authorizationAllowHosts)) currentHeaders.delete("authorization");
|
|
486
|
+
return res;
|
|
513
487
|
}
|
|
514
488
|
async function safeFetchWithPolicy(params) {
|
|
515
489
|
return await safeFetch({
|
package/dist/runtime-api.cjs
CHANGED
|
@@ -11,6 +11,7 @@ let openclaw_plugin_sdk_dangerous_name_runtime = require("openclaw/plugin-sdk/da
|
|
|
11
11
|
let openclaw_plugin_sdk_runtime_group_policy = require("openclaw/plugin-sdk/runtime-group-policy");
|
|
12
12
|
let openclaw_plugin_sdk_file_lock = require("openclaw/plugin-sdk/file-lock");
|
|
13
13
|
let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runtime");
|
|
14
|
+
let openclaw_plugin_sdk_account_helpers = require("openclaw/plugin-sdk/account-helpers");
|
|
14
15
|
let openclaw_plugin_sdk_outbound_media = require("openclaw/plugin-sdk/outbound-media");
|
|
15
16
|
let openclaw_plugin_sdk_reply_payload = require("openclaw/plugin-sdk/reply-payload");
|
|
16
17
|
let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
|
|
@@ -164,7 +165,7 @@ Object.defineProperty(exports, "resolveChannelEntryMatchWithFallback", {
|
|
|
164
165
|
Object.defineProperty(exports, "resolveChannelMediaMaxBytes", {
|
|
165
166
|
enumerable: true,
|
|
166
167
|
get: function() {
|
|
167
|
-
return
|
|
168
|
+
return openclaw_plugin_sdk_account_helpers.resolveChannelMediaMaxBytes;
|
|
168
169
|
}
|
|
169
170
|
});
|
|
170
171
|
Object.defineProperty(exports, "resolveDefaultGroupPolicy", {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require("openclaw/plugin-sdk/secret-input");
|