m365-agent-cli 1.2.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.
Files changed (92) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +916 -0
  3. package/package.json +50 -0
  4. package/src/cli.ts +100 -0
  5. package/src/commands/auto-reply.ts +182 -0
  6. package/src/commands/calendar.ts +576 -0
  7. package/src/commands/counter.ts +87 -0
  8. package/src/commands/create-event.ts +544 -0
  9. package/src/commands/delegates.ts +286 -0
  10. package/src/commands/delete-event.ts +321 -0
  11. package/src/commands/drafts.ts +502 -0
  12. package/src/commands/files.ts +532 -0
  13. package/src/commands/find.ts +195 -0
  14. package/src/commands/findtime.ts +270 -0
  15. package/src/commands/folders.ts +177 -0
  16. package/src/commands/forward-event.ts +49 -0
  17. package/src/commands/graph-calendar.ts +217 -0
  18. package/src/commands/login.ts +195 -0
  19. package/src/commands/mail.ts +950 -0
  20. package/src/commands/oof.ts +263 -0
  21. package/src/commands/outlook-categories.ts +173 -0
  22. package/src/commands/outlook-graph.ts +880 -0
  23. package/src/commands/planner.ts +1678 -0
  24. package/src/commands/respond.ts +291 -0
  25. package/src/commands/rooms.ts +210 -0
  26. package/src/commands/rules.ts +511 -0
  27. package/src/commands/schedule.ts +109 -0
  28. package/src/commands/send.ts +204 -0
  29. package/src/commands/serve.ts +14 -0
  30. package/src/commands/sharepoint.ts +179 -0
  31. package/src/commands/site-pages.ts +163 -0
  32. package/src/commands/subscribe.ts +103 -0
  33. package/src/commands/subscriptions.ts +29 -0
  34. package/src/commands/suggest.ts +155 -0
  35. package/src/commands/todo.ts +2092 -0
  36. package/src/commands/update-event.ts +608 -0
  37. package/src/commands/update.ts +88 -0
  38. package/src/commands/verify-token.ts +62 -0
  39. package/src/commands/whoami.ts +74 -0
  40. package/src/index.ts +190 -0
  41. package/src/lib/atomic-write.ts +20 -0
  42. package/src/lib/attach-link-spec.test.ts +24 -0
  43. package/src/lib/attach-link-spec.ts +70 -0
  44. package/src/lib/attachments.ts +79 -0
  45. package/src/lib/auth.ts +192 -0
  46. package/src/lib/calendar-range.test.ts +41 -0
  47. package/src/lib/calendar-range.ts +103 -0
  48. package/src/lib/dates.test.ts +74 -0
  49. package/src/lib/dates.ts +137 -0
  50. package/src/lib/delegate-client.test.ts +74 -0
  51. package/src/lib/delegate-client.ts +322 -0
  52. package/src/lib/ews-client.ts +3418 -0
  53. package/src/lib/git-commit.ts +4 -0
  54. package/src/lib/glitchtip-eligibility.ts +220 -0
  55. package/src/lib/glitchtip.ts +253 -0
  56. package/src/lib/global-env.ts +3 -0
  57. package/src/lib/graph-auth.ts +223 -0
  58. package/src/lib/graph-calendar-client.test.ts +118 -0
  59. package/src/lib/graph-calendar-client.ts +112 -0
  60. package/src/lib/graph-client.test.ts +107 -0
  61. package/src/lib/graph-client.ts +1058 -0
  62. package/src/lib/graph-constants.ts +12 -0
  63. package/src/lib/graph-directory.ts +116 -0
  64. package/src/lib/graph-event.ts +134 -0
  65. package/src/lib/graph-schedule.ts +173 -0
  66. package/src/lib/graph-subscriptions.ts +94 -0
  67. package/src/lib/graph-user-path.ts +13 -0
  68. package/src/lib/jwt-utils.ts +34 -0
  69. package/src/lib/markdown.test.ts +21 -0
  70. package/src/lib/markdown.ts +174 -0
  71. package/src/lib/mime-type.ts +106 -0
  72. package/src/lib/oof-client.test.ts +59 -0
  73. package/src/lib/oof-client.ts +122 -0
  74. package/src/lib/outlook-graph-client.test.ts +146 -0
  75. package/src/lib/outlook-graph-client.ts +649 -0
  76. package/src/lib/outlook-master-categories.ts +145 -0
  77. package/src/lib/package-info.ts +59 -0
  78. package/src/lib/places-client.ts +144 -0
  79. package/src/lib/planner-client.ts +1226 -0
  80. package/src/lib/rules-client.ts +178 -0
  81. package/src/lib/sharepoint-client.ts +101 -0
  82. package/src/lib/site-pages-client.ts +73 -0
  83. package/src/lib/todo-client.test.ts +298 -0
  84. package/src/lib/todo-client.ts +1309 -0
  85. package/src/lib/url-validation.ts +40 -0
  86. package/src/lib/utils.ts +45 -0
  87. package/src/lib/webhook-server.ts +51 -0
  88. package/src/test/auth.test.ts +104 -0
  89. package/src/test/cli.integration.test.ts +1083 -0
  90. package/src/test/ews-client.test.ts +268 -0
  91. package/src/test/mocks/index.ts +375 -0
  92. package/src/test/mocks/responses.ts +861 -0
