@openclaw/msteams 2026.7.2-beta.7 → 2026.8.1-beta.3

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.
@@ -0,0 +1,311 @@
1
+ const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.cjs");
2
+ let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
3
+ let node_crypto = require("node:crypto");
4
+ let node_fs_promises = require("node:fs/promises");
5
+ node_fs_promises = require_rolldown_runtime.__toESM(node_fs_promises, 1);
6
+ let node_path = require("node:path");
7
+ node_path = require_rolldown_runtime.__toESM(node_path, 1);
8
+ let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
9
+ let node_timers_promises = require("node:timers/promises");
10
+ let node_url = require("node:url");
11
+ let node_events = require("node:events");
12
+ let node_http = require("node:http");
13
+ //#region extensions/msteams/src/qa/bot-framework-server.ts
14
+ const AMBIGUOUS_GATEWAY_TIMEOUT_MARKER = "QA-MSTEAMS-AMBIGUOUS-504";
15
+ async function readJson(request) {
16
+ const chunks = [];
17
+ for await (const chunk of request) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
18
+ const body = Buffer.concat(chunks).toString("utf8");
19
+ return body ? JSON.parse(body) : {};
20
+ }
21
+ function sendJson(response, status, body) {
22
+ response.writeHead(status, { "content-type": "application/json" });
23
+ response.end(JSON.stringify(body));
24
+ }
25
+ function parseConversationPath(pathname) {
26
+ const match = /\/v3\/conversations\/([^/]+)\/activities(?:\/[^/]+)?$/u.exec(pathname);
27
+ if (!match) return;
28
+ const encodedConversationId = match[1];
29
+ if (!encodedConversationId) return;
30
+ const [conversationId, threadId] = decodeURIComponent(encodedConversationId).split(";messageid=", 2);
31
+ if (!conversationId) return;
32
+ return {
33
+ conversationId,
34
+ threadId
35
+ };
36
+ }
37
+ async function startMSTeamsQaBotFrameworkServer(options) {
38
+ const server = (0, node_http.createServer)((request, response) => {
39
+ (async () => {
40
+ if (request.headers["x-openclaw-msteams-qa-nonce"] !== options.nonce || request.headers.authorization !== `Bearer ${options.botToken}`) {
41
+ sendJson(response, 401, { error: "unauthorized" });
42
+ return;
43
+ }
44
+ const route = parseConversationPath(new URL(request.url ?? "/", "http://127.0.0.1").pathname);
45
+ if (request.method !== "POST" || !route) {
46
+ sendJson(response, 404, { error: "not found" });
47
+ return;
48
+ }
49
+ const activity = await readJson(request);
50
+ const activityId = `qa-outbound-${(0, node_crypto.randomUUID)()}`;
51
+ await options.onOutbound({
52
+ activity,
53
+ activityId,
54
+ conversationId: route.conversationId,
55
+ ...route.threadId ? { threadId: route.threadId } : {}
56
+ });
57
+ const acceptedButTimedOut = typeof activity.text === "string" && activity.text.includes(AMBIGUOUS_GATEWAY_TIMEOUT_MARKER);
58
+ sendJson(response, acceptedButTimedOut ? 504 : 200, acceptedButTimedOut ? { error: "gateway timeout after acceptance" } : { id: activityId });
59
+ })().catch((error) => {
60
+ sendJson(response, 500, { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(error) });
61
+ });
62
+ });
63
+ server.listen(0, "127.0.0.1");
64
+ await (0, node_events.once)(server, "listening");
65
+ const { port } = server.address();
66
+ return {
67
+ baseUrl: `http://127.0.0.1:${port}/`,
68
+ async close() {
69
+ server.close();
70
+ await (0, node_events.once)(server, "close");
71
+ }
72
+ };
73
+ }
74
+ async function reserveMSTeamsQaWebhookPort() {
75
+ const server = (0, node_http.createServer)();
76
+ server.listen(0, "127.0.0.1");
77
+ await (0, node_events.once)(server, "listening");
78
+ const { port } = server.address();
79
+ server.close();
80
+ await (0, node_events.once)(server, "close");
81
+ return port;
82
+ }
83
+ //#endregion
84
+ //#region extensions/msteams/src/qa/adapter.runtime.ts
85
+ const SERVICE_URL = "https://smba.trafficmanager.net/qa";
86
+ const APP_ID = "qa-msteams-app";
87
+ const TENANT_ID = "qa-msteams-tenant";
88
+ const DRIVER_ID = "qa-msteams-driver";
89
+ const DRIVER_AAD_OBJECT_ID = "00000000-0000-4000-8000-000000000002";
90
+ const TEAM_ID = "qa-msteams-team";
91
+ const TEAM_AAD_GROUP_ID = "00000000-0000-4000-8000-000000000001";
92
+ const DEFAULT_ACCOUNT_ID = "default";
93
+ function nativeConversationId(logicalId) {
94
+ return logicalId.startsWith("19:") ? logicalId : `19:${logicalId}@thread.tacv2`;
95
+ }
96
+ function renderMSTeamsQaText(text) {
97
+ const mentionText = "<at>openclaw</at>";
98
+ const renderedText = text.replace(/@openclaw\b/giu, mentionText);
99
+ return {
100
+ text: renderedText,
101
+ ...renderedText === text ? {} : { entities: [{
102
+ type: "mention",
103
+ text: mentionText,
104
+ mentioned: {
105
+ id: APP_ID,
106
+ name: "OpenClaw QA"
107
+ }
108
+ }] }
109
+ };
110
+ }
111
+ function activityText(activity) {
112
+ return typeof activity.text === "string" ? activity.text : "";
113
+ }
114
+ function createMSTeamsQaBotToken() {
115
+ const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
116
+ return [
117
+ encode({
118
+ alg: "none",
119
+ typ: "JWT"
120
+ }),
121
+ encode({
122
+ appid: APP_ID,
123
+ jti: (0, node_crypto.randomUUID)(),
124
+ tid: TENANT_ID
125
+ }),
126
+ "qa"
127
+ ].join(".");
128
+ }
129
+ async function waitForMSTeamsChannelReady(gateway, timeoutMs = 6e4, pollIntervalMs = 500) {
130
+ const deadline = Date.now() + timeoutMs;
131
+ let lastAccounts;
132
+ while (Date.now() < deadline) {
133
+ const accounts = (await gateway.call("channels.status", {
134
+ probe: false,
135
+ timeoutMs: 2e3
136
+ }, { timeoutMs: 5e3 })).channelAccounts?.msteams ?? [];
137
+ lastAccounts = accounts;
138
+ const account = accounts.find((entry) => entry.accountId === DEFAULT_ACCOUNT_ID);
139
+ if (account?.running === true && account.restartPending !== true) return;
140
+ await (0, node_timers_promises.setTimeout)(pollIntervalMs);
141
+ }
142
+ throw new Error(`msteams did not become ready; last accounts: ${JSON.stringify(lastAccounts)}`);
143
+ }
144
+ async function createMSTeamsQaTransportAdapter(context) {
145
+ const accountId = context.adapterOptions?.sutAccountId?.trim() || DEFAULT_ACCOUNT_ID;
146
+ const webhookPort = await reserveMSTeamsQaWebhookPort();
147
+ const nonce = (0, node_crypto.randomUUID)();
148
+ const botToken = createMSTeamsQaBotToken();
149
+ const bootstrapPath = node_path.default.join(context.outputDir, ".msteams-private-qa-bootstrap.mjs");
150
+ const busByNativeMessageId = /* @__PURE__ */ new Map();
151
+ const nativeByBusMessageId = /* @__PURE__ */ new Map();
152
+ const conversationKindByNativeId = /* @__PURE__ */ new Map();
153
+ const logicalConversationByNativeId = /* @__PURE__ */ new Map();
154
+ const requireGroupMention = context.adapterOptions?.transportPolicy?.requireGroupMention === true;
155
+ const connector = await startMSTeamsQaBotFrameworkServer({
156
+ botToken,
157
+ nonce,
158
+ async onOutbound(outbound) {
159
+ const kind = conversationKindByNativeId.get(outbound.conversationId) ?? "channel";
160
+ const conversationId = logicalConversationByNativeId.get(outbound.conversationId) ?? outbound.conversationId;
161
+ const message = await context.messages.addOutboundMessage({
162
+ accountId,
163
+ to: `${kind === "direct" ? "dm" : kind}:${conversationId}`,
164
+ senderId: APP_ID,
165
+ text: activityText(outbound.activity),
166
+ timestamp: Date.now(),
167
+ ...outbound.threadId ? { threadId: busByNativeMessageId.get(outbound.threadId) ?? outbound.threadId } : {}
168
+ });
169
+ nativeByBusMessageId.set(message.id, outbound.activityId);
170
+ busByNativeMessageId.set(outbound.activityId, message.id);
171
+ conversationKindByNativeId.set(outbound.conversationId, kind);
172
+ }
173
+ });
174
+ try {
175
+ await node_fs_promises.default.mkdir(context.outputDir, { recursive: true });
176
+ await node_fs_promises.default.writeFile(bootstrapPath, [
177
+ "const key = Symbol.for(\"openclaw.msteams.privateQaRuntime\");",
178
+ `globalThis[key] = ${JSON.stringify({
179
+ connectorUrl: connector.baseUrl,
180
+ nonce,
181
+ botToken
182
+ })};`,
183
+ ""
184
+ ].join("\n"), {
185
+ encoding: "utf8",
186
+ mode: 384
187
+ });
188
+ } catch (error) {
189
+ await connector.close().catch(() => void 0);
190
+ throw error;
191
+ }
192
+ return {
193
+ id: "msteams",
194
+ label: "Microsoft Teams live",
195
+ accountId,
196
+ requiredPluginIds: ["msteams"],
197
+ supportedActions: [],
198
+ async sendInbound(input) {
199
+ const conversationId = nativeConversationId(input.conversation.id);
200
+ const nativeThreadId = input.threadId ? nativeByBusMessageId.get(input.threadId) ?? input.threadId : void 0;
201
+ conversationKindByNativeId.set(conversationId, input.conversation.kind);
202
+ logicalConversationByNativeId.set(conversationId, input.conversation.id);
203
+ const activityId = `qa-inbound-${(0, node_crypto.randomUUID)()}`;
204
+ const activity = {
205
+ id: activityId,
206
+ type: "message",
207
+ ...renderMSTeamsQaText(input.text),
208
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
209
+ channelId: "msteams",
210
+ serviceUrl: SERVICE_URL,
211
+ from: {
212
+ id: DRIVER_ID,
213
+ aadObjectId: DRIVER_AAD_OBJECT_ID,
214
+ name: input.senderName ?? "Teams QA Driver"
215
+ },
216
+ recipient: {
217
+ id: APP_ID,
218
+ name: "OpenClaw QA"
219
+ },
220
+ conversation: {
221
+ id: nativeThreadId ? `${conversationId};messageid=${nativeThreadId}` : conversationId,
222
+ conversationType: input.conversation.kind === "direct" ? "personal" : input.conversation.kind === "group" ? "groupChat" : "channel",
223
+ tenantId: TENANT_ID
224
+ },
225
+ ...input.replyToId ? { replyToId: input.replyToId } : {},
226
+ channelData: {
227
+ tenant: { id: TENANT_ID },
228
+ team: {
229
+ id: TEAM_ID,
230
+ aadGroupId: TEAM_AAD_GROUP_ID
231
+ },
232
+ channel: { id: conversationId }
233
+ }
234
+ };
235
+ const webhookUrl = `http://127.0.0.1:${webhookPort}/api/messages`;
236
+ const { response, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
237
+ url: webhookUrl,
238
+ init: {
239
+ method: "POST",
240
+ headers: {
241
+ authorization: "Bearer private-qa",
242
+ "content-type": "application/json"
243
+ },
244
+ body: JSON.stringify(activity)
245
+ },
246
+ policy: (0, openclaw_plugin_sdk_ssrf_runtime.ssrfPolicyFromHttpBaseUrlAllowedOrigin)(webhookUrl),
247
+ maxRedirects: 0,
248
+ auditContext: "msteams-private-qa-ingress"
249
+ });
250
+ try {
251
+ await response.text();
252
+ if (!response.ok) throw new Error(`Microsoft Teams QA ingress returned HTTP ${response.status}`);
253
+ } finally {
254
+ await release();
255
+ }
256
+ const message = await context.messages.addInboundMessage({
257
+ ...input,
258
+ accountId,
259
+ senderId: DRIVER_ID
260
+ });
261
+ nativeByBusMessageId.set(message.id, activityId);
262
+ busByNativeMessageId.set(activityId, message.id);
263
+ return message;
264
+ },
265
+ resetTransport() {
266
+ busByNativeMessageId.clear();
267
+ nativeByBusMessageId.clear();
268
+ conversationKindByNativeId.clear();
269
+ logicalConversationByNativeId.clear();
270
+ },
271
+ createGatewayConfig: () => ({ channels: { msteams: {
272
+ enabled: true,
273
+ appId: APP_ID,
274
+ appPassword: "private-qa-secret",
275
+ tenantId: TENANT_ID,
276
+ dmPolicy: "allowlist",
277
+ allowFrom: [DRIVER_AAD_OBJECT_ID],
278
+ groupPolicy: "open",
279
+ requireMention: requireGroupMention,
280
+ replyStyle: "thread",
281
+ webhook: {
282
+ port: webhookPort,
283
+ path: "/api/messages"
284
+ }
285
+ } } }),
286
+ createRuntimeEnvPatch: () => ({
287
+ OPENCLAW_BUILD_PRIVATE_QA: "1",
288
+ NODE_OPTIONS: [process.env.NODE_OPTIONS?.trim(), `--import=${(0, node_url.pathToFileURL)(bootstrapPath).href}`].filter(Boolean).join(" ")
289
+ }),
290
+ waitReady: async ({ gateway, timeoutMs, pollIntervalMs }) => await waitForMSTeamsChannelReady(gateway, timeoutMs, pollIntervalMs),
291
+ buildAgentDelivery: ({ target }) => ({
292
+ channel: "msteams",
293
+ to: target,
294
+ replyChannel: "msteams",
295
+ replyTo: target
296
+ }),
297
+ async handleAction() {
298
+ throw new Error("Microsoft Teams live QA adapter does not implement transport actions");
299
+ },
300
+ createReportNotes: () => ["Uses a loopback Bot Framework Connector with the real Microsoft Teams plugin and Gateway."],
301
+ async cleanup() {
302
+ try {
303
+ await connector.close();
304
+ } finally {
305
+ await node_fs_promises.default.rm(bootstrapPath, { force: true });
306
+ }
307
+ }
308
+ };
309
+ }
310
+ //#endregion
311
+ exports.createMSTeamsQaTransportAdapter = createMSTeamsQaTransportAdapter;
package/dist/api.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_channel = require("./channel-C7QxRjBW.cjs");
3
- const require_setup_surface = require("./setup-surface-ByWjaXEn.cjs");
4
- exports.createMSTeamsSetupWizardBase = require_setup_surface.createMSTeamsSetupWizardBase;
2
+ const require_channel = require("./channel-KpWENcgK.cjs");
3
+ const require_channel_setup = require("./channel.setup-mOFtsfYz.cjs");
4
+ exports.createMSTeamsSetupWizardBase = require_channel_setup.createMSTeamsSetupWizardBase;
5
5
  exports.msteamsPlugin = require_channel.msteamsPlugin;
6
- exports.msteamsSetupAdapter = require_setup_surface.msteamsSetupAdapter;
7
- exports.msteamsSetupWizard = require_setup_surface.msteamsSetupWizard;
8
- exports.openDelegatedOAuthUrl = require_setup_surface.openDelegatedOAuthUrl;
6
+ exports.msteamsSetupAdapter = require_channel_setup.msteamsSetupAdapter;
7
+ exports.msteamsSetupWizard = require_channel_setup.msteamsSetupWizard;
8
+ exports.openDelegatedOAuthUrl = require_channel_setup.openDelegatedOAuthUrl;