@openclaw/msteams 2026.9.8 → 2026.10.1-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/.setup/{adapter.runtime-BiWeMKXE.cjs → adapter.runtime-D0HYByze.cjs} +9 -7
- package/dist/.setup/{approval-handler.runtime-D1s7UR3U.cjs → approval-handler.runtime-C-RJ3wXc.cjs} +3 -3
- package/dist/.setup/{channel-ByzS7QJ0.cjs → channel-BFhXodl2.cjs} +61 -75
- package/dist/.setup/{channel.runtime-Baz8XDFs.cjs → channel.runtime-D9jrw2SN.cjs} +22 -46
- package/dist/.setup/{channel.setup-DL8lcWT_.cjs → channel.setup-ss0TO-ap.cjs} +30 -36
- package/dist/.setup/{conversation-store-state-B7JuyZTm.cjs → conversation-store-state-f7V8CVNy.cjs} +62 -128
- package/dist/.setup/{monitor-BJ7IyH4y.cjs → monitor-DfSPCRkk.cjs} +71 -171
- package/dist/.setup/{oauth-DQjOnJ4l.cjs → oauth-CYxMYIOQ.cjs} +1 -1
- package/dist/.setup/{reaction-types-CQ4oSUe7.cjs → reaction-types-B-GEv57k.cjs} +1 -1
- package/dist/.setup/{resolve-allowlist-Pl_NQFYH.cjs → resolve-allowlist-Ct9yxQUw.cjs} +16 -134
- package/dist/.setup/{send-VUM7Sryb.cjs → send-7VV1iCai.cjs} +24 -70
- package/dist/.setup/{setup-plugin-api-Dyf8kF-l.cjs → setup-plugin-api-B0OFG32J.cjs} +1 -1
- package/dist/.setup/{thread-session--rw3BTal.cjs → thread-session-ClI-lSyG.cjs} +1 -1
- package/dist/.setup/token-config-DOt3XwVa.cjs +139 -0
- package/dist/api.cjs +2 -2
- package/dist/channel-plugin-api.cjs +1 -1
- package/dist/directory-contract-api.cjs +3 -2
- package/dist/doctor-backup-resources.cjs +21 -0
- package/dist/doctor-contract-api.cjs +37 -19
- package/dist/qa-runner-api.cjs +1 -1
- package/dist/runtime-api.cjs +0 -20
- package/dist/setup-entry.cjs +1 -1
- package/dist/setup-plugin-api.cjs +1 -1
- package/package.json +4 -4
- package/dist/.setup/delegated-state-u-ywZyZT.cjs +0 -70
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_resolve_allowlist = require("./resolve-allowlist-
|
|
2
|
-
const
|
|
1
|
+
const require_resolve_allowlist = require("./resolve-allowlist-Ct9yxQUw.cjs");
|
|
2
|
+
const require_token_config = require("./token-config-DOt3XwVa.cjs");
|
|
3
3
|
const require_config_schema = require("./config-schema-0KoBzb0t.cjs");
|
|
4
4
|
let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
|
|
5
5
|
let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
|
|
@@ -26,7 +26,7 @@ const msteamsMeta = {
|
|
|
26
26
|
};
|
|
27
27
|
function resolveMSTeamsAccount(cfg) {
|
|
28
28
|
const config = cfg.channels?.msteams;
|
|
29
|
-
const credentials =
|
|
29
|
+
const credentials = require_token_config.resolveMSTeamsCredentials(config);
|
|
30
30
|
const certificatePath = credentials?.type === "federated" && !credentials.useManagedIdentity ? credentials.certificatePath : void 0;
|
|
31
31
|
const certificate = certificatePath ? (0, openclaw_plugin_sdk_secret_file_runtime.tryReadSecretFileSync)(certificatePath, "Microsoft Teams certificate", void 0, { configPath: config?.certificatePath?.trim() ? "channels.msteams.certificatePath" : "env.MSTEAMS_CERTIFICATE_PATH" }) : void 0;
|
|
32
32
|
const unavailable = certificate?.status === "configured_unavailable";
|
|
@@ -95,39 +95,36 @@ function createMSTeamsSetupWizardBase() {
|
|
|
95
95
|
configuredScore: 2,
|
|
96
96
|
unconfiguredScore: 0,
|
|
97
97
|
includeStatusLine: true,
|
|
98
|
-
resolveConfigured: ({ cfg }) => Boolean(
|
|
98
|
+
resolveConfigured: ({ cfg }) => Boolean(require_token_config.resolveMSTeamsCredentials(cfg.channels?.msteams)) || require_token_config.hasConfiguredMSTeamsCredentials(cfg.channels?.msteams)
|
|
99
99
|
}),
|
|
100
100
|
credentials: [],
|
|
101
101
|
finalize: async ({ cfg, prompter }) => {
|
|
102
|
-
const resolved =
|
|
103
|
-
const hasConfigCreds =
|
|
102
|
+
const resolved = require_token_config.resolveMSTeamsCredentials(cfg.channels?.msteams);
|
|
103
|
+
const hasConfigCreds = require_token_config.hasConfiguredMSTeamsCredentials(cfg.channels?.msteams);
|
|
104
104
|
const canUseEnv = Boolean(!hasConfigCreds && (0, openclaw_plugin_sdk_secret_input.normalizeSecretInputString)(process.env.MSTEAMS_APP_ID) && (0, openclaw_plugin_sdk_secret_input.normalizeSecretInputString)(process.env.MSTEAMS_APP_PASSWORD) && (0, openclaw_plugin_sdk_secret_input.normalizeSecretInputString)(process.env.MSTEAMS_TENANT_ID));
|
|
105
105
|
let next = cfg;
|
|
106
|
-
let appId = null;
|
|
107
|
-
let appPassword = null;
|
|
108
|
-
let tenantId = null;
|
|
109
106
|
if (!resolved && !hasConfigCreds) await noteMSTeamsCredentialHelp(prompter);
|
|
110
|
-
if (canUseEnv || hasConfigCreds) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
})) next = msteamsSetupAdapter.applyAccountConfig({
|
|
115
|
-
cfg: next,
|
|
116
|
-
accountId: openclaw_plugin_sdk_setup.DEFAULT_ACCOUNT_ID,
|
|
117
|
-
input: {}
|
|
118
|
-
});
|
|
119
|
-
else ({appId, appPassword, tenantId} = await promptMSTeamsCredentials(prompter));
|
|
120
|
-
} else ({appId, appPassword, tenantId} = await promptMSTeamsCredentials(prompter));
|
|
121
|
-
if (appId && appPassword && tenantId) next = (0, openclaw_plugin_sdk_setup.patchTopLevelChannelConfigSection)({
|
|
107
|
+
if ((canUseEnv || hasConfigCreds) && await prompter.confirm({
|
|
108
|
+
message: t$1(canUseEnv ? "wizard.msteams.envPrompt" : "wizard.msteams.credentialsKeep"),
|
|
109
|
+
initialValue: true
|
|
110
|
+
})) next = msteamsSetupAdapter.applyAccountConfig({
|
|
122
111
|
cfg: next,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
patch: {
|
|
126
|
-
appId,
|
|
127
|
-
appPassword,
|
|
128
|
-
tenantId
|
|
129
|
-
}
|
|
112
|
+
accountId: openclaw_plugin_sdk_setup.DEFAULT_ACCOUNT_ID,
|
|
113
|
+
input: {}
|
|
130
114
|
});
|
|
115
|
+
else {
|
|
116
|
+
const { appId, appPassword, tenantId } = await promptMSTeamsCredentials(prompter);
|
|
117
|
+
if (appId && appPassword && tenantId) next = (0, openclaw_plugin_sdk_setup.patchTopLevelChannelConfigSection)({
|
|
118
|
+
cfg: next,
|
|
119
|
+
channel: channel$1,
|
|
120
|
+
enabled: true,
|
|
121
|
+
patch: {
|
|
122
|
+
appId,
|
|
123
|
+
appPassword,
|
|
124
|
+
tenantId
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
131
128
|
return {
|
|
132
129
|
cfg: next,
|
|
133
130
|
accountId: openclaw_plugin_sdk_setup.DEFAULT_ACCOUNT_ID
|
|
@@ -140,12 +137,9 @@ function createMSTeamsSetupWizardBase() {
|
|
|
140
137
|
const MAX_SAFE_RETRY_AFTER_SECONDS = Number.MAX_SAFE_INTEGER / 1e3;
|
|
141
138
|
function formatUnknownError(err) {
|
|
142
139
|
if (err instanceof Error) return err.message;
|
|
143
|
-
if (typeof err === "string") return err;
|
|
144
|
-
if (err === null) return "null";
|
|
145
|
-
if (err === void 0) return "undefined";
|
|
146
|
-
if (typeof err === "number" || typeof err === "boolean" || typeof err === "bigint") return String(err);
|
|
147
140
|
if (typeof err === "symbol") return err.description ?? err.toString();
|
|
148
141
|
if (typeof err === "function") return err.name ? `[function ${err.name}]` : "[function]";
|
|
142
|
+
if (err === null || typeof err !== "object") return String(err);
|
|
149
143
|
try {
|
|
150
144
|
return JSON.stringify(err) ?? "unknown error";
|
|
151
145
|
} catch {
|
|
@@ -402,7 +396,7 @@ function listMSTeamsGroupEntries(cfg) {
|
|
|
402
396
|
}
|
|
403
397
|
async function resolveMSTeamsGroupAllowlist(params) {
|
|
404
398
|
let resolvedEntries = params.entries.map((entry) => require_resolve_allowlist.parseMSTeamsTeamEntry(entry)).filter(Boolean);
|
|
405
|
-
if (params.entries.length === 0 || !
|
|
399
|
+
if (params.entries.length === 0 || !require_token_config.resolveMSTeamsCredentials(params.cfg.channels?.msteams)) return resolvedEntries;
|
|
406
400
|
try {
|
|
407
401
|
const lookups = await require_resolve_allowlist.resolveMSTeamsChannelAllowlist({
|
|
408
402
|
cfg: params.cfg,
|
|
@@ -459,7 +453,7 @@ const msteamsSetupWizard = {
|
|
|
459
453
|
const baseFinalize = msteamsSetupWizardBase.finalize;
|
|
460
454
|
const baseResult = baseFinalize ? await baseFinalize(params) : void 0;
|
|
461
455
|
let next = baseResult?.cfg ?? params.cfg;
|
|
462
|
-
const finalCreds =
|
|
456
|
+
const finalCreds = require_token_config.resolveMSTeamsCredentials(next.channels?.msteams);
|
|
463
457
|
if (finalCreds?.type === "secret") {
|
|
464
458
|
if (await params.prompter.confirm({
|
|
465
459
|
message: t("wizard.msteams.delegatedAuthPrompt"),
|
|
@@ -475,7 +469,7 @@ const msteamsSetupWizard = {
|
|
|
475
469
|
};
|
|
476
470
|
let oauthModule;
|
|
477
471
|
try {
|
|
478
|
-
oauthModule = await Promise.resolve().then(() => require("./oauth-
|
|
472
|
+
oauthModule = await Promise.resolve().then(() => require("./oauth-CYxMYIOQ.cjs"));
|
|
479
473
|
} catch (err) {
|
|
480
474
|
await noteDelegatedAuthFailure(err);
|
|
481
475
|
return {
|
|
@@ -515,7 +509,7 @@ const msteamsSetupWizard = {
|
|
|
515
509
|
progress.stop();
|
|
516
510
|
throw err;
|
|
517
511
|
}
|
|
518
|
-
await
|
|
512
|
+
await require_token_config.saveMSTeamsDelegatedTokens(tokens);
|
|
519
513
|
progress.stop(t("wizard.msteams.delegatedAuthConfigured"));
|
|
520
514
|
}
|
|
521
515
|
}
|
package/dist/.setup/{conversation-store-state-B7JuyZTm.cjs → conversation-store-state-f7V8CVNy.cjs}
RENAMED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-BloeesfJ.cjs");
|
|
2
2
|
const require_runtime = require("./runtime-gZTAuFux.cjs");
|
|
3
3
|
require("../runtime-api.cjs");
|
|
4
|
-
const require_resolve_allowlist = require("./resolve-allowlist-
|
|
4
|
+
const require_resolve_allowlist = require("./resolve-allowlist-Ct9yxQUw.cjs");
|
|
5
5
|
const require_bot_framework_service_url = require("./bot-framework-service-url-BUhTe8F3.cjs");
|
|
6
|
-
const require_channel_setup = require("./channel.setup-
|
|
6
|
+
const require_channel_setup = require("./channel.setup-ss0TO-ap.cjs");
|
|
7
7
|
let openclaw_plugin_sdk_lazy_runtime = require("openclaw/plugin-sdk/lazy-runtime");
|
|
8
8
|
let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
|
|
9
9
|
let openclaw_plugin_sdk_fetch_runtime = require("openclaw/plugin-sdk/fetch-runtime");
|
|
10
10
|
let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
|
|
11
|
-
let openclaw_plugin_sdk_file_lock = require("openclaw/plugin-sdk/file-lock");
|
|
12
11
|
let openclaw_plugin_sdk_reply_payload = require("openclaw/plugin-sdk/reply-payload");
|
|
13
12
|
let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
|
|
14
13
|
let openclaw_plugin_sdk_text_chunking = require("openclaw/plugin-sdk/text-chunking");
|
|
@@ -25,6 +24,7 @@ let openclaw_plugin_sdk_markdown_table_runtime = require("openclaw/plugin-sdk/ma
|
|
|
25
24
|
let openclaw_plugin_sdk_blob_runtime = require("openclaw/plugin-sdk/blob-runtime");
|
|
26
25
|
let node_crypto = require("node:crypto");
|
|
27
26
|
node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
|
|
27
|
+
let openclaw_plugin_sdk_file_lock = require("openclaw/plugin-sdk/file-lock");
|
|
28
28
|
let openclaw_plugin_sdk_keyed_async_queue = require("openclaw/plugin-sdk/keyed-async-queue");
|
|
29
29
|
let openclaw_plugin_sdk_global_singleton = require("openclaw/plugin-sdk/global-singleton");
|
|
30
30
|
let openclaw_plugin_sdk_expect_runtime = require("openclaw/plugin-sdk/expect-runtime");
|
|
@@ -53,15 +53,6 @@ const CONSENT_UPLOAD_HOST_ALLOWLIST = [
|
|
|
53
53
|
"graph.microsoft.de",
|
|
54
54
|
"graph.microsoft.cn"
|
|
55
55
|
];
|
|
56
|
-
/**
|
|
57
|
-
* Validate that a consent upload URL is safe to PUT to.
|
|
58
|
-
* Checks:
|
|
59
|
-
* 1. Protocol is HTTPS
|
|
60
|
-
* 2. Hostname matches the consent upload allowlist
|
|
61
|
-
* 3. Resolved IP is not in a private/reserved range (anti-SSRF)
|
|
62
|
-
*
|
|
63
|
-
* @throws Error if the URL fails validation
|
|
64
|
-
*/
|
|
65
56
|
async function validateConsentUploadUrl(url, opts) {
|
|
66
57
|
let parsed;
|
|
67
58
|
try {
|
|
@@ -119,10 +110,6 @@ function buildFileInfoCard(params) {
|
|
|
119
110
|
}
|
|
120
111
|
};
|
|
121
112
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Parse a fileConsent/invoke activity.
|
|
124
|
-
* Returns null if the activity is not a file consent invoke.
|
|
125
|
-
*/
|
|
126
113
|
function parseFileConsentInvoke(activity) {
|
|
127
114
|
if (activity.name !== "fileConsent/invoke") return null;
|
|
128
115
|
const value = activity.value;
|
|
@@ -136,8 +123,6 @@ function parseFileConsentInvoke(activity) {
|
|
|
136
123
|
/**
|
|
137
124
|
* Upload a file to the consent URL provided by Teams.
|
|
138
125
|
* The URL is provided in the fileConsent/invoke response after user accepts.
|
|
139
|
-
*
|
|
140
|
-
* @throws Error if the URL fails SSRF validation (non-HTTPS, disallowed host, private IP)
|
|
141
126
|
*/
|
|
142
127
|
async function uploadToConsentUrl(params) {
|
|
143
128
|
await validateConsentUploadUrl(params.url, params.validationOpts);
|
|
@@ -435,6 +420,7 @@ function setPendingUploadActivityId(uploadId, activityId) {
|
|
|
435
420
|
}
|
|
436
421
|
//#endregion
|
|
437
422
|
//#region extensions/msteams/src/file-consent-helpers.ts
|
|
423
|
+
const FILE_CONSENT_THRESHOLD_BYTES = 4194304;
|
|
438
424
|
function buildConsentActivity(params) {
|
|
439
425
|
const { media, description, uploadId } = params;
|
|
440
426
|
return {
|
|
@@ -447,15 +433,7 @@ function buildConsentActivity(params) {
|
|
|
447
433
|
})]
|
|
448
434
|
};
|
|
449
435
|
}
|
|
450
|
-
/**
|
|
451
|
-
* Prepare a FileConsentCard activity for large files or non-images in personal chats.
|
|
452
|
-
* Returns the activity object and uploadId - caller is responsible for sending.
|
|
453
|
-
*
|
|
454
|
-
* This variant only writes to the in-memory store. Use it when the caller and
|
|
455
|
-
* the `fileConsent/invoke` handler share the same process (for example the
|
|
456
|
-
* messenger reply path). For proactive CLI sends where the invoke arrives in
|
|
457
|
-
* a different process, use {@link prepareFileConsentActivityFs} instead.
|
|
458
|
-
*/
|
|
436
|
+
/** In-process replies keep consent bytes in memory; CLI sends use the persisted variant below. */
|
|
459
437
|
function prepareFileConsentActivity(params) {
|
|
460
438
|
const { media, conversationId, description } = params;
|
|
461
439
|
const uploadId = storePendingUpload({
|
|
@@ -473,42 +451,20 @@ function prepareFileConsentActivity(params) {
|
|
|
473
451
|
uploadId
|
|
474
452
|
};
|
|
475
453
|
}
|
|
476
|
-
/**
|
|
477
|
-
* Prepare a FileConsentCard activity and persist the pending upload to the
|
|
478
|
-
* filesystem so a different process can read it when the user accepts.
|
|
479
|
-
*
|
|
480
|
-
* This is used by the proactive CLI `message send --media` path: the CLI
|
|
481
|
-
* process sends the card and exits, but the `fileConsent/invoke` callback is
|
|
482
|
-
* delivered to the long-lived gateway monitor process. The FS-backed store
|
|
483
|
-
* bridges those two processes. The in-memory store is also populated so
|
|
484
|
-
* same-process flows keep the fast path.
|
|
485
|
-
*/
|
|
454
|
+
/** Persist consent bytes for callbacks received by another process after the CLI exits. */
|
|
486
455
|
async function prepareFileConsentActivityFs(params) {
|
|
487
|
-
const
|
|
488
|
-
const upload = {
|
|
489
|
-
buffer: media.buffer,
|
|
490
|
-
filename: media.filename,
|
|
491
|
-
contentType: media.contentType,
|
|
492
|
-
conversationId
|
|
493
|
-
};
|
|
494
|
-
const uploadId = storePendingUpload(upload);
|
|
456
|
+
const result = prepareFileConsentActivity(params);
|
|
495
457
|
await storePendingUploadFs({
|
|
496
|
-
id: uploadId,
|
|
497
|
-
...
|
|
458
|
+
id: result.uploadId,
|
|
459
|
+
...params.media,
|
|
460
|
+
conversationId: params.conversationId
|
|
498
461
|
});
|
|
499
|
-
return
|
|
500
|
-
activity: buildConsentActivity({
|
|
501
|
-
media,
|
|
502
|
-
description,
|
|
503
|
-
uploadId
|
|
504
|
-
}),
|
|
505
|
-
uploadId
|
|
506
|
-
};
|
|
462
|
+
return result;
|
|
507
463
|
}
|
|
508
464
|
function requiresFileConsent(params) {
|
|
509
465
|
const isPersonal = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationType) === "personal";
|
|
510
466
|
const isImage = params.contentType?.startsWith("image/") ?? false;
|
|
511
|
-
const isLargeFile = params.bufferSize >=
|
|
467
|
+
const isLargeFile = params.bufferSize >= FILE_CONSENT_THRESHOLD_BYTES;
|
|
512
468
|
return isPersonal && (isLargeFile || !isImage);
|
|
513
469
|
}
|
|
514
470
|
//#endregion
|
|
@@ -985,10 +941,7 @@ async function uploadAndShareSharePoint(params) {
|
|
|
985
941
|
}
|
|
986
942
|
//#endregion
|
|
987
943
|
//#region extensions/msteams/src/media-helpers.ts
|
|
988
|
-
|
|
989
|
-
* Detect MIME type from URL extension or data URL.
|
|
990
|
-
* Uses shared MIME detection for consistency with core handling.
|
|
991
|
-
*/
|
|
944
|
+
const MSTEAMS_MAX_MEDIA_BYTES = 104857600;
|
|
992
945
|
async function getMimeType(url) {
|
|
993
946
|
if (url.startsWith("data:")) {
|
|
994
947
|
const match = url.match(/^data:([^;,]+)/);
|
|
@@ -1027,9 +980,6 @@ async function extractFilename(url) {
|
|
|
1027
980
|
function isLocalPath(url) {
|
|
1028
981
|
return /^file:\/\//iu.test(url) || url.startsWith("/") || url.startsWith("~") || url.startsWith("\\") || /^[a-zA-Z]:[\\/]/.test(url);
|
|
1029
982
|
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Extract the message ID from a Bot Framework response.
|
|
1032
|
-
*/
|
|
1033
983
|
function extractMessageId(response) {
|
|
1034
984
|
if (!response || typeof response !== "object" || !("id" in response)) return null;
|
|
1035
985
|
const { id } = response;
|
|
@@ -1052,14 +1002,10 @@ function isValidTeamsId(id) {
|
|
|
1052
1002
|
return TEAMS_BOT_ID_PATTERN.test(id) || AAD_OBJECT_ID_PATTERN.test(id);
|
|
1053
1003
|
}
|
|
1054
1004
|
/**
|
|
1055
|
-
*
|
|
1056
|
-
* Example: "Hello @[John Doe](28:xxx-yyy-zzz)!"
|
|
1057
|
-
*
|
|
1005
|
+
* Convert @[Name](id) into matching <at> text and mention entities.
|
|
1058
1006
|
* Only matches where the id looks like a real Teams user/bot ID are treated
|
|
1059
1007
|
* as mentions. This avoids false positives from documentation or code samples
|
|
1060
1008
|
* embedded in the message (e.g. `@[表示名](ユーザーID)` in backticks).
|
|
1061
|
-
*
|
|
1062
|
-
* Returns both the formatted text with <at> tags and the entities array.
|
|
1063
1009
|
*/
|
|
1064
1010
|
function parseMentions(text) {
|
|
1065
1011
|
const mentionPattern = /@\[((?:\\[\s\S]|[^\]\\])+)\]\(([^)]+)\)/g;
|
|
@@ -1287,16 +1233,6 @@ async function deleteMSTeamsActivityWithReference(app, source, activityId, optio
|
|
|
1287
1233
|
}
|
|
1288
1234
|
//#endregion
|
|
1289
1235
|
//#region extensions/msteams/src/messenger.ts
|
|
1290
|
-
/**
|
|
1291
|
-
* MSTeams-specific media size limit (100MB).
|
|
1292
|
-
* Higher than the default to support Teams file-consent and SharePoint uploads.
|
|
1293
|
-
*/
|
|
1294
|
-
const MSTEAMS_MAX_MEDIA_BYTES = 104857600;
|
|
1295
|
-
/**
|
|
1296
|
-
* Threshold for large files that require FileConsentCard flow in personal chats.
|
|
1297
|
-
* Files >= 4MB use consent flow; smaller images can use inline base64.
|
|
1298
|
-
*/
|
|
1299
|
-
const FILE_CONSENT_THRESHOLD_BYTES = 4194304;
|
|
1300
1236
|
function buildConversationReference(ref) {
|
|
1301
1237
|
const conversationId = ref.conversation?.id?.trim();
|
|
1302
1238
|
if (!conversationId) throw new Error("Invalid stored reference: missing conversation.id");
|
|
@@ -1394,7 +1330,7 @@ async function buildActivity(msg, conversationRef, tokenProvider, sharePointSite
|
|
|
1394
1330
|
let contentType = await getMimeType(msg.mediaUrl);
|
|
1395
1331
|
let fileName = await extractFilename(msg.mediaUrl);
|
|
1396
1332
|
if (isLocalPath(msg.mediaUrl)) {
|
|
1397
|
-
const maxBytes = mediaMaxBytes ??
|
|
1333
|
+
const maxBytes = mediaMaxBytes ?? 104857600;
|
|
1398
1334
|
const media = await (0, openclaw_plugin_sdk_web_media.loadWebMedia)(msg.mediaUrl, maxBytes);
|
|
1399
1335
|
contentType = media.contentType ?? contentType;
|
|
1400
1336
|
fileName = media.fileName ?? fileName;
|
|
@@ -1404,8 +1340,7 @@ async function buildActivity(msg, conversationRef, tokenProvider, sharePointSite
|
|
|
1404
1340
|
if (requiresFileConsent({
|
|
1405
1341
|
conversationType,
|
|
1406
1342
|
contentType,
|
|
1407
|
-
bufferSize: media.buffer.length
|
|
1408
|
-
thresholdBytes: FILE_CONSENT_THRESHOLD_BYTES
|
|
1343
|
+
bufferSize: media.buffer.length
|
|
1409
1344
|
})) {
|
|
1410
1345
|
const conversationId = conversationRef.conversation?.id ?? "unknown";
|
|
1411
1346
|
require_resolve_allowlist.assertMSTeamsSendHandoff(options);
|
|
@@ -1509,11 +1444,6 @@ async function sendMSTeamsMessages(params) {
|
|
|
1509
1444
|
if (pendingUploadId && messageId !== "unknown") setPendingUploadActivityId(pendingUploadId, messageId);
|
|
1510
1445
|
return messageId;
|
|
1511
1446
|
};
|
|
1512
|
-
const sendMessageBatchInContext = async (sendFn, batch, startIndex) => {
|
|
1513
|
-
const messageIds = [];
|
|
1514
|
-
for (const [idx, message] of batch.entries()) messageIds.push(await sendMessageInContext(sendFn, message, startIndex + idx));
|
|
1515
|
-
return messageIds;
|
|
1516
|
-
};
|
|
1517
1447
|
const sendProactively = async (batch, startIndex, threadActivityId) => {
|
|
1518
1448
|
let baseRef;
|
|
1519
1449
|
try {
|
|
@@ -1529,7 +1459,9 @@ async function sendMSTeamsMessages(params) {
|
|
|
1529
1459
|
threadActivityId: isChannel ? threadActivityId : void 0,
|
|
1530
1460
|
serviceUrlBoundary: params.serviceUrlBoundary
|
|
1531
1461
|
});
|
|
1532
|
-
|
|
1462
|
+
const messageIds = [];
|
|
1463
|
+
for (const [idx, message] of batch.entries()) messageIds.push(await sendMessageInContext(sendFn, message, startIndex + idx));
|
|
1464
|
+
return messageIds;
|
|
1533
1465
|
};
|
|
1534
1466
|
const resolvedThreadId = params.conversationRef.threadId ?? params.conversationRef.activityId;
|
|
1535
1467
|
if (params.replyStyle === "thread") {
|
|
@@ -1636,7 +1568,7 @@ function buildMSTeamsPollCard(params) {
|
|
|
1636
1568
|
title: option,
|
|
1637
1569
|
value: String(index)
|
|
1638
1570
|
}));
|
|
1639
|
-
const hint = cappedMaxSelections > 1 ? `Select up to ${cappedMaxSelections}
|
|
1571
|
+
const hint = cappedMaxSelections > 1 ? `Select up to ${cappedMaxSelections} options.` : "Select one option.";
|
|
1640
1572
|
const card = {
|
|
1641
1573
|
type: "AdaptiveCard",
|
|
1642
1574
|
version: "1.5",
|
|
@@ -1841,12 +1773,6 @@ function createMSTeamsPollStoreState(params) {
|
|
|
1841
1773
|
}
|
|
1842
1774
|
//#endregion
|
|
1843
1775
|
//#region extensions/msteams/src/conversation-store-helpers.ts
|
|
1844
|
-
function toConversationStoreEntries(entries) {
|
|
1845
|
-
return Array.from(entries, ([conversationId, reference]) => ({
|
|
1846
|
-
conversationId,
|
|
1847
|
-
reference
|
|
1848
|
-
}));
|
|
1849
|
-
}
|
|
1850
1776
|
function mergeStoredConversationReference(existing, incoming, nowIso) {
|
|
1851
1777
|
return {
|
|
1852
1778
|
...existing?.timezone && !incoming.timezone ? { timezone: existing.timezone } : {},
|
|
@@ -1859,18 +1785,22 @@ function mergeStoredConversationReference(existing, incoming, nowIso) {
|
|
|
1859
1785
|
function findPreferredDmConversationByUserId(entries, id) {
|
|
1860
1786
|
const target = id.trim();
|
|
1861
1787
|
if (!target) return null;
|
|
1862
|
-
|
|
1863
|
-
|
|
1788
|
+
let preferred = null;
|
|
1789
|
+
let preferredIsPersonal = false;
|
|
1790
|
+
let preferredTimestamp = 0;
|
|
1864
1791
|
for (const entry of entries) {
|
|
1865
1792
|
if (entry.reference.user?.aadObjectId !== target && entry.reference.user?.id !== target) continue;
|
|
1866
1793
|
const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(entry.reference.conversation?.conversationType ?? "");
|
|
1867
|
-
if (convType === "
|
|
1868
|
-
|
|
1794
|
+
if (convType === "channel" || convType === "groupchat") continue;
|
|
1795
|
+
const isPersonal = convType === "personal";
|
|
1796
|
+
const timestamp = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(entry.reference.lastSeenAt) ?? 0;
|
|
1797
|
+
if (!preferred || isPersonal && !preferredIsPersonal || isPersonal === preferredIsPersonal && timestamp > preferredTimestamp) {
|
|
1798
|
+
preferred = entry;
|
|
1799
|
+
preferredIsPersonal = isPersonal;
|
|
1800
|
+
preferredTimestamp = timestamp;
|
|
1801
|
+
}
|
|
1869
1802
|
}
|
|
1870
|
-
|
|
1871
|
-
if (candidates.length === 0) return null;
|
|
1872
|
-
if (candidates.length > 1) candidates.sort((a, b) => ((0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(b.reference.lastSeenAt) ?? 0) - ((0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(a.reference.lastSeenAt) ?? 0));
|
|
1873
|
-
return candidates[0] ?? null;
|
|
1803
|
+
return preferred;
|
|
1874
1804
|
}
|
|
1875
1805
|
//#endregion
|
|
1876
1806
|
//#region extensions/msteams/src/conversation-store-state.ts
|
|
@@ -1879,32 +1809,20 @@ const MSTEAMS_MAX_CONVERSATIONS = 1e3;
|
|
|
1879
1809
|
const MSTEAMS_SQLITE_MAX_CONVERSATION_ROWS = 2e3;
|
|
1880
1810
|
const MSTEAMS_CONVERSATION_TTL_MS = 31536e6;
|
|
1881
1811
|
const CONVERSATION_MUTATION_KEY = "conversations";
|
|
1882
|
-
function createConversationStateStore(params) {
|
|
1883
|
-
return require_runtime.getMSTeamsRuntime().state.openKeyedStore({
|
|
1884
|
-
namespace: MSTEAMS_CONVERSATIONS_NAMESPACE,
|
|
1885
|
-
maxEntries: MSTEAMS_SQLITE_MAX_CONVERSATION_ROWS,
|
|
1886
|
-
env: resolveMSTeamsSqliteStateEnv(params)
|
|
1887
|
-
});
|
|
1888
|
-
}
|
|
1889
1812
|
function buildMSTeamsConversationStateKey(conversationId) {
|
|
1890
1813
|
return node_crypto.default.createHash("sha256").update(conversationId).digest("hex");
|
|
1891
1814
|
}
|
|
1892
|
-
function prepareMSTeamsConversationReferenceForStorage(conversationId, reference) {
|
|
1893
|
-
return {
|
|
1894
|
-
...reference,
|
|
1895
|
-
conversation: {
|
|
1896
|
-
...reference.conversation,
|
|
1897
|
-
id: conversationId
|
|
1898
|
-
}
|
|
1899
|
-
};
|
|
1900
|
-
}
|
|
1901
1815
|
function getStoredConversationId(reference) {
|
|
1902
1816
|
const rawId = reference.conversation?.id;
|
|
1903
1817
|
return rawId ? require_resolve_allowlist.normalizeMSTeamsConversationId(rawId) : null;
|
|
1904
1818
|
}
|
|
1905
1819
|
function createMSTeamsConversationStoreState(params) {
|
|
1906
1820
|
const ttlMs = params?.ttlMs ?? MSTEAMS_CONVERSATION_TTL_MS;
|
|
1907
|
-
const conversationStore =
|
|
1821
|
+
const conversationStore = require_runtime.getMSTeamsRuntime().state.openKeyedStore({
|
|
1822
|
+
namespace: MSTEAMS_CONVERSATIONS_NAMESPACE,
|
|
1823
|
+
maxEntries: MSTEAMS_SQLITE_MAX_CONVERSATION_ROWS,
|
|
1824
|
+
env: resolveMSTeamsSqliteStateEnv(params)
|
|
1825
|
+
});
|
|
1908
1826
|
const isExpired = (reference) => {
|
|
1909
1827
|
const lastSeenAt = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(reference.lastSeenAt);
|
|
1910
1828
|
return lastSeenAt != null && Date.now() - lastSeenAt > ttlMs;
|
|
@@ -1912,23 +1830,30 @@ function createMSTeamsConversationStoreState(params) {
|
|
|
1912
1830
|
const lookupStored = async (conversationId) => {
|
|
1913
1831
|
const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
|
|
1914
1832
|
const value = await conversationStore.lookup(buildMSTeamsConversationStateKey(normalizedId));
|
|
1915
|
-
|
|
1916
|
-
if (isExpired(value)) return null;
|
|
1917
|
-
return value;
|
|
1833
|
+
return value && !isExpired(value) ? value : null;
|
|
1918
1834
|
};
|
|
1919
|
-
const
|
|
1835
|
+
const list = async () => {
|
|
1920
1836
|
const rows = await conversationStore.entries();
|
|
1921
1837
|
const kept = [];
|
|
1922
1838
|
for (const row of rows) {
|
|
1923
1839
|
if (isExpired(row.value)) continue;
|
|
1924
1840
|
const conversationId = getStoredConversationId(row.value);
|
|
1925
|
-
if (conversationId) kept.push(
|
|
1841
|
+
if (conversationId) kept.push({
|
|
1842
|
+
conversationId,
|
|
1843
|
+
reference: row.value
|
|
1844
|
+
});
|
|
1926
1845
|
}
|
|
1927
1846
|
return kept;
|
|
1928
1847
|
};
|
|
1929
1848
|
const register = async (conversationId, reference) => {
|
|
1930
1849
|
const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
|
|
1931
|
-
await conversationStore.register(buildMSTeamsConversationStateKey(normalizedId), toPluginJsonValue(
|
|
1850
|
+
await conversationStore.register(buildMSTeamsConversationStateKey(normalizedId), toPluginJsonValue({
|
|
1851
|
+
...reference,
|
|
1852
|
+
conversation: {
|
|
1853
|
+
...reference.conversation,
|
|
1854
|
+
id: normalizedId
|
|
1855
|
+
}
|
|
1856
|
+
}));
|
|
1932
1857
|
const rows = [];
|
|
1933
1858
|
for (const row of await conversationStore.entries()) {
|
|
1934
1859
|
if (isExpired(row.value)) {
|
|
@@ -1947,9 +1872,6 @@ function createMSTeamsConversationStoreState(params) {
|
|
|
1947
1872
|
});
|
|
1948
1873
|
for (const row of sorted.slice(0, rows.length - MSTEAMS_MAX_CONVERSATIONS)) await conversationStore.delete(row.key);
|
|
1949
1874
|
};
|
|
1950
|
-
const list = async () => {
|
|
1951
|
-
return toConversationStoreEntries(await entries());
|
|
1952
|
-
};
|
|
1953
1875
|
const findPreferredDmByUserId = async (id) => {
|
|
1954
1876
|
return findPreferredDmConversationByUserId(await list(), id);
|
|
1955
1877
|
};
|
|
@@ -1975,6 +1897,18 @@ function createMSTeamsConversationStoreState(params) {
|
|
|
1975
1897
|
};
|
|
1976
1898
|
}
|
|
1977
1899
|
//#endregion
|
|
1900
|
+
Object.defineProperty(exports, "FILE_CONSENT_THRESHOLD_BYTES", {
|
|
1901
|
+
enumerable: true,
|
|
1902
|
+
get: function() {
|
|
1903
|
+
return FILE_CONSENT_THRESHOLD_BYTES;
|
|
1904
|
+
}
|
|
1905
|
+
});
|
|
1906
|
+
Object.defineProperty(exports, "MSTEAMS_MAX_MEDIA_BYTES", {
|
|
1907
|
+
enumerable: true,
|
|
1908
|
+
get: function() {
|
|
1909
|
+
return MSTEAMS_MAX_MEDIA_BYTES;
|
|
1910
|
+
}
|
|
1911
|
+
});
|
|
1978
1912
|
Object.defineProperty(exports, "buildConversationReference", {
|
|
1979
1913
|
enumerable: true,
|
|
1980
1914
|
get: function() {
|