@openclaw/msteams 2026.6.1-beta.3 → 2026.6.2-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-Dhw8AvTl.js → channel-BwNnuHbh.js} +4 -4
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-t52N99bX.js → channel.runtime-JR5pBsop.js} +3 -3
- package/dist/directory-contract-api.js +35 -0
- package/dist/{oauth-ei63gdyS.js → oauth-CJj-b_Kn.js} +1 -1
- package/dist/{probe-DYb-0Hmx.js → probe-bq_Uev4c.js} +3 -3
- package/dist/{errors-Dpn8B05h.js → resolve-allowlist-C6VVTfbj.js} +332 -164
- package/dist/setup-plugin-api.js +2 -2
- package/dist/{setup-surface-DGTz8Mlf.js → setup-surface-DdyMxq-W.js} +180 -220
- package/dist/{src-DZ76sgTp.js → src-_wVYF1sz.js} +4 -4
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
- package/dist/oauth.token-BKzEFepQ.js +0 -132
package/dist/api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
1
|
+
import { t as msteamsPlugin } from "./channel-BwNnuHbh.js";
|
|
2
|
+
import { c as msteamsSetupAdapter, n as openDelegatedOAuthUrl, s as createMSTeamsSetupWizardBase, t as msteamsSetupWizard } from "./setup-surface-DdyMxq-W.js";
|
|
3
3
|
export { createMSTeamsSetupWizardBase, msteamsPlugin, msteamsSetupAdapter, msteamsSetupWizard, openDelegatedOAuthUrl };
|
|
@@ -1,7 +1,7 @@
|
|
|
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 {
|
|
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-DGTz8Mlf.js";
|
|
2
|
+
import { C as resolveMSTeamsCredentials, a as parseMSTeamsTeamChannelInput, c as resolveMSTeamsUserAllowlist, i as parseMSTeamsConversationId, n as normalizeMSTeamsMessagingTarget, r as normalizeMSTeamsUserInput, s as resolveMSTeamsChannelAllowlist, t as looksLikeMSTeamsTargetId } from "./resolve-allowlist-C6VVTfbj.js";
|
|
4
3
|
import { t as MSTeamsChannelConfigSchema } from "./config-schema-BL4qQZiA.js";
|
|
4
|
+
import { c as msteamsSetupAdapter, t as msteamsSetupWizard } from "./setup-surface-DdyMxq-W.js";
|
|
5
5
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
6
6
|
import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
|
|
7
7
|
import { createTopLevelChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
@@ -331,7 +331,7 @@ const collectMSTeamsSecurityWarnings = createAllowlistProviderGroupPolicyWarning
|
|
|
331
331
|
resolveGroupPolicy: ({ cfg }) => cfg.channels?.msteams?.groupPolicy,
|
|
332
332
|
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."] : []
|
|
333
333
|
});
|
|
334
|
-
const loadMSTeamsChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-
|
|
334
|
+
const loadMSTeamsChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-JR5pBsop.js"), "msTeamsChannelRuntime");
|
|
335
335
|
const resolveMSTeamsChannelConfig = (cfg) => ({
|
|
336
336
|
allowFrom: cfg.channels?.msteams?.allowFrom,
|
|
337
337
|
defaultTo: cfg.channels?.msteams?.defaultTo
|
|
@@ -1117,7 +1117,7 @@ const msteamsPlugin = createChatChannelPlugin({
|
|
|
1117
1117
|
})
|
|
1118
1118
|
}),
|
|
1119
1119
|
gateway: { startAccount: async (ctx) => {
|
|
1120
|
-
const { monitorMSTeamsProvider } = await import("./src-
|
|
1120
|
+
const { monitorMSTeamsProvider } = await import("./src-_wVYF1sz.js");
|
|
1121
1121
|
const port = ctx.cfg.channels?.msteams?.webhook?.port ?? 3978;
|
|
1122
1122
|
ctx.setStatus({
|
|
1123
1123
|
accountId: ctx.accountId,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as msteamsPlugin } from "./channel-
|
|
1
|
+
import { t as msteamsPlugin } from "./channel-BwNnuHbh.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 {
|
|
3
|
-
import { n as MSTEAMS_PRESENTATION_CAPABILITIES, r as buildMSTeamsPresentationCard } from "./channel-
|
|
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-
|
|
2
|
+
import { _ as patchGraphJson, b as resolveGraphToken, d as escapeOData, f as fetchGraphAbsoluteUrl, g as normalizeQuery, h as listTeamsByName, l as searchGraphUsers, m as listChannelsForTeam, p as fetchGraphJson, u as deleteGraphRequest, v as postGraphBetaJson, y as postGraphJson } from "./resolve-allowlist-C6VVTfbj.js";
|
|
3
|
+
import { n as MSTEAMS_PRESENTATION_CAPABILITIES, r as buildMSTeamsPresentationCard } from "./channel-BwNnuHbh.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-bq_Uev4c.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";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { C as resolveMSTeamsCredentials, n as normalizeMSTeamsMessagingTarget } from "./resolve-allowlist-C6VVTfbj.js";
|
|
2
|
+
import { listDirectoryEntriesFromSources } from "openclaw/plugin-sdk/directory-runtime";
|
|
3
|
+
const msteamsDirectoryContractPlugin = {
|
|
4
|
+
id: "msteams",
|
|
5
|
+
directory: {
|
|
6
|
+
self: async ({ cfg }) => {
|
|
7
|
+
const creds = resolveMSTeamsCredentials(cfg.channels?.msteams);
|
|
8
|
+
return creds ? {
|
|
9
|
+
kind: "user",
|
|
10
|
+
id: creds.appId,
|
|
11
|
+
name: creds.appId
|
|
12
|
+
} : null;
|
|
13
|
+
},
|
|
14
|
+
listPeers: async ({ cfg, query, limit }) => listDirectoryEntriesFromSources({
|
|
15
|
+
kind: "user",
|
|
16
|
+
sources: [cfg.channels?.msteams?.allowFrom ?? [], Object.keys(cfg.channels?.msteams?.dms ?? {})],
|
|
17
|
+
query,
|
|
18
|
+
limit,
|
|
19
|
+
normalizeId: (raw) => {
|
|
20
|
+
const normalized = normalizeMSTeamsMessagingTarget(raw) ?? raw;
|
|
21
|
+
const lowered = normalized.toLowerCase();
|
|
22
|
+
return lowered.startsWith("user:") || lowered.startsWith("conversation:") ? normalized : `user:${normalized}`;
|
|
23
|
+
}
|
|
24
|
+
}),
|
|
25
|
+
listGroups: async ({ cfg, query, limit }) => listDirectoryEntriesFromSources({
|
|
26
|
+
kind: "group",
|
|
27
|
+
sources: [Object.values(cfg.channels?.msteams?.teams ?? {}).flatMap((team) => Object.keys(team.channels ?? {}))],
|
|
28
|
+
query,
|
|
29
|
+
limit,
|
|
30
|
+
normalizeId: (raw) => `conversation:${raw.replace(/^conversation:/i, "").trim()}`
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { msteamsDirectoryContractPlugin };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as MSTEAMS_OAUTH_CALLBACK_PORT, D as exchangeMSTeamsCodeForTokens, M as buildMSTeamsAuthEndpoint, O as MSTEAMS_DEFAULT_DELEGATED_SCOPES, j as MSTEAMS_OAUTH_REDIRECT_URI, k as MSTEAMS_OAUTH_CALLBACK_PATH } from "./resolve-allowlist-C6VVTfbj.js";
|
|
2
2
|
import { generateHexPkceVerifierChallenge } from "openclaw/plugin-sdk/provider-auth";
|
|
3
3
|
import { generateOAuthState, parseOAuthCallbackInput, waitForLocalOAuthCallback } from "openclaw/plugin-sdk/provider-auth-runtime";
|
|
4
4
|
import { isWSL2Sync } from "openclaw/plugin-sdk/runtime-env";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { C as normalizeStringEntries$1, E as resolveChannelMediaMaxBytes, M as getMSTeamsRuntime, d as detectMime, g as getFileExtension, m as extractOriginalFilename, p as extensionForMime, y as loadOutboundMediaFromUrl } from "./runtime-api-BlvMnDKz.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { a as
|
|
2
|
+
import { B as isAllowedBotFrameworkServiceUrl, C as resolveMSTeamsCredentials, F as createMSTeamsTokenProvider, G as validateMSTeamsProactiveServiceUrlBoundary, I as loadMSTeamsSdkWithAuth, L as buildUserAgent, N as readAccessToken, S as loadDelegatedTokens, T as resolveMSTeamsStorePath, U as createMSTeamsHttpError, V as normalizeBotFrameworkServiceUrl, W as resolveMSTeamsSdkCloudOptions, z as describeBotFrameworkServiceUrlHost } from "./resolve-allowlist-C6VVTfbj.js";
|
|
3
|
+
import { a as resolveMSTeamsReplyPolicy, o as resolveMSTeamsRouteConfig } from "./channel-BwNnuHbh.js";
|
|
4
|
+
import { a as formatUnknownError, i as formatMSTeamsSendErrorHint, o as isRevokedProxyError, r as classifyMSTeamsSendError } from "./setup-surface-DdyMxq-W.js";
|
|
5
5
|
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
|
|
6
6
|
import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
7
|
import { withFileLock } from "openclaw/plugin-sdk/file-lock";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { M as getMSTeamsRuntime, h as fetchWithSsrFGuard$1 } from "./runtime-api-BlvMnDKz.js";
|
|
2
|
-
import { c as createMSTeamsHttpError, n as refreshMSTeamsDelegatedTokens } from "./oauth.token-BKzEFepQ.js";
|
|
3
2
|
import { createRequire } from "node:module";
|
|
4
|
-
import {
|
|
3
|
+
import { mapAllowlistResolutionInputs } from "openclaw/plugin-sdk/allow-from";
|
|
4
|
+
import { isRecord as isRecord$1, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
5
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
6
|
-
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
|
6
|
+
import { createProviderHttpError, readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
|
7
7
|
import { Buffer } from "node:buffer";
|
|
8
8
|
import { lookup } from "node:dns/promises";
|
|
9
9
|
import { buildHostnameAllowlistPolicyFromSuffixAllowlist, isHttpsUrlAllowedByHostnameSuffixAllowlist, isPrivateIpAddress, normalizeHostnameSuffixAllowlist } from "openclaw/plugin-sdk/ssrf-policy";
|
|
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 {
|
|
13
|
+
import { isFutureDateTimestampMs, resolveExpiresAtMsFromDurationSeconds } from "openclaw/plugin-sdk/number-runtime";
|
|
14
14
|
import { privateFileStoreSync } from "openclaw/plugin-sdk/security-runtime";
|
|
15
15
|
import { hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
16
16
|
//#region extensions/msteams/src/attachments/shared.ts
|
|
@@ -539,6 +539,11 @@ function validateMSTeamsProactiveServiceUrlBoundary(params) {
|
|
|
539
539
|
if (isChinaBotFrameworkServiceUrl(stored.value)) throw new Error(`msteams proactive send blocked for ${params.conversationId}: stored conversation serviceUrl (${stored.value}) requires channels.msteams.cloud=China.`);
|
|
540
540
|
if (stored.host !== PUBLIC_MSTEAMS_SERVICE_HOST) throw new Error(`msteams proactive send blocked for ${params.conversationId}: stored conversation serviceUrl (${stored.value}) is not a Microsoft Teams public-cloud Bot Connector endpoint. Set channels.msteams.cloud and channels.msteams.serviceUrl for the supported Teams cloud that owns this conversation.`);
|
|
541
541
|
}
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region extensions/msteams/src/http-error.ts
|
|
544
|
+
async function createMSTeamsHttpError(response, label, options) {
|
|
545
|
+
return await createProviderHttpError(response, label, options);
|
|
546
|
+
}
|
|
542
547
|
const BOT_FRAMEWORK_SERVICE_URL_HOST_ALLOWLIST = normalizeHostnameSuffixAllowlist([
|
|
543
548
|
"smba.trafficmanager.net",
|
|
544
549
|
"smba.infra.gcc.teams.microsoft.com",
|
|
@@ -728,6 +733,129 @@ function readAccessToken(value) {
|
|
|
728
733
|
return null;
|
|
729
734
|
}
|
|
730
735
|
//#endregion
|
|
736
|
+
//#region extensions/msteams/src/oauth.shared.ts
|
|
737
|
+
const MSTEAMS_OAUTH_REDIRECT_URI = "http://localhost:8086/oauth2callback";
|
|
738
|
+
const MSTEAMS_OAUTH_CALLBACK_PORT = 8086;
|
|
739
|
+
const MSTEAMS_OAUTH_CALLBACK_PATH = "/oauth2callback";
|
|
740
|
+
const MSTEAMS_DEFAULT_TOKEN_FETCH_TIMEOUT_MS = 1e4;
|
|
741
|
+
const MSTEAMS_DEFAULT_DELEGATED_SCOPES = [
|
|
742
|
+
"ChatMessage.Send",
|
|
743
|
+
"ChannelMessage.Send",
|
|
744
|
+
"Chat.ReadWrite",
|
|
745
|
+
"offline_access"
|
|
746
|
+
];
|
|
747
|
+
function buildMSTeamsAuthEndpoint(tenantId) {
|
|
748
|
+
return `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/authorize`;
|
|
749
|
+
}
|
|
750
|
+
function buildMSTeamsTokenEndpoint(tenantId) {
|
|
751
|
+
return `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/token`;
|
|
752
|
+
}
|
|
753
|
+
//#endregion
|
|
754
|
+
//#region extensions/msteams/src/oauth.token.ts
|
|
755
|
+
/** Five-minute buffer subtracted from token expiry to avoid edge-case clock drift. */
|
|
756
|
+
const EXPIRY_BUFFER_MS = 300 * 1e3;
|
|
757
|
+
function createMSTeamsTokenBody(params) {
|
|
758
|
+
const body = new URLSearchParams({
|
|
759
|
+
client_id: params.clientId,
|
|
760
|
+
client_secret: params.clientSecret,
|
|
761
|
+
grant_type: params.grantType,
|
|
762
|
+
scope: [...params.scopes].join(" ")
|
|
763
|
+
});
|
|
764
|
+
for (const [key, value] of Object.entries(params.values ?? {})) body.set(key, value);
|
|
765
|
+
return body;
|
|
766
|
+
}
|
|
767
|
+
function resolveMSTeamsTokenExpiresAt(value) {
|
|
768
|
+
return resolveExpiresAtMsFromDurationSeconds(value, { bufferMs: EXPIRY_BUFFER_MS });
|
|
769
|
+
}
|
|
770
|
+
function parseMSTeamsTokenResponse(data, failureLabel) {
|
|
771
|
+
const expiresAt = resolveMSTeamsTokenExpiresAt(data.expires_in);
|
|
772
|
+
if (typeof data.access_token !== "string" || !data.access_token || expiresAt === void 0 || data.refresh_token !== void 0 && typeof data.refresh_token !== "string" || data.scope !== void 0 && typeof data.scope !== "string") throw new Error(`MSTeams ${failureLabel} failed: invalid token response fields`);
|
|
773
|
+
return {
|
|
774
|
+
access_token: data.access_token,
|
|
775
|
+
refresh_token: data.refresh_token,
|
|
776
|
+
expiresAt,
|
|
777
|
+
scope: data.scope
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
async function fetchMSTeamsTokens(params) {
|
|
781
|
+
const currentFetch = globalThis.fetch;
|
|
782
|
+
const { response, release } = await fetchWithSsrFGuard({
|
|
783
|
+
url: params.tokenUrl,
|
|
784
|
+
fetchImpl: async (input, guardedInit) => await currentFetch(input, guardedInit),
|
|
785
|
+
init: {
|
|
786
|
+
method: "POST",
|
|
787
|
+
headers: {
|
|
788
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
789
|
+
Accept: "application/json"
|
|
790
|
+
},
|
|
791
|
+
body: params.body,
|
|
792
|
+
signal: AbortSignal.timeout(MSTEAMS_DEFAULT_TOKEN_FETCH_TIMEOUT_MS)
|
|
793
|
+
},
|
|
794
|
+
auditContext: params.auditContext
|
|
795
|
+
});
|
|
796
|
+
try {
|
|
797
|
+
if (!response.ok) throw await createMSTeamsHttpError(response, `MSTeams ${params.failureLabel} failed`);
|
|
798
|
+
return parseMSTeamsTokenResponse(await readProviderJsonResponse(response, `MSTeams ${params.failureLabel} failed`), params.failureLabel);
|
|
799
|
+
} finally {
|
|
800
|
+
await release();
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
async function requestMSTeamsDelegatedTokens(params) {
|
|
804
|
+
const scopes = params.scopes ?? MSTEAMS_DEFAULT_DELEGATED_SCOPES;
|
|
805
|
+
const body = createMSTeamsTokenBody({
|
|
806
|
+
clientId: params.clientId,
|
|
807
|
+
clientSecret: params.clientSecret,
|
|
808
|
+
grantType: params.grantType,
|
|
809
|
+
scopes,
|
|
810
|
+
values: params.values
|
|
811
|
+
});
|
|
812
|
+
const data = await fetchMSTeamsTokens({
|
|
813
|
+
tokenUrl: buildMSTeamsTokenEndpoint(params.tenantId),
|
|
814
|
+
body,
|
|
815
|
+
auditContext: params.auditContext,
|
|
816
|
+
failureLabel: params.failureLabel
|
|
817
|
+
});
|
|
818
|
+
return {
|
|
819
|
+
accessToken: data.access_token,
|
|
820
|
+
refreshToken: params.resolveRefreshToken(data),
|
|
821
|
+
expiresAt: data.expiresAt,
|
|
822
|
+
scopes: data.scope ? data.scope.split(" ") : [...scopes]
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
async function exchangeMSTeamsCodeForTokens(params) {
|
|
826
|
+
return await requestMSTeamsDelegatedTokens({
|
|
827
|
+
tenantId: params.tenantId,
|
|
828
|
+
clientId: params.clientId,
|
|
829
|
+
clientSecret: params.clientSecret,
|
|
830
|
+
grantType: "authorization_code",
|
|
831
|
+
scopes: params.scopes,
|
|
832
|
+
values: {
|
|
833
|
+
code: params.code,
|
|
834
|
+
redirect_uri: MSTEAMS_OAUTH_REDIRECT_URI,
|
|
835
|
+
code_verifier: params.verifier
|
|
836
|
+
},
|
|
837
|
+
auditContext: "msteams-oauth-token-exchange",
|
|
838
|
+
failureLabel: "token exchange",
|
|
839
|
+
resolveRefreshToken: (data) => {
|
|
840
|
+
if (!data.refresh_token) throw new Error("No refresh token received from Azure AD. Please try again.");
|
|
841
|
+
return data.refresh_token;
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
async function refreshMSTeamsDelegatedTokens(params) {
|
|
846
|
+
return await requestMSTeamsDelegatedTokens({
|
|
847
|
+
tenantId: params.tenantId,
|
|
848
|
+
clientId: params.clientId,
|
|
849
|
+
clientSecret: params.clientSecret,
|
|
850
|
+
grantType: "refresh_token",
|
|
851
|
+
scopes: params.scopes,
|
|
852
|
+
values: { refresh_token: params.refreshToken },
|
|
853
|
+
auditContext: "msteams-oauth-token-refresh",
|
|
854
|
+
failureLabel: "token refresh",
|
|
855
|
+
resolveRefreshToken: (data) => data.refresh_token ?? params.refreshToken
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
//#endregion
|
|
731
859
|
//#region extensions/msteams/src/storage.ts
|
|
732
860
|
function resolveMSTeamsStorePath(params) {
|
|
733
861
|
if (params.storePath) return params.storePath;
|
|
@@ -1032,179 +1160,219 @@ async function searchGraphUsers(params) {
|
|
|
1032
1160
|
})).value ?? [];
|
|
1033
1161
|
}
|
|
1034
1162
|
//#endregion
|
|
1035
|
-
//#region extensions/msteams/src/
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
if (
|
|
1042
|
-
|
|
1043
|
-
if (
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
return JSON.stringify(err) ?? "unknown error";
|
|
1047
|
-
} catch {
|
|
1048
|
-
return "unknown error";
|
|
1163
|
+
//#region extensions/msteams/src/resolve-allowlist.ts
|
|
1164
|
+
function stripProviderPrefix(raw) {
|
|
1165
|
+
return raw.replace(/^(msteams|teams):/i, "");
|
|
1166
|
+
}
|
|
1167
|
+
function normalizeMSTeamsMessagingTarget(raw) {
|
|
1168
|
+
let trimmed = raw.trim();
|
|
1169
|
+
if (!trimmed) return;
|
|
1170
|
+
trimmed = stripProviderPrefix(trimmed).trim();
|
|
1171
|
+
if (/^conversation:/i.test(trimmed)) {
|
|
1172
|
+
const id = trimmed.slice(13).trim();
|
|
1173
|
+
return id ? `conversation:${id}` : void 0;
|
|
1049
1174
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
const parseStatusCode = (value) => {
|
|
1054
|
-
if (typeof value === "number") return Number.isInteger(value) && value >= 100 && value <= 599 ? value : null;
|
|
1055
|
-
if (typeof value === "string") {
|
|
1056
|
-
const trimmed = value.trim();
|
|
1057
|
-
if (!/^\d{3}$/.test(trimmed)) return null;
|
|
1058
|
-
const parsed = Number(trimmed);
|
|
1059
|
-
return parsed >= 100 && parsed <= 599 ? parsed : null;
|
|
1060
|
-
}
|
|
1061
|
-
return null;
|
|
1062
|
-
};
|
|
1063
|
-
const directStatus = parseStatusCode(err.statusCode ?? err.status);
|
|
1064
|
-
if (directStatus !== null) return directStatus;
|
|
1065
|
-
const response = err.response;
|
|
1066
|
-
if (isRecord(response)) {
|
|
1067
|
-
const responseStatus = parseStatusCode(response.status);
|
|
1068
|
-
if (responseStatus !== null) return responseStatus;
|
|
1175
|
+
if (/^user:/i.test(trimmed)) {
|
|
1176
|
+
const id = trimmed.slice(5).trim();
|
|
1177
|
+
return id ? `user:${id}` : void 0;
|
|
1069
1178
|
}
|
|
1070
|
-
return
|
|
1179
|
+
return trimmed || void 0;
|
|
1071
1180
|
}
|
|
1072
|
-
function
|
|
1073
|
-
|
|
1074
|
-
const direct = err.code;
|
|
1075
|
-
if (typeof direct === "string" && direct.trim()) return direct;
|
|
1076
|
-
const response = err.response;
|
|
1077
|
-
if (!isRecord(response)) return null;
|
|
1078
|
-
const body = response.body;
|
|
1079
|
-
if (isRecord(body)) {
|
|
1080
|
-
const error = body.error;
|
|
1081
|
-
if (isRecord(error) && typeof error.code === "string" && error.code.trim()) return error.code;
|
|
1082
|
-
}
|
|
1083
|
-
return null;
|
|
1181
|
+
function normalizeMSTeamsUserInput(raw) {
|
|
1182
|
+
return stripProviderPrefix(raw).replace(/^(user|conversation):/i, "").trim();
|
|
1084
1183
|
}
|
|
1085
|
-
function
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
max: Number.MAX_SAFE_INTEGER
|
|
1090
|
-
});
|
|
1091
|
-
if (directMs !== void 0) return directMs;
|
|
1092
|
-
const retryAfter = err.retryAfter ?? err.retry_after;
|
|
1093
|
-
const retryAfterSeconds = asFiniteNumberInRange(retryAfter, {
|
|
1094
|
-
min: 0,
|
|
1095
|
-
max: MAX_SAFE_RETRY_AFTER_SECONDS
|
|
1096
|
-
});
|
|
1097
|
-
if (retryAfterSeconds !== void 0) return retryAfterSeconds * 1e3;
|
|
1098
|
-
if (typeof retryAfter === "string") {
|
|
1099
|
-
const parsed = parseNonNegativeRetryAfterSeconds(retryAfter);
|
|
1100
|
-
if (parsed !== void 0) return parsed * 1e3;
|
|
1101
|
-
}
|
|
1102
|
-
const response = err.response;
|
|
1103
|
-
if (!isRecord(response)) return null;
|
|
1104
|
-
const headers = response.headers;
|
|
1105
|
-
if (!headers) return null;
|
|
1106
|
-
if (isRecord(headers)) {
|
|
1107
|
-
const raw = headers["retry-after"] ?? headers["Retry-After"];
|
|
1108
|
-
if (typeof raw === "string") {
|
|
1109
|
-
const parsed = parseNonNegativeRetryAfterSeconds(raw);
|
|
1110
|
-
if (parsed !== void 0) return parsed * 1e3;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
if (typeof headers === "object" && headers !== null && "get" in headers && typeof headers.get === "function") {
|
|
1114
|
-
const raw = headers.get("retry-after");
|
|
1115
|
-
if (raw) {
|
|
1116
|
-
const parsed = parseNonNegativeRetryAfterSeconds(raw);
|
|
1117
|
-
if (parsed !== void 0) return parsed * 1e3;
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
return null;
|
|
1121
|
-
}
|
|
1122
|
-
function parseNonNegativeRetryAfterSeconds(raw) {
|
|
1123
|
-
const trimmed = raw.trim();
|
|
1124
|
-
if (!/^\d+(?:\.\d+)?$/.test(trimmed)) return;
|
|
1125
|
-
return asFiniteNumberInRange(parseStrictFiniteNumber(trimmed), {
|
|
1126
|
-
min: 0,
|
|
1127
|
-
max: MAX_SAFE_RETRY_AFTER_SECONDS
|
|
1128
|
-
});
|
|
1184
|
+
function parseMSTeamsConversationId(raw) {
|
|
1185
|
+
const trimmed = stripProviderPrefix(raw).trim();
|
|
1186
|
+
if (!/^conversation:/i.test(trimmed)) return null;
|
|
1187
|
+
return trimmed.slice(13).trim();
|
|
1129
1188
|
}
|
|
1130
1189
|
/**
|
|
1131
|
-
*
|
|
1190
|
+
* Detect whether a raw target string looks like a Microsoft Teams conversation
|
|
1191
|
+
* or user id that cron announce delivery and other explicit-target paths can
|
|
1192
|
+
* forward verbatim to the channel adapter.
|
|
1132
1193
|
*
|
|
1133
|
-
*
|
|
1134
|
-
*
|
|
1135
|
-
*
|
|
1136
|
-
*
|
|
1194
|
+
* Accepts both prefixed and bare formats:
|
|
1195
|
+
* - `conversation:<id>` — explicit conversation prefix
|
|
1196
|
+
* - `user:<aad-guid>` — user id (16+ hex chars, UUID-like)
|
|
1197
|
+
* - `19:abc@thread.tacv2` / `19:abc@thread.skype` — channel / legacy group
|
|
1198
|
+
* - `19:{userId}_{appId}@unq.gbl.spaces` — Graph 1:1 chat thread format
|
|
1199
|
+
* - `a:1xxx` — Bot Framework personal (1:1) chat id
|
|
1200
|
+
* - `8:orgid:xxx` — Bot Framework org-scoped personal chat id
|
|
1201
|
+
* - `29:xxx` — Bot Framework user id
|
|
1202
|
+
*
|
|
1203
|
+
* Display-name user targets such as `user:John Smith` intentionally return
|
|
1204
|
+
* false so that the Graph API directory lookup still runs for them.
|
|
1137
1205
|
*/
|
|
1138
|
-
function
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
if (
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
errorCode
|
|
1146
|
-
};
|
|
1147
|
-
if (statusCode === 403) {
|
|
1148
|
-
if (errorCode === "ContentStreamNotAllowed") return {
|
|
1149
|
-
kind: "permanent",
|
|
1150
|
-
statusCode,
|
|
1151
|
-
errorCode
|
|
1152
|
-
};
|
|
1153
|
-
return {
|
|
1154
|
-
kind: "auth",
|
|
1155
|
-
statusCode,
|
|
1156
|
-
errorCode
|
|
1157
|
-
};
|
|
1206
|
+
function looksLikeMSTeamsTargetId(raw) {
|
|
1207
|
+
const trimmed = raw.trim();
|
|
1208
|
+
if (!trimmed) return false;
|
|
1209
|
+
if (/^conversation:/i.test(trimmed)) return true;
|
|
1210
|
+
if (/^user:/i.test(trimmed)) {
|
|
1211
|
+
const id = trimmed.slice(5).trim();
|
|
1212
|
+
return /^[0-9a-fA-F-]{16,}$/.test(id);
|
|
1158
1213
|
}
|
|
1159
|
-
if (
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1214
|
+
if (/^19:.+@thread\.(tacv2|skype)$/i.test(trimmed)) return true;
|
|
1215
|
+
if (/^19:.+@unq\.gbl\.spaces$/i.test(trimmed)) return true;
|
|
1216
|
+
if (/^a:1[A-Za-z0-9_-]+$/i.test(trimmed)) return true;
|
|
1217
|
+
if (/^8:orgid:[A-Za-z0-9-]+$/i.test(trimmed)) return true;
|
|
1218
|
+
if (/^29:[A-Za-z0-9_-]+$/i.test(trimmed)) return true;
|
|
1219
|
+
return /@thread\b/i.test(trimmed);
|
|
1220
|
+
}
|
|
1221
|
+
function normalizeMSTeamsTeamKey(raw) {
|
|
1222
|
+
return stripProviderPrefix(raw).replace(/^team:/i, "").trim() || void 0;
|
|
1223
|
+
}
|
|
1224
|
+
function normalizeMSTeamsChannelKey(raw) {
|
|
1225
|
+
return (raw?.trim().replace(/^#/, "").trim() ?? "") || void 0;
|
|
1226
|
+
}
|
|
1227
|
+
function normalizeMSTeamsConversationTargetId(raw) {
|
|
1228
|
+
const trimmed = stripProviderPrefix(raw).trim();
|
|
1229
|
+
return parseMSTeamsConversationId(trimmed) ?? trimmed;
|
|
1230
|
+
}
|
|
1231
|
+
function looksLikeMSTeamsThreadConversationId(raw) {
|
|
1232
|
+
const normalized = normalizeMSTeamsConversationTargetId(raw);
|
|
1233
|
+
return /^19:.+@thread\./i.test(normalized);
|
|
1234
|
+
}
|
|
1235
|
+
function parseMSTeamsTeamChannelInput(raw) {
|
|
1236
|
+
const trimmed = stripProviderPrefix(raw).trim();
|
|
1237
|
+
if (!trimmed) return {};
|
|
1238
|
+
const parts = trimmed.split("/");
|
|
1239
|
+
const team = normalizeMSTeamsTeamKey(parts[0] ?? "");
|
|
1240
|
+
const channel = parts.length > 1 ? normalizeMSTeamsChannelKey(parts.slice(1).join("/")) : void 0;
|
|
1241
|
+
return {
|
|
1242
|
+
...team ? { team } : {},
|
|
1243
|
+
...channel ? { channel } : {}
|
|
1175
1244
|
};
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
errorCode: networkCode
|
|
1181
|
-
};
|
|
1182
|
-
}
|
|
1245
|
+
}
|
|
1246
|
+
function parseMSTeamsTeamEntry(raw) {
|
|
1247
|
+
const { team, channel } = parseMSTeamsTeamChannelInput(raw);
|
|
1248
|
+
if (!team) return null;
|
|
1183
1249
|
return {
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
retryAfterMs: retryAfterMs ?? void 0,
|
|
1187
|
-
errorCode
|
|
1250
|
+
teamKey: team,
|
|
1251
|
+
...channel ? { channelKey: channel } : {}
|
|
1188
1252
|
};
|
|
1189
1253
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1254
|
+
async function resolveMSTeamsChannelAllowlist(params) {
|
|
1255
|
+
let tokenPromise;
|
|
1256
|
+
const getToken = () => {
|
|
1257
|
+
tokenPromise ??= resolveGraphToken(params.cfg);
|
|
1258
|
+
return tokenPromise;
|
|
1259
|
+
};
|
|
1260
|
+
return await mapAllowlistResolutionInputs({
|
|
1261
|
+
inputs: params.entries,
|
|
1262
|
+
mapInput: async (input) => {
|
|
1263
|
+
const { team, channel } = parseMSTeamsTeamChannelInput(input);
|
|
1264
|
+
if (!team) return {
|
|
1265
|
+
input,
|
|
1266
|
+
resolved: false
|
|
1267
|
+
};
|
|
1268
|
+
if (looksLikeMSTeamsThreadConversationId(team)) {
|
|
1269
|
+
const teamId = normalizeMSTeamsConversationTargetId(team);
|
|
1270
|
+
if (!channel) return {
|
|
1271
|
+
input,
|
|
1272
|
+
resolved: true,
|
|
1273
|
+
teamId,
|
|
1274
|
+
teamName: teamId
|
|
1275
|
+
};
|
|
1276
|
+
if (!looksLikeMSTeamsThreadConversationId(channel)) return {
|
|
1277
|
+
input,
|
|
1278
|
+
resolved: false,
|
|
1279
|
+
teamId,
|
|
1280
|
+
teamName: teamId,
|
|
1281
|
+
note: "channel id required for conversation-id team"
|
|
1282
|
+
};
|
|
1283
|
+
const channelId = normalizeMSTeamsConversationTargetId(channel);
|
|
1284
|
+
return {
|
|
1285
|
+
input,
|
|
1286
|
+
resolved: true,
|
|
1287
|
+
teamId,
|
|
1288
|
+
teamName: teamId,
|
|
1289
|
+
channelId,
|
|
1290
|
+
channelName: channelId
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
const token = await getToken();
|
|
1294
|
+
const teams = /^[0-9a-fA-F-]{16,}$/.test(team) ? [{
|
|
1295
|
+
id: team,
|
|
1296
|
+
displayName: team
|
|
1297
|
+
}] : await listTeamsByName(token, team);
|
|
1298
|
+
if (teams.length === 0) return {
|
|
1299
|
+
input,
|
|
1300
|
+
resolved: false,
|
|
1301
|
+
note: "team not found"
|
|
1302
|
+
};
|
|
1303
|
+
const teamMatch = teams[0];
|
|
1304
|
+
const graphTeamId = teamMatch.id?.trim();
|
|
1305
|
+
const teamName = teamMatch.displayName?.trim() || team;
|
|
1306
|
+
if (!graphTeamId) return {
|
|
1307
|
+
input,
|
|
1308
|
+
resolved: false,
|
|
1309
|
+
note: "team id missing"
|
|
1310
|
+
};
|
|
1311
|
+
let teamChannels = [];
|
|
1312
|
+
try {
|
|
1313
|
+
teamChannels = await listChannelsForTeam(token, graphTeamId);
|
|
1314
|
+
} catch {}
|
|
1315
|
+
const teamId = teamChannels.find((ch) => normalizeOptionalLowercaseString(ch.displayName) === "general")?.id?.trim() || graphTeamId;
|
|
1316
|
+
if (!channel) return {
|
|
1317
|
+
input,
|
|
1318
|
+
resolved: true,
|
|
1319
|
+
teamId,
|
|
1320
|
+
teamName,
|
|
1321
|
+
note: teams.length > 1 ? "multiple teams; chose first" : void 0
|
|
1322
|
+
};
|
|
1323
|
+
const normalizedChannel = normalizeOptionalLowercaseString(channel);
|
|
1324
|
+
const channelMatch = teamChannels.find((item) => item.id === channel) ?? teamChannels.find((item) => normalizeOptionalLowercaseString(item.displayName) === normalizedChannel) ?? teamChannels.find((item) => normalizeLowercaseStringOrEmpty(item.displayName ?? "").includes(normalizedChannel ?? ""));
|
|
1325
|
+
if (!channelMatch?.id) return {
|
|
1326
|
+
input,
|
|
1327
|
+
resolved: false,
|
|
1328
|
+
note: "channel not found"
|
|
1329
|
+
};
|
|
1330
|
+
return {
|
|
1331
|
+
input,
|
|
1332
|
+
resolved: true,
|
|
1333
|
+
teamId,
|
|
1334
|
+
teamName,
|
|
1335
|
+
channelId: channelMatch.id,
|
|
1336
|
+
channelName: channelMatch.displayName ?? channel,
|
|
1337
|
+
note: teamChannels.length > 1 ? "multiple channels; chose first" : void 0
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1201
1341
|
}
|
|
1202
|
-
function
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1342
|
+
async function resolveMSTeamsUserAllowlist(params) {
|
|
1343
|
+
const token = await resolveGraphToken(params.cfg);
|
|
1344
|
+
return await mapAllowlistResolutionInputs({
|
|
1345
|
+
inputs: params.entries,
|
|
1346
|
+
mapInput: async (input) => {
|
|
1347
|
+
const query = normalizeQuery(normalizeMSTeamsUserInput(input));
|
|
1348
|
+
if (!query) return {
|
|
1349
|
+
input,
|
|
1350
|
+
resolved: false
|
|
1351
|
+
};
|
|
1352
|
+
if (/^[0-9a-fA-F-]{16,}$/.test(query)) return {
|
|
1353
|
+
input,
|
|
1354
|
+
resolved: true,
|
|
1355
|
+
id: query
|
|
1356
|
+
};
|
|
1357
|
+
const users = await searchGraphUsers({
|
|
1358
|
+
token,
|
|
1359
|
+
query,
|
|
1360
|
+
top: 10
|
|
1361
|
+
});
|
|
1362
|
+
const match = users[0];
|
|
1363
|
+
if (!match?.id) return {
|
|
1364
|
+
input,
|
|
1365
|
+
resolved: false
|
|
1366
|
+
};
|
|
1367
|
+
return {
|
|
1368
|
+
input,
|
|
1369
|
+
resolved: true,
|
|
1370
|
+
id: match.id,
|
|
1371
|
+
name: match.displayName ?? void 0,
|
|
1372
|
+
note: users.length > 1 ? "multiple matches; chose first" : void 0
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1208
1376
|
}
|
|
1209
1377
|
//#endregion
|
|
1210
|
-
export {
|
|
1378
|
+
export { extractInlineImageCandidates as $, MSTEAMS_OAUTH_CALLBACK_PORT as A, isAllowedBotFrameworkServiceUrl as B, resolveMSTeamsCredentials as C, exchangeMSTeamsCodeForTokens as D, normalizeSecretInputString as E, createMSTeamsTokenProvider as F, validateMSTeamsProactiveServiceUrlBoundary as G, tryNormalizeBotFrameworkServiceUrl as H, loadMSTeamsSdkWithAuth as I, IMG_SRC_RE as J, ATTACHMENT_TAG_RE as K, buildUserAgent as L, buildMSTeamsAuthEndpoint as M, readAccessToken as N, MSTEAMS_DEFAULT_DELEGATED_SCOPES as O, createMSTeamsExpressAdapter as P, extractHtmlFromAttachment as Q, ensureUserAgentHeader as R, loadDelegatedTokens as S, resolveMSTeamsStorePath as T, createMSTeamsHttpError as U, normalizeBotFrameworkServiceUrl as V, resolveMSTeamsSdkCloudOptions as W, encodeGraphShareId as X, applyAuthorizationHeaderForUrl as Y, estimateBase64DecodedBytes as Z, patchGraphJson as _, parseMSTeamsTeamChannelInput as a, normalizeContentType as at, resolveGraphToken as b, resolveMSTeamsUserAllowlist as c, resolveMediaSsrfPolicy as ct, escapeOData as d, safeHostForUrl as dt, inferPlaceholder as et, fetchGraphAbsoluteUrl as f, tryBuildGraphSharesUrlForSharedLink as ft, normalizeQuery as g, listTeamsByName as h, parseMSTeamsConversationId as i, isUrlAllowed as it, MSTEAMS_OAUTH_REDIRECT_URI as j, MSTEAMS_OAUTH_CALLBACK_PATH as k, searchGraphUsers as l, resolveRequestUrl as lt, listChannelsForTeam as m, normalizeMSTeamsMessagingTarget as n, isLikelyImageAttachment as nt, parseMSTeamsTeamEntry as o, readNestedString as ot, fetchGraphJson as p, GRAPH_ROOT as q, normalizeMSTeamsUserInput as r, isRecord$1 as rt, resolveMSTeamsChannelAllowlist as s, resolveAttachmentFetchPolicy as st, looksLikeMSTeamsTargetId as t, isDownloadableAttachment as tt, deleteGraphRequest as u, safeFetchWithPolicy as ut, postGraphBetaJson as v, saveDelegatedTokens as w, hasConfiguredMSTeamsCredentials as x, postGraphJson as y, describeBotFrameworkServiceUrlHost as z };
|
package/dist/setup-plugin-api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as msteamsSetupAdapter, t as msteamsSetupWizard } from "./setup-surface-DGTz8Mlf.js";
|
|
1
|
+
import { C as resolveMSTeamsCredentials } from "./resolve-allowlist-C6VVTfbj.js";
|
|
3
2
|
import { t as MSTeamsChannelConfigSchema } from "./config-schema-BL4qQZiA.js";
|
|
3
|
+
import { c as msteamsSetupAdapter, t as msteamsSetupWizard } from "./setup-surface-DdyMxq-W.js";
|
|
4
4
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
5
5
|
import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
|
|
6
6
|
import { createTopLevelChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
@@ -1,223 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { C as resolveMSTeamsCredentials, E as normalizeSecretInputString, c as resolveMSTeamsUserAllowlist, o as parseMSTeamsTeamEntry, s as resolveMSTeamsChannelAllowlist, w as saveDelegatedTokens, x as hasConfiguredMSTeamsCredentials } from "./resolve-allowlist-C6VVTfbj.js";
|
|
2
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
import { asFiniteNumberInRange, parseStrictFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
|
|
4
4
|
import { DEFAULT_ACCOUNT_ID, createSetupTranslator, createStandardChannelSetupStatus, createTopLevelChannelAllowFromSetter, createTopLevelChannelDmPolicy, createTopLevelChannelGroupPolicySetter, mergeAllowFromEntries, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
5
5
|
import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
|
6
|
-
//#region extensions/msteams/src/resolve-allowlist.ts
|
|
7
|
-
function stripProviderPrefix(raw) {
|
|
8
|
-
return raw.replace(/^(msteams|teams):/i, "");
|
|
9
|
-
}
|
|
10
|
-
function normalizeMSTeamsMessagingTarget(raw) {
|
|
11
|
-
let trimmed = raw.trim();
|
|
12
|
-
if (!trimmed) return;
|
|
13
|
-
trimmed = stripProviderPrefix(trimmed).trim();
|
|
14
|
-
if (/^conversation:/i.test(trimmed)) {
|
|
15
|
-
const id = trimmed.slice(13).trim();
|
|
16
|
-
return id ? `conversation:${id}` : void 0;
|
|
17
|
-
}
|
|
18
|
-
if (/^user:/i.test(trimmed)) {
|
|
19
|
-
const id = trimmed.slice(5).trim();
|
|
20
|
-
return id ? `user:${id}` : void 0;
|
|
21
|
-
}
|
|
22
|
-
return trimmed || void 0;
|
|
23
|
-
}
|
|
24
|
-
function normalizeMSTeamsUserInput(raw) {
|
|
25
|
-
return stripProviderPrefix(raw).replace(/^(user|conversation):/i, "").trim();
|
|
26
|
-
}
|
|
27
|
-
function parseMSTeamsConversationId(raw) {
|
|
28
|
-
const trimmed = stripProviderPrefix(raw).trim();
|
|
29
|
-
if (!/^conversation:/i.test(trimmed)) return null;
|
|
30
|
-
return trimmed.slice(13).trim();
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Detect whether a raw target string looks like a Microsoft Teams conversation
|
|
34
|
-
* or user id that cron announce delivery and other explicit-target paths can
|
|
35
|
-
* forward verbatim to the channel adapter.
|
|
36
|
-
*
|
|
37
|
-
* Accepts both prefixed and bare formats:
|
|
38
|
-
* - `conversation:<id>` — explicit conversation prefix
|
|
39
|
-
* - `user:<aad-guid>` — user id (16+ hex chars, UUID-like)
|
|
40
|
-
* - `19:abc@thread.tacv2` / `19:abc@thread.skype` — channel / legacy group
|
|
41
|
-
* - `19:{userId}_{appId}@unq.gbl.spaces` — Graph 1:1 chat thread format
|
|
42
|
-
* - `a:1xxx` — Bot Framework personal (1:1) chat id
|
|
43
|
-
* - `8:orgid:xxx` — Bot Framework org-scoped personal chat id
|
|
44
|
-
* - `29:xxx` — Bot Framework user id
|
|
45
|
-
*
|
|
46
|
-
* Display-name user targets such as `user:John Smith` intentionally return
|
|
47
|
-
* false so that the Graph API directory lookup still runs for them.
|
|
48
|
-
*/
|
|
49
|
-
function looksLikeMSTeamsTargetId(raw) {
|
|
50
|
-
const trimmed = raw.trim();
|
|
51
|
-
if (!trimmed) return false;
|
|
52
|
-
if (/^conversation:/i.test(trimmed)) return true;
|
|
53
|
-
if (/^user:/i.test(trimmed)) {
|
|
54
|
-
const id = trimmed.slice(5).trim();
|
|
55
|
-
return /^[0-9a-fA-F-]{16,}$/.test(id);
|
|
56
|
-
}
|
|
57
|
-
if (/^19:.+@thread\.(tacv2|skype)$/i.test(trimmed)) return true;
|
|
58
|
-
if (/^19:.+@unq\.gbl\.spaces$/i.test(trimmed)) return true;
|
|
59
|
-
if (/^a:1[A-Za-z0-9_-]+$/i.test(trimmed)) return true;
|
|
60
|
-
if (/^8:orgid:[A-Za-z0-9-]+$/i.test(trimmed)) return true;
|
|
61
|
-
if (/^29:[A-Za-z0-9_-]+$/i.test(trimmed)) return true;
|
|
62
|
-
return /@thread\b/i.test(trimmed);
|
|
63
|
-
}
|
|
64
|
-
function normalizeMSTeamsTeamKey(raw) {
|
|
65
|
-
return stripProviderPrefix(raw).replace(/^team:/i, "").trim() || void 0;
|
|
66
|
-
}
|
|
67
|
-
function normalizeMSTeamsChannelKey(raw) {
|
|
68
|
-
return (raw?.trim().replace(/^#/, "").trim() ?? "") || void 0;
|
|
69
|
-
}
|
|
70
|
-
function normalizeMSTeamsConversationTargetId(raw) {
|
|
71
|
-
const trimmed = stripProviderPrefix(raw).trim();
|
|
72
|
-
return parseMSTeamsConversationId(trimmed) ?? trimmed;
|
|
73
|
-
}
|
|
74
|
-
function looksLikeMSTeamsThreadConversationId(raw) {
|
|
75
|
-
const normalized = normalizeMSTeamsConversationTargetId(raw);
|
|
76
|
-
return /^19:.+@thread\./i.test(normalized);
|
|
77
|
-
}
|
|
78
|
-
function parseMSTeamsTeamChannelInput(raw) {
|
|
79
|
-
const trimmed = stripProviderPrefix(raw).trim();
|
|
80
|
-
if (!trimmed) return {};
|
|
81
|
-
const parts = trimmed.split("/");
|
|
82
|
-
const team = normalizeMSTeamsTeamKey(parts[0] ?? "");
|
|
83
|
-
const channel = parts.length > 1 ? normalizeMSTeamsChannelKey(parts.slice(1).join("/")) : void 0;
|
|
84
|
-
return {
|
|
85
|
-
...team ? { team } : {},
|
|
86
|
-
...channel ? { channel } : {}
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
function parseMSTeamsTeamEntry(raw) {
|
|
90
|
-
const { team, channel } = parseMSTeamsTeamChannelInput(raw);
|
|
91
|
-
if (!team) return null;
|
|
92
|
-
return {
|
|
93
|
-
teamKey: team,
|
|
94
|
-
...channel ? { channelKey: channel } : {}
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
async function resolveMSTeamsChannelAllowlist(params) {
|
|
98
|
-
let tokenPromise;
|
|
99
|
-
const getToken = () => {
|
|
100
|
-
tokenPromise ??= resolveGraphToken(params.cfg);
|
|
101
|
-
return tokenPromise;
|
|
102
|
-
};
|
|
103
|
-
return await mapAllowlistResolutionInputs({
|
|
104
|
-
inputs: params.entries,
|
|
105
|
-
mapInput: async (input) => {
|
|
106
|
-
const { team, channel } = parseMSTeamsTeamChannelInput(input);
|
|
107
|
-
if (!team) return {
|
|
108
|
-
input,
|
|
109
|
-
resolved: false
|
|
110
|
-
};
|
|
111
|
-
if (looksLikeMSTeamsThreadConversationId(team)) {
|
|
112
|
-
const teamId = normalizeMSTeamsConversationTargetId(team);
|
|
113
|
-
if (!channel) return {
|
|
114
|
-
input,
|
|
115
|
-
resolved: true,
|
|
116
|
-
teamId,
|
|
117
|
-
teamName: teamId
|
|
118
|
-
};
|
|
119
|
-
if (!looksLikeMSTeamsThreadConversationId(channel)) return {
|
|
120
|
-
input,
|
|
121
|
-
resolved: false,
|
|
122
|
-
teamId,
|
|
123
|
-
teamName: teamId,
|
|
124
|
-
note: "channel id required for conversation-id team"
|
|
125
|
-
};
|
|
126
|
-
const channelId = normalizeMSTeamsConversationTargetId(channel);
|
|
127
|
-
return {
|
|
128
|
-
input,
|
|
129
|
-
resolved: true,
|
|
130
|
-
teamId,
|
|
131
|
-
teamName: teamId,
|
|
132
|
-
channelId,
|
|
133
|
-
channelName: channelId
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
const token = await getToken();
|
|
137
|
-
const teams = /^[0-9a-fA-F-]{16,}$/.test(team) ? [{
|
|
138
|
-
id: team,
|
|
139
|
-
displayName: team
|
|
140
|
-
}] : await listTeamsByName(token, team);
|
|
141
|
-
if (teams.length === 0) return {
|
|
142
|
-
input,
|
|
143
|
-
resolved: false,
|
|
144
|
-
note: "team not found"
|
|
145
|
-
};
|
|
146
|
-
const teamMatch = teams[0];
|
|
147
|
-
const graphTeamId = teamMatch.id?.trim();
|
|
148
|
-
const teamName = teamMatch.displayName?.trim() || team;
|
|
149
|
-
if (!graphTeamId) return {
|
|
150
|
-
input,
|
|
151
|
-
resolved: false,
|
|
152
|
-
note: "team id missing"
|
|
153
|
-
};
|
|
154
|
-
let teamChannels = [];
|
|
155
|
-
try {
|
|
156
|
-
teamChannels = await listChannelsForTeam(token, graphTeamId);
|
|
157
|
-
} catch {}
|
|
158
|
-
const teamId = teamChannels.find((ch) => normalizeOptionalLowercaseString(ch.displayName) === "general")?.id?.trim() || graphTeamId;
|
|
159
|
-
if (!channel) return {
|
|
160
|
-
input,
|
|
161
|
-
resolved: true,
|
|
162
|
-
teamId,
|
|
163
|
-
teamName,
|
|
164
|
-
note: teams.length > 1 ? "multiple teams; chose first" : void 0
|
|
165
|
-
};
|
|
166
|
-
const normalizedChannel = normalizeOptionalLowercaseString(channel);
|
|
167
|
-
const channelMatch = teamChannels.find((item) => item.id === channel) ?? teamChannels.find((item) => normalizeOptionalLowercaseString(item.displayName) === normalizedChannel) ?? teamChannels.find((item) => normalizeLowercaseStringOrEmpty(item.displayName ?? "").includes(normalizedChannel ?? ""));
|
|
168
|
-
if (!channelMatch?.id) return {
|
|
169
|
-
input,
|
|
170
|
-
resolved: false,
|
|
171
|
-
note: "channel not found"
|
|
172
|
-
};
|
|
173
|
-
return {
|
|
174
|
-
input,
|
|
175
|
-
resolved: true,
|
|
176
|
-
teamId,
|
|
177
|
-
teamName,
|
|
178
|
-
channelId: channelMatch.id,
|
|
179
|
-
channelName: channelMatch.displayName ?? channel,
|
|
180
|
-
note: teamChannels.length > 1 ? "multiple channels; chose first" : void 0
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
async function resolveMSTeamsUserAllowlist(params) {
|
|
186
|
-
const token = await resolveGraphToken(params.cfg);
|
|
187
|
-
return await mapAllowlistResolutionInputs({
|
|
188
|
-
inputs: params.entries,
|
|
189
|
-
mapInput: async (input) => {
|
|
190
|
-
const query = normalizeQuery(normalizeMSTeamsUserInput(input));
|
|
191
|
-
if (!query) return {
|
|
192
|
-
input,
|
|
193
|
-
resolved: false
|
|
194
|
-
};
|
|
195
|
-
if (/^[0-9a-fA-F-]{16,}$/.test(query)) return {
|
|
196
|
-
input,
|
|
197
|
-
resolved: true,
|
|
198
|
-
id: query
|
|
199
|
-
};
|
|
200
|
-
const users = await searchGraphUsers({
|
|
201
|
-
token,
|
|
202
|
-
query,
|
|
203
|
-
top: 10
|
|
204
|
-
});
|
|
205
|
-
const match = users[0];
|
|
206
|
-
if (!match?.id) return {
|
|
207
|
-
input,
|
|
208
|
-
resolved: false
|
|
209
|
-
};
|
|
210
|
-
return {
|
|
211
|
-
input,
|
|
212
|
-
resolved: true,
|
|
213
|
-
id: match.id,
|
|
214
|
-
name: match.displayName ?? void 0,
|
|
215
|
-
note: users.length > 1 ? "multiple matches; chose first" : void 0
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
//#endregion
|
|
221
6
|
//#region extensions/msteams/src/setup-core.ts
|
|
222
7
|
const t$1 = createSetupTranslator();
|
|
223
8
|
const msteamsSetupAdapter = {
|
|
@@ -321,6 +106,181 @@ function createMSTeamsSetupWizardBase() {
|
|
|
321
106
|
};
|
|
322
107
|
}
|
|
323
108
|
//#endregion
|
|
109
|
+
//#region extensions/msteams/src/errors.ts
|
|
110
|
+
const MAX_SAFE_RETRY_AFTER_SECONDS = Number.MAX_SAFE_INTEGER / 1e3;
|
|
111
|
+
function formatUnknownError(err) {
|
|
112
|
+
if (err instanceof Error) return err.message;
|
|
113
|
+
if (typeof err === "string") return err;
|
|
114
|
+
if (err === null) return "null";
|
|
115
|
+
if (err === void 0) return "undefined";
|
|
116
|
+
if (typeof err === "number" || typeof err === "boolean" || typeof err === "bigint") return String(err);
|
|
117
|
+
if (typeof err === "symbol") return err.description ?? err.toString();
|
|
118
|
+
if (typeof err === "function") return err.name ? `[function ${err.name}]` : "[function]";
|
|
119
|
+
try {
|
|
120
|
+
return JSON.stringify(err) ?? "unknown error";
|
|
121
|
+
} catch {
|
|
122
|
+
return "unknown error";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function extractStatusCode(err) {
|
|
126
|
+
if (!isRecord(err)) return null;
|
|
127
|
+
const parseStatusCode = (value) => {
|
|
128
|
+
if (typeof value === "number") return Number.isInteger(value) && value >= 100 && value <= 599 ? value : null;
|
|
129
|
+
if (typeof value === "string") {
|
|
130
|
+
const trimmed = value.trim();
|
|
131
|
+
if (!/^\d{3}$/.test(trimmed)) return null;
|
|
132
|
+
const parsed = Number(trimmed);
|
|
133
|
+
return parsed >= 100 && parsed <= 599 ? parsed : null;
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
};
|
|
137
|
+
const directStatus = parseStatusCode(err.statusCode ?? err.status);
|
|
138
|
+
if (directStatus !== null) return directStatus;
|
|
139
|
+
const response = err.response;
|
|
140
|
+
if (isRecord(response)) {
|
|
141
|
+
const responseStatus = parseStatusCode(response.status);
|
|
142
|
+
if (responseStatus !== null) return responseStatus;
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
function extractErrorCode(err) {
|
|
147
|
+
if (!isRecord(err)) return null;
|
|
148
|
+
const direct = err.code;
|
|
149
|
+
if (typeof direct === "string" && direct.trim()) return direct;
|
|
150
|
+
const response = err.response;
|
|
151
|
+
if (!isRecord(response)) return null;
|
|
152
|
+
const body = response.body;
|
|
153
|
+
if (isRecord(body)) {
|
|
154
|
+
const error = body.error;
|
|
155
|
+
if (isRecord(error) && typeof error.code === "string" && error.code.trim()) return error.code;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
function extractRetryAfterMs(err) {
|
|
160
|
+
if (!isRecord(err)) return null;
|
|
161
|
+
const directMs = asFiniteNumberInRange(err.retryAfterMs ?? err.retry_after_ms, {
|
|
162
|
+
min: 0,
|
|
163
|
+
max: Number.MAX_SAFE_INTEGER
|
|
164
|
+
});
|
|
165
|
+
if (directMs !== void 0) return directMs;
|
|
166
|
+
const retryAfter = err.retryAfter ?? err.retry_after;
|
|
167
|
+
const retryAfterSeconds = asFiniteNumberInRange(retryAfter, {
|
|
168
|
+
min: 0,
|
|
169
|
+
max: MAX_SAFE_RETRY_AFTER_SECONDS
|
|
170
|
+
});
|
|
171
|
+
if (retryAfterSeconds !== void 0) return retryAfterSeconds * 1e3;
|
|
172
|
+
if (typeof retryAfter === "string") {
|
|
173
|
+
const parsed = parseNonNegativeRetryAfterSeconds(retryAfter);
|
|
174
|
+
if (parsed !== void 0) return parsed * 1e3;
|
|
175
|
+
}
|
|
176
|
+
const response = err.response;
|
|
177
|
+
if (!isRecord(response)) return null;
|
|
178
|
+
const headers = response.headers;
|
|
179
|
+
if (!headers) return null;
|
|
180
|
+
if (isRecord(headers)) {
|
|
181
|
+
const raw = headers["retry-after"] ?? headers["Retry-After"];
|
|
182
|
+
if (typeof raw === "string") {
|
|
183
|
+
const parsed = parseNonNegativeRetryAfterSeconds(raw);
|
|
184
|
+
if (parsed !== void 0) return parsed * 1e3;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (typeof headers === "object" && headers !== null && "get" in headers && typeof headers.get === "function") {
|
|
188
|
+
const raw = headers.get("retry-after");
|
|
189
|
+
if (raw) {
|
|
190
|
+
const parsed = parseNonNegativeRetryAfterSeconds(raw);
|
|
191
|
+
if (parsed !== void 0) return parsed * 1e3;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
function parseNonNegativeRetryAfterSeconds(raw) {
|
|
197
|
+
const trimmed = raw.trim();
|
|
198
|
+
if (!/^\d+(?:\.\d+)?$/.test(trimmed)) return;
|
|
199
|
+
return asFiniteNumberInRange(parseStrictFiniteNumber(trimmed), {
|
|
200
|
+
min: 0,
|
|
201
|
+
max: MAX_SAFE_RETRY_AFTER_SECONDS
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Classify outbound send errors for safe retries and actionable logs.
|
|
206
|
+
*
|
|
207
|
+
* Important: We only mark errors as retryable when we have an explicit HTTP
|
|
208
|
+
* status code that indicates the message was not accepted (e.g. 429, 5xx).
|
|
209
|
+
* For transport-level errors where delivery is ambiguous, we prefer to avoid
|
|
210
|
+
* retries to reduce the chance of duplicate posts.
|
|
211
|
+
*/
|
|
212
|
+
function classifyMSTeamsSendError(err) {
|
|
213
|
+
const statusCode = extractStatusCode(err);
|
|
214
|
+
const retryAfterMs = extractRetryAfterMs(err);
|
|
215
|
+
const errorCode = extractErrorCode(err) ?? void 0;
|
|
216
|
+
if (statusCode === 401) return {
|
|
217
|
+
kind: "auth",
|
|
218
|
+
statusCode,
|
|
219
|
+
errorCode
|
|
220
|
+
};
|
|
221
|
+
if (statusCode === 403) {
|
|
222
|
+
if (errorCode === "ContentStreamNotAllowed") return {
|
|
223
|
+
kind: "permanent",
|
|
224
|
+
statusCode,
|
|
225
|
+
errorCode
|
|
226
|
+
};
|
|
227
|
+
return {
|
|
228
|
+
kind: "auth",
|
|
229
|
+
statusCode,
|
|
230
|
+
errorCode
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
if (statusCode === 429) return {
|
|
234
|
+
kind: "throttled",
|
|
235
|
+
statusCode,
|
|
236
|
+
retryAfterMs: retryAfterMs ?? void 0,
|
|
237
|
+
errorCode
|
|
238
|
+
};
|
|
239
|
+
if (statusCode === 408 || statusCode != null && statusCode >= 500) return {
|
|
240
|
+
kind: "transient",
|
|
241
|
+
statusCode,
|
|
242
|
+
retryAfterMs: retryAfterMs ?? void 0,
|
|
243
|
+
errorCode
|
|
244
|
+
};
|
|
245
|
+
if (statusCode != null && statusCode >= 400) return {
|
|
246
|
+
kind: "permanent",
|
|
247
|
+
statusCode,
|
|
248
|
+
errorCode
|
|
249
|
+
};
|
|
250
|
+
if (statusCode == null) {
|
|
251
|
+
const networkCode = isRecord(err) && typeof err.code === "string" ? err.code : null;
|
|
252
|
+
if (networkCode === "ECONNREFUSED" || networkCode === "ENOTFOUND" || networkCode === "EHOSTUNREACH" || networkCode === "ETIMEDOUT" || networkCode === "ECONNRESET") return {
|
|
253
|
+
kind: "network",
|
|
254
|
+
errorCode: networkCode
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
kind: "unknown",
|
|
259
|
+
statusCode: statusCode ?? void 0,
|
|
260
|
+
retryAfterMs: retryAfterMs ?? void 0,
|
|
261
|
+
errorCode
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Detect whether an error is caused by a revoked Proxy.
|
|
266
|
+
*
|
|
267
|
+
* The Bot Framework SDK wraps TurnContext in a Proxy that is revoked once the
|
|
268
|
+
* turn handler returns. Any later access (e.g. from a debounced callback)
|
|
269
|
+
* throws a TypeError whose message contains the distinctive "proxy that has
|
|
270
|
+
* been revoked" string.
|
|
271
|
+
*/
|
|
272
|
+
function isRevokedProxyError(err) {
|
|
273
|
+
if (!(err instanceof TypeError)) return false;
|
|
274
|
+
return /proxy that has been revoked/i.test(err.message);
|
|
275
|
+
}
|
|
276
|
+
function formatMSTeamsSendErrorHint(classification) {
|
|
277
|
+
if (classification.kind === "auth") return "check msteams appId/appPassword/tenantId (or env vars MSTEAMS_APP_ID/MSTEAMS_APP_PASSWORD/MSTEAMS_TENANT_ID)";
|
|
278
|
+
if (classification.errorCode === "ContentStreamNotAllowed") return "Teams expired the content stream; stop streaming earlier and fall back to normal message delivery";
|
|
279
|
+
if (classification.kind === "throttled") return "Teams throttled the bot; backing off may help";
|
|
280
|
+
if (classification.kind === "transient") return "transient Teams/Bot Framework error; retry may succeed";
|
|
281
|
+
if (classification.kind === "network") return "transport-level failure sending reply to Teams Bot Connector (smba.trafficmanager.net) — check egress firewall rules allow outbound HTTPS to smba.trafficmanager.net";
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
324
284
|
//#region extensions/msteams/src/setup-surface.ts
|
|
325
285
|
const t = createSetupTranslator();
|
|
326
286
|
const channel = "msteams";
|
|
@@ -489,7 +449,7 @@ const msteamsSetupWizard = {
|
|
|
489
449
|
}
|
|
490
450
|
};
|
|
491
451
|
try {
|
|
492
|
-
const { loginMSTeamsDelegated } = await import("./oauth-
|
|
452
|
+
const { loginMSTeamsDelegated } = await import("./oauth-CJj-b_Kn.js");
|
|
493
453
|
const progress = params.prompter.progress(t("wizard.msteams.delegatedOAuthProgress"));
|
|
494
454
|
saveDelegatedTokens(await loginMSTeamsDelegated({
|
|
495
455
|
isRemote: true,
|
|
@@ -530,4 +490,4 @@ const msteamsSetupWizard = {
|
|
|
530
490
|
})
|
|
531
491
|
};
|
|
532
492
|
//#endregion
|
|
533
|
-
export {
|
|
493
|
+
export { formatUnknownError as a, msteamsSetupAdapter as c, formatMSTeamsSendErrorHint as i, openDelegatedOAuthUrl as n, isRevokedProxyError as o, classifyMSTeamsSendError as r, createMSTeamsSetupWizardBase as s, msteamsSetupWizard as t };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { A as summarizeMapping, D as resolveDefaultGroupPolicy, E as resolveChannelMediaMaxBytes, M as getMSTeamsRuntime, N as getOptionalMSTeamsRuntime, _ as isDangerousNameMatchingEnabled, a as buildMediaPayload, b as logTypingFailure, c as createChannelMessageReplyPipeline, f as dispatchReplyFromConfigWithSettledDispatcher$1, l as createChannelPairingController, n as DEFAULT_WEBHOOK_MAX_BODY_BYTES, t as DEFAULT_ACCOUNT_ID, v as keepHttpServerTaskAlive, x as mergeAllowlist } from "./runtime-api-BlvMnDKz.js";
|
|
2
|
-
import { $ as
|
|
3
|
-
import {
|
|
4
|
-
import { a as
|
|
5
|
-
import { C as resolveMSTeamsSqliteStateEnv, E as readJsonFile, S as createMSTeamsConversationStoreState, T as withMSTeamsSqliteMutationLock, _ as buildFileInfoCard, b as createMSTeamsPollStoreState, c as renderReplyPayloadsToMessages, d as withRevokedProxyFallback, f as resolveGraphChatId, g as removePendingUploadFs, h as getPendingUploadFs, l as sendMSTeamsMessages, m as removePendingUpload, p as getPendingUpload, s as buildConversationReference, u as sendMSTeamsActivityWithReference, v as parseFileConsentInvoke, w as toPluginJsonValue, x as extractMSTeamsPollVote, y as uploadToConsentUrl } from "./probe-
|
|
2
|
+
import { $ as extractInlineImageCandidates, C as resolveMSTeamsCredentials, F as createMSTeamsTokenProvider, H as tryNormalizeBotFrameworkServiceUrl, I as loadMSTeamsSdkWithAuth, J as IMG_SRC_RE, K as ATTACHMENT_TAG_RE, P as createMSTeamsExpressAdapter, Q as extractHtmlFromAttachment, R as ensureUserAgentHeader, T as resolveMSTeamsStorePath, W as resolveMSTeamsSdkCloudOptions, X as encodeGraphShareId, Y as applyAuthorizationHeaderForUrl, Z as estimateBase64DecodedBytes, at as normalizeContentType, c as resolveMSTeamsUserAllowlist, ct as resolveMediaSsrfPolicy, dt as safeHostForUrl, et as inferPlaceholder, ft as tryBuildGraphSharesUrlForSharedLink, it as isUrlAllowed, lt as resolveRequestUrl, nt as isLikelyImageAttachment, ot as readNestedString, p as fetchGraphJson, q as GRAPH_ROOT, rt as isRecord$1, s as resolveMSTeamsChannelAllowlist, st as resolveAttachmentFetchPolicy, tt as isDownloadableAttachment, ut as safeFetchWithPolicy } from "./resolve-allowlist-C6VVTfbj.js";
|
|
3
|
+
import { a as resolveMSTeamsReplyPolicy, i as resolveMSTeamsAllowlistMatch, o as resolveMSTeamsRouteConfig } from "./channel-BwNnuHbh.js";
|
|
4
|
+
import { a as formatUnknownError, i as formatMSTeamsSendErrorHint, r as classifyMSTeamsSendError } from "./setup-surface-DdyMxq-W.js";
|
|
5
|
+
import { C as resolveMSTeamsSqliteStateEnv, E as readJsonFile, S as createMSTeamsConversationStoreState, T as withMSTeamsSqliteMutationLock, _ as buildFileInfoCard, b as createMSTeamsPollStoreState, c as renderReplyPayloadsToMessages, d as withRevokedProxyFallback, f as resolveGraphChatId, g as removePendingUploadFs, h as getPendingUploadFs, l as sendMSTeamsMessages, m as removePendingUpload, p as getPendingUpload, s as buildConversationReference, u as sendMSTeamsActivityWithReference, v as parseFileConsentInvoke, w as toPluginJsonValue, x as extractMSTeamsPollVote, y as uploadToConsentUrl } from "./probe-bq_Uev4c.js";
|
|
6
6
|
import { formatAllowlistMatchMeta } from "openclaw/plugin-sdk/allow-from";
|
|
7
7
|
import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, normalizeChannelProgressDraftLineIdentity, resolveChannelPreviewStreamMode, resolveChannelProgressDraftMaxLines, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
|
|
8
8
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/msteams",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.2-beta.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/msteams",
|
|
9
|
-
"version": "2026.6.
|
|
9
|
+
"version": "2026.6.2-beta.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@azure/identity": "4.13.1",
|
|
12
12
|
"@microsoft/teams.api": "2.0.12",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"typebox": "1.1.39"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"openclaw": ">=2026.6.
|
|
18
|
+
"openclaw": ">=2026.6.2-beta.1"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
21
21
|
"openclaw": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/msteams",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.2-beta.1",
|
|
4
4
|
"description": "OpenClaw Microsoft Teams channel plugin for bot conversations.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"typebox": "1.1.39"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"openclaw": ">=2026.6.
|
|
18
|
+
"openclaw": ">=2026.6.2-beta.1"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
21
21
|
"openclaw": {
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"minHostVersion": ">=2026.4.10"
|
|
52
52
|
},
|
|
53
53
|
"compat": {
|
|
54
|
-
"pluginApi": ">=2026.6.
|
|
54
|
+
"pluginApi": ">=2026.6.2-beta.1"
|
|
55
55
|
},
|
|
56
56
|
"build": {
|
|
57
|
-
"openclawVersion": "2026.6.
|
|
57
|
+
"openclawVersion": "2026.6.2-beta.1"
|
|
58
58
|
},
|
|
59
59
|
"release": {
|
|
60
60
|
"publishToClawHub": true,
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
2
|
-
import { createProviderHttpError, readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
|
3
|
-
import { resolveExpiresAtMsFromDurationSeconds } from "openclaw/plugin-sdk/number-runtime";
|
|
4
|
-
//#region extensions/msteams/src/http-error.ts
|
|
5
|
-
async function createMSTeamsHttpError(response, label, options) {
|
|
6
|
-
return await createProviderHttpError(response, label, options);
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
|
-
//#region extensions/msteams/src/oauth.shared.ts
|
|
10
|
-
const MSTEAMS_OAUTH_REDIRECT_URI = "http://localhost:8086/oauth2callback";
|
|
11
|
-
const MSTEAMS_OAUTH_CALLBACK_PORT = 8086;
|
|
12
|
-
const MSTEAMS_OAUTH_CALLBACK_PATH = "/oauth2callback";
|
|
13
|
-
const MSTEAMS_DEFAULT_TOKEN_FETCH_TIMEOUT_MS = 1e4;
|
|
14
|
-
const MSTEAMS_DEFAULT_DELEGATED_SCOPES = [
|
|
15
|
-
"ChatMessage.Send",
|
|
16
|
-
"ChannelMessage.Send",
|
|
17
|
-
"Chat.ReadWrite",
|
|
18
|
-
"offline_access"
|
|
19
|
-
];
|
|
20
|
-
function buildMSTeamsAuthEndpoint(tenantId) {
|
|
21
|
-
return `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/authorize`;
|
|
22
|
-
}
|
|
23
|
-
function buildMSTeamsTokenEndpoint(tenantId) {
|
|
24
|
-
return `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/token`;
|
|
25
|
-
}
|
|
26
|
-
//#endregion
|
|
27
|
-
//#region extensions/msteams/src/oauth.token.ts
|
|
28
|
-
/** Five-minute buffer subtracted from token expiry to avoid edge-case clock drift. */
|
|
29
|
-
const EXPIRY_BUFFER_MS = 300 * 1e3;
|
|
30
|
-
function createMSTeamsTokenBody(params) {
|
|
31
|
-
const body = new URLSearchParams({
|
|
32
|
-
client_id: params.clientId,
|
|
33
|
-
client_secret: params.clientSecret,
|
|
34
|
-
grant_type: params.grantType,
|
|
35
|
-
scope: [...params.scopes].join(" ")
|
|
36
|
-
});
|
|
37
|
-
for (const [key, value] of Object.entries(params.values ?? {})) body.set(key, value);
|
|
38
|
-
return body;
|
|
39
|
-
}
|
|
40
|
-
function resolveMSTeamsTokenExpiresAt(value) {
|
|
41
|
-
return resolveExpiresAtMsFromDurationSeconds(value, { bufferMs: EXPIRY_BUFFER_MS });
|
|
42
|
-
}
|
|
43
|
-
function parseMSTeamsTokenResponse(data, failureLabel) {
|
|
44
|
-
const expiresAt = resolveMSTeamsTokenExpiresAt(data.expires_in);
|
|
45
|
-
if (typeof data.access_token !== "string" || !data.access_token || expiresAt === void 0 || data.refresh_token !== void 0 && typeof data.refresh_token !== "string" || data.scope !== void 0 && typeof data.scope !== "string") throw new Error(`MSTeams ${failureLabel} failed: invalid token response fields`);
|
|
46
|
-
return {
|
|
47
|
-
access_token: data.access_token,
|
|
48
|
-
refresh_token: data.refresh_token,
|
|
49
|
-
expiresAt,
|
|
50
|
-
scope: data.scope
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
async function fetchMSTeamsTokens(params) {
|
|
54
|
-
const currentFetch = globalThis.fetch;
|
|
55
|
-
const { response, release } = await fetchWithSsrFGuard({
|
|
56
|
-
url: params.tokenUrl,
|
|
57
|
-
fetchImpl: async (input, guardedInit) => await currentFetch(input, guardedInit),
|
|
58
|
-
init: {
|
|
59
|
-
method: "POST",
|
|
60
|
-
headers: {
|
|
61
|
-
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
62
|
-
Accept: "application/json"
|
|
63
|
-
},
|
|
64
|
-
body: params.body,
|
|
65
|
-
signal: AbortSignal.timeout(MSTEAMS_DEFAULT_TOKEN_FETCH_TIMEOUT_MS)
|
|
66
|
-
},
|
|
67
|
-
auditContext: params.auditContext
|
|
68
|
-
});
|
|
69
|
-
try {
|
|
70
|
-
if (!response.ok) throw await createMSTeamsHttpError(response, `MSTeams ${params.failureLabel} failed`);
|
|
71
|
-
return parseMSTeamsTokenResponse(await readProviderJsonResponse(response, `MSTeams ${params.failureLabel} failed`), params.failureLabel);
|
|
72
|
-
} finally {
|
|
73
|
-
await release();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
async function requestMSTeamsDelegatedTokens(params) {
|
|
77
|
-
const scopes = params.scopes ?? MSTEAMS_DEFAULT_DELEGATED_SCOPES;
|
|
78
|
-
const body = createMSTeamsTokenBody({
|
|
79
|
-
clientId: params.clientId,
|
|
80
|
-
clientSecret: params.clientSecret,
|
|
81
|
-
grantType: params.grantType,
|
|
82
|
-
scopes,
|
|
83
|
-
values: params.values
|
|
84
|
-
});
|
|
85
|
-
const data = await fetchMSTeamsTokens({
|
|
86
|
-
tokenUrl: buildMSTeamsTokenEndpoint(params.tenantId),
|
|
87
|
-
body,
|
|
88
|
-
auditContext: params.auditContext,
|
|
89
|
-
failureLabel: params.failureLabel
|
|
90
|
-
});
|
|
91
|
-
return {
|
|
92
|
-
accessToken: data.access_token,
|
|
93
|
-
refreshToken: params.resolveRefreshToken(data),
|
|
94
|
-
expiresAt: data.expiresAt,
|
|
95
|
-
scopes: data.scope ? data.scope.split(" ") : [...scopes]
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
async function exchangeMSTeamsCodeForTokens(params) {
|
|
99
|
-
return await requestMSTeamsDelegatedTokens({
|
|
100
|
-
tenantId: params.tenantId,
|
|
101
|
-
clientId: params.clientId,
|
|
102
|
-
clientSecret: params.clientSecret,
|
|
103
|
-
grantType: "authorization_code",
|
|
104
|
-
scopes: params.scopes,
|
|
105
|
-
values: {
|
|
106
|
-
code: params.code,
|
|
107
|
-
redirect_uri: MSTEAMS_OAUTH_REDIRECT_URI,
|
|
108
|
-
code_verifier: params.verifier
|
|
109
|
-
},
|
|
110
|
-
auditContext: "msteams-oauth-token-exchange",
|
|
111
|
-
failureLabel: "token exchange",
|
|
112
|
-
resolveRefreshToken: (data) => {
|
|
113
|
-
if (!data.refresh_token) throw new Error("No refresh token received from Azure AD. Please try again.");
|
|
114
|
-
return data.refresh_token;
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
async function refreshMSTeamsDelegatedTokens(params) {
|
|
119
|
-
return await requestMSTeamsDelegatedTokens({
|
|
120
|
-
tenantId: params.tenantId,
|
|
121
|
-
clientId: params.clientId,
|
|
122
|
-
clientSecret: params.clientSecret,
|
|
123
|
-
grantType: "refresh_token",
|
|
124
|
-
scopes: params.scopes,
|
|
125
|
-
values: { refresh_token: params.refreshToken },
|
|
126
|
-
auditContext: "msteams-oauth-token-refresh",
|
|
127
|
-
failureLabel: "token refresh",
|
|
128
|
-
resolveRefreshToken: (data) => data.refresh_token ?? params.refreshToken
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
//#endregion
|
|
132
|
-
export { MSTEAMS_OAUTH_CALLBACK_PORT as a, createMSTeamsHttpError as c, MSTEAMS_OAUTH_CALLBACK_PATH as i, refreshMSTeamsDelegatedTokens as n, MSTEAMS_OAUTH_REDIRECT_URI as o, MSTEAMS_DEFAULT_DELEGATED_SCOPES as r, buildMSTeamsAuthEndpoint as s, exchangeMSTeamsCodeForTokens as t };
|