@auxiora/connector-microsoft 1.0.0
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/LICENSE +191 -0
- package/dist/connector.d.ts +2 -0
- package/dist/connector.d.ts.map +1 -0
- package/dist/connector.js +657 -0
- package/dist/connector.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +25 -0
- package/src/connector.ts +679 -0
- package/src/index.ts +1 -0
- package/tests/connector.test.ts +294 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
package/src/connector.ts
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
import { defineConnector } from '@auxiora/connectors';
|
|
2
|
+
import type { TriggerEvent } from '@auxiora/connectors';
|
|
3
|
+
|
|
4
|
+
const GRAPH_BASE = 'https://graph.microsoft.com/v1.0';
|
|
5
|
+
|
|
6
|
+
async function graphFetch(token: string, path: string, options?: { method?: string; body?: unknown }) {
|
|
7
|
+
const res = await fetch(`${GRAPH_BASE}${path}`, {
|
|
8
|
+
method: options?.method ?? 'GET',
|
|
9
|
+
headers: {
|
|
10
|
+
'Authorization': `Bearer ${token}`,
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
},
|
|
13
|
+
body: options?.body ? JSON.stringify(options.body) : undefined,
|
|
14
|
+
});
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
const err = await res.json().catch(() => ({})) as { error?: { message?: string } };
|
|
17
|
+
throw new Error(`Graph API error: ${res.status} ${err.error?.message ?? res.statusText}`);
|
|
18
|
+
}
|
|
19
|
+
if (res.status === 204) return undefined;
|
|
20
|
+
return res.json();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const microsoftConnector = defineConnector({
|
|
24
|
+
id: 'microsoft-365',
|
|
25
|
+
name: 'Microsoft 365',
|
|
26
|
+
description: 'Integration with Outlook Mail, Calendar, OneDrive, and Contacts via Microsoft Graph API',
|
|
27
|
+
version: '1.0.0',
|
|
28
|
+
category: 'productivity',
|
|
29
|
+
icon: 'microsoft',
|
|
30
|
+
|
|
31
|
+
auth: {
|
|
32
|
+
type: 'oauth2',
|
|
33
|
+
oauth2: {
|
|
34
|
+
authUrl: 'https://login.microsoftonline.com/common/oauth2/v2/authorize',
|
|
35
|
+
tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2/token',
|
|
36
|
+
scopes: [
|
|
37
|
+
'Mail.ReadWrite',
|
|
38
|
+
'Mail.Send',
|
|
39
|
+
'Calendars.ReadWrite',
|
|
40
|
+
'Contacts.Read',
|
|
41
|
+
'Files.ReadWrite',
|
|
42
|
+
'User.Read',
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
actions: [
|
|
48
|
+
// --- Mail ---
|
|
49
|
+
{
|
|
50
|
+
id: 'mail-list-messages',
|
|
51
|
+
name: 'List Mail Messages',
|
|
52
|
+
description: 'List recent emails from Outlook',
|
|
53
|
+
trustMinimum: 1,
|
|
54
|
+
trustDomain: 'email',
|
|
55
|
+
reversible: false,
|
|
56
|
+
sideEffects: false,
|
|
57
|
+
params: {
|
|
58
|
+
folderId: { type: 'string', description: 'Mail folder ID' },
|
|
59
|
+
maxResults: { type: 'number', description: 'Max messages to return', default: 10 },
|
|
60
|
+
query: { type: 'string', description: 'OData filter query' },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'mail-read-message',
|
|
65
|
+
name: 'Read Mail Message',
|
|
66
|
+
description: 'Read a specific email message',
|
|
67
|
+
trustMinimum: 1,
|
|
68
|
+
trustDomain: 'email',
|
|
69
|
+
reversible: false,
|
|
70
|
+
sideEffects: false,
|
|
71
|
+
params: {
|
|
72
|
+
messageId: { type: 'string', description: 'Message ID', required: true },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'mail-send',
|
|
77
|
+
name: 'Send Email',
|
|
78
|
+
description: 'Send an email via Outlook',
|
|
79
|
+
trustMinimum: 3,
|
|
80
|
+
trustDomain: 'email',
|
|
81
|
+
reversible: false,
|
|
82
|
+
sideEffects: true,
|
|
83
|
+
params: {
|
|
84
|
+
to: { type: 'string', description: 'Recipient email', required: true },
|
|
85
|
+
subject: { type: 'string', description: 'Email subject', required: true },
|
|
86
|
+
body: { type: 'string', description: 'Email body', required: true },
|
|
87
|
+
cc: { type: 'string', description: 'CC recipients' },
|
|
88
|
+
bcc: { type: 'string', description: 'BCC recipients' },
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'mail-reply',
|
|
93
|
+
name: 'Reply to Email',
|
|
94
|
+
description: 'Reply to an email message',
|
|
95
|
+
trustMinimum: 3,
|
|
96
|
+
trustDomain: 'email',
|
|
97
|
+
reversible: false,
|
|
98
|
+
sideEffects: true,
|
|
99
|
+
params: {
|
|
100
|
+
messageId: { type: 'string', description: 'Message ID to reply to', required: true },
|
|
101
|
+
body: { type: 'string', description: 'Reply body', required: true },
|
|
102
|
+
replyAll: { type: 'boolean', description: 'Reply to all recipients' },
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'mail-forward',
|
|
107
|
+
name: 'Forward Email',
|
|
108
|
+
description: 'Forward an email message',
|
|
109
|
+
trustMinimum: 3,
|
|
110
|
+
trustDomain: 'email',
|
|
111
|
+
reversible: false,
|
|
112
|
+
sideEffects: true,
|
|
113
|
+
params: {
|
|
114
|
+
messageId: { type: 'string', description: 'Message ID to forward', required: true },
|
|
115
|
+
to: { type: 'string', description: 'Recipient email', required: true },
|
|
116
|
+
comment: { type: 'string', description: 'Comment to include' },
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'mail-move',
|
|
121
|
+
name: 'Move Email',
|
|
122
|
+
description: 'Move an email to a different folder',
|
|
123
|
+
trustMinimum: 2,
|
|
124
|
+
trustDomain: 'email',
|
|
125
|
+
reversible: true,
|
|
126
|
+
sideEffects: true,
|
|
127
|
+
params: {
|
|
128
|
+
messageId: { type: 'string', description: 'Message ID', required: true },
|
|
129
|
+
destinationFolderId: { type: 'string', description: 'Destination folder ID', required: true },
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'mail-archive',
|
|
134
|
+
name: 'Archive Email',
|
|
135
|
+
description: 'Archive an email message',
|
|
136
|
+
trustMinimum: 2,
|
|
137
|
+
trustDomain: 'email',
|
|
138
|
+
reversible: true,
|
|
139
|
+
sideEffects: true,
|
|
140
|
+
params: {
|
|
141
|
+
messageId: { type: 'string', description: 'Message ID', required: true },
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'mail-flag',
|
|
146
|
+
name: 'Flag Email',
|
|
147
|
+
description: 'Flag an email message for follow-up',
|
|
148
|
+
trustMinimum: 1,
|
|
149
|
+
trustDomain: 'email',
|
|
150
|
+
reversible: true,
|
|
151
|
+
sideEffects: true,
|
|
152
|
+
params: {
|
|
153
|
+
messageId: { type: 'string', description: 'Message ID', required: true },
|
|
154
|
+
flagStatus: { type: 'string', description: 'Flag status', default: 'flagged' },
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'mail-search',
|
|
159
|
+
name: 'Search Mail',
|
|
160
|
+
description: 'Search emails in Outlook',
|
|
161
|
+
trustMinimum: 1,
|
|
162
|
+
trustDomain: 'email',
|
|
163
|
+
reversible: false,
|
|
164
|
+
sideEffects: false,
|
|
165
|
+
params: {
|
|
166
|
+
query: { type: 'string', description: 'Search query', required: true },
|
|
167
|
+
maxResults: { type: 'number', description: 'Max results', default: 10 },
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'mail-draft',
|
|
172
|
+
name: 'Create Draft',
|
|
173
|
+
description: 'Create a draft email in Outlook',
|
|
174
|
+
trustMinimum: 2,
|
|
175
|
+
trustDomain: 'email',
|
|
176
|
+
reversible: true,
|
|
177
|
+
sideEffects: true,
|
|
178
|
+
params: {
|
|
179
|
+
to: { type: 'string', description: 'Recipient email', required: true },
|
|
180
|
+
subject: { type: 'string', description: 'Email subject', required: true },
|
|
181
|
+
body: { type: 'string', description: 'Email body', required: true },
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
// --- Calendar ---
|
|
185
|
+
{
|
|
186
|
+
id: 'calendar-list-events',
|
|
187
|
+
name: 'List Calendar Events',
|
|
188
|
+
description: 'List upcoming events from Outlook Calendar',
|
|
189
|
+
trustMinimum: 1,
|
|
190
|
+
trustDomain: 'calendar',
|
|
191
|
+
reversible: false,
|
|
192
|
+
sideEffects: false,
|
|
193
|
+
params: {
|
|
194
|
+
calendarId: { type: 'string', description: 'Calendar ID' },
|
|
195
|
+
maxResults: { type: 'number', description: 'Max events to return', default: 10 },
|
|
196
|
+
startDateTime: { type: 'string', description: 'Start time (ISO 8601)' },
|
|
197
|
+
endDateTime: { type: 'string', description: 'End time (ISO 8601)' },
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'calendar-create-event',
|
|
202
|
+
name: 'Create Calendar Event',
|
|
203
|
+
description: 'Create a new event in Outlook Calendar',
|
|
204
|
+
trustMinimum: 2,
|
|
205
|
+
trustDomain: 'calendar',
|
|
206
|
+
reversible: true,
|
|
207
|
+
sideEffects: true,
|
|
208
|
+
params: {
|
|
209
|
+
subject: { type: 'string', description: 'Event subject', required: true },
|
|
210
|
+
start: { type: 'string', description: 'Start time (ISO 8601)', required: true },
|
|
211
|
+
end: { type: 'string', description: 'End time (ISO 8601)', required: true },
|
|
212
|
+
body: { type: 'string', description: 'Event body' },
|
|
213
|
+
attendees: { type: 'array', description: 'List of attendee emails' },
|
|
214
|
+
location: { type: 'string', description: 'Event location' },
|
|
215
|
+
isOnlineMeeting: { type: 'boolean', description: 'Create as online meeting' },
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'calendar-update-event',
|
|
220
|
+
name: 'Update Calendar Event',
|
|
221
|
+
description: 'Update an existing calendar event',
|
|
222
|
+
trustMinimum: 2,
|
|
223
|
+
trustDomain: 'calendar',
|
|
224
|
+
reversible: true,
|
|
225
|
+
sideEffects: true,
|
|
226
|
+
params: {
|
|
227
|
+
eventId: { type: 'string', description: 'Event ID', required: true },
|
|
228
|
+
subject: { type: 'string', description: 'Updated subject' },
|
|
229
|
+
start: { type: 'string', description: 'Updated start time' },
|
|
230
|
+
end: { type: 'string', description: 'Updated end time' },
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: 'calendar-delete-event',
|
|
235
|
+
name: 'Delete Calendar Event',
|
|
236
|
+
description: 'Delete a calendar event',
|
|
237
|
+
trustMinimum: 3,
|
|
238
|
+
trustDomain: 'calendar',
|
|
239
|
+
reversible: false,
|
|
240
|
+
sideEffects: true,
|
|
241
|
+
params: {
|
|
242
|
+
eventId: { type: 'string', description: 'Event ID', required: true },
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: 'calendar-find-availability',
|
|
247
|
+
name: 'Find Availability',
|
|
248
|
+
description: 'Find free time slots for attendees',
|
|
249
|
+
trustMinimum: 1,
|
|
250
|
+
trustDomain: 'calendar',
|
|
251
|
+
reversible: false,
|
|
252
|
+
sideEffects: false,
|
|
253
|
+
params: {
|
|
254
|
+
attendees: { type: 'array', description: 'List of attendee emails', required: true },
|
|
255
|
+
startDateTime: { type: 'string', description: 'Start of window (ISO 8601)', required: true },
|
|
256
|
+
endDateTime: { type: 'string', description: 'End of window (ISO 8601)', required: true },
|
|
257
|
+
durationMinutes: { type: 'number', description: 'Desired slot duration in minutes', default: 30 },
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
// --- Contacts ---
|
|
261
|
+
{
|
|
262
|
+
id: 'contacts-list',
|
|
263
|
+
name: 'List Contacts',
|
|
264
|
+
description: 'List contacts from Outlook',
|
|
265
|
+
trustMinimum: 1,
|
|
266
|
+
trustDomain: 'integrations',
|
|
267
|
+
reversible: false,
|
|
268
|
+
sideEffects: false,
|
|
269
|
+
params: {
|
|
270
|
+
maxResults: { type: 'number', description: 'Max contacts to return', default: 20 },
|
|
271
|
+
search: { type: 'string', description: 'Search query' },
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: 'contacts-get',
|
|
276
|
+
name: 'Get Contact',
|
|
277
|
+
description: 'Get a specific contact',
|
|
278
|
+
trustMinimum: 1,
|
|
279
|
+
trustDomain: 'integrations',
|
|
280
|
+
reversible: false,
|
|
281
|
+
sideEffects: false,
|
|
282
|
+
params: {
|
|
283
|
+
contactId: { type: 'string', description: 'Contact ID', required: true },
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
// --- OneDrive ---
|
|
287
|
+
{
|
|
288
|
+
id: 'files-list',
|
|
289
|
+
name: 'List OneDrive Files',
|
|
290
|
+
description: 'List files in OneDrive',
|
|
291
|
+
trustMinimum: 1,
|
|
292
|
+
trustDomain: 'files',
|
|
293
|
+
reversible: false,
|
|
294
|
+
sideEffects: false,
|
|
295
|
+
params: {
|
|
296
|
+
folderId: { type: 'string', description: 'Folder ID (default: root)' },
|
|
297
|
+
maxResults: { type: 'number', description: 'Max files to return', default: 20 },
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
id: 'files-download',
|
|
302
|
+
name: 'Download File',
|
|
303
|
+
description: 'Download a file from OneDrive',
|
|
304
|
+
trustMinimum: 1,
|
|
305
|
+
trustDomain: 'files',
|
|
306
|
+
reversible: false,
|
|
307
|
+
sideEffects: false,
|
|
308
|
+
params: {
|
|
309
|
+
fileId: { type: 'string', description: 'File ID', required: true },
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: 'files-upload',
|
|
314
|
+
name: 'Upload File',
|
|
315
|
+
description: 'Upload a file to OneDrive',
|
|
316
|
+
trustMinimum: 2,
|
|
317
|
+
trustDomain: 'files',
|
|
318
|
+
reversible: true,
|
|
319
|
+
sideEffects: true,
|
|
320
|
+
params: {
|
|
321
|
+
name: { type: 'string', description: 'File name', required: true },
|
|
322
|
+
content: { type: 'string', description: 'File content', required: true },
|
|
323
|
+
folderId: { type: 'string', description: 'Parent folder ID' },
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
id: 'files-search',
|
|
328
|
+
name: 'Search Files',
|
|
329
|
+
description: 'Search files in OneDrive',
|
|
330
|
+
trustMinimum: 1,
|
|
331
|
+
trustDomain: 'files',
|
|
332
|
+
reversible: false,
|
|
333
|
+
sideEffects: false,
|
|
334
|
+
params: {
|
|
335
|
+
query: { type: 'string', description: 'Search query', required: true },
|
|
336
|
+
maxResults: { type: 'number', description: 'Max results', default: 10 },
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
|
|
341
|
+
triggers: [
|
|
342
|
+
{
|
|
343
|
+
id: 'new-email',
|
|
344
|
+
name: 'New Email',
|
|
345
|
+
description: 'Triggered when a new email arrives',
|
|
346
|
+
type: 'poll',
|
|
347
|
+
pollIntervalMs: 60_000,
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
id: 'event-starting-soon',
|
|
351
|
+
name: 'Event Starting Soon',
|
|
352
|
+
description: 'Triggered when a calendar event is about to start',
|
|
353
|
+
type: 'poll',
|
|
354
|
+
pollIntervalMs: 60_000,
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
id: 'calendar-event-created',
|
|
358
|
+
name: 'Calendar Event Created',
|
|
359
|
+
description: 'Triggered when a new calendar event is created',
|
|
360
|
+
type: 'poll',
|
|
361
|
+
pollIntervalMs: 300_000,
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
|
|
365
|
+
entities: [
|
|
366
|
+
{
|
|
367
|
+
id: 'email-message',
|
|
368
|
+
name: 'Email Message',
|
|
369
|
+
description: 'An Outlook email message',
|
|
370
|
+
fields: { id: 'string', from: 'string', to: 'string', subject: 'string', bodyPreview: 'string', receivedDateTime: 'string', importance: 'string', isRead: 'string' },
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: 'calendar-event',
|
|
374
|
+
name: 'Calendar Event',
|
|
375
|
+
description: 'An Outlook Calendar event',
|
|
376
|
+
fields: { id: 'string', subject: 'string', start: 'string', end: 'string', attendees: 'array', location: 'string', isOnlineMeeting: 'string' },
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
id: 'contact',
|
|
380
|
+
name: 'Contact',
|
|
381
|
+
description: 'An Outlook contact',
|
|
382
|
+
fields: { id: 'string', displayName: 'string', emailAddresses: 'array', companyName: 'string', jobTitle: 'string' },
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
id: 'drive-item',
|
|
386
|
+
name: 'Drive Item',
|
|
387
|
+
description: 'A OneDrive file or folder',
|
|
388
|
+
fields: { id: 'string', name: 'string', size: 'number', lastModifiedDateTime: 'string', webUrl: 'string' },
|
|
389
|
+
},
|
|
390
|
+
],
|
|
391
|
+
|
|
392
|
+
async executeAction(actionId: string, params: Record<string, unknown>, token: string): Promise<unknown> {
|
|
393
|
+
switch (actionId) {
|
|
394
|
+
// --- Mail ---
|
|
395
|
+
case 'mail-list-messages': {
|
|
396
|
+
const folderId = (params.folderId as string) ?? 'inbox';
|
|
397
|
+
const top = (params.maxResults as number) ?? 10;
|
|
398
|
+
let path = `/me/mailFolders/${encodeURIComponent(folderId)}/messages?$top=${top}`;
|
|
399
|
+
if (params.query) path += `&$filter=${encodeURIComponent(params.query as string)}`;
|
|
400
|
+
const res = await graphFetch(token, path) as { value: unknown[] };
|
|
401
|
+
return { messages: res.value, folderId };
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
case 'mail-read-message': {
|
|
405
|
+
const msg = await graphFetch(token, `/me/messages/${encodeURIComponent(params.messageId as string)}`);
|
|
406
|
+
return msg;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
case 'mail-send': {
|
|
410
|
+
const toRecipients = [{ emailAddress: { address: params.to as string } }];
|
|
411
|
+
const ccRecipients = params.cc
|
|
412
|
+
? (params.cc as string).split(',').map(e => ({ emailAddress: { address: e.trim() } }))
|
|
413
|
+
: undefined;
|
|
414
|
+
const bccRecipients = params.bcc
|
|
415
|
+
? (params.bcc as string).split(',').map(e => ({ emailAddress: { address: e.trim() } }))
|
|
416
|
+
: undefined;
|
|
417
|
+
await graphFetch(token, '/me/sendMail', {
|
|
418
|
+
method: 'POST',
|
|
419
|
+
body: {
|
|
420
|
+
message: {
|
|
421
|
+
toRecipients,
|
|
422
|
+
subject: params.subject as string,
|
|
423
|
+
body: { contentType: 'Text', content: params.body as string },
|
|
424
|
+
...(ccRecipients ? { ccRecipients } : {}),
|
|
425
|
+
...(bccRecipients ? { bccRecipients } : {}),
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
return { status: 'sent' };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
case 'mail-reply': {
|
|
433
|
+
const action = params.replyAll ? 'replyAll' : 'reply';
|
|
434
|
+
await graphFetch(token, `/me/messages/${encodeURIComponent(params.messageId as string)}/${action}`, {
|
|
435
|
+
method: 'POST',
|
|
436
|
+
body: { comment: params.body as string },
|
|
437
|
+
});
|
|
438
|
+
return { messageId: params.messageId, status: 'replied' };
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
case 'mail-forward': {
|
|
442
|
+
await graphFetch(token, `/me/messages/${encodeURIComponent(params.messageId as string)}/forward`, {
|
|
443
|
+
method: 'POST',
|
|
444
|
+
body: {
|
|
445
|
+
toRecipients: [{ emailAddress: { address: params.to as string } }],
|
|
446
|
+
comment: (params.comment as string) ?? '',
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
return { messageId: params.messageId, status: 'forwarded', to: params.to };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
case 'mail-move': {
|
|
453
|
+
const moved = await graphFetch(token, `/me/messages/${encodeURIComponent(params.messageId as string)}/move`, {
|
|
454
|
+
method: 'POST',
|
|
455
|
+
body: { destinationId: params.destinationFolderId as string },
|
|
456
|
+
});
|
|
457
|
+
return { messageId: (moved as { id: string }).id, status: 'moved', destinationFolderId: params.destinationFolderId };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
case 'mail-archive': {
|
|
461
|
+
const archived = await graphFetch(token, `/me/messages/${encodeURIComponent(params.messageId as string)}/move`, {
|
|
462
|
+
method: 'POST',
|
|
463
|
+
body: { destinationId: 'archive' },
|
|
464
|
+
});
|
|
465
|
+
return { messageId: (archived as { id: string }).id, status: 'archived' };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
case 'mail-flag': {
|
|
469
|
+
const flagged = await graphFetch(token, `/me/messages/${encodeURIComponent(params.messageId as string)}`, {
|
|
470
|
+
method: 'PATCH',
|
|
471
|
+
body: { flag: { flagStatus: (params.flagStatus as string) ?? 'flagged' } },
|
|
472
|
+
});
|
|
473
|
+
return { messageId: (flagged as { id: string }).id, status: 'flagged' };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
case 'mail-search': {
|
|
477
|
+
const top = (params.maxResults as number) ?? 10;
|
|
478
|
+
const query = encodeURIComponent(`"${params.query as string}"`);
|
|
479
|
+
const res = await graphFetch(token, `/me/messages?$search=${query}&$top=${top}`) as { value: unknown[] };
|
|
480
|
+
return { messages: res.value, query: params.query };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
case 'mail-draft': {
|
|
484
|
+
const draft = await graphFetch(token, '/me/messages', {
|
|
485
|
+
method: 'POST',
|
|
486
|
+
body: {
|
|
487
|
+
toRecipients: [{ emailAddress: { address: params.to as string } }],
|
|
488
|
+
subject: params.subject as string,
|
|
489
|
+
body: { contentType: 'Text', content: params.body as string },
|
|
490
|
+
isDraft: true,
|
|
491
|
+
},
|
|
492
|
+
}) as { id: string };
|
|
493
|
+
return { draftId: draft.id, status: 'created' };
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// --- Calendar ---
|
|
497
|
+
case 'calendar-list-events': {
|
|
498
|
+
const top = (params.maxResults as number) ?? 10;
|
|
499
|
+
let res: { value: unknown[] };
|
|
500
|
+
if (params.startDateTime && params.endDateTime) {
|
|
501
|
+
const start = encodeURIComponent(params.startDateTime as string);
|
|
502
|
+
const end = encodeURIComponent(params.endDateTime as string);
|
|
503
|
+
res = await graphFetch(token, `/me/calendarView?startDateTime=${start}&endDateTime=${end}&$top=${top}`) as { value: unknown[] };
|
|
504
|
+
} else {
|
|
505
|
+
res = await graphFetch(token, `/me/events?$top=${top}&$orderby=start/dateTime`) as { value: unknown[] };
|
|
506
|
+
}
|
|
507
|
+
return { events: res.value };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
case 'calendar-create-event': {
|
|
511
|
+
const attendees = params.attendees
|
|
512
|
+
? (params.attendees as string[]).map(email => ({
|
|
513
|
+
emailAddress: { address: email },
|
|
514
|
+
type: 'required',
|
|
515
|
+
}))
|
|
516
|
+
: undefined;
|
|
517
|
+
const eventBody: Record<string, unknown> = {
|
|
518
|
+
subject: params.subject as string,
|
|
519
|
+
start: { dateTime: params.start as string, timeZone: 'UTC' },
|
|
520
|
+
end: { dateTime: params.end as string, timeZone: 'UTC' },
|
|
521
|
+
};
|
|
522
|
+
if (params.body) eventBody.body = { contentType: 'Text', content: params.body as string };
|
|
523
|
+
if (attendees) eventBody.attendees = attendees;
|
|
524
|
+
if (params.location) eventBody.location = { displayName: params.location as string };
|
|
525
|
+
if (params.isOnlineMeeting) eventBody.isOnlineMeeting = true;
|
|
526
|
+
const created = await graphFetch(token, '/me/events', {
|
|
527
|
+
method: 'POST',
|
|
528
|
+
body: eventBody,
|
|
529
|
+
}) as { id: string; subject: string };
|
|
530
|
+
return { eventId: created.id, status: 'created', subject: created.subject };
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
case 'calendar-update-event': {
|
|
534
|
+
const updates: Record<string, unknown> = {};
|
|
535
|
+
if (params.subject) updates.subject = params.subject;
|
|
536
|
+
if (params.start) updates.start = { dateTime: params.start as string, timeZone: 'UTC' };
|
|
537
|
+
if (params.end) updates.end = { dateTime: params.end as string, timeZone: 'UTC' };
|
|
538
|
+
const updated = await graphFetch(token, `/me/events/${encodeURIComponent(params.eventId as string)}`, {
|
|
539
|
+
method: 'PATCH',
|
|
540
|
+
body: updates,
|
|
541
|
+
}) as { id: string };
|
|
542
|
+
return { eventId: updated.id, status: 'updated' };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
case 'calendar-delete-event': {
|
|
546
|
+
await graphFetch(token, `/me/events/${encodeURIComponent(params.eventId as string)}`, {
|
|
547
|
+
method: 'DELETE',
|
|
548
|
+
});
|
|
549
|
+
return { eventId: params.eventId, status: 'deleted' };
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
case 'calendar-find-availability': {
|
|
553
|
+
const schedules = (params.attendees as string[]);
|
|
554
|
+
const res = await graphFetch(token, '/me/calendar/getSchedule', {
|
|
555
|
+
method: 'POST',
|
|
556
|
+
body: {
|
|
557
|
+
schedules,
|
|
558
|
+
startTime: { dateTime: params.startDateTime as string, timeZone: 'UTC' },
|
|
559
|
+
endTime: { dateTime: params.endDateTime as string, timeZone: 'UTC' },
|
|
560
|
+
availabilityViewInterval: (params.durationMinutes as number) ?? 30,
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
return res;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// --- Contacts ---
|
|
567
|
+
case 'contacts-list': {
|
|
568
|
+
const top = (params.maxResults as number) ?? 20;
|
|
569
|
+
let path = `/me/contacts?$top=${top}`;
|
|
570
|
+
if (params.search) path += `&$search="${encodeURIComponent(params.search as string)}"`;
|
|
571
|
+
const res = await graphFetch(token, path) as { value: unknown[] };
|
|
572
|
+
return { contacts: res.value };
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
case 'contacts-get': {
|
|
576
|
+
const contact = await graphFetch(token, `/me/contacts/${encodeURIComponent(params.contactId as string)}`);
|
|
577
|
+
return contact;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// --- OneDrive ---
|
|
581
|
+
case 'files-list': {
|
|
582
|
+
const top = (params.maxResults as number) ?? 20;
|
|
583
|
+
const path = params.folderId
|
|
584
|
+
? `/me/drive/items/${encodeURIComponent(params.folderId as string)}/children?$top=${top}`
|
|
585
|
+
: `/me/drive/root/children?$top=${top}`;
|
|
586
|
+
const res = await graphFetch(token, path) as { value: unknown[] };
|
|
587
|
+
return { files: res.value };
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
case 'files-download': {
|
|
591
|
+
const downloadRes = await fetch(`${GRAPH_BASE}/me/drive/items/${encodeURIComponent(params.fileId as string)}/content`, {
|
|
592
|
+
headers: { 'Authorization': `Bearer ${token}` },
|
|
593
|
+
redirect: 'follow',
|
|
594
|
+
});
|
|
595
|
+
if (!downloadRes.ok) {
|
|
596
|
+
throw new Error(`Graph API error: ${downloadRes.status} ${downloadRes.statusText}`);
|
|
597
|
+
}
|
|
598
|
+
const content = await downloadRes.text();
|
|
599
|
+
return { fileId: params.fileId, content };
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
case 'files-upload': {
|
|
603
|
+
const fileName = encodeURIComponent(params.name as string);
|
|
604
|
+
const uploadPath = params.folderId
|
|
605
|
+
? `/me/drive/items/${encodeURIComponent(params.folderId as string)}:/${fileName}:/content`
|
|
606
|
+
: `/me/drive/root:/${fileName}:/content`;
|
|
607
|
+
const uploadRes = await fetch(`${GRAPH_BASE}${uploadPath}`, {
|
|
608
|
+
method: 'PUT',
|
|
609
|
+
headers: {
|
|
610
|
+
'Authorization': `Bearer ${token}`,
|
|
611
|
+
'Content-Type': 'application/octet-stream',
|
|
612
|
+
},
|
|
613
|
+
body: params.content as string,
|
|
614
|
+
});
|
|
615
|
+
if (!uploadRes.ok) {
|
|
616
|
+
const err = await uploadRes.json().catch(() => ({})) as { error?: { message?: string } };
|
|
617
|
+
throw new Error(`Graph API error: ${uploadRes.status} ${err.error?.message ?? uploadRes.statusText}`);
|
|
618
|
+
}
|
|
619
|
+
const uploaded = await uploadRes.json() as { id: string; name: string };
|
|
620
|
+
return { fileId: uploaded.id, status: 'uploaded', name: uploaded.name };
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
case 'files-search': {
|
|
624
|
+
const q = encodeURIComponent(params.query as string);
|
|
625
|
+
const top = (params.maxResults as number) ?? 10;
|
|
626
|
+
const res = await graphFetch(token, `/me/drive/root/search(q='${q}')?$top=${top}`) as { value: unknown[] };
|
|
627
|
+
return { files: res.value, query: params.query };
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
default:
|
|
631
|
+
throw new Error(`Unknown action: ${actionId}`);
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
|
|
635
|
+
async pollTrigger(triggerId: string, token: string, lastPollAt?: number): Promise<TriggerEvent[]> {
|
|
636
|
+
switch (triggerId) {
|
|
637
|
+
case 'new-email': {
|
|
638
|
+
const res = await graphFetch(token, '/me/mailFolders/inbox/messages?$orderby=receivedDateTime desc&$top=10') as { value: Array<{ id: string; receivedDateTime: string; subject?: string; from?: unknown }> };
|
|
639
|
+
const since = lastPollAt ?? (Date.now() - 120_000);
|
|
640
|
+
const newMessages = res.value.filter(m => new Date(m.receivedDateTime).getTime() > since);
|
|
641
|
+
return newMessages.map(m => ({
|
|
642
|
+
triggerId: 'new-email',
|
|
643
|
+
connectorId: 'microsoft-365',
|
|
644
|
+
timestamp: new Date(m.receivedDateTime).getTime(),
|
|
645
|
+
data: { messageId: m.id, subject: m.subject, from: m.from },
|
|
646
|
+
}));
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
case 'event-starting-soon': {
|
|
650
|
+
const now = new Date();
|
|
651
|
+
const soon = new Date(now.getTime() + 15 * 60_000);
|
|
652
|
+
const start = encodeURIComponent(now.toISOString());
|
|
653
|
+
const end = encodeURIComponent(soon.toISOString());
|
|
654
|
+
const res = await graphFetch(token, `/me/calendarView?startDateTime=${start}&endDateTime=${end}&$top=10`) as { value: Array<{ id: string; subject?: string; start?: unknown; end?: unknown }> };
|
|
655
|
+
return res.value.map(e => ({
|
|
656
|
+
triggerId: 'event-starting-soon',
|
|
657
|
+
connectorId: 'microsoft-365',
|
|
658
|
+
timestamp: Date.now(),
|
|
659
|
+
data: { eventId: e.id, subject: e.subject, start: e.start, end: e.end },
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
case 'calendar-event-created': {
|
|
664
|
+
const res = await graphFetch(token, '/me/events?$orderby=createdDateTime desc&$top=10') as { value: Array<{ id: string; createdDateTime: string; subject?: string; start?: unknown }> };
|
|
665
|
+
const since = lastPollAt ?? (Date.now() - 300_000);
|
|
666
|
+
const newEvents = res.value.filter(e => new Date(e.createdDateTime).getTime() > since);
|
|
667
|
+
return newEvents.map(e => ({
|
|
668
|
+
triggerId: 'calendar-event-created',
|
|
669
|
+
connectorId: 'microsoft-365',
|
|
670
|
+
timestamp: new Date(e.createdDateTime).getTime(),
|
|
671
|
+
data: { eventId: e.id, subject: e.subject, start: e.start },
|
|
672
|
+
}));
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
default:
|
|
676
|
+
return [];
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { microsoftConnector } from './connector.js';
|