@@ -0,0 +1,322 @@
1
+ // Delegate management via EWS SOAP (AddDelegate / GetDelegate / UpdateDelegate / RemoveDelegate)
2
+ // No Microsoft Graph equivalent — only available via EWS.
3
+
4
+ import {
5
+ callEws,
6
+ EWS_USERNAME,
7
+ ewsError,
8
+ ewsResult,
9
+ extractBlocks,
10
+ extractTag,
11
+ soapEnvelope,
12
+ xmlEscape
13
+ } from './ews-client.js';
14
+
15
+ // ─── Types ───
16
+
17
+ export type DelegateFolderPermissionLevel =
18
+ | 'None'
19
+ | 'Owner'
20
+ | 'PublishingEditor'
21
+ | 'Editor'
22
+ | 'PublishingAuthor'
23
+ | 'Author'
24
+ | 'Reviewer'
25
+ | 'NonEditingAuthor'
26
+ | 'FolderVisible';
27
+
28
+ export type DeliverMeetingRequests =
29
+ | 'DelegatesAndMe'
30
+ | 'DelegatesOnly'
31
+ | 'DelegatesAndSendInformationToMe'
32
+ | 'NoForward';
33
+
34
+ export interface DelegatePermissions {
35
+ calendar?: DelegateFolderPermissionLevel;
36
+ inbox?: DelegateFolderPermissionLevel;
37
+ contacts?: DelegateFolderPermissionLevel;
38
+ tasks?: DelegateFolderPermissionLevel;
39
+ notes?: DelegateFolderPermissionLevel;
40
+ }
41
+
42
+ export interface DelegateInfo {
43
+ userId: string;
44
+ displayName?: string;
45
+ primaryEmail?: string;
46
+ permissions: DelegatePermissions;
47
+ viewPrivateItems: boolean;
48
+ deliverMeetingRequests: DeliverMeetingRequests;
49
+ }
50
+
51
+ export interface AddDelegateOptions {
52
+ token: string;
53
+ delegateEmail: string;
54
+ delegateName?: string;
55
+ permissions: DelegatePermissions;
56
+ viewPrivateItems?: boolean;
57
+ deliverMeetingRequests?: DeliverMeetingRequests;
58
+ mailbox?: string;
59
+ }
60
+
61
+ export interface UpdateDelegateOptions {
62
+ token: string;
63
+ delegateEmail: string;
64
+ permissions?: DelegatePermissions;
65
+ viewPrivateItems?: boolean;
66
+ deliverMeetingRequests?: DeliverMeetingRequests;
67
+ mailbox?: string;
68
+ }
69
+
70
+ export interface RemoveDelegateOptions {
71
+ token: string;
72
+ delegateEmail: string;
73
+ mailbox?: string;
74
+ }
75
+
76
+ // ─── Helpers ───
77
+
78
+ const FOLDER_PERMISSION_ELEMENT_MAP: Record<string, string> = {
79
+ calendar: 'CalendarFolderPermissionLevel',
80
+ inbox: 'InboxFolderPermissionLevel',
81
+ contacts: 'ContactsFolderPermissionLevel',
82
+ tasks: 'TasksFolderPermissionLevel',
83
+ notes: 'NotesFolderPermissionLevel'
84
+ };
85
+
86
+ function buildDelegatePermissionsXml(permissions: DelegatePermissions): string {
87
+ const entries = Object.entries(permissions).filter(([, level]) => level !== undefined);
88
+
89
+ if (entries.length === 0) return '';
90
+
91
+ return entries
92
+ .map(([folder, level]) => {
93
+ const elementName = FOLDER_PERMISSION_ELEMENT_MAP[folder];
94
+ if (!elementName) return '';
95
+ return `<t:${elementName}>${level}</t:${elementName}>`;
96
+ })
97
+ .join('\n ');
98
+ }
99
+
100
+ function parseDelegateInfo(block: string, globalDeliver?: DeliverMeetingRequests): DelegateInfo {
101
+ const primaryEmail = extractTag(block, 'PrimarySmtpAddress') || undefined;
102
+ const smtpAddress = extractTag(block, 'SmtpAddress') || undefined;
103
+ const userId = primaryEmail || smtpAddress || '';
104
+ const displayName = extractTag(block, 'DisplayName') || undefined;
105
+
106
+ const viewPrivateStr = extractTag(block, 'ViewPrivateItems').toLowerCase();
107
+ const viewPrivateItems = viewPrivateStr === 'true';
108
+
109
+ const deliverStr = globalDeliver || extractTag(block, 'DeliverMeetingRequests');
110
+ const deliverMeetingRequests = (deliverStr || 'DelegatesAndMe') as DeliverMeetingRequests;
111
+
112
+ // Parse per-folder permissions
113
+ const permissions: DelegatePermissions = {};
114
+ const permissionsBlock = extractBlocks(block, 'DelegatePermissions')[0] || block;
115
+
116
+ for (const [folder, elementName] of Object.entries(FOLDER_PERMISSION_ELEMENT_MAP)) {
117
+ const level = extractTag(permissionsBlock, elementName) as DelegateFolderPermissionLevel | '';
118
+ if (level) {
119
+ (permissions as Record<string, string>)[folder] = level;
120
+ }
121
+ }
122
+
123
+ return {
124
+ userId,
125
+ displayName,
126
+ primaryEmail,
127
+ permissions,
128
+ viewPrivateItems,
129
+ deliverMeetingRequests
130
+ };
131
+ }
132
+
133
+ // ─── Operations ───
134
+
135
+ /**
136
+ * Get all delegates (and their permissions) on a mailbox.
137
+ * https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/getdelegate-operation
138
+ */
139
+ export async function getDelegates(
140
+ token: string,
141
+ mailbox?: string
142
+ ): Promise<{ ok: boolean; status: number; data?: DelegateInfo[]; error?: { code: string; message: string } }> {
143
+ try {
144
+ const address = mailbox || EWS_USERNAME;
145
+ const envelope = soapEnvelope(`
146
+ <m:GetDelegate IncludePermissions="true">
147
+ <m:Mailbox>
148
+ <t:EmailAddress>${xmlEscape(address)}</t:EmailAddress>
149
+ </m:Mailbox>
150
+ <m:UserIds />
151
+ </m:GetDelegate>`);
152
+
153
+ const xml = await callEws(token, envelope, address);
154
+
155
+ const delegateBlocks = extractBlocks(xml, 'DelegateUser');
156
+ const globalDeliverStr = extractTag(xml, 'DeliverMeetingRequests');
157
+ const globalDeliver = globalDeliverStr ? (globalDeliverStr as DeliverMeetingRequests) : undefined;
158
+ const delegates = delegateBlocks.map((block) => parseDelegateInfo(block, globalDeliver));
159
+
160
+ return ewsResult(delegates);
161
+ } catch (err) {
162
+ return ewsError(err);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Add a delegate with per-folder permissions.
168
+ * https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/adddelegate-operation
169
+ */
170
+ export async function addDelegate(
171
+ options: AddDelegateOptions
172
+ ): Promise<{ ok: boolean; status: number; data?: DelegateInfo; error?: { code: string; message: string } }> {
173
+ try {
174
+ const {
175
+ token,
176
+ delegateEmail,
177
+ delegateName,
178
+ permissions,
179
+ viewPrivateItems = false,
180
+ deliverMeetingRequests = 'DelegatesAndMe',
181
+ mailbox
182
+ } = options;
183
+
184
+ const address = mailbox || EWS_USERNAME;
185
+
186
+ const delegateUserXml = `
187
+ <t:DelegateUser>
188
+ <t:UserId>
189
+ <t:PrimarySmtpAddress>${xmlEscape(delegateEmail)}</t:PrimarySmtpAddress>
190
+ ${delegateName ? `<t:DisplayName>${xmlEscape(delegateName)}</t:DisplayName>` : ''}
191
+ </t:UserId>
192
+ <t:DelegatePermissions>
193
+ ${buildDelegatePermissionsXml(permissions)}
194
+ </t:DelegatePermissions>
195
+ <t:ViewPrivateItems>${viewPrivateItems}</t:ViewPrivateItems>
196
+ </t:DelegateUser>`.trim();
197
+
198
+ const envelope = soapEnvelope(`
199
+ <m:AddDelegate>
200
+ <m:Mailbox>
201
+ <t:EmailAddress>${xmlEscape(address)}</t:EmailAddress>
202
+ </m:Mailbox>
203
+ <m:DelegateUsers>
204
+ ${delegateUserXml}
205
+ </m:DelegateUsers>
206
+ <m:DeliverMeetingRequests>${deliverMeetingRequests}</m:DeliverMeetingRequests>
207
+ </m:AddDelegate>`);
208
+
209
+ const xml = await callEws(token, envelope, address);
210
+ const delegateBlock = extractBlocks(xml, 'DelegateUser')[0] || '';
211
+ const globalDeliverStr = extractTag(xml, 'DeliverMeetingRequests');
212
+ const globalDeliver = globalDeliverStr ? (globalDeliverStr as DeliverMeetingRequests) : undefined;
213
+
214
+ return ewsResult(parseDelegateInfo(delegateBlock, globalDeliver));
215
+ } catch (err) {
216
+ return ewsError(err);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Update an existing delegate's permissions.
222
+ * https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/updatedelegate-operation
223
+ */
224
+ export async function updateDelegate(
225
+ options: UpdateDelegateOptions
226
+ ): Promise<{ ok: boolean; status: number; data?: DelegateInfo; error?: { code: string; message: string } }> {
227
+ try {
228
+ const { token, delegateEmail, permissions, viewPrivateItems, deliverMeetingRequests, mailbox } = options;
229
+
230
+ const address = mailbox || EWS_USERNAME;
231
+
232
+ // Build update elements — only include fields that are defined
233
+ const permissionsParts: string[] = [];
234
+ const delegateUserParts: string[] = [];
235
+ let deliverMeetingRequestsXml = '';
236
+
237
+ if (permissions !== undefined) {
238
+ const permXml = buildDelegatePermissionsXml(permissions);
239
+ if (permXml) {
240
+ permissionsParts.push(permXml);
241
+ }
242
+ }
243
+
244
+ if (viewPrivateItems !== undefined) {
245
+ delegateUserParts.push(`<t:ViewPrivateItems>${viewPrivateItems}</t:ViewPrivateItems>`);
246
+ }
247
+
248
+ if (deliverMeetingRequests !== undefined) {
249
+ deliverMeetingRequestsXml = `<m:DeliverMeetingRequests>${deliverMeetingRequests}</m:DeliverMeetingRequests>`;
250
+ }
251
+
252
+ if (permissionsParts.length === 0 && delegateUserParts.length === 0 && !deliverMeetingRequestsXml) {
253
+ return { ok: false, status: 400, error: { code: 'NO_UPDATES', message: 'No fields to update' } };
254
+ }
255
+
256
+ const delegatePermissionsXml =
257
+ permissionsParts.length > 0
258
+ ? `<t:DelegatePermissions>
259
+ ${permissionsParts.join('\n')}
260
+ </t:DelegatePermissions>`
261
+ : '';
262
+
263
+ const delegateUserXml = `
264
+ <t:DelegateUser>
265
+ <t:UserId>
266
+ <t:PrimarySmtpAddress>${xmlEscape(delegateEmail)}</t:PrimarySmtpAddress>
267
+ </t:UserId>
268
+ ${delegatePermissionsXml}
269
+ ${delegateUserParts.join('\n ')}
270
+ </t:DelegateUser>`.trim();
271
+
272
+ const envelope = soapEnvelope(`
273
+ <m:UpdateDelegate>
274
+ <m:Mailbox>
275
+ <t:EmailAddress>${xmlEscape(address)}</t:EmailAddress>
276
+ </m:Mailbox>
277
+ <m:DelegateUsers>
278
+ ${delegateUserXml}
279
+ </m:DelegateUsers>
280
+ ${deliverMeetingRequestsXml}
281
+ </m:UpdateDelegate>`);
282
+
283
+ const xml = await callEws(token, envelope, address);
284
+ const delegateBlock = extractBlocks(xml, 'DelegateUser')[0] || '';
285
+ const globalDeliverStr = extractTag(xml, 'DeliverMeetingRequests');
286
+ const globalDeliver = globalDeliverStr ? (globalDeliverStr as DeliverMeetingRequests) : undefined;
287
+
288
+ return ewsResult(parseDelegateInfo(delegateBlock, globalDeliver));
289
+ } catch (err) {
290
+ return ewsError(err);
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Remove a delegate from a mailbox.
296
+ * https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/removedelegate-operation
297
+ */
298
+ export async function removeDelegate(
299
+ options: RemoveDelegateOptions
300
+ ): Promise<{ ok: boolean; status: number; error?: { code: string; message: string } }> {
301
+ try {
302
+ const { token, delegateEmail, mailbox } = options;
303
+ const address = mailbox || EWS_USERNAME;
304
+
305
+ const envelope = soapEnvelope(`
306
+ <m:RemoveDelegate>
307
+ <m:Mailbox>
308
+ <t:EmailAddress>${xmlEscape(address)}</t:EmailAddress>
309
+ </m:Mailbox>
310
+ <m:UserIds>
311
+ <t:UserId>
312
+ <t:PrimarySmtpAddress>${xmlEscape(delegateEmail)}</t:PrimarySmtpAddress>
313
+ </t:UserId>
314
+ </m:UserIds>
315
+ </m:RemoveDelegate>`);
316
+
317
+ await callEws(token, envelope, address);
318
+ return { ok: true, status: 200 };
319
+ } catch (err) {
320
+ return ewsError(err);
321
+ }
322
+ }