@openclaw/msteams 2026.9.6 → 2026.9.8

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.
Files changed (33) hide show
  1. package/assets/icon.png +0 -0
  2. package/dist/.setup/{adapter.runtime-CQWZeqB5.cjs → adapter.runtime-BiWeMKXE.cjs} +21 -29
  3. package/dist/.setup/{approval-card-CPNH_q3P.cjs → approval-card-DWPDItvq.cjs} +6 -3
  4. package/dist/.setup/{approval-handler.runtime-CUnanUdR.cjs → approval-handler.runtime-D1s7UR3U.cjs} +4 -4
  5. package/dist/.setup/bot-framework-service-url-BUhTe8F3.cjs +55 -0
  6. package/dist/.setup/{channel-BDp16XRR.cjs → channel-ByzS7QJ0.cjs} +286 -180
  7. package/dist/.setup/{channel.runtime-Cv5F8JCa.cjs → channel.runtime-Baz8XDFs.cjs} +43 -142
  8. package/dist/.setup/{channel.setup-BVDfgdCc.cjs → channel.setup-DL8lcWT_.cjs} +6 -6
  9. package/dist/.setup/{config-schema-D22Lr1Qk.cjs → config-schema-0KoBzb0t.cjs} +24 -38
  10. package/dist/.setup/{messenger-8RA1FUJa.cjs → conversation-store-state-B7JuyZTm.cjs} +693 -287
  11. package/dist/.setup/{src-BfOCwlHp.cjs → monitor-BJ7IyH4y.cjs} +422 -592
  12. package/dist/.setup/{oauth-C6m7dvHj.cjs → oauth-DQjOnJ4l.cjs} +1 -1
  13. package/dist/.setup/{reaction-types-C7RA7Wxt.cjs → reaction-types-CQ4oSUe7.cjs} +2 -6
  14. package/dist/.setup/{resolve-allowlist-CHzaaWiC.cjs → resolve-allowlist-Pl_NQFYH.cjs} +55 -312
  15. package/dist/.setup/{rolldown-runtime-VH7oDXx4.cjs → rolldown-runtime-BloeesfJ.cjs} +22 -0
  16. package/dist/.setup/{send-CYN_N7O1.cjs → send-VUM7Sryb.cjs} +43 -96
  17. package/dist/.setup/{setup-plugin-api-DpiaA8z1.cjs → setup-plugin-api-Dyf8kF-l.cjs} +1 -1
  18. package/dist/.setup/{thread-session-CXgHK-yi.cjs → thread-session--rw3BTal.cjs} +2 -5
  19. package/dist/api.cjs +2 -2
  20. package/dist/channel-config-api.cjs +1 -1
  21. package/dist/channel-plugin-api.cjs +1 -1
  22. package/dist/configured-state.cjs +0 -1
  23. package/dist/directory-contract-api.cjs +1 -1
  24. package/dist/doctor-contract-api.cjs +201 -371
  25. package/dist/qa-runner-api.cjs +1 -1
  26. package/dist/runtime-api.cjs +0 -6
  27. package/dist/setup-entry.cjs +1 -1
  28. package/dist/setup-plugin-api.cjs +1 -1
  29. package/openclaw.plugin.json +46 -5
  30. package/package.json +9 -9
  31. package/dist/.setup/polls-BJ4-4uZm.cjs +0 -668
  32. package/dist/.setup/secret-input-C65t6kjM.cjs +0 -1
  33. package/dist/.setup/sso-token-store-YYLHXgFH.cjs +0 -111
@@ -1,66 +1,192 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_rolldown_runtime = require("./.setup/rolldown-runtime-VH7oDXx4.cjs");
2
+ const require_rolldown_runtime = require("./.setup/rolldown-runtime-BloeesfJ.cjs");
3
3
  const require_delegated_state = require("./.setup/delegated-state-u-ywZyZT.cjs");
4
- const require_polls = require("./.setup/polls-BJ4-4uZm.cjs");
5
- const require_sso_token_store = require("./.setup/sso-token-store-YYLHXgFH.cjs");
6
4
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
7
- let node_crypto = require("node:crypto");
8
- node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
5
+ let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
9
6
  let node_fs_promises = require("node:fs/promises");
10
7
  node_fs_promises = require_rolldown_runtime.__toESM(node_fs_promises, 1);
11
8
  let node_path = require("node:path");
12
9
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
13
- let openclaw_plugin_sdk_runtime_doctor_migrations = require("openclaw/plugin-sdk/runtime-doctor-migrations");
14
10
  let openclaw_plugin_sdk_session_store_paths = require("openclaw/plugin-sdk/session-store-paths");
