@openclaw/msteams 2026.5.2 → 2026.5.3-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 +3 -0
- package/dist/channel-D7hdreTh.js +984 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-BC1ruIfN.js +573 -0
- package/dist/config-schema-B8QezH6t.js +15 -0
- package/dist/contract-api.js +2 -0
- package/dist/graph-users-9uQJepqr.js +1354 -0
- package/dist/index.js +22 -0
- package/dist/oauth-BWJyilR1.js +114 -0
- package/dist/oauth.token-xxpoLWy5.js +115 -0
- package/dist/policy-DTnU2GR7.js +142 -0
- package/dist/probe-D_H8yFps.js +2194 -0
- package/dist/resolve-allowlist-D41JSziq.js +219 -0
- package/dist/runtime-api-DV1iVMn1.js +28 -0
- package/dist/runtime-api.js +2 -0
- package/dist/secret-contract-BuoEXmPS.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-BLkFQYIQ.js +313 -0
- package/dist/src-CFp1QpFd.js +4064 -0
- package/dist/test-api.js +2 -0
- package/package.json +14 -6
- package/api.ts +0 -3
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -2
- package/config-api.ts +0 -4
- package/contract-api.ts +0 -4
- package/index.ts +0 -20
- package/runtime-api.ts +0 -73
- package/secret-contract-api.ts +0 -5
- package/setup-entry.ts +0 -13
- package/setup-plugin-api.ts +0 -3
- package/src/ai-entity.ts +0 -7
- package/src/approval-auth.ts +0 -44
- package/src/attachments/bot-framework.test.ts +0 -461
- package/src/attachments/bot-framework.ts +0 -362
- package/src/attachments/download.ts +0 -311
- package/src/attachments/graph.test.ts +0 -416
- package/src/attachments/graph.ts +0 -484
- package/src/attachments/html.ts +0 -122
- package/src/attachments/payload.ts +0 -14
- package/src/attachments/remote-media.test.ts +0 -137
- package/src/attachments/remote-media.ts +0 -112
- package/src/attachments/shared.test.ts +0 -530
- package/src/attachments/shared.ts +0 -626
- package/src/attachments/types.ts +0 -47
- package/src/attachments.graph.test.ts +0 -342
- package/src/attachments.helpers.test.ts +0 -246
- package/src/attachments.test-helpers.ts +0 -17
- package/src/attachments.test.ts +0 -687
- package/src/attachments.ts +0 -18
- package/src/block-streaming-config.test.ts +0 -61
- package/src/channel-api.ts +0 -1
- package/src/channel.actions.test.ts +0 -742
- package/src/channel.directory.test.ts +0 -200
- package/src/channel.runtime.ts +0 -56
- package/src/channel.setup.ts +0 -77
- package/src/channel.test.ts +0 -128
- package/src/channel.ts +0 -1136
- package/src/config-schema.ts +0 -6
- package/src/config-ui-hints.ts +0 -12
- package/src/conversation-store-fs.test.ts +0 -74
- package/src/conversation-store-fs.ts +0 -149
- package/src/conversation-store-helpers.test.ts +0 -202
- package/src/conversation-store-helpers.ts +0 -105
- package/src/conversation-store-memory.ts +0 -51
- package/src/conversation-store.shared.test.ts +0 -225
- package/src/conversation-store.ts +0 -71
- package/src/directory-live.test.ts +0 -156
- package/src/directory-live.ts +0 -111
- package/src/doctor.ts +0 -27
- package/src/errors.test.ts +0 -133
- package/src/errors.ts +0 -246
- package/src/feedback-reflection-prompt.ts +0 -117
- package/src/feedback-reflection-store.ts +0 -114
- package/src/feedback-reflection.test.ts +0 -237
- package/src/feedback-reflection.ts +0 -283
- package/src/file-consent-helpers.test.ts +0 -326
- package/src/file-consent-helpers.ts +0 -126
- package/src/file-consent-invoke.ts +0 -150
- package/src/file-consent.test.ts +0 -363
- package/src/file-consent.ts +0 -287
- package/src/graph-chat.ts +0 -55
- package/src/graph-group-management.test.ts +0 -318
- package/src/graph-group-management.ts +0 -168
- package/src/graph-members.test.ts +0 -89
- package/src/graph-members.ts +0 -48
- package/src/graph-messages.actions.test.ts +0 -243
- package/src/graph-messages.read.test.ts +0 -391
- package/src/graph-messages.search.test.ts +0 -213
- package/src/graph-messages.test-helpers.ts +0 -50
- package/src/graph-messages.ts +0 -534
- package/src/graph-teams.test.ts +0 -215
- package/src/graph-teams.ts +0 -114
- package/src/graph-thread.test.ts +0 -246
- package/src/graph-thread.ts +0 -146
- package/src/graph-upload.test.ts +0 -258
- package/src/graph-upload.ts +0 -531
- package/src/graph-users.ts +0 -29
- package/src/graph.test.ts +0 -516
- package/src/graph.ts +0 -293
- package/src/inbound.test.ts +0 -221
- package/src/inbound.ts +0 -148
- package/src/index.ts +0 -4
- package/src/media-helpers.test.ts +0 -202
- package/src/media-helpers.ts +0 -105
- package/src/mentions.test.ts +0 -244
- package/src/mentions.ts +0 -114
- package/src/messenger.test.ts +0 -865
- package/src/messenger.ts +0 -605
- package/src/monitor-handler/access.ts +0 -125
- package/src/monitor-handler/inbound-media.test.ts +0 -289
- package/src/monitor-handler/inbound-media.ts +0 -180
- package/src/monitor-handler/message-handler-mock-support.test-support.ts +0 -28
- package/src/monitor-handler/message-handler.authz.test.ts +0 -669
- package/src/monitor-handler/message-handler.dm-media.test.ts +0 -54
- package/src/monitor-handler/message-handler.test-support.ts +0 -100
- package/src/monitor-handler/message-handler.thread-parent.test.ts +0 -223
- package/src/monitor-handler/message-handler.thread-session.test.ts +0 -77
- package/src/monitor-handler/message-handler.ts +0 -1000
- package/src/monitor-handler/reaction-handler.test.ts +0 -267
- package/src/monitor-handler/reaction-handler.ts +0 -210
- package/src/monitor-handler/thread-session.ts +0 -17
- package/src/monitor-handler.adaptive-card.test.ts +0 -162
- package/src/monitor-handler.feedback-authz.test.ts +0 -314
- package/src/monitor-handler.file-consent.test.ts +0 -423
- package/src/monitor-handler.sso.test.ts +0 -563
- package/src/monitor-handler.test-helpers.ts +0 -180
- package/src/monitor-handler.ts +0 -534
- package/src/monitor-handler.types.ts +0 -27
- package/src/monitor-types.ts +0 -6
- package/src/monitor.lifecycle.test.ts +0 -278
- package/src/monitor.test.ts +0 -119
- package/src/monitor.ts +0 -442
- package/src/oauth.flow.ts +0 -77
- package/src/oauth.shared.ts +0 -37
- package/src/oauth.test.ts +0 -305
- package/src/oauth.token.ts +0 -158
- package/src/oauth.ts +0 -130
- package/src/outbound.test.ts +0 -130
- package/src/outbound.ts +0 -71
- package/src/pending-uploads-fs.test.ts +0 -246
- package/src/pending-uploads-fs.ts +0 -235
- package/src/pending-uploads.test.ts +0 -173
- package/src/pending-uploads.ts +0 -121
- package/src/policy.test.ts +0 -240
- package/src/policy.ts +0 -262
- package/src/polls-store-memory.ts +0 -32
- package/src/polls.test.ts +0 -160
- package/src/polls.ts +0 -323
- package/src/presentation.ts +0 -68
- package/src/probe.test.ts +0 -77
- package/src/probe.ts +0 -132
- package/src/reply-dispatcher.test.ts +0 -437
- package/src/reply-dispatcher.ts +0 -346
- package/src/reply-stream-controller.test.ts +0 -235
- package/src/reply-stream-controller.ts +0 -147
- package/src/resolve-allowlist.test.ts +0 -250
- package/src/resolve-allowlist.ts +0 -309
- package/src/revoked-context.ts +0 -17
- package/src/runtime.ts +0 -9
- package/src/sdk-types.ts +0 -59
- package/src/sdk.test.ts +0 -666
- package/src/sdk.ts +0 -884
- package/src/secret-contract.ts +0 -49
- package/src/secret-input.ts +0 -7
- package/src/send-context.ts +0 -231
- package/src/send.test.ts +0 -493
- package/src/send.ts +0 -637
- package/src/sent-message-cache.test.ts +0 -15
- package/src/sent-message-cache.ts +0 -56
- package/src/session-route.ts +0 -40
- package/src/setup-core.ts +0 -160
- package/src/setup-surface.test.ts +0 -202
- package/src/setup-surface.ts +0 -320
- package/src/sso-token-store.test.ts +0 -72
- package/src/sso-token-store.ts +0 -166
- package/src/sso.ts +0 -300
- package/src/storage.ts +0 -25
- package/src/store-fs.ts +0 -44
- package/src/streaming-message.test.ts +0 -262
- package/src/streaming-message.ts +0 -297
- package/src/test-runtime.ts +0 -16
- package/src/thread-parent-context.test.ts +0 -224
- package/src/thread-parent-context.ts +0 -159
- package/src/token-response.ts +0 -11
- package/src/token.test.ts +0 -259
- package/src/token.ts +0 -195
- package/src/user-agent.test.ts +0 -86
- package/src/user-agent.ts +0 -53
- package/src/webhook-timeouts.ts +0 -27
- package/src/welcome-card.test.ts +0 -81
- package/src/welcome-card.ts +0 -57
- package/test-api.ts +0 -1
- package/tsconfig.json +0 -16
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import type { OpenClawConfig } from "../runtime-api.js";
|
|
3
|
-
import {
|
|
4
|
-
addParticipantMSTeams,
|
|
5
|
-
removeParticipantMSTeams,
|
|
6
|
-
renameGroupMSTeams,
|
|
7
|
-
} from "./graph-group-management.js";
|
|
8
|
-
|
|
9
|
-
const mockState = vi.hoisted(() => ({
|
|
10
|
-
resolveGraphToken: vi.fn(),
|
|
11
|
-
fetchGraphJson: vi.fn(),
|
|
12
|
-
postGraphJson: vi.fn(),
|
|
13
|
-
deleteGraphRequest: vi.fn(),
|
|
14
|
-
patchGraphJson: vi.fn(),
|
|
15
|
-
findPreferredDmByUserId: vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
vi.mock("./graph.js", async (importOriginal) => {
|
|
19
|
-
const actual = await importOriginal<typeof import("./graph.js")>();
|
|
20
|
-
return {
|
|
21
|
-
...actual,
|
|
22
|
-
resolveGraphToken: mockState.resolveGraphToken,
|
|
23
|
-
fetchGraphJson: mockState.fetchGraphJson,
|
|
24
|
-
postGraphJson: mockState.postGraphJson,
|
|
25
|
-
deleteGraphRequest: mockState.deleteGraphRequest,
|
|
26
|
-
patchGraphJson: mockState.patchGraphJson,
|
|
27
|
-
};
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
vi.mock("./conversation-store-fs.js", () => ({
|
|
31
|
-
createMSTeamsConversationStoreFs: () => ({
|
|
32
|
-
findPreferredDmByUserId: mockState.findPreferredDmByUserId,
|
|
33
|
-
}),
|
|
34
|
-
}));
|
|
35
|
-
|
|
36
|
-
const TOKEN = "test-graph-token";
|
|
37
|
-
const CHAT_ID = "19:abc@thread.tacv2";
|
|
38
|
-
const CHANNEL_TO = "team-id-1/channel-id-1";
|
|
39
|
-
|
|
40
|
-
describe("addParticipantMSTeams", () => {
|
|
41
|
-
beforeEach(() => {
|
|
42
|
-
vi.clearAllMocks();
|
|
43
|
-
mockState.resolveGraphToken.mockResolvedValue(TOKEN);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("adds member to a chat with default role", async () => {
|
|
47
|
-
mockState.postGraphJson.mockResolvedValue({});
|
|
48
|
-
|
|
49
|
-
const result = await addParticipantMSTeams({
|
|
50
|
-
cfg: {} as OpenClawConfig,
|
|
51
|
-
to: CHAT_ID,
|
|
52
|
-
userId: "user-aad-id-1",
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
expect(result).toEqual({ added: { userId: "user-aad-id-1", chatId: CHAT_ID } });
|
|
56
|
-
expect(mockState.postGraphJson).toHaveBeenCalledWith({
|
|
57
|
-
token: TOKEN,
|
|
58
|
-
path: `/chats/${encodeURIComponent(CHAT_ID)}/members`,
|
|
59
|
-
body: {
|
|
60
|
-
"@odata.type": "#microsoft.graph.aadUserConversationMember",
|
|
61
|
-
roles: ["member"],
|
|
62
|
-
"user@odata.bind": "https://graph.microsoft.com/v1.0/users('user-aad-id-1')",
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("adds member to a chat with owner role", async () => {
|
|
68
|
-
mockState.postGraphJson.mockResolvedValue({});
|
|
69
|
-
|
|
70
|
-
const result = await addParticipantMSTeams({
|
|
71
|
-
cfg: {} as OpenClawConfig,
|
|
72
|
-
to: CHAT_ID,
|
|
73
|
-
userId: "user-aad-id-2",
|
|
74
|
-
role: "owner",
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
expect(result).toEqual({ added: { userId: "user-aad-id-2", chatId: CHAT_ID } });
|
|
78
|
-
expect(mockState.postGraphJson).toHaveBeenCalledWith({
|
|
79
|
-
token: TOKEN,
|
|
80
|
-
path: `/chats/${encodeURIComponent(CHAT_ID)}/members`,
|
|
81
|
-
body: {
|
|
82
|
-
"@odata.type": "#microsoft.graph.aadUserConversationMember",
|
|
83
|
-
roles: ["owner"],
|
|
84
|
-
"user@odata.bind": "https://graph.microsoft.com/v1.0/users('user-aad-id-2')",
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("normalizes role casing and whitespace", async () => {
|
|
90
|
-
mockState.postGraphJson.mockResolvedValue({});
|
|
91
|
-
|
|
92
|
-
await addParticipantMSTeams({
|
|
93
|
-
cfg: {} as OpenClawConfig,
|
|
94
|
-
to: CHAT_ID,
|
|
95
|
-
userId: "user-aad-id-2",
|
|
96
|
-
role: " OWNER ",
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(mockState.postGraphJson).toHaveBeenCalledWith(
|
|
100
|
-
expect.objectContaining({
|
|
101
|
-
body: expect.objectContaining({
|
|
102
|
-
roles: ["owner"],
|
|
103
|
-
}),
|
|
104
|
-
}),
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("rejects unknown roles", async () => {
|
|
109
|
-
await expect(
|
|
110
|
-
addParticipantMSTeams({
|
|
111
|
-
cfg: {} as OpenClawConfig,
|
|
112
|
-
to: CHAT_ID,
|
|
113
|
-
userId: "user-aad-id-2",
|
|
114
|
-
role: "admin",
|
|
115
|
-
}),
|
|
116
|
-
).rejects.toThrow('role must be "member" or "owner"');
|
|
117
|
-
|
|
118
|
-
expect(mockState.postGraphJson).not.toHaveBeenCalled();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("constructs correct user@odata.bind URL", async () => {
|
|
122
|
-
mockState.postGraphJson.mockResolvedValue({});
|
|
123
|
-
|
|
124
|
-
await addParticipantMSTeams({
|
|
125
|
-
cfg: {} as OpenClawConfig,
|
|
126
|
-
to: CHAT_ID,
|
|
127
|
-
userId: "abc-def-123",
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const calledBody = mockState.postGraphJson.mock.calls[0][0].body;
|
|
131
|
-
expect(calledBody["user@odata.bind"]).toBe(
|
|
132
|
-
"https://graph.microsoft.com/v1.0/users('abc-def-123')",
|
|
133
|
-
);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("escapes user ids before building the OData bind URL", async () => {
|
|
137
|
-
mockState.postGraphJson.mockResolvedValue({});
|
|
138
|
-
|
|
139
|
-
await addParticipantMSTeams({
|
|
140
|
-
cfg: {} as OpenClawConfig,
|
|
141
|
-
to: CHAT_ID,
|
|
142
|
-
userId: "o'hara@example.com",
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
const calledBody = mockState.postGraphJson.mock.calls[0][0].body;
|
|
146
|
-
expect(calledBody["user@odata.bind"]).toBe(
|
|
147
|
-
"https://graph.microsoft.com/v1.0/users('o''hara@example.com')",
|
|
148
|
-
);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("adds member to a channel", async () => {
|
|
152
|
-
mockState.postGraphJson.mockResolvedValue({});
|
|
153
|
-
|
|
154
|
-
const result = await addParticipantMSTeams({
|
|
155
|
-
cfg: {} as OpenClawConfig,
|
|
156
|
-
to: CHANNEL_TO,
|
|
157
|
-
userId: "user-aad-id-3",
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
expect(result).toEqual({ added: { userId: "user-aad-id-3", chatId: CHANNEL_TO } });
|
|
161
|
-
expect(mockState.postGraphJson).toHaveBeenCalledWith({
|
|
162
|
-
token: TOKEN,
|
|
163
|
-
path: "/teams/team-id-1/channels/channel-id-1/members",
|
|
164
|
-
body: {
|
|
165
|
-
"@odata.type": "#microsoft.graph.aadUserConversationMember",
|
|
166
|
-
roles: ["member"],
|
|
167
|
-
"user@odata.bind": "https://graph.microsoft.com/v1.0/users('user-aad-id-3')",
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
describe("removeParticipantMSTeams", () => {
|
|
174
|
-
beforeEach(() => {
|
|
175
|
-
vi.clearAllMocks();
|
|
176
|
-
mockState.resolveGraphToken.mockResolvedValue(TOKEN);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it("lists members, finds match, deletes by membershipId", async () => {
|
|
180
|
-
mockState.fetchGraphJson.mockResolvedValue({
|
|
181
|
-
value: [
|
|
182
|
-
{ id: "membership-1", userId: "user-aad-id-1" },
|
|
183
|
-
{ id: "membership-2", userId: "user-aad-id-2" },
|
|
184
|
-
],
|
|
185
|
-
});
|
|
186
|
-
mockState.deleteGraphRequest.mockResolvedValue(undefined);
|
|
187
|
-
|
|
188
|
-
const result = await removeParticipantMSTeams({
|
|
189
|
-
cfg: {} as OpenClawConfig,
|
|
190
|
-
to: CHAT_ID,
|
|
191
|
-
userId: "user-aad-id-2",
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
expect(result).toEqual({ removed: { userId: "user-aad-id-2", chatId: CHAT_ID } });
|
|
195
|
-
expect(mockState.fetchGraphJson).toHaveBeenCalledWith({
|
|
196
|
-
token: TOKEN,
|
|
197
|
-
path: `/chats/${encodeURIComponent(CHAT_ID)}/members`,
|
|
198
|
-
});
|
|
199
|
-
expect(mockState.deleteGraphRequest).toHaveBeenCalledWith({
|
|
200
|
-
token: TOKEN,
|
|
201
|
-
path: `/chats/${encodeURIComponent(CHAT_ID)}/members/membership-2`,
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it("throws when user not found in member list", async () => {
|
|
206
|
-
mockState.fetchGraphJson.mockResolvedValue({
|
|
207
|
-
value: [
|
|
208
|
-
{ id: "membership-1", userId: "user-aad-id-1" },
|
|
209
|
-
{ id: "membership-3", userId: "user-aad-id-3" },
|
|
210
|
-
],
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
await expect(
|
|
214
|
-
removeParticipantMSTeams({
|
|
215
|
-
cfg: {} as OpenClawConfig,
|
|
216
|
-
to: CHAT_ID,
|
|
217
|
-
userId: "user-not-in-list",
|
|
218
|
-
}),
|
|
219
|
-
).rejects.toThrow("User user-not-in-list is not a member of this conversation");
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it("removes member from a channel", async () => {
|
|
223
|
-
mockState.fetchGraphJson.mockResolvedValue({
|
|
224
|
-
value: [{ id: "membership-5", userId: "user-aad-id-5" }],
|
|
225
|
-
});
|
|
226
|
-
mockState.deleteGraphRequest.mockResolvedValue(undefined);
|
|
227
|
-
|
|
228
|
-
const result = await removeParticipantMSTeams({
|
|
229
|
-
cfg: {} as OpenClawConfig,
|
|
230
|
-
to: CHANNEL_TO,
|
|
231
|
-
userId: "user-aad-id-5",
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
expect(result).toEqual({ removed: { userId: "user-aad-id-5", chatId: CHANNEL_TO } });
|
|
235
|
-
expect(mockState.fetchGraphJson).toHaveBeenCalledWith({
|
|
236
|
-
token: TOKEN,
|
|
237
|
-
path: "/teams/team-id-1/channels/channel-id-1/members",
|
|
238
|
-
});
|
|
239
|
-
expect(mockState.deleteGraphRequest).toHaveBeenCalledWith({
|
|
240
|
-
token: TOKEN,
|
|
241
|
-
path: "/teams/team-id-1/channels/channel-id-1/members/membership-5",
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("follows member pagination before concluding the user is missing", async () => {
|
|
246
|
-
mockState.fetchGraphJson
|
|
247
|
-
.mockResolvedValueOnce({
|
|
248
|
-
value: [{ id: "membership-1", userId: "user-aad-id-1" }],
|
|
249
|
-
"@odata.nextLink":
|
|
250
|
-
"https://graph.microsoft.com/v1.0/chats/19%3Aabc%40thread.tacv2/members?$skip=2",
|
|
251
|
-
})
|
|
252
|
-
.mockResolvedValueOnce({
|
|
253
|
-
value: [{ id: "membership-9", userId: "user-aad-id-9" }],
|
|
254
|
-
});
|
|
255
|
-
mockState.deleteGraphRequest.mockResolvedValue(undefined);
|
|
256
|
-
|
|
257
|
-
const result = await removeParticipantMSTeams({
|
|
258
|
-
cfg: {} as OpenClawConfig,
|
|
259
|
-
to: CHAT_ID,
|
|
260
|
-
userId: "user-aad-id-9",
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
expect(result).toEqual({ removed: { userId: "user-aad-id-9", chatId: CHAT_ID } });
|
|
264
|
-
expect(mockState.fetchGraphJson).toHaveBeenNthCalledWith(1, {
|
|
265
|
-
token: TOKEN,
|
|
266
|
-
path: `/chats/${encodeURIComponent(CHAT_ID)}/members`,
|
|
267
|
-
});
|
|
268
|
-
expect(mockState.fetchGraphJson).toHaveBeenNthCalledWith(2, {
|
|
269
|
-
token: TOKEN,
|
|
270
|
-
path: `/chats/${encodeURIComponent(CHAT_ID)}/members?$skip=2`,
|
|
271
|
-
});
|
|
272
|
-
expect(mockState.deleteGraphRequest).toHaveBeenCalledWith({
|
|
273
|
-
token: TOKEN,
|
|
274
|
-
path: `/chats/${encodeURIComponent(CHAT_ID)}/members/membership-9`,
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
describe("renameGroupMSTeams", () => {
|
|
280
|
-
beforeEach(() => {
|
|
281
|
-
vi.clearAllMocks();
|
|
282
|
-
mockState.resolveGraphToken.mockResolvedValue(TOKEN);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it("renames a chat with topic", async () => {
|
|
286
|
-
mockState.patchGraphJson.mockResolvedValue(undefined);
|
|
287
|
-
|
|
288
|
-
const result = await renameGroupMSTeams({
|
|
289
|
-
cfg: {} as OpenClawConfig,
|
|
290
|
-
to: CHAT_ID,
|
|
291
|
-
name: "New Chat Name",
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
expect(result).toEqual({ renamed: { chatId: CHAT_ID, newName: "New Chat Name" } });
|
|
295
|
-
expect(mockState.patchGraphJson).toHaveBeenCalledWith({
|
|
296
|
-
token: TOKEN,
|
|
297
|
-
path: `/chats/${encodeURIComponent(CHAT_ID)}`,
|
|
298
|
-
body: { topic: "New Chat Name" },
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it("renames a channel with displayName", async () => {
|
|
303
|
-
mockState.patchGraphJson.mockResolvedValue(undefined);
|
|
304
|
-
|
|
305
|
-
const result = await renameGroupMSTeams({
|
|
306
|
-
cfg: {} as OpenClawConfig,
|
|
307
|
-
to: CHANNEL_TO,
|
|
308
|
-
name: "New Channel Name",
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
expect(result).toEqual({ renamed: { chatId: CHANNEL_TO, newName: "New Channel Name" } });
|
|
312
|
-
expect(mockState.patchGraphJson).toHaveBeenCalledWith({
|
|
313
|
-
token: TOKEN,
|
|
314
|
-
path: "/teams/team-id-1/channels/channel-id-1",
|
|
315
|
-
body: { displayName: "New Channel Name" },
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
});
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import type { OpenClawConfig } from "../runtime-api.js";
|
|
2
|
-
import { resolveConversationPath, resolveGraphConversationId } from "./graph-messages.js";
|
|
3
|
-
import {
|
|
4
|
-
deleteGraphRequest,
|
|
5
|
-
escapeOData,
|
|
6
|
-
fetchGraphJson,
|
|
7
|
-
patchGraphJson,
|
|
8
|
-
postGraphJson,
|
|
9
|
-
resolveGraphToken,
|
|
10
|
-
} from "./graph.js";
|
|
11
|
-
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
// Add Participant
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
|
-
|
|
16
|
-
type AddParticipantMSTeamsParams = {
|
|
17
|
-
cfg: OpenClawConfig;
|
|
18
|
-
to: string;
|
|
19
|
-
userId: string;
|
|
20
|
-
role?: string;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
type AddParticipantMSTeamsResult = {
|
|
24
|
-
added: { userId: string; chatId: string };
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
type ConversationMemberRole = "member" | "owner";
|
|
28
|
-
|
|
29
|
-
function normalizeConversationMemberRole(role: string | undefined): ConversationMemberRole {
|
|
30
|
-
const normalized = role?.trim().toLowerCase() ?? "";
|
|
31
|
-
if (!normalized) {
|
|
32
|
-
return "member";
|
|
33
|
-
}
|
|
34
|
-
if (normalized === "member" || normalized === "owner") {
|
|
35
|
-
return normalized;
|
|
36
|
-
}
|
|
37
|
-
throw new Error('MS Teams participant role must be "member" or "owner".');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Add a user to a chat or channel via Graph API.
|
|
42
|
-
*/
|
|
43
|
-
export async function addParticipantMSTeams(
|
|
44
|
-
params: AddParticipantMSTeamsParams,
|
|
45
|
-
): Promise<AddParticipantMSTeamsResult> {
|
|
46
|
-
const token = await resolveGraphToken(params.cfg);
|
|
47
|
-
const conversationId = await resolveGraphConversationId(params.to);
|
|
48
|
-
const conv = resolveConversationPath(conversationId);
|
|
49
|
-
|
|
50
|
-
const body = {
|
|
51
|
-
"@odata.type": "#microsoft.graph.aadUserConversationMember",
|
|
52
|
-
roles: [normalizeConversationMemberRole(params.role)],
|
|
53
|
-
"user@odata.bind": `https://graph.microsoft.com/v1.0/users('${escapeOData(params.userId)}')`,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
await postGraphJson<unknown>({
|
|
57
|
-
token,
|
|
58
|
-
path: `${conv.basePath}/members`,
|
|
59
|
-
body,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return { added: { userId: params.userId, chatId: conversationId } };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
// Remove Participant
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
type RemoveParticipantMSTeamsParams = {
|
|
70
|
-
cfg: OpenClawConfig;
|
|
71
|
-
to: string;
|
|
72
|
-
userId: string;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
type RemoveParticipantMSTeamsResult = {
|
|
76
|
-
removed: { userId: string; chatId: string };
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
type GraphConversationMember = {
|
|
80
|
-
id?: string;
|
|
81
|
-
userId?: string;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
type GraphConversationMemberResponse = {
|
|
85
|
-
value?: GraphConversationMember[];
|
|
86
|
-
"@odata.nextLink"?: string;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Remove a user from a chat or channel via Graph API.
|
|
91
|
-
* Lists members first to resolve the membership ID, then deletes.
|
|
92
|
-
*/
|
|
93
|
-
export async function removeParticipantMSTeams(
|
|
94
|
-
params: RemoveParticipantMSTeamsParams,
|
|
95
|
-
): Promise<RemoveParticipantMSTeamsResult> {
|
|
96
|
-
const token = await resolveGraphToken(params.cfg);
|
|
97
|
-
const conversationId = await resolveGraphConversationId(params.to);
|
|
98
|
-
const conv = resolveConversationPath(conversationId);
|
|
99
|
-
|
|
100
|
-
// List members to find the membership ID for the target user. Graph can
|
|
101
|
-
// paginate large chats/channels, so walk `@odata.nextLink` before concluding
|
|
102
|
-
// the user is missing.
|
|
103
|
-
const MAX_PAGES = 10;
|
|
104
|
-
let nextPath: string | undefined = `${conv.basePath}/members`;
|
|
105
|
-
let page = 0;
|
|
106
|
-
let member: GraphConversationMember | undefined;
|
|
107
|
-
while (nextPath && page < MAX_PAGES && !member) {
|
|
108
|
-
const membersRes: GraphConversationMemberResponse =
|
|
109
|
-
await fetchGraphJson<GraphConversationMemberResponse>({
|
|
110
|
-
token,
|
|
111
|
-
path: nextPath,
|
|
112
|
-
});
|
|
113
|
-
member = (membersRes.value ?? []).find(
|
|
114
|
-
(candidate: GraphConversationMember) => candidate.userId === params.userId,
|
|
115
|
-
);
|
|
116
|
-
if (member) {
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
const nextLink: string | undefined = membersRes["@odata.nextLink"];
|
|
120
|
-
nextPath = nextLink ? nextLink.replace("https://graph.microsoft.com/v1.0", "") : undefined;
|
|
121
|
-
page++;
|
|
122
|
-
}
|
|
123
|
-
if (!member?.id) {
|
|
124
|
-
throw new Error(`User ${params.userId} is not a member of this conversation`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
await deleteGraphRequest({
|
|
128
|
-
token,
|
|
129
|
-
path: `${conv.basePath}/members/${encodeURIComponent(member.id)}`,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
return { removed: { userId: params.userId, chatId: conversationId } };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ---------------------------------------------------------------------------
|
|
136
|
-
// Rename Group
|
|
137
|
-
// ---------------------------------------------------------------------------
|
|
138
|
-
|
|
139
|
-
type RenameGroupMSTeamsParams = {
|
|
140
|
-
cfg: OpenClawConfig;
|
|
141
|
-
to: string;
|
|
142
|
-
name: string;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
type RenameGroupMSTeamsResult = {
|
|
146
|
-
renamed: { chatId: string; newName: string };
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Rename a chat (topic) or channel (displayName) via Graph API.
|
|
151
|
-
*/
|
|
152
|
-
export async function renameGroupMSTeams(
|
|
153
|
-
params: RenameGroupMSTeamsParams,
|
|
154
|
-
): Promise<RenameGroupMSTeamsResult> {
|
|
155
|
-
const token = await resolveGraphToken(params.cfg);
|
|
156
|
-
const conversationId = await resolveGraphConversationId(params.to);
|
|
157
|
-
const conv = resolveConversationPath(conversationId);
|
|
158
|
-
|
|
159
|
-
const body = conv.kind === "chat" ? { topic: params.name } : { displayName: params.name };
|
|
160
|
-
|
|
161
|
-
await patchGraphJson<unknown>({
|
|
162
|
-
token,
|
|
163
|
-
path: conv.basePath,
|
|
164
|
-
body,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
return { renamed: { chatId: conversationId, newName: params.name } };
|
|
168
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import type { OpenClawConfig } from "../runtime-api.js";
|
|
3
|
-
import { getMemberInfoMSTeams } from "./graph-members.js";
|
|
4
|
-
|
|
5
|
-
const mockState = vi.hoisted(() => ({
|
|
6
|
-
resolveGraphToken: vi.fn(),
|
|
7
|
-
fetchGraphJson: vi.fn(),
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
vi.mock("./graph.js", () => {
|
|
11
|
-
return {
|
|
12
|
-
resolveGraphToken: mockState.resolveGraphToken,
|
|
13
|
-
fetchGraphJson: mockState.fetchGraphJson,
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const TOKEN = "test-graph-token";
|
|
18
|
-
|
|
19
|
-
describe("getMemberInfoMSTeams", () => {
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
vi.clearAllMocks();
|
|
22
|
-
mockState.resolveGraphToken.mockResolvedValue(TOKEN);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("fetches user profile and maps all fields", async () => {
|
|
26
|
-
mockState.fetchGraphJson.mockResolvedValue({
|
|
27
|
-
id: "user-123",
|
|
28
|
-
displayName: "Alice Smith",
|
|
29
|
-
mail: "alice@contoso.com",
|
|
30
|
-
jobTitle: "Engineer",
|
|
31
|
-
userPrincipalName: "alice@contoso.com",
|
|
32
|
-
officeLocation: "Building 1",
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const result = await getMemberInfoMSTeams({
|
|
36
|
-
cfg: {} as OpenClawConfig,
|
|
37
|
-
userId: "user-123",
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
expect(result).toEqual({
|
|
41
|
-
user: {
|
|
42
|
-
id: "user-123",
|
|
43
|
-
displayName: "Alice Smith",
|
|
44
|
-
mail: "alice@contoso.com",
|
|
45
|
-
jobTitle: "Engineer",
|
|
46
|
-
userPrincipalName: "alice@contoso.com",
|
|
47
|
-
officeLocation: "Building 1",
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
expect(mockState.fetchGraphJson).toHaveBeenCalledWith({
|
|
51
|
-
token: TOKEN,
|
|
52
|
-
path: `/users/${encodeURIComponent("user-123")}?$select=id,displayName,mail,jobTitle,userPrincipalName,officeLocation`,
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("handles sparse data with some fields undefined", async () => {
|
|
57
|
-
mockState.fetchGraphJson.mockResolvedValue({
|
|
58
|
-
id: "user-456",
|
|
59
|
-
displayName: "Bob",
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const result = await getMemberInfoMSTeams({
|
|
63
|
-
cfg: {} as OpenClawConfig,
|
|
64
|
-
userId: "user-456",
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
expect(result).toEqual({
|
|
68
|
-
user: {
|
|
69
|
-
id: "user-456",
|
|
70
|
-
displayName: "Bob",
|
|
71
|
-
mail: undefined,
|
|
72
|
-
jobTitle: undefined,
|
|
73
|
-
userPrincipalName: undefined,
|
|
74
|
-
officeLocation: undefined,
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("propagates Graph API errors", async () => {
|
|
80
|
-
mockState.fetchGraphJson.mockRejectedValue(new Error("Graph API 404: user not found"));
|
|
81
|
-
|
|
82
|
-
await expect(
|
|
83
|
-
getMemberInfoMSTeams({
|
|
84
|
-
cfg: {} as OpenClawConfig,
|
|
85
|
-
userId: "nonexistent-user",
|
|
86
|
-
}),
|
|
87
|
-
).rejects.toThrow("Graph API 404: user not found");
|
|
88
|
-
});
|
|
89
|
-
});
|
package/src/graph-members.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { OpenClawConfig } from "../runtime-api.js";
|
|
2
|
-
import { fetchGraphJson, resolveGraphToken } from "./graph.js";
|
|
3
|
-
|
|
4
|
-
type GraphUserProfile = {
|
|
5
|
-
id?: string;
|
|
6
|
-
displayName?: string;
|
|
7
|
-
mail?: string;
|
|
8
|
-
jobTitle?: string;
|
|
9
|
-
userPrincipalName?: string;
|
|
10
|
-
officeLocation?: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
type GetMemberInfoMSTeamsParams = {
|
|
14
|
-
cfg: OpenClawConfig;
|
|
15
|
-
userId: string;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
type GetMemberInfoMSTeamsResult = {
|
|
19
|
-
user: {
|
|
20
|
-
id: string | undefined;
|
|
21
|
-
displayName: string | undefined;
|
|
22
|
-
mail: string | undefined;
|
|
23
|
-
jobTitle: string | undefined;
|
|
24
|
-
userPrincipalName: string | undefined;
|
|
25
|
-
officeLocation: string | undefined;
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Fetch a user profile from Microsoft Graph by user ID.
|
|
31
|
-
*/
|
|
32
|
-
export async function getMemberInfoMSTeams(
|
|
33
|
-
params: GetMemberInfoMSTeamsParams,
|
|
34
|
-
): Promise<GetMemberInfoMSTeamsResult> {
|
|
35
|
-
const token = await resolveGraphToken(params.cfg);
|
|
36
|
-
const path = `/users/${encodeURIComponent(params.userId)}?$select=id,displayName,mail,jobTitle,userPrincipalName,officeLocation`;
|
|
37
|
-
const user = await fetchGraphJson<GraphUserProfile>({ token, path });
|
|
38
|
-
return {
|
|
39
|
-
user: {
|
|
40
|
-
id: user.id,
|
|
41
|
-
displayName: user.displayName,
|
|
42
|
-
mail: user.mail,
|
|
43
|
-
jobTitle: user.jobTitle,
|
|
44
|
-
userPrincipalName: user.userPrincipalName,
|
|
45
|
-
officeLocation: user.officeLocation,
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
}
|