@openclaw/msteams 2026.5.28 → 2026.5.31-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/api.js +2 -2
- package/dist/{channel-2_L55KDI.js → channel-CqRTzeBc.js} +5 -5
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-CxxY1xk6.js → channel.runtime-C38epVZr.js} +35 -28
- package/dist/doctor-contract-api.js +171 -0
- package/dist/{errors-DZGI_mqq.js → errors-Dpn8B05h.js} +3 -3
- package/dist/{probe-BoUA5GpA.js → probe-DMNefdBZ.js} +555 -148
- package/dist/setup-plugin-api.js +2 -2
- package/dist/{setup-surface-C9IApOv3.js → setup-surface-B7a1pD-K.js} +1 -1
- package/dist/{src-D_rcW2Zm.js → src-B6CFMcvV.js} +107 -79
- package/node_modules/es-object-atoms/CHANGELOG.md +21 -14
- package/node_modules/es-object-atoms/isObject.d.ts +1 -1
- package/node_modules/es-object-atoms/package.json +6 -7
- package/node_modules/es-object-atoms/tsconfig.json +1 -0
- package/node_modules/hasown/CHANGELOG.md +7 -0
- package/node_modules/hasown/index.d.ts +0 -1
- package/node_modules/hasown/package.json +4 -5
- package/node_modules/typebox/build/type/script/mapping.d.mts +5 -2
- package/node_modules/typebox/build/type/script/mapping.mjs +15 -8
- package/node_modules/typebox/build/type/script/parser.d.mts +3 -1
- package/node_modules/typebox/build/type/script/parser.mjs +2 -1
- package/node_modules/typebox/package.json +29 -29
- package/npm-shrinkwrap.json +13 -13
- package/package.json +5 -5
package/dist/api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { i as msteamsSetupAdapter, n as openDelegatedOAuthUrl, r as createMSTeamsSetupWizardBase, t as msteamsSetupWizard } from "./setup-surface-
|
|
2
|
-
import { t as msteamsPlugin } from "./channel-
|
|
1
|
+
import { i as msteamsSetupAdapter, n as openDelegatedOAuthUrl, r as createMSTeamsSetupWizardBase, t as msteamsSetupWizard } from "./setup-surface-B7a1pD-K.js";
|
|
2
|
+
import { t as msteamsPlugin } from "./channel-CqRTzeBc.js";
|
|
3
3
|
export { createMSTeamsSetupWizardBase, msteamsPlugin, msteamsSetupAdapter, msteamsSetupWizard, openDelegatedOAuthUrl };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { O as resolveNestedAllowlistDecision, S as normalizeChannelSlug, T as resolveChannelEntryMatchWithFallback, _ as isDangerousNameMatchingEnabled, i as buildChannelKeyCandidates, k as resolveToolsBySender, o as buildProbeChannelStatusSummary, r as PAIRING_APPROVED_MESSAGE, s as chunkTextForOutbound, t as DEFAULT_ACCOUNT_ID, u as createDefaultChannelRuntimeState, w as resolveAllowlistMatchSimple } from "./runtime-api-BlvMnDKz.js";
|
|
2
|
-
import { y as resolveMSTeamsCredentials } from "./errors-
|
|
3
|
-
import { a as looksLikeMSTeamsTargetId, c as parseMSTeamsConversationId, d as resolveMSTeamsUserAllowlist, i as msteamsSetupAdapter, l as parseMSTeamsTeamChannelInput, o as normalizeMSTeamsMessagingTarget, s as normalizeMSTeamsUserInput, t as msteamsSetupWizard, u as resolveMSTeamsChannelAllowlist } from "./setup-surface-
|
|
2
|
+
import { y as resolveMSTeamsCredentials } from "./errors-Dpn8B05h.js";
|
|
3
|
+
import { a as looksLikeMSTeamsTargetId, c as parseMSTeamsConversationId, d as resolveMSTeamsUserAllowlist, i as msteamsSetupAdapter, l as parseMSTeamsTeamChannelInput, o as normalizeMSTeamsMessagingTarget, s as normalizeMSTeamsUserInput, t as msteamsSetupWizard, u as resolveMSTeamsChannelAllowlist } from "./setup-surface-B7a1pD-K.js";
|
|
4
4
|
import { t as MSTeamsChannelConfigSchema } from "./config-schema-BL4qQZiA.js";
|
|
5
5
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
6
6
|
import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
|
|
@@ -283,7 +283,7 @@ function buildMSTeamsPresentationCard(params) {
|
|
|
283
283
|
//#endregion
|
|
284
284
|
//#region extensions/msteams/src/session-route.ts
|
|
285
285
|
function resolveMSTeamsOutboundSessionRoute(params) {
|
|
286
|
-
|
|
286
|
+
const trimmed = stripChannelTargetPrefix(params.target, "msteams", "teams");
|
|
287
287
|
if (!trimmed) return null;
|
|
288
288
|
const isUser = normalizeLowercaseStringOrEmpty(trimmed).startsWith("user:");
|
|
289
289
|
const rawId = stripTargetKindPrefix(trimmed);
|
|
@@ -330,7 +330,7 @@ const collectMSTeamsSecurityWarnings = createAllowlistProviderGroupPolicyWarning
|
|
|
330
330
|
resolveGroupPolicy: ({ cfg }) => cfg.channels?.msteams?.groupPolicy,
|
|
331
331
|
collect: ({ groupPolicy }) => groupPolicy === "open" ? ["- MS Teams groups: groupPolicy=\"open\" allows any member to trigger (mention-gated). Set channels.msteams.groupPolicy=\"allowlist\" + channels.msteams.groupAllowFrom to restrict senders."] : []
|
|
332
332
|
});
|
|
333
|
-
const loadMSTeamsChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-
|
|
333
|
+
const loadMSTeamsChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-C38epVZr.js"), "msTeamsChannelRuntime");
|
|
334
334
|
const resolveMSTeamsChannelConfig = (cfg) => ({
|
|
335
335
|
allowFrom: cfg.channels?.msteams?.allowFrom,
|
|
336
336
|
defaultTo: cfg.channels?.msteams?.defaultTo
|
|
@@ -1116,7 +1116,7 @@ const msteamsPlugin = createChatChannelPlugin({
|
|
|
1116
1116
|
})
|
|
1117
1117
|
}),
|
|
1118
1118
|
gateway: { startAccount: async (ctx) => {
|
|
1119
|
-
const { monitorMSTeamsProvider } = await import("./src-
|
|
1119
|
+
const { monitorMSTeamsProvider } = await import("./src-B6CFMcvV.js");
|
|
1120
1120
|
const port = ctx.cfg.channels?.msteams?.webhook?.port ?? 3978;
|
|
1121
1121
|
ctx.setStatus({
|
|
1122
1122
|
accountId: ctx.accountId,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as msteamsPlugin } from "./channel-
|
|
1
|
+
import { t as msteamsPlugin } from "./channel-CqRTzeBc.js";
|
|
2
2
|
export { msteamsPlugin };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { C as normalizeStringEntries$1, s as chunkTextForOutbound } from "./runtime-api-BlvMnDKz.js";
|
|
2
|
-
import { a as searchGraphUsers, c as fetchGraphAbsoluteUrl, d as listTeamsByName, f as normalizeQuery, g as resolveGraphToken, h as postGraphJson, l as fetchGraphJson, m as postGraphBetaJson, o as deleteGraphRequest, p as patchGraphJson, s as escapeOData, u as listChannelsForTeam } from "./errors-
|
|
3
|
-
import { n as MSTEAMS_PRESENTATION_CAPABILITIES, r as buildMSTeamsPresentationCard } from "./channel-
|
|
4
|
-
import { S as
|
|
2
|
+
import { a as searchGraphUsers, c as fetchGraphAbsoluteUrl, d as listTeamsByName, f as normalizeQuery, g as resolveGraphToken, h as postGraphJson, l as fetchGraphJson, m as postGraphBetaJson, o as deleteGraphRequest, p as patchGraphJson, s as escapeOData, u as listChannelsForTeam } from "./errors-Dpn8B05h.js";
|
|
3
|
+
import { n as MSTEAMS_PRESENTATION_CAPABILITIES, r as buildMSTeamsPresentationCard } from "./channel-CqRTzeBc.js";
|
|
4
|
+
import { S as createMSTeamsConversationStoreState, a as sendMessageMSTeams, b as createMSTeamsPollStoreState, i as sendAdaptiveCardMSTeams, n as deleteMessageMSTeams, o as sendPollMSTeams, r as editMessageMSTeams, t as probeMSTeams } from "./probe-DMNefdBZ.js";
|
|
5
5
|
import { resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-outbound";
|
|
6
6
|
import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
7
|
import { resolvePayloadMediaUrls, resolveTextChunksWithFallback, sendPayloadMediaSequence } from "openclaw/plugin-sdk/reply-payload";
|
|
@@ -96,7 +96,7 @@ async function resolveGraphConversationId(to) {
|
|
|
96
96
|
const isUserTarget = /^user:/i.test(trimmed);
|
|
97
97
|
const cleaned = stripTargetPrefix(trimmed);
|
|
98
98
|
if (!isUserTarget) return cleaned;
|
|
99
|
-
const found = await
|
|
99
|
+
const found = await createMSTeamsConversationStoreState().findPreferredDmByUserId(cleaned);
|
|
100
100
|
if (!found) throw new Error(`No conversation found for user:${cleaned}. The bot must receive a message from this user before Graph API operations work.`);
|
|
101
101
|
if (found.reference.graphChatId) return found.reference.graphChatId;
|
|
102
102
|
if (found.conversationId.startsWith("19:")) return found.conversationId;
|
|
@@ -473,7 +473,7 @@ async function listChannelsMSTeams(params) {
|
|
|
473
473
|
description: ch.description,
|
|
474
474
|
membershipType: ch.membershipType
|
|
475
475
|
})),
|
|
476
|
-
truncated:
|
|
476
|
+
truncated: Boolean(nextPath)
|
|
477
477
|
};
|
|
478
478
|
}
|
|
479
479
|
/**
|
|
@@ -500,6 +500,23 @@ function asObjectRecord(value) {
|
|
|
500
500
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
501
501
|
}
|
|
502
502
|
const MSTEAMS_TEXT_CHUNK_LIMIT = 4e3;
|
|
503
|
+
function resolveMSTeamsTextSend(params) {
|
|
504
|
+
return resolveOutboundSendDep(params.deps, "msteams") ?? ((to, text) => sendMessageMSTeams({
|
|
505
|
+
cfg: params.cfg,
|
|
506
|
+
to,
|
|
507
|
+
text
|
|
508
|
+
}));
|
|
509
|
+
}
|
|
510
|
+
function resolveMSTeamsMediaSend(params) {
|
|
511
|
+
return resolveOutboundSendDep(params.deps, "msteams") ?? ((to, text, opts) => sendMessageMSTeams({
|
|
512
|
+
cfg: params.cfg,
|
|
513
|
+
to,
|
|
514
|
+
text,
|
|
515
|
+
mediaUrl: opts?.mediaUrl,
|
|
516
|
+
mediaLocalRoots: opts?.mediaLocalRoots,
|
|
517
|
+
mediaReadFile: opts?.mediaReadFile
|
|
518
|
+
}));
|
|
519
|
+
}
|
|
503
520
|
//#endregion
|
|
504
521
|
//#region extensions/msteams/src/channel.runtime.ts
|
|
505
522
|
const msTeamsChannelRuntime = {
|
|
@@ -564,14 +581,10 @@ const msTeamsChannelRuntime = {
|
|
|
564
581
|
mediaUrl: payload.mediaUrl ?? mediaUrl
|
|
565
582
|
}));
|
|
566
583
|
if (mediaUrls.length > 0) {
|
|
567
|
-
const send =
|
|
584
|
+
const send = resolveMSTeamsMediaSend({
|
|
568
585
|
cfg,
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
mediaUrl: opts?.mediaUrl,
|
|
572
|
-
mediaLocalRoots: opts?.mediaLocalRoots,
|
|
573
|
-
mediaReadFile: opts?.mediaReadFile
|
|
574
|
-
}));
|
|
586
|
+
deps
|
|
587
|
+
});
|
|
575
588
|
const result = await sendPayloadMediaSequence({
|
|
576
589
|
text,
|
|
577
590
|
mediaUrls,
|
|
@@ -584,11 +597,10 @@ const msTeamsChannelRuntime = {
|
|
|
584
597
|
if (result) return attachChannelToResult("msteams", result);
|
|
585
598
|
}
|
|
586
599
|
if (text.trim()) {
|
|
587
|
-
const send =
|
|
600
|
+
const send = resolveMSTeamsTextSend({
|
|
588
601
|
cfg,
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}));
|
|
602
|
+
deps
|
|
603
|
+
});
|
|
592
604
|
const chunks = resolveTextChunksWithFallback(text, chunkTextForOutbound(text, MSTEAMS_TEXT_CHUNK_LIMIT));
|
|
593
605
|
let result;
|
|
594
606
|
for (const chunk of chunks) result = await send(to, chunk);
|
|
@@ -599,21 +611,16 @@ const msTeamsChannelRuntime = {
|
|
|
599
611
|
...createAttachedChannelResultAdapter({
|
|
600
612
|
channel: "msteams",
|
|
601
613
|
sendText: async ({ cfg, to, text, deps }) => {
|
|
602
|
-
return await (
|
|
614
|
+
return await resolveMSTeamsTextSend({
|
|
603
615
|
cfg,
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
})))(to, text);
|
|
616
|
+
deps
|
|
617
|
+
})(to, text);
|
|
607
618
|
},
|
|
608
619
|
sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, mediaReadFile, deps }) => {
|
|
609
|
-
return await (
|
|
620
|
+
return await resolveMSTeamsMediaSend({
|
|
610
621
|
cfg,
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
mediaUrl: opts?.mediaUrl,
|
|
614
|
-
mediaLocalRoots: opts?.mediaLocalRoots,
|
|
615
|
-
mediaReadFile: opts?.mediaReadFile
|
|
616
|
-
})))(to, text, {
|
|
622
|
+
deps
|
|
623
|
+
})(to, text, {
|
|
617
624
|
mediaUrl,
|
|
618
625
|
mediaLocalRoots,
|
|
619
626
|
mediaReadFile
|
|
@@ -628,7 +635,7 @@ const msTeamsChannelRuntime = {
|
|
|
628
635
|
options: poll.options,
|
|
629
636
|
maxSelections
|
|
630
637
|
});
|
|
631
|
-
await
|
|
638
|
+
await createMSTeamsPollStoreState().createPoll({
|
|
632
639
|
id: result.pollId,
|
|
633
640
|
question: poll.question,
|
|
634
641
|
options: poll.options,
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import { resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
5
|
+
//#region extensions/msteams/doctor-contract-api.ts
|
|
6
|
+
const LEARNINGS_NAMESPACE = "feedback-learnings";
|
|
7
|
+
const MAX_LEARNING_ENTRIES = 1e4;
|
|
8
|
+
function encodeSessionKey(sessionKey) {
|
|
9
|
+
return Buffer.from(sessionKey, "utf8").toString("base64url");
|
|
10
|
+
}
|
|
11
|
+
function learningStoreKey(storePath, sessionKey) {
|
|
12
|
+
return crypto.createHash("sha256").update(`${storePath}\0${sessionKey}`, "utf8").digest("hex");
|
|
13
|
+
}
|
|
14
|
+
function decodeSessionKey(fileStem) {
|
|
15
|
+
try {
|
|
16
|
+
const decoded = Buffer.from(fileStem, "base64url").toString("utf8");
|
|
17
|
+
return encodeSessionKey(decoded) === fileStem && decoded.trim() ? decoded : null;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function resolveLearningSessionKey(fileStem) {
|
|
23
|
+
return decodeSessionKey(fileStem);
|
|
24
|
+
}
|
|
25
|
+
function legacySanitizeSessionKey(sessionKey) {
|
|
26
|
+
return sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
27
|
+
}
|
|
28
|
+
async function listKnownSessionKeys(storePath) {
|
|
29
|
+
const candidates = [storePath, path.join(storePath, "sessions.json")];
|
|
30
|
+
for (const candidate of candidates) try {
|
|
31
|
+
const parsed = JSON.parse(await fs.readFile(candidate, "utf8"));
|
|
32
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
|
|
33
|
+
const sessions = parsed.sessions && typeof parsed.sessions === "object" && !Array.isArray(parsed.sessions) ? parsed.sessions : parsed;
|
|
34
|
+
return Object.keys(sessions).filter((key) => key.trim());
|
|
35
|
+
} catch {}
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
function resolveLegacySanitizedSessionKey(fileStem, knownSessionKeys) {
|
|
39
|
+
const matches = knownSessionKeys.filter((sessionKey) => legacySanitizeSessionKey(sessionKey) === fileStem);
|
|
40
|
+
return matches.length === 1 ? matches[0] : null;
|
|
41
|
+
}
|
|
42
|
+
function listAgentIds(config) {
|
|
43
|
+
const ids = new Set(["main"]);
|
|
44
|
+
for (const agent of config.agents?.list ?? []) if (typeof agent.id === "string" && agent.id.trim()) ids.add(agent.id.trim());
|
|
45
|
+
return [...ids];
|
|
46
|
+
}
|
|
47
|
+
function listCandidateStorePaths(params) {
|
|
48
|
+
const paths = /* @__PURE__ */ new Set();
|
|
49
|
+
paths.add(resolveStorePath(params.config.session?.store, { env: params.env }));
|
|
50
|
+
for (const agentId of listAgentIds(params.config)) paths.add(resolveStorePath(params.config.session?.store, {
|
|
51
|
+
agentId,
|
|
52
|
+
env: params.env
|
|
53
|
+
}));
|
|
54
|
+
return [...paths];
|
|
55
|
+
}
|
|
56
|
+
async function fileExists(filePath) {
|
|
57
|
+
try {
|
|
58
|
+
return (await fs.stat(filePath)).isFile();
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function listLegacyLearningFiles(storePath) {
|
|
64
|
+
let entries = [];
|
|
65
|
+
try {
|
|
66
|
+
entries = await fs.readdir(storePath, { withFileTypes: true });
|
|
67
|
+
} catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
const suffix = ".learnings.json";
|
|
71
|
+
const knownSessionKeys = await listKnownSessionKeys(storePath);
|
|
72
|
+
const files = [];
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
if (!entry.isFile() || !entry.name.endsWith(suffix)) continue;
|
|
75
|
+
const fileStem = entry.name.slice(0, -15);
|
|
76
|
+
const sessionKey = resolveLearningSessionKey(fileStem) ?? resolveLegacySanitizedSessionKey(fileStem, knownSessionKeys);
|
|
77
|
+
const filePath = path.join(storePath, entry.name);
|
|
78
|
+
try {
|
|
79
|
+
const parsed = JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
80
|
+
if (Array.isArray(parsed)) {
|
|
81
|
+
const learnings = parsed.filter((item) => typeof item === "string");
|
|
82
|
+
if (learnings.length > 0) files.push({
|
|
83
|
+
storePath,
|
|
84
|
+
sessionKey,
|
|
85
|
+
filePath,
|
|
86
|
+
learnings: learnings.slice(-10)
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
} catch {}
|
|
90
|
+
}
|
|
91
|
+
return files;
|
|
92
|
+
}
|
|
93
|
+
async function archiveLegacySource(params) {
|
|
94
|
+
const archivedPath = `${params.filePath}.migrated`;
|
|
95
|
+
if (await fileExists(archivedPath)) {
|
|
96
|
+
params.warnings.push(`Left migrated Microsoft Teams feedback-learning source in place because ${archivedPath} already exists`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
await fs.rename(params.filePath, archivedPath);
|
|
101
|
+
params.changes.push(`Archived Microsoft Teams feedback-learning legacy source -> ${archivedPath}`);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
params.warnings.push(`Failed archiving Microsoft Teams feedback-learning legacy source: ${String(err)}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function mergeLearnings(legacy, existing) {
|
|
107
|
+
const seen = /* @__PURE__ */ new Set();
|
|
108
|
+
const merged = [];
|
|
109
|
+
for (const learning of [...legacy, ...existing?.learnings ?? []]) {
|
|
110
|
+
if (seen.has(learning)) continue;
|
|
111
|
+
seen.add(learning);
|
|
112
|
+
merged.push(learning);
|
|
113
|
+
}
|
|
114
|
+
return merged.slice(-10);
|
|
115
|
+
}
|
|
116
|
+
const stateMigrations = [{
|
|
117
|
+
id: "msteams-feedback-learnings-json-to-plugin-state",
|
|
118
|
+
label: "Microsoft Teams feedback learnings",
|
|
119
|
+
async detectLegacyState(params) {
|
|
120
|
+
const files = (await Promise.all(listCandidateStorePaths(params).map((storePath) => listLegacyLearningFiles(storePath)))).flat();
|
|
121
|
+
if (files.length === 0) return null;
|
|
122
|
+
return { preview: [`- Microsoft Teams feedback learnings: ${files.length} ${files.length === 1 ? "file" : "files"} -> plugin state (${LEARNINGS_NAMESPACE})`] };
|
|
123
|
+
},
|
|
124
|
+
async migrateLegacyState(params) {
|
|
125
|
+
const changes = [];
|
|
126
|
+
const warnings = [];
|
|
127
|
+
const files = (await Promise.all(listCandidateStorePaths(params).map((storePath) => listLegacyLearningFiles(storePath)))).flat();
|
|
128
|
+
const store = params.context.openPluginStateKeyedStore({
|
|
129
|
+
namespace: LEARNINGS_NAMESPACE,
|
|
130
|
+
maxEntries: MAX_LEARNING_ENTRIES
|
|
131
|
+
});
|
|
132
|
+
const existingEntries = await store.entries();
|
|
133
|
+
const existingKeys = new Set(existingEntries.map((entry) => entry.key));
|
|
134
|
+
const importableFiles = files.filter((file) => file.sessionKey);
|
|
135
|
+
const missingKeys = new Set(importableFiles.map((file) => learningStoreKey(file.storePath, file.sessionKey ?? "")).filter((key) => !existingKeys.has(key)));
|
|
136
|
+
if (missingKeys.size > MAX_LEARNING_ENTRIES - existingKeys.size) {
|
|
137
|
+
warnings.push(`Skipped Microsoft Teams feedback-learning migration because plugin state has room for ${MAX_LEARNING_ENTRIES - existingKeys.size} of ${missingKeys.size} missing entries; left legacy sources in place`);
|
|
138
|
+
return {
|
|
139
|
+
changes,
|
|
140
|
+
warnings
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
let imported = 0;
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
if (!file.sessionKey) {
|
|
146
|
+
warnings.push(`Left Microsoft Teams feedback-learning source in place because its legacy filename cannot be mapped to a session key: ${file.filePath}`);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const key = learningStoreKey(file.storePath, file.sessionKey);
|
|
150
|
+
const existing = await store.lookup(key);
|
|
151
|
+
await store.register(key, {
|
|
152
|
+
sessionKey: existing?.sessionKey ?? file.sessionKey,
|
|
153
|
+
learnings: mergeLearnings(file.learnings, existing),
|
|
154
|
+
updatedAt: Date.now()
|
|
155
|
+
});
|
|
156
|
+
imported++;
|
|
157
|
+
await archiveLegacySource({
|
|
158
|
+
filePath: file.filePath,
|
|
159
|
+
changes,
|
|
160
|
+
warnings
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (imported > 0) changes.unshift(`Migrated ${imported} Microsoft Teams feedback-learning ${imported === 1 ? "entry" : "entries"} -> plugin state`);
|
|
164
|
+
return {
|
|
165
|
+
changes,
|
|
166
|
+
warnings
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}];
|
|
170
|
+
//#endregion
|
|
171
|
+
export { stateMigrations };
|
|
@@ -10,8 +10,8 @@ import { buildHostnameAllowlistPolicyFromSuffixAllowlist, isHttpsUrlAllowedByHos
|
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
11
|
import { readFileSync } from "node:fs";
|
|
12
12
|
import path, { basename, dirname } from "node:path";
|
|
13
|
+
import { asFiniteNumberInRange, isFutureDateTimestampMs, parseStrictFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
|
|
13
14
|
import { privateFileStoreSync } from "openclaw/plugin-sdk/security-runtime";
|
|
14
|
-
import { asFiniteNumberInRange, parseStrictFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
|
|
15
15
|
import { hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
16
16
|
//#region extensions/msteams/src/attachments/shared.ts
|
|
17
17
|
const IMAGE_EXT_RE = /\.(avif|bmp|gif|heic|heif|jpe?g|png|tiff?|webp)$/i;
|
|
@@ -296,7 +296,7 @@ function resolveAuthAllowedHosts(input) {
|
|
|
296
296
|
}
|
|
297
297
|
function isMockFetchFn(fetchFn) {
|
|
298
298
|
const candidate = fetchFn;
|
|
299
|
-
return Boolean(candidate.mock || Object.
|
|
299
|
+
return Boolean(candidate.mock || Object.hasOwn(candidate, "_isMockFunction"));
|
|
300
300
|
}
|
|
301
301
|
function resolveGuardedFetchImpl(params) {
|
|
302
302
|
if (!params.fetchFn) return;
|
|
@@ -807,7 +807,7 @@ function saveDelegatedTokens(tokens) {
|
|
|
807
807
|
async function resolveDelegatedAccessToken(params) {
|
|
808
808
|
const tokens = loadDelegatedTokens();
|
|
809
809
|
if (!tokens) return;
|
|
810
|
-
if (tokens.expiresAt
|
|
810
|
+
if (isFutureDateTimestampMs(tokens.expiresAt)) return tokens.accessToken;
|
|
811
811
|
try {
|
|
812
812
|
const refreshed = await refreshMSTeamsDelegatedTokens({
|
|
813
813
|
tenantId: params.tenantId,
|