@kodelyth/msteams 2026.5.39 → 2026.5.42
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/api.ts +3 -0
- package/channel-config-api.ts +1 -0
- package/channel-plugin-api.ts +2 -0
- package/config-api.ts +4 -0
- package/contract-api.ts +4 -0
- package/dist/api.js +3 -0
- package/dist/channel-BvTXHuGs.js +1161 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-NssGKZm5.js +650 -0
- package/dist/config-schema-Btk-XCOd.js +43 -0
- package/dist/contract-api.js +2 -0
- package/dist/graph-users-D-gKCguI.js +1411 -0
- package/dist/index.js +22 -0
- package/dist/oauth-BUxlphX3.js +114 -0
- package/dist/oauth.token-ebId9946.js +116 -0
- package/dist/probe-Cj2KsAGF.js +2190 -0
- package/dist/runtime-api-BL4DOWXD.js +28 -0
- package/dist/runtime-api.js +2 -0
- package/dist/secret-contract-Bo7kdUrT.js +35 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-plugin-api.js +64 -0
- package/dist/setup-surface-COTQDcTQ.js +531 -0
- package/dist/src-tvpsGYPV.js +4226 -0
- package/dist/test-api.js +2 -0
- package/index.ts +20 -0
- package/klaw.plugin.json +2 -726
- package/package.json +4 -4
- package/runtime-api.ts +66 -0
- package/secret-contract-api.ts +5 -0
- package/setup-entry.ts +13 -0
- package/setup-plugin-api.ts +3 -0
- package/src/ai-entity.ts +7 -0
- package/src/approval-auth.ts +44 -0
- package/src/attachments/bot-framework.test.ts +506 -0
- package/src/attachments/bot-framework.ts +348 -0
- package/src/attachments/download.ts +328 -0
- package/src/attachments/graph.test.ts +441 -0
- package/src/attachments/graph.ts +489 -0
- package/src/attachments/html.ts +122 -0
- package/src/attachments/payload.ts +14 -0
- package/src/attachments/remote-media.test.ts +187 -0
- package/src/attachments/remote-media.ts +86 -0
- package/src/attachments/shared.test.ts +547 -0
- package/src/attachments/shared.ts +655 -0
- package/src/attachments/types.ts +47 -0
- package/src/attachments.graph.test.ts +414 -0
- package/src/attachments.helpers.test.ts +245 -0
- package/src/attachments.test-helpers.ts +17 -0
- package/src/attachments.test.ts +754 -0
- package/src/attachments.ts +18 -0
- package/src/block-streaming-config.test.ts +61 -0
- package/src/channel-api.ts +1 -0
- package/src/channel.actions.test.ts +797 -0
- package/src/channel.directory.test.ts +176 -0
- package/src/channel.message-adapter.test.ts +227 -0
- package/src/channel.runtime.ts +56 -0
- package/src/channel.setup.ts +77 -0
- package/src/channel.test.ts +136 -0
- package/src/channel.ts +1176 -0
- package/src/config-schema.ts +6 -0
- package/src/config-ui-hints.ts +40 -0
- package/src/conversation-store-fs.test.ts +81 -0
- package/src/conversation-store-fs.ts +149 -0
- package/src/conversation-store-helpers.test.ts +202 -0
- package/src/conversation-store-helpers.ts +105 -0
- package/src/conversation-store-memory.ts +51 -0
- package/src/conversation-store.shared.test.ts +260 -0
- package/src/conversation-store.ts +71 -0
- package/src/directory-live.test.ts +156 -0
- package/src/directory-live.ts +111 -0
- package/src/doctor.ts +27 -0
- package/src/errors.test.ts +154 -0
- package/src/errors.ts +270 -0
- package/src/feedback-reflection-prompt.ts +117 -0
- package/src/feedback-reflection-store.ts +113 -0
- package/src/feedback-reflection.test.ts +237 -0
- package/src/feedback-reflection.ts +268 -0
- package/src/file-consent-helpers.test.ts +328 -0
- package/src/file-consent-helpers.ts +115 -0
- package/src/file-consent-invoke.ts +150 -0
- package/src/file-consent.test.ts +378 -0
- package/src/file-consent.ts +223 -0
- package/src/graph-chat.ts +36 -0
- package/src/graph-group-management.test.ts +332 -0
- package/src/graph-group-management.ts +168 -0
- package/src/graph-members.test.ts +89 -0
- package/src/graph-members.ts +48 -0
- package/src/graph-messages.actions.test.ts +253 -0
- package/src/graph-messages.read.test.ts +391 -0
- package/src/graph-messages.search.test.ts +227 -0
- package/src/graph-messages.test-helpers.ts +50 -0
- package/src/graph-messages.ts +534 -0
- package/src/graph-teams.test.ts +222 -0
- package/src/graph-teams.ts +114 -0
- package/src/graph-thread.test.ts +252 -0
- package/src/graph-thread.ts +146 -0
- package/src/graph-upload.test.ts +253 -0
- package/src/graph-upload.ts +531 -0
- package/src/graph-users.ts +29 -0
- package/src/graph.test.ts +540 -0
- package/src/graph.ts +308 -0
- package/src/inbound.test.ts +221 -0
- package/src/inbound.ts +148 -0
- package/src/index.ts +4 -0
- package/src/media-helpers.test.ts +220 -0
- package/src/media-helpers.ts +105 -0
- package/src/mentions.test.ts +254 -0
- package/src/mentions.ts +114 -0
- package/src/messenger.test.ts +961 -0
- package/src/messenger.ts +608 -0
- package/src/monitor-handler/access.ts +136 -0
- package/src/monitor-handler/inbound-media.test.ts +314 -0
- package/src/monitor-handler/inbound-media.ts +180 -0
- package/src/monitor-handler/message-handler-mock-support.test-support.ts +28 -0
- package/src/monitor-handler/message-handler.authz.test.ts +739 -0
- package/src/monitor-handler/message-handler.dm-media.test.ts +54 -0
- package/src/monitor-handler/message-handler.test-support.ts +99 -0
- package/src/monitor-handler/message-handler.thread-parent.test.ts +225 -0
- package/src/monitor-handler/message-handler.thread-session.test.ts +132 -0
- package/src/monitor-handler/message-handler.ts +1003 -0
- package/src/monitor-handler/reaction-handler.test.ts +325 -0
- package/src/monitor-handler/reaction-handler.ts +122 -0
- package/src/monitor-handler/thread-session.ts +30 -0
- package/src/monitor-handler.adaptive-card.test.ts +158 -0
- package/src/monitor-handler.feedback-authz.test.ts +357 -0
- package/src/monitor-handler.file-consent.test.ts +443 -0
- package/src/monitor-handler.sso.test.ts +576 -0
- package/src/monitor-handler.test-helpers.ts +181 -0
- package/src/monitor-handler.ts +538 -0
- package/src/monitor-handler.types.ts +27 -0
- package/src/monitor-types.ts +6 -0
- package/src/monitor.lifecycle.test.ts +457 -0
- package/src/monitor.test.ts +119 -0
- package/src/monitor.ts +476 -0
- package/src/oauth.flow.ts +77 -0
- package/src/oauth.shared.ts +37 -0
- package/src/oauth.test.ts +350 -0
- package/src/oauth.token.ts +162 -0
- package/src/oauth.ts +130 -0
- package/src/outbound.test.ts +400 -0
- package/src/outbound.ts +198 -0
- package/src/pending-uploads-fs.test.ts +261 -0
- package/src/pending-uploads-fs.ts +235 -0
- package/src/pending-uploads.test.ts +186 -0
- package/src/pending-uploads.ts +121 -0
- package/src/policy.test.ts +156 -0
- package/src/policy.ts +245 -0
- package/src/polls-store-memory.ts +32 -0
- package/src/polls.test.ts +169 -0
- package/src/polls.ts +312 -0
- package/src/presentation.ts +93 -0
- package/src/probe.test.ts +79 -0
- package/src/probe.ts +132 -0
- package/src/reply-dispatcher.test.ts +543 -0
- package/src/reply-dispatcher.ts +523 -0
- package/src/reply-stream-controller.test.ts +424 -0
- package/src/reply-stream-controller.ts +334 -0
- package/src/resolve-allowlist.test.ts +253 -0
- package/src/resolve-allowlist.ts +309 -0
- package/src/revoked-context.ts +17 -0
- package/src/runtime.ts +12 -0
- package/src/sdk-types.ts +59 -0
- package/src/sdk.test.ts +727 -0
- package/src/sdk.ts +916 -0
- package/src/secret-contract.ts +49 -0
- package/src/secret-input.ts +7 -0
- package/src/send-context.test.ts +93 -0
- package/src/send-context.ts +269 -0
- package/src/send.test.ts +588 -0
- package/src/send.ts +697 -0
- package/src/sent-message-cache.test.ts +106 -0
- package/src/sent-message-cache.ts +174 -0
- package/src/session-route.ts +40 -0
- package/src/setup-core.ts +162 -0
- package/src/setup-surface.test.ts +175 -0
- package/src/setup-surface.ts +319 -0
- package/src/sso-token-store.test.ts +74 -0
- package/src/sso-token-store.ts +166 -0
- package/src/sso.ts +300 -0
- package/src/storage.ts +25 -0
- package/src/store-fs.ts +42 -0
- package/src/streaming-message.test.ts +323 -0
- package/src/streaming-message.ts +327 -0
- package/src/test-runtime.ts +16 -0
- package/src/thread-parent-context.test.ts +224 -0
- package/src/thread-parent-context.ts +159 -0
- package/src/token-response.ts +11 -0
- package/src/token.test.ts +268 -0
- package/src/token.ts +194 -0
- package/src/user-agent.test.ts +121 -0
- package/src/user-agent.ts +53 -0
- package/src/webhook-timeouts.ts +27 -0
- package/src/welcome-card.test.ts +104 -0
- package/src/welcome-card.ts +57 -0
- package/test-api.ts +1 -0
- package/tsconfig.json +16 -0
- package/api.js +0 -7
- package/channel-config-api.js +0 -7
- package/channel-plugin-api.js +0 -7
- package/contract-api.js +0 -7
- package/index.js +0 -7
- package/runtime-api.js +0 -7
- package/secret-contract-api.js +0 -7
- package/setup-entry.js +0 -7
- package/setup-plugin-api.js +0 -7
- package/test-api.js +0 -7
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
// Mock the runtime before importing buildUserAgent
|
|
4
|
+
const mockRuntime = {
|
|
5
|
+
version: "2026.3.19",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
vi.mock("./runtime.js", () => ({
|
|
9
|
+
getMSTeamsRuntime: vi.fn(() => mockRuntime),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock("../runtime-api.js", async (importOriginal) => {
|
|
13
|
+
const original = await importOriginal<typeof import("../runtime-api.js")>();
|
|
14
|
+
return {
|
|
15
|
+
...original,
|
|
16
|
+
fetchWithSsrFGuard: async (params: { url: string; init?: RequestInit }) => ({
|
|
17
|
+
response: await globalThis.fetch(params.url, params.init),
|
|
18
|
+
finalUrl: params.url,
|
|
19
|
+
release: async () => undefined,
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
import { fetchGraphJson } from "./graph.js";
|
|
25
|
+
import { getMSTeamsRuntime } from "./runtime.js";
|
|
26
|
+
import { buildUserAgent, ensureUserAgentHeader, resetUserAgentCache } from "./user-agent.js";
|
|
27
|
+
|
|
28
|
+
function readFirstFetchInit(mockFetch: { mock: { calls: unknown[][] } }): {
|
|
29
|
+
headers: Record<string, string>;
|
|
30
|
+
} {
|
|
31
|
+
const [call] = mockFetch.mock.calls;
|
|
32
|
+
if (!call) {
|
|
33
|
+
throw new Error("Expected Graph fetch call");
|
|
34
|
+
}
|
|
35
|
+
const [, init] = call;
|
|
36
|
+
if (
|
|
37
|
+
!init ||
|
|
38
|
+
typeof init !== "object" ||
|
|
39
|
+
!("headers" in init) ||
|
|
40
|
+
typeof init.headers !== "object" ||
|
|
41
|
+
init.headers === null
|
|
42
|
+
) {
|
|
43
|
+
throw new Error("Expected Graph fetch init headers");
|
|
44
|
+
}
|
|
45
|
+
return init as { headers: Record<string, string> };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe("buildUserAgent", () => {
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
resetUserAgentCache();
|
|
51
|
+
vi.mocked(getMSTeamsRuntime).mockReturnValue(mockRuntime as never);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
vi.unstubAllGlobals();
|
|
56
|
+
vi.restoreAllMocks();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("returns teams.ts[apps]/<sdk> Klaw/<version> format", () => {
|
|
60
|
+
const ua = buildUserAgent();
|
|
61
|
+
expect(ua).toMatch(/^teams\.ts\[apps\]\/.+ Klaw\/2026\.3\.19$/);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("reflects the runtime version", () => {
|
|
65
|
+
vi.mocked(getMSTeamsRuntime).mockReturnValue({ version: "1.2.3" } as never);
|
|
66
|
+
const ua = buildUserAgent();
|
|
67
|
+
expect(ua).toMatch(/Klaw\/1\.2\.3$/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("returns Klaw/unknown when runtime is not initialized", () => {
|
|
71
|
+
vi.mocked(getMSTeamsRuntime).mockImplementation(() => {
|
|
72
|
+
throw new Error("MSTeams runtime not initialized");
|
|
73
|
+
});
|
|
74
|
+
const ua = buildUserAgent();
|
|
75
|
+
expect(ua).toMatch(/Klaw\/unknown$/);
|
|
76
|
+
// SDK version should still be present
|
|
77
|
+
expect(ua).toMatch(/^teams\.ts\[apps\]\//);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("sends the generated User-Agent in Graph requests by default", async () => {
|
|
81
|
+
const mockFetch = vi.fn().mockResolvedValueOnce(
|
|
82
|
+
new Response(JSON.stringify({ value: [] }), {
|
|
83
|
+
headers: { "content-type": "application/json" },
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
vi.stubGlobal("fetch", mockFetch);
|
|
87
|
+
|
|
88
|
+
await fetchGraphJson({ token: "test-token", path: "/groups" });
|
|
89
|
+
|
|
90
|
+
expect(mockFetch).toHaveBeenCalledOnce();
|
|
91
|
+
const init = readFirstFetchInit(mockFetch);
|
|
92
|
+
expect(init.headers["User-Agent"]).toMatch(/^teams\.ts\[apps\]\/.+ Klaw\/2026\.3\.19$/);
|
|
93
|
+
expect(init.headers).toHaveProperty("Authorization", "Bearer test-token");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("lets caller headers override the default Graph User-Agent", async () => {
|
|
97
|
+
const mockFetch = vi.fn().mockResolvedValueOnce(
|
|
98
|
+
new Response(JSON.stringify({ value: [] }), {
|
|
99
|
+
headers: { "content-type": "application/json" },
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
vi.stubGlobal("fetch", mockFetch);
|
|
103
|
+
|
|
104
|
+
await fetchGraphJson({
|
|
105
|
+
token: "test-token",
|
|
106
|
+
path: "/groups",
|
|
107
|
+
headers: { "User-Agent": "custom-agent/1.0" },
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const init = readFirstFetchInit(mockFetch);
|
|
111
|
+
expect(init.headers["User-Agent"]).toBe("custom-agent/1.0");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("adds the generated User-Agent to Headers instances without overwriting callers", () => {
|
|
115
|
+
const generated = ensureUserAgentHeader();
|
|
116
|
+
expect(generated.get("User-Agent")).toMatch(/^teams\.ts\[apps\]\/.+ Klaw\/2026\.3\.19$/);
|
|
117
|
+
|
|
118
|
+
const custom = ensureUserAgentHeader({ "User-Agent": "custom-agent/2.0" });
|
|
119
|
+
expect(custom.get("User-Agent")).toBe("custom-agent/2.0");
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { getMSTeamsRuntime } from "./runtime.js";
|
|
3
|
+
|
|
4
|
+
let cachedUserAgent: string | undefined;
|
|
5
|
+
|
|
6
|
+
function resolveTeamsSdkVersion(): string {
|
|
7
|
+
try {
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const pkg = require("@microsoft/teams.apps/package.json") as { version?: string };
|
|
10
|
+
return pkg.version ?? "unknown";
|
|
11
|
+
} catch {
|
|
12
|
+
return "unknown";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function resolveKlawVersion(): string {
|
|
17
|
+
try {
|
|
18
|
+
return getMSTeamsRuntime().version;
|
|
19
|
+
} catch {
|
|
20
|
+
return "unknown";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build a combined User-Agent string that preserves the Teams SDK identity
|
|
26
|
+
* and appends the Klaw version.
|
|
27
|
+
*
|
|
28
|
+
* Format: "teams.ts[apps]/<sdk-version> Klaw/<klaw-version>"
|
|
29
|
+
* Example: "teams.ts[apps]/2.0.5 Klaw/2026.3.22"
|
|
30
|
+
*
|
|
31
|
+
* This lets the Teams backend track SDK usage while also identifying the
|
|
32
|
+
* host application.
|
|
33
|
+
*/
|
|
34
|
+
/** Reset the cached User-Agent (for testing). */
|
|
35
|
+
export function resetUserAgentCache(): void {
|
|
36
|
+
cachedUserAgent = undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function buildUserAgent(): string {
|
|
40
|
+
if (cachedUserAgent) {
|
|
41
|
+
return cachedUserAgent;
|
|
42
|
+
}
|
|
43
|
+
cachedUserAgent = `teams.ts[apps]/${resolveTeamsSdkVersion()} Klaw/${resolveKlawVersion()}`;
|
|
44
|
+
return cachedUserAgent;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function ensureUserAgentHeader(headers?: HeadersInit): Headers {
|
|
48
|
+
const nextHeaders = new Headers(headers);
|
|
49
|
+
if (!nextHeaders.has("User-Agent")) {
|
|
50
|
+
nextHeaders.set("User-Agent", buildUserAgent());
|
|
51
|
+
}
|
|
52
|
+
return nextHeaders;
|
|
53
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Server } from "node:http";
|
|
2
|
+
|
|
3
|
+
const MSTEAMS_WEBHOOK_INACTIVITY_TIMEOUT_MS = 30_000;
|
|
4
|
+
const MSTEAMS_WEBHOOK_REQUEST_TIMEOUT_MS = 30_000;
|
|
5
|
+
const MSTEAMS_WEBHOOK_HEADERS_TIMEOUT_MS = 15_000;
|
|
6
|
+
|
|
7
|
+
type ApplyMSTeamsWebhookTimeoutsOpts = {
|
|
8
|
+
inactivityTimeoutMs?: number;
|
|
9
|
+
requestTimeoutMs?: number;
|
|
10
|
+
headersTimeoutMs?: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function applyMSTeamsWebhookTimeouts(
|
|
14
|
+
httpServer: Server,
|
|
15
|
+
opts?: ApplyMSTeamsWebhookTimeoutsOpts,
|
|
16
|
+
): void {
|
|
17
|
+
const inactivityTimeoutMs = opts?.inactivityTimeoutMs ?? MSTEAMS_WEBHOOK_INACTIVITY_TIMEOUT_MS;
|
|
18
|
+
const requestTimeoutMs = opts?.requestTimeoutMs ?? MSTEAMS_WEBHOOK_REQUEST_TIMEOUT_MS;
|
|
19
|
+
const headersTimeoutMs = Math.min(
|
|
20
|
+
opts?.headersTimeoutMs ?? MSTEAMS_WEBHOOK_HEADERS_TIMEOUT_MS,
|
|
21
|
+
requestTimeoutMs,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
httpServer.setTimeout(inactivityTimeoutMs);
|
|
25
|
+
httpServer.requestTimeout = requestTimeoutMs;
|
|
26
|
+
httpServer.headersTimeout = headersTimeoutMs;
|
|
27
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildMSTeamsPresentationCard } from "./presentation.js";
|
|
3
|
+
import { buildGroupWelcomeText, buildWelcomeCard } from "./welcome-card.js";
|
|
4
|
+
|
|
5
|
+
describe("buildMSTeamsPresentationCard", () => {
|
|
6
|
+
it("preserves message text when rendering presentation controls", () => {
|
|
7
|
+
expect(
|
|
8
|
+
buildMSTeamsPresentationCard({
|
|
9
|
+
text: "Deploy finished",
|
|
10
|
+
presentation: {
|
|
11
|
+
blocks: [
|
|
12
|
+
{
|
|
13
|
+
type: "buttons",
|
|
14
|
+
buttons: [{ label: "Open", value: "open" }],
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
}),
|
|
19
|
+
).toEqual({
|
|
20
|
+
type: "AdaptiveCard",
|
|
21
|
+
version: "1.4",
|
|
22
|
+
body: [{ type: "TextBlock", text: "Deploy finished", wrap: true }],
|
|
23
|
+
actions: [{ type: "Action.Submit", title: "Open", data: { value: "open", label: "Open" } }],
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("renders web app button links as open-url actions", () => {
|
|
28
|
+
expect(
|
|
29
|
+
buildMSTeamsPresentationCard({
|
|
30
|
+
presentation: {
|
|
31
|
+
blocks: [
|
|
32
|
+
{
|
|
33
|
+
type: "buttons",
|
|
34
|
+
buttons: [
|
|
35
|
+
{ label: "Open app", webApp: { url: "https://example.com/app" } },
|
|
36
|
+
{ label: "Legacy app", web_app: { url: "https://example.com/legacy" } },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
).toMatchObject({
|
|
43
|
+
actions: [
|
|
44
|
+
{ type: "Action.OpenUrl", title: "Open app", url: "https://example.com/app" },
|
|
45
|
+
{ type: "Action.OpenUrl", title: "Legacy app", url: "https://example.com/legacy" },
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("buildWelcomeCard", () => {
|
|
52
|
+
it("builds card with default prompt starters", () => {
|
|
53
|
+
const card = buildWelcomeCard();
|
|
54
|
+
expect(card.type).toBe("AdaptiveCard");
|
|
55
|
+
expect(card.version).toBe("1.5");
|
|
56
|
+
|
|
57
|
+
const body = card.body as Array<{ text: string }>;
|
|
58
|
+
expect(body[0]?.text).toContain("Klaw");
|
|
59
|
+
|
|
60
|
+
const actions = card.actions as Array<{ title: string; data: unknown }>;
|
|
61
|
+
expect(actions.length).toBe(3);
|
|
62
|
+
expect(actions[0]?.title).toBe("What can you do?");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("uses custom bot name", () => {
|
|
66
|
+
const card = buildWelcomeCard({ botName: "TestBot" });
|
|
67
|
+
const body = card.body as Array<{ text: string }>;
|
|
68
|
+
expect(body[0]?.text).toContain("TestBot");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("uses custom prompt starters", () => {
|
|
72
|
+
const card = buildWelcomeCard({
|
|
73
|
+
promptStarters: ["Do X", "Do Y"],
|
|
74
|
+
});
|
|
75
|
+
const actions = card.actions as Array<{ title: string; data: unknown }>;
|
|
76
|
+
expect(actions.length).toBe(2);
|
|
77
|
+
expect(actions[0]?.title).toBe("Do X");
|
|
78
|
+
expect(actions[1]?.title).toBe("Do Y");
|
|
79
|
+
|
|
80
|
+
// Verify imBack data
|
|
81
|
+
const data = actions[0]?.data as { msteams: { type: string; value: string } };
|
|
82
|
+
expect(data.msteams.type).toBe("imBack");
|
|
83
|
+
expect(data.msteams.value).toBe("Do X");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("falls back to defaults when promptStarters is empty", () => {
|
|
87
|
+
const card = buildWelcomeCard({ promptStarters: [] });
|
|
88
|
+
const actions = card.actions as Array<{ title: string }>;
|
|
89
|
+
expect(actions.length).toBe(3);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("buildGroupWelcomeText", () => {
|
|
94
|
+
it("includes bot name", () => {
|
|
95
|
+
const text = buildGroupWelcomeText("MyBot");
|
|
96
|
+
expect(text).toContain("MyBot");
|
|
97
|
+
expect(text).toContain("@MyBot");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("defaults to Klaw", () => {
|
|
101
|
+
const text = buildGroupWelcomeText();
|
|
102
|
+
expect(text).toContain("Klaw");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds an Adaptive Card for welcoming users when the bot is added to a conversation.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const DEFAULT_PROMPT_STARTERS = [
|
|
6
|
+
"What can you do?",
|
|
7
|
+
"Summarize my last meeting",
|
|
8
|
+
"Help me draft an email",
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
type WelcomeCardOptions = {
|
|
12
|
+
/** Bot display name. Falls back to "Klaw". */
|
|
13
|
+
botName?: string;
|
|
14
|
+
/** Custom prompt starters. Falls back to defaults. */
|
|
15
|
+
promptStarters?: string[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Build a welcome Adaptive Card for 1:1 personal chats.
|
|
20
|
+
*/
|
|
21
|
+
export function buildWelcomeCard(options?: WelcomeCardOptions): Record<string, unknown> {
|
|
22
|
+
const botName = options?.botName || "Klaw";
|
|
23
|
+
const starters = options?.promptStarters?.length
|
|
24
|
+
? options.promptStarters
|
|
25
|
+
: DEFAULT_PROMPT_STARTERS;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
type: "AdaptiveCard",
|
|
29
|
+
version: "1.5",
|
|
30
|
+
body: [
|
|
31
|
+
{
|
|
32
|
+
type: "TextBlock",
|
|
33
|
+
text: `Hi! I'm ${botName}.`,
|
|
34
|
+
weight: "bolder",
|
|
35
|
+
size: "medium",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: "TextBlock",
|
|
39
|
+
text: "I can help you with questions, tasks, and more. Here are some things to try:",
|
|
40
|
+
wrap: true,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
actions: starters.map((label) => ({
|
|
44
|
+
type: "Action.Submit",
|
|
45
|
+
title: label,
|
|
46
|
+
data: { msteams: { type: "imBack", value: label } },
|
|
47
|
+
})),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Build a brief welcome message for group chats (when the bot is @mentioned).
|
|
53
|
+
*/
|
|
54
|
+
export function buildGroupWelcomeText(botName?: string): string {
|
|
55
|
+
const name = botName || "Klaw";
|
|
56
|
+
return `Hi! I'm ${name}. Mention me with @${name} to get started.`;
|
|
57
|
+
}
|
package/test-api.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { msteamsPlugin } from "./src/channel.js";
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.package-boundary.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "."
|
|
5
|
+
},
|
|
6
|
+
"include": ["./*.ts", "./src/**/*.ts"],
|
|
7
|
+
"exclude": [
|
|
8
|
+
"./**/*.test.ts",
|
|
9
|
+
"./dist/**",
|
|
10
|
+
"./node_modules/**",
|
|
11
|
+
"./src/test-support/**",
|
|
12
|
+
"./src/**/*test-helpers.ts",
|
|
13
|
+
"./src/**/*test-harness.ts",
|
|
14
|
+
"./src/**/*test-support.ts"
|
|
15
|
+
]
|
|
16
|
+
}
|
package/api.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/api.js";
|
|
2
|
-
import * as module from "../../../dist/extensions/msteams/api.js";
|
|
3
|
-
let defaultExport = "default" in module ? module.default : module;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|
package/channel-config-api.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/channel-config-api.js";
|
|
2
|
-
import * as module from "../../../dist/extensions/msteams/channel-config-api.js";
|
|
3
|
-
let defaultExport = "default" in module ? module.default : module;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|
package/channel-plugin-api.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/channel-plugin-api.js";
|
|
2
|
-
import * as module from "../../../dist/extensions/msteams/channel-plugin-api.js";
|
|
3
|
-
let defaultExport = "default" in module ? module.default : module;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|
package/contract-api.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/contract-api.js";
|
|
2
|
-
import * as module from "../../../dist/extensions/msteams/contract-api.js";
|
|
3
|
-
let defaultExport = "default" in module ? module.default : module;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|
package/index.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/index.js";
|
|
2
|
-
import defaultModule from "../../../dist/extensions/msteams/index.js";
|
|
3
|
-
let defaultExport = defaultModule;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|
package/runtime-api.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/runtime-api.js";
|
|
2
|
-
import * as module from "../../../dist/extensions/msteams/runtime-api.js";
|
|
3
|
-
let defaultExport = "default" in module ? module.default : module;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|
package/secret-contract-api.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/secret-contract-api.js";
|
|
2
|
-
import * as module from "../../../dist/extensions/msteams/secret-contract-api.js";
|
|
3
|
-
let defaultExport = "default" in module ? module.default : module;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|
package/setup-entry.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/setup-entry.js";
|
|
2
|
-
import defaultModule from "../../../dist/extensions/msteams/setup-entry.js";
|
|
3
|
-
let defaultExport = defaultModule;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|
package/setup-plugin-api.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/setup-plugin-api.js";
|
|
2
|
-
import * as module from "../../../dist/extensions/msteams/setup-plugin-api.js";
|
|
3
|
-
let defaultExport = "default" in module ? module.default : module;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|
package/test-api.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from "../../../dist/extensions/msteams/test-api.js";
|
|
2
|
-
import * as module from "../../../dist/extensions/msteams/test-api.js";
|
|
3
|
-
let defaultExport = "default" in module ? module.default : module;
|
|
4
|
-
for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
|
|
5
|
-
defaultExport = defaultExport.default;
|
|
6
|
-
}
|
|
7
|
-
export { defaultExport as default };
|