11
+ //#region packages/normalization-core/src/record-coerce.ts
12
+ /** Type guard for non-array object records at browser-safe boundaries. */
13
+ function isRecord$1(value) {
14
+ return value !== null && typeof value === "object" && !Array.isArray(value);
15
+ }
16
+ /** Returns a non-array record or null. */
17
+ function asNullableRecord(value) {
18
+ return isRecord$1(value) ? value : null;
19
+ }
20
+ //#endregion
21
+ //#region src/config/channel-config-normalization.ts
22
+ /** Applies one channel-specific doctor migration to every object-shaped account. */
23
+ function normalizeChannelAccounts(params) {
24
+ const rawAccounts = asNullableRecord(params.entry.accounts);
25
+ if (!rawAccounts) return {
26
+ entry: params.entry,
27
+ changed: false
28
+ };
29
+ let changed = false;
30
+ const accounts = { ...rawAccounts };
31
+ for (const [accountId, value] of Object.entries(rawAccounts)) {
32
+ const account = asNullableRecord(value);
33
+ if (!account) continue;
34
+ const normalized = params.normalizeAccount({
35
+ account,
36
+ accountId,
37
+ pathPrefix: `${params.pathPrefix}.accounts.${accountId}`,
38
+ changes: params.changes
39
+ });
40
+ if (normalized.changed) {
41
+ accounts[accountId] = normalized.entry;
42
+ changed = true;
43
+ }
44
+ }
45
+ return changed ? {
46
+ entry: {
47
+ ...params.entry,
48
+ accounts
49
+ },
50
+ changed: true
51
+ } : {
52
+ entry: params.entry,
53
+ changed: false
54
+ };
55
+ }
56
+ /** Applies the same channel-specific doctor migration at root and account scope. */
57
+ function normalizeChannelConfigEntries(params) {
58
+ const changes = params.changes ?? [];
59
+ const channels = params.cfg.channels;
60
+ const entry = asNullableRecord(channels?.[params.channelId]);
61
+ if (!entry) return {
62
+ config: params.cfg,
63
+ changes
64
+ };
65
+ const channelPath = `channels.${params.channelId}`;
66
+ const root = params.normalizeEntry({
67
+ entry,
68
+ pathPrefix: channelPath,
69
+ changes
70
+ });
71
+ const accounts = normalizeChannelAccounts({
72
+ entry: root.entry,
73
+ pathPrefix: channelPath,
74
+ changes,
75
+ normalizeAccount: (accountParams) => params.normalizeEntry({
76
+ entry: accountParams.account,
77
+ accountId: accountParams.accountId,
78
+ pathPrefix: accountParams.pathPrefix,
79
+ changes: accountParams.changes
80
+ })
81
+ });
82
+ if (!root.changed && !accounts.changed) return {
83
+ config: params.cfg,
84
+ changes
85
+ };
86
+ return {
87
+ config: {
88
+ ...params.cfg,
89
+ channels: {
90
+ ...channels,
91
+ [params.channelId]: accounts.entry
92
+ }
93
+ },
94
+ changes
95
+ };
96
+ }
97
+ //#endregion
98
+ //#region src/plugin-sdk/legacy-webhook-listener-migration.ts
99
+ /** Preserve explicitly configured webhook listeners while moving ingress onto Gateway routes. */
100
+ function createLegacyWebhookListenerDoctorContract(params) {
101
+ const portKey = params.portKey ?? "webhookPort";
102
+ const hostKey = params.hostKey === void 0 ? "webhookHost" : params.hostKey;
103
+ const source = (entry) => params.webhookKey ? asNullableRecord(entry[params.webhookKey]) : entry;
104
+ const hasLegacy = (value) => {
105
+ const entry = asNullableRecord(value);
106
+ const listener = entry && source(entry);
107
+ return Boolean(listener && (Object.hasOwn(listener, portKey) || hostKey && Object.hasOwn(listener, hostKey)));
108
+ };
109
+ const prefix = `channels.${params.channelKey}`;
110
+ return {
111
+ legacyConfigRules: [{
112
+ path: ["channels", params.channelKey],
113
+ message: `${prefix} webhook listeners moved to Gateway routes. Run "openclaw doctor --fix" to preserve explicitly configured listener settings as legacyWebhook.`,
114
+ match: (value) => {
115
+ const accounts = asNullableRecord(asNullableRecord(value)?.accounts);
116
+ return hasLegacy(value) || Object.values(accounts ?? {}).some(hasLegacy);
117
+ }
118
+ }],
119
+ normalizeCompatibilityConfig: ({ cfg }) => {
120
+ const root = asNullableRecord(asNullableRecord(cfg.channels)?.[params.channelKey]);
121
+ const inherited = root && source(root);
122
+ const canonicalRoot = asNullableRecord(root?.legacyWebhook);
123
+ return normalizeChannelConfigEntries({
124
+ cfg,
125
+ channelId: params.channelKey,
126
+ normalizeEntry: ({ entry, accountId, pathPrefix, changes }) => {
127
+ const listener = source(entry);
128
+ if (!listener || !hasLegacy(entry)) return {
129
+ entry,
130
+ changed: false
131
+ };
132
+ const next = { ...entry };
133
+ const port = Object.hasOwn(listener, portKey) ? listener[portKey] : (accountId ? canonicalRoot?.port ?? inherited?.[portKey] : void 0) ?? params.defaultPort;
134
+ const inheritedHost = accountId ? canonicalRoot ? canonicalRoot.host : (hostKey ? inherited?.[hostKey] : void 0) ?? params.defaultHost : params.defaultHost;
135
+ const host = hostKey ? listener[hostKey] ?? inheritedHost : inheritedHost;
136
+ const legacyPath = [pathPrefix, params.webhookKey].filter(Boolean).join(".");
137
+ if (Object.hasOwn(entry, "legacyWebhook")) changes.push(`Removed ${legacyPath} legacy listener keys; ${pathPrefix}.legacyWebhook is already configured.`);
138
+ else if (accountId && root?.legacyWebhook === false) changes.push(`Removed ${legacyPath} legacy listener keys; ${prefix}.legacyWebhook: false keeps this account's inherited listener disabled.`);
139
+ else {
140
+ next.legacyWebhook = {
141
+ port,
142
+ ...host !== void 0 ? { host } : {}
143
+ };
144
+ changes.push(`Moved ${legacyPath} listener settings to ${pathPrefix}.legacyWebhook. Point the external callback or reverse proxy at the Gateway port and webhook path, verify delivery, then set legacyWebhook: false to disable legacy forwarding.`);
145
+ }
146
+ const updated = params.webhookKey ? { ...listener } : next;
147
+ delete updated[portKey];
148
+ if (hostKey) delete updated[hostKey];
149
+ if (params.webhookKey) {
150
+ if (Object.keys(updated).length) next[params.webhookKey] = updated;
151
+ else delete next[params.webhookKey];
152
+ }
153
+ return {
154
+ entry: next,
155
+ changed: true
156
+ };
157
+ }
158
+ });
159
+ }
160
+ };
161
+ }
162
+ //#endregion
163
+ //#region \0openclaw:bundled-sdk:openclaw/plugin-sdk/runtime-doctor-migrations
164
+ var runtime_doctor_migrations_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({ createLegacyWebhookListenerDoctorContract: () => createLegacyWebhookListenerDoctorContract });
165
+ require_rolldown_runtime.__reExport(runtime_doctor_migrations_exports, require("openclaw/plugin-sdk/runtime-doctor-migrations"));
166
+ //#endregion
15
167
  //#region extensions/msteams/config-doctor-api.ts
16
- const streamingAliasMigration = (0, openclaw_plugin_sdk_runtime_doctor_migrations.defineChannelAliasMigration)({
168
+ const webhookMigration = createLegacyWebhookListenerDoctorContract({
169
+ channelKey: "msteams",
170
+ defaultPort: 3978,
171
+ webhookKey: "webhook",
172
+ portKey: "port",
173
+ hostKey: null
174
+ });
175
+ const streamingAliasMigration = (0, runtime_doctor_migrations_exports.defineChannelAliasMigration)({
17
176
  channelId: "msteams",
18
177
  streaming: { defaultMode: "partial" }
19
178
  });
20
- const legacyConfigRules = streamingAliasMigration.legacyConfigRules;
179
+ const legacyConfigRules = [...webhookMigration.legacyConfigRules, ...streamingAliasMigration.legacyConfigRules];
21
180
  function normalizeCompatibilityConfig({ cfg }) {
22
- return streamingAliasMigration.normalizeChannelConfig({ cfg });
181
+ const webhook = webhookMigration.normalizeCompatibilityConfig({ cfg });
182
+ return streamingAliasMigration.normalizeChannelConfig({
183
+ cfg: webhook.config,
184
+ changes: webhook.changes
185
+ });
23
186
  }
24
187
  //#endregion
25
188
  //#region extensions/msteams/doctor-contract-api.ts
26
- const LEARNINGS_NAMESPACE = "feedback-learnings";
27
- const MAX_LEARNING_ENTRIES = 1e4;
28
189
  const MSTEAMS_PLUGIN_ID = "Microsoft Teams";
29
- function encodeSessionKey(sessionKey) {
30
- return Buffer.from(sessionKey, "utf8").toString("base64url");
31
- }
32
- function learningStoreKey(storePath, sessionKey) {
33
- return node_crypto.default.createHash("sha256").update(`${storePath}\0${sessionKey}`, "utf8").digest("hex");
34
- }
35
- function decodeSessionKey(fileStem) {
36
- try {
37
- const decoded = Buffer.from(fileStem, "base64url").toString("utf8");
38
- return encodeSessionKey(decoded) === fileStem && decoded.trim() ? decoded : null;
39
- } catch {
40
- return null;
41
- }
42
- }
43
- function resolveLearningSessionKey(fileStem) {
44
- return decodeSessionKey(fileStem);
45
- }
46
- function legacySanitizeSessionKey(sessionKey) {
47
- return sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_");
48
- }
49
- async function listKnownSessionKeys(storePath) {
50
- const candidates = [storePath, node_path.default.join(storePath, "sessions.json")];
51
- for (const candidate of candidates) try {
52
- const parsed = JSON.parse(await node_fs_promises.default.readFile(candidate, "utf8"));
53
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
54
- const sessions = parsed.sessions && typeof parsed.sessions === "object" && !Array.isArray(parsed.sessions) ? parsed.sessions : parsed;
55
- return Object.keys(sessions).filter((key) => key.trim());
56
- } catch {}
57
- return [];
58
- }
59
- function resolveLegacySanitizedSessionKey(fileStem, knownSessionKeys) {
60
- const matches = knownSessionKeys.filter((sessionKey) => legacySanitizeSessionKey(sessionKey) === fileStem);
61
- const [match] = matches;
62
- return matches.length === 1 && match ? match : null;
63
- }
64
190
  function listAgentIds(config) {
65
191
  const ids = /* @__PURE__ */ new Set(["main"]);
66
192
  if ((0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(config.agents?.entries)) {
@@ -77,290 +203,48 @@ function listCandidateStorePaths(params) {
77
203
  }));
78
204
  return [...paths];
79
205
  }
80
- function resolveStateFilePath(stateDir, filename) {
81
- return node_path.default.join(stateDir, filename);
82
- }
83
- async function readLegacyJsonFile(filePath, parse) {
84
- try {
85
- return parse(JSON.parse(await node_fs_promises.default.readFile(filePath, "utf8")));
86
- } catch {
87
- return null;
88
- }
89
- }
90
- function isStringArray(value) {
91
- return Array.isArray(value) && value.every((entry) => typeof entry === "string");
92
- }
93
- function parseLegacyConversationStore(value) {
94
- if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value) || value.version !== 1 || !(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value.conversations)) return null;
95
- return require_polls.normalizeMSTeamsLegacyConversationStore({
96
- version: 1,
97
- conversations: value.conversations
98
- });
99
- }
100
- function parseLegacyPoll(value) {
101
- if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value)) return null;
102
- const votes = (0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value.votes) ? value.votes : null;
103
- if (typeof value.id !== "string" || !value.id || typeof value.question !== "string" || !value.question || !isStringArray(value.options) || typeof value.maxSelections !== "number" || !Number.isFinite(value.maxSelections) || typeof value.createdAt !== "string" || !votes) return null;
104
- const normalizedVotes = {};
105
- for (const [voterId, selections] of Object.entries(votes)) if (typeof voterId === "string" && isStringArray(selections)) normalizedVotes[voterId] = selections;
106
- return {
107
- id: value.id,
108
- question: value.question,
109
- options: value.options,
110
- maxSelections: value.maxSelections,
111
- createdAt: value.createdAt,
112
- ...typeof value.updatedAt === "string" ? { updatedAt: value.updatedAt } : {},
113
- ...typeof value.conversationId === "string" ? { conversationId: value.conversationId } : {},
114
- ...typeof value.messageId === "string" ? { messageId: value.messageId } : {},
115
- votes: normalizedVotes
116
- };
117
- }
118
- function parseLegacyPollStore(value) {
119
- if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value) || value.version !== 1 || !(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value.polls)) return null;
120
- const polls = {};
121
- for (const [pollId, poll] of Object.entries(value.polls)) {
122
- const parsed = parseLegacyPoll(poll);
123
- if (parsed) polls[pollId] = parsed;
124
- }
125
- return {
126
- version: 1,
127
- polls
128
- };
129
- }
130
- async function listLegacyLearningFiles(storePath) {
131
- let entries;
206
+ async function hasRetiredSource(filePath) {
132
207
  try {
133
- entries = await node_fs_promises.default.readdir(storePath, { withFileTypes: true });
134
- } catch {
135
- return [];
136
- }
137
- const suffix = ".learnings.json";
138
- const knownSessionKeys = await listKnownSessionKeys(storePath);
139
- const files = [];
140
- for (const entry of entries) {
141
- if (!entry.isFile() || !entry.name.endsWith(suffix)) continue;
142
- const fileStem = entry.name.slice(0, -15);
143
- const sessionKey = resolveLearningSessionKey(fileStem) ?? resolveLegacySanitizedSessionKey(fileStem, knownSessionKeys);
144
- const filePath = node_path.default.join(storePath, entry.name);
145
- try {
146
- const parsed = JSON.parse(await node_fs_promises.default.readFile(filePath, "utf8"));
147
- if (Array.isArray(parsed)) {
148
- const learnings = parsed.filter((item) => typeof item === "string");
149
- if (learnings.length > 0) files.push({
150
- storePath,
151
- sessionKey,
152
- filePath,
153
- learnings: learnings.slice(-10)
154
- });
155
- }
156
- } catch {}
208
+ await node_fs_promises.default.lstat(filePath);
209
+ return true;
210
+ } catch (error) {
211
+ if ((0, openclaw_plugin_sdk_error_runtime.extractErrorCode)(error) === "ENOENT") return false;
212
+ throw error;
157
213
  }
158
- return files;
159
214
  }
160
- function mergeLearnings(legacy, existing) {
161
- const seen = /* @__PURE__ */ new Set();
162
- const merged = [];
163
- for (const learning of [...legacy, ...existing?.learnings ?? []]) {
164
- if (seen.has(learning)) continue;
165
- seen.add(learning);
166
- merged.push(learning);
215
+ async function hasRetiredLearnings(params) {
216
+ for (const storePath of listCandidateStorePaths(params)) try {
217
+ if ((await node_fs_promises.default.stat(storePath)).isDirectory() && (await node_fs_promises.default.readdir(storePath)).some((name) => name.endsWith(".learnings.json"))) return true;
218
+ } catch (error) {
219
+ if ((0, openclaw_plugin_sdk_error_runtime.extractErrorCode)(error) !== "ENOENT") throw error;
167
220
  }
168
- return merged.slice(-10);
221
+ return false;
169
222
  }
170
- async function completeLegacyKeyedImport(params) {
171
- const { filePath, label, imported, warnings } = params;
172
- const changes = [];
173
- let missing = 0;
174
- for (const { store, requiredKeys } of params.stores) {
175
- const retainedKeys = new Set((await store.entries()).map((entry) => entry.key));
176
- missing += [...requiredKeys].filter((key) => !retainedKeys.has(key)).length;
177
- }
178
- if (missing > 0) {
179
- warnings.push(`Incomplete ${label} migration: plugin state failed to retain every required entry (${missing} missing); left legacy source in place`);
180
- return {
181
- changes,
182
- warnings
183
- };
184
- }
185
- changes.push(`Migrated ${imported} ${label} ${imported === 1 ? "entry" : "entries"} -> plugin state`);
186
- await (0, openclaw_plugin_sdk_runtime_doctor_migrations.archiveLegacyStateSource)({
187
- filePath,
188
- label: params.archiveLabel ?? label,
189
- changes,
190
- warnings
191
- });
223
+ function retiredJsonMigration(name, label, hasSource) {
224
+ const guidance = `Microsoft Teams ${label} JSON state predates June 2026. Install OpenClaw 2026.9.5, run "openclaw doctor --fix", then upgrade to latest. The legacy source was left untouched.`;
192
225
  return {
193
- changes,
194
- warnings
195
- };
196
- }
197
- const stateMigrations = [
198
- {
199
- id: "msteams-conversations-json-to-plugin-state",
200
- label: "Microsoft Teams conversations",
201
- async detectLegacyState(params) {
202
- const state = await readLegacyJsonFile(resolveStateFilePath(params.stateDir, require_polls.MSTEAMS_CONVERSATIONS_LEGACY_FILENAME), parseLegacyConversationStore);
203
- if (!state || Object.keys(state.conversations).length === 0) return null;
204
- return { preview: [`- ${MSTEAMS_PLUGIN_ID} conversations: ${Object.keys(state.conversations).length} entries -> plugin state (${require_polls.MSTEAMS_CONVERSATIONS_NAMESPACE})`] };
205
- },
206
- async migrateLegacyState(params) {
207
- const filePath = resolveStateFilePath(params.stateDir, require_polls.MSTEAMS_CONVERSATIONS_LEGACY_FILENAME);
208
- const state = await readLegacyJsonFile(filePath, parseLegacyConversationStore);
209
- if (!state) return {
210
- changes: [],
211
- warnings: []
212
- };
213
- const store = params.context.openPluginStateKeyedStore({
214
- namespace: require_polls.MSTEAMS_CONVERSATIONS_NAMESPACE,
215
- maxEntries: require_polls.MSTEAMS_SQLITE_MAX_CONVERSATION_ROWS
216
- });
217
- const requiredKeys = new Set((await store.entries()).map((entry) => entry.key));
218
- let imported = 0;
219
- for (const [rawConversationId, reference] of require_polls.selectRetainedMSTeamsConversations(state.conversations)) {
220
- const conversationId = require_polls.normalizeStoredConversationId(rawConversationId);
221
- if (!conversationId) continue;
222
- const key = require_polls.buildMSTeamsConversationStateKey(conversationId);
223
- requiredKeys.add(key);
224
- const storedReference = require_polls.prepareMSTeamsConversationReferenceForStorage(conversationId, reference);
225
- if (await store.registerIfAbsent(key, storedReference)) imported++;
226
- }
227
- return completeLegacyKeyedImport({
228
- filePath,
229
- label: `${MSTEAMS_PLUGIN_ID} conversation`,
230
- imported,
231
- warnings: [],
232
- stores: [{
233
- store,
234
- requiredKeys
235
- }]
236
- });
237
- }
238
- },
239
- {
240
- id: "msteams-polls-json-to-plugin-state",
241
- label: "Microsoft Teams polls",
242
- async detectLegacyState(params) {
243
- const state = await readLegacyJsonFile(resolveStateFilePath(params.stateDir, require_polls.MSTEAMS_POLLS_LEGACY_FILENAME), parseLegacyPollStore);
244
- if (!state || Object.keys(state.polls).length === 0) return null;
245
- return { preview: [`- ${MSTEAMS_PLUGIN_ID} polls: ${Object.keys(state.polls).length} entries -> plugin state (${require_polls.MSTEAMS_POLLS_NAMESPACE})`] };
246
- },
247
- async migrateLegacyState(params) {
248
- return await require_polls.withMSTeamsSqliteMutationLock({ stateDir: params.stateDir }, require_polls.MSTEAMS_POLLS_NAMESPACE, async () => {
249
- const filePath = resolveStateFilePath(params.stateDir, require_polls.MSTEAMS_POLLS_LEGACY_FILENAME);
250
- const state = await readLegacyJsonFile(filePath, parseLegacyPollStore);
251
- if (!state) return {
252
- changes: [],
253
- warnings: []
254
- };
255
- const pollStore = params.context.openPluginStateKeyedStore({
256
- namespace: require_polls.MSTEAMS_POLLS_NAMESPACE,
257
- maxEntries: require_polls.MSTEAMS_SQLITE_MAX_POLL_ROWS
258
- });
259
- const voteBucketStore = params.context.openPluginStateKeyedStore({
260
- namespace: require_polls.MSTEAMS_POLL_VOTE_BUCKETS_NAMESPACE,
261
- maxEntries: require_polls.MSTEAMS_MAX_POLL_VOTE_BUCKET_ROWS
262
- });
263
- const requiredPollKeys = new Set((await pollStore.entries()).map((entry) => entry.key));
264
- const requiredVoteKeys = new Set((await voteBucketStore.entries()).map((entry) => entry.key));
265
- let imported = 0;
266
- for (const [pollId, poll] of require_polls.selectRetainedMSTeamsPolls(state.polls)) {
267
- const { metadata, votes } = require_polls.splitMSTeamsPoll(poll);
268
- const pollKey = require_polls.buildMSTeamsPollStateKey(pollId);
269
- requiredPollKeys.add(pollKey);
270
- const didImportPoll = await pollStore.registerIfAbsent(pollKey, metadata);
271
- const buckets = /* @__PURE__ */ new Map();
272
- for (const [voterId, selections] of Object.entries(votes)) {
273
- const bucket = require_polls.selectMSTeamsPollVoteBucket(pollId, voterId);
274
- const bucketVotes = buckets.get(bucket) ?? {};
275
- bucketVotes[voterId] = selections;
276
- buckets.set(bucket, bucketVotes);
277
- }
278
- let importedVoteBucket = false;
279
- for (const [bucket, bucketVotes] of buckets) {
280
- const key = require_polls.buildMSTeamsPollVoteBucketKey(pollId, bucket);
281
- requiredVoteKeys.add(key);
282
- const existing = await voteBucketStore.lookup(key);
283
- await voteBucketStore.register(key, {
284
- pollId,
285
- bucket,
286
- votes: {
287
- ...bucketVotes,
288
- ...existing?.votes
289
- },
290
- updatedAt: poll.updatedAt ?? poll.createdAt
291
- });
292
- importedVoteBucket = true;
293
- }
294
- if (didImportPoll || importedVoteBucket) imported++;
295
- }
296
- return completeLegacyKeyedImport({
297
- filePath,
298
- label: `${MSTEAMS_PLUGIN_ID} poll`,
299
- imported,
300
- warnings: [],
301
- stores: [{
302
- store: pollStore,
303
- requiredKeys: requiredPollKeys
304
- }, {
305
- store: voteBucketStore,
306
- requiredKeys: requiredVoteKeys
307
- }]
308
- });
309
- });
310
- }
311
- },
312
- {
313
- id: "msteams-sso-tokens-json-to-plugin-state",
314
- label: "Microsoft Teams SSO tokens",
226
+ id: `msteams-${name}-json-to-plugin-state`,
227
+ label: `Microsoft Teams ${label}`,
315
228
  async detectLegacyState(params) {
316
- const state = await readLegacyJsonFile(resolveStateFilePath(params.stateDir, require_sso_token_store.MSTEAMS_SSO_TOKENS_LEGACY_FILENAME), (value) => require_sso_token_store.isMSTeamsSsoStoreData(value) ? value : null);
317
- if (!state || Object.keys(state.tokens).length === 0) return null;
318
- return { preview: [`- ${MSTEAMS_PLUGIN_ID} SSO tokens: ${Object.keys(state.tokens).length} entries -> plugin state (${require_sso_token_store.MSTEAMS_SSO_TOKENS_NAMESPACE})`] };
229
+ return await hasSource(params) ? { preview: [guidance] } : null;
319
230
  },
320
231
  async migrateLegacyState(params) {
321
- const warnings = [];
322
- const filePath = resolveStateFilePath(params.stateDir, require_sso_token_store.MSTEAMS_SSO_TOKENS_LEGACY_FILENAME);
323
- const state = await readLegacyJsonFile(filePath, (value) => require_sso_token_store.isMSTeamsSsoStoreData(value) ? value : null);
324
- if (!state) return {
232
+ return {
325
233
  changes: [],
326
- warnings
234
+ warnings: await hasSource(params) ? [guidance] : []
327
235
  };
328
- const store = params.context.openPluginStateKeyedStore({
329
- namespace: require_sso_token_store.MSTEAMS_SSO_TOKENS_NAMESPACE,
330
- maxEntries: require_sso_token_store.MSTEAMS_MAX_SSO_TOKENS
331
- });
332
- const requiredKeys = new Set((await store.entries()).map((entry) => entry.key));
333
- let imported = 0;
334
- let skipped = 0;
335
- for (const token of Object.values(state.tokens)) {
336
- const normalized = require_sso_token_store.normalizeMSTeamsSsoStoredToken(token);
337
- if (!normalized) {
338
- skipped++;
339
- continue;
340
- }
341
- const key = require_sso_token_store.makeMSTeamsSsoTokenStoreKey(normalized.connectionName, normalized.userId);
342
- requiredKeys.add(key);
343
- if (await store.registerIfAbsent(key, normalized)) imported++;
344
- }
345
- if (skipped > 0) warnings.push(`Skipped ${skipped} malformed ${MSTEAMS_PLUGIN_ID} SSO token ${skipped === 1 ? "entry" : "entries"} during migration`);
346
- return completeLegacyKeyedImport({
347
- filePath,
348
- label: `${MSTEAMS_PLUGIN_ID} SSO token`,
349
- archiveLabel: `${MSTEAMS_PLUGIN_ID} SSO-token`,
350
- imported,
351
- warnings,
352
- stores: [{
353
- store,
354
- requiredKeys
355
- }]
356
- });
357
236
  }
358
- },
237
+ };
238
+ }
239
+ const stateMigrations = [
240
+ retiredJsonMigration("conversations", "conversations", (params) => hasRetiredSource(node_path.default.join(params.stateDir, "msteams-conversations.json"))),
241
+ retiredJsonMigration("polls", "polls", (params) => hasRetiredSource(node_path.default.join(params.stateDir, "msteams-polls.json"))),
242
+ retiredJsonMigration("sso-tokens", "SSO tokens", (params) => hasRetiredSource(node_path.default.join(params.stateDir, "msteams-sso-tokens.json"))),
359
243
  {
360
244
  id: "msteams-delegated-token-json-to-plugin-state",
361
245
  label: "Microsoft Teams delegated OAuth token",
362
246
  async detectLegacyState(params) {
363
- const filePath = resolveStateFilePath(params.stateDir, require_delegated_state.MSTEAMS_DELEGATED_TOKEN_LEGACY_FILENAME);
247
+ const filePath = node_path.default.join(params.stateDir, require_delegated_state.MSTEAMS_DELEGATED_TOKEN_LEGACY_FILENAME);
364
248
  try {
365
249
  return (await node_fs_promises.default.stat(filePath)).isFile() ? { preview: [`- ${MSTEAMS_PLUGIN_ID} delegated OAuth token -> plugin state (${require_delegated_state.MSTEAMS_DELEGATED_TOKEN_NAMESPACE})`] } : null;
366
250
  } catch {
@@ -370,7 +254,7 @@ const stateMigrations = [
370
254
  async migrateLegacyState(params) {
371
255
  const changes = [];
372
256
  const warnings = [];
373
- const filePath = resolveStateFilePath(params.stateDir, require_delegated_state.MSTEAMS_DELEGATED_TOKEN_LEGACY_FILENAME);
257
+ const filePath = node_path.default.join(params.stateDir, require_delegated_state.MSTEAMS_DELEGATED_TOKEN_LEGACY_FILENAME);
374
258
  let token;
375
259
  try {
376
260
  token = require_delegated_state.normalizeMSTeamsDelegatedTokens(JSON.parse(await node_fs_promises.default.readFile(filePath, "utf8")));
@@ -423,7 +307,7 @@ const stateMigrations = [
423
307
  };
424
308
  }
425
309
  changes.push(`Migrated ${MSTEAMS_PLUGIN_ID} delegated OAuth token -> plugin state`);
426
- await (0, openclaw_plugin_sdk_runtime_doctor_migrations.archiveLegacyStateSource)({
310
+ await (0, runtime_doctor_migrations_exports.archiveLegacyStateSource)({
427
311
  filePath,
428
312
  label: `${MSTEAMS_PLUGIN_ID} delegated OAuth token`,
429
313
  changes,
@@ -435,61 +319,7 @@ const stateMigrations = [
435
319
  };
436
320
  }
437
321
  },
438
- {
439
- id: "msteams-feedback-learnings-json-to-plugin-state",
440
- label: "Microsoft Teams feedback learnings",
441
- async detectLegacyState(params) {
442
- const files = (await Promise.all(listCandidateStorePaths(params).map((storePath) => listLegacyLearningFiles(storePath)))).flat();
443
- if (files.length === 0) return null;
444
- return { preview: [`- Microsoft Teams feedback learnings: ${files.length} ${files.length === 1 ? "file" : "files"} -> plugin state (${LEARNINGS_NAMESPACE})`] };
445
- },
446
- async migrateLegacyState(params) {
447
- const changes = [];
448
- const warnings = [];
449
- const files = (await Promise.all(listCandidateStorePaths(params).map((storePath) => listLegacyLearningFiles(storePath)))).flat();
450
- const store = params.context.openPluginStateKeyedStore({
451
- namespace: LEARNINGS_NAMESPACE,
452
- maxEntries: MAX_LEARNING_ENTRIES
453
- });
454
- const existingEntries = await store.entries();
455
- const existingKeys = new Set(existingEntries.map((entry) => entry.key));
456
- const importableFiles = files.filter((file) => file.sessionKey);
457
- const missingKeys = new Set(importableFiles.map((file) => learningStoreKey(file.storePath, file.sessionKey ?? "")).filter((key) => !existingKeys.has(key)));
458
- if (missingKeys.size > MAX_LEARNING_ENTRIES - existingKeys.size) {
459
- 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`);
460
- return {
461
- changes,
462
- warnings
463
- };
464
- }
465
- let imported = 0;
466
- for (const file of files) {
467
- if (!file.sessionKey) {
468
- warnings.push(`Left Microsoft Teams feedback-learning source in place because its legacy filename cannot be mapped to a session key: ${file.filePath}`);
469
- continue;
470
- }
471
- const key = learningStoreKey(file.storePath, file.sessionKey);
472
- const existing = await store.lookup(key);
473
- await store.register(key, {
474
- sessionKey: existing?.sessionKey ?? file.sessionKey,
475
- learnings: mergeLearnings(file.learnings, existing),
476
- updatedAt: Date.now()
477
- });
478
- imported++;
479
- await (0, openclaw_plugin_sdk_runtime_doctor_migrations.archiveLegacyStateSource)({
480
- filePath: file.filePath,
481
- label: "Microsoft Teams feedback-learning",
482
- changes,
483
- warnings
484
- });
485
- }
486
- if (imported > 0) changes.unshift(`Migrated ${imported} Microsoft Teams feedback-learning ${imported === 1 ? "entry" : "entries"} -> plugin state`);
487
- return {
488
- changes,
489
- warnings
490
- };
491
- }
492
- }
322
+ retiredJsonMigration("feedback-learnings", "feedback learnings", hasRetiredLearnings)
493
323
  ];
494
324
  //#endregion
495
325
  exports.legacyConfigRules = legacyConfigRules;
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let openclaw_plugin_sdk_qa_runner_runtime = require("openclaw/plugin-sdk/qa-runner-runtime");
3
3
  //#region extensions/msteams/src/qa/cli.ts
4
4
  const DEFAULT_MSTEAMS_QA_SCENARIOS = ["channel-canary"];
5
- const loadMSTeamsQaAdapterRuntime = (0, openclaw_plugin_sdk_qa_runner_runtime.createLazyCliRuntimeLoader)(() => Promise.resolve().then(() => require("./.setup/adapter.runtime-CQWZeqB5.cjs")));
5
+ const loadMSTeamsQaAdapterRuntime = (0, openclaw_plugin_sdk_qa_runner_runtime.createLazyCliRuntimeLoader)(() => Promise.resolve().then(() => require("./.setup/adapter.runtime-BiWeMKXE.cjs")));
6
6
  async function runQaMSTeams(options) {
7
7
  await (0, openclaw_plugin_sdk_qa_runner_runtime.runLiveTransportQaSuiteCommand)({
8
8
  channelId: "msteams",
@@ -114,12 +114,6 @@ Object.defineProperty(exports, "isDangerousNameMatchingEnabled", {
114
114
  return openclaw_plugin_sdk_dangerous_name_runtime.isDangerousNameMatchingEnabled;
115
115
  }
116
116
  });
117
- Object.defineProperty(exports, "keepHttpServerTaskAlive", {
118
- enumerable: true,
119
- get: function() {
120
- return openclaw_plugin_sdk_channel_outbound.keepHttpServerTaskAlive;
121
- }
122
- });
123
117
  Object.defineProperty(exports, "loadOutboundMediaFromUrl", {
124
118
  enumerable: true,
125
119
  get: function() {
@@ -4,7 +4,7 @@ let node_url = require("node:url");
4
4
  var setup_entry_default = (0, openclaw_plugin_sdk_channel_entry_contract.defineBundledChannelSetupEntry)({
5
5
  importMetaUrl: require("url").pathToFileURL(__filename).href,
6
6
  plugin: {
7
- specifier: (0, node_url.fileURLToPath)(new URL(".setup/setup-plugin-api-DpiaA8z1.cjs", require("url").pathToFileURL(__filename).href).href),
7
+ specifier: (0, node_url.fileURLToPath)(new URL(".setup/setup-plugin-api-Dyf8kF-l.cjs", require("url").pathToFileURL(__filename).href).href),
8
8
  exportName: "msteamsSetupPlugin"
9
9
  },
10
10
  secrets: {
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_channel_setup = require("./.setup/channel.setup-BVDfgdCc.cjs");
2
+ const require_channel_setup = require("./.setup/channel.setup-DL8lcWT_.cjs");
3
3
  exports.msteamsSetupPlugin = require_channel_setup.msteamsSetupPlugin;