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
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "m365-agent-cli",
3
+ "version": "1.2.0",
4
+ "description": "CLI for Microsoft 365/EWS using OAuth2 refresh token authentication",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/markus-lassfolk/m365-agent-cli.git"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/markus-lassfolk/m365-agent-cli/issues"
11
+ },
12
+ "type": "module",
13
+ "main": "src/index.ts",
14
+ "bin": {
15
+ "m365-agent-cli": "src/cli.ts"
16
+ },
17
+ "scripts": {
18
+ "start": "bun run src/cli.ts",
19
+ "typecheck": "tsc --noEmit",
20
+ "lint": "biome lint --max-diagnostics=none --diagnostic-level=error .",
21
+ "lint:fix": "biome lint --write --unsafe --diagnostic-level=error .",
22
+ "format": "biome format --write .",
23
+ "format:check": "biome format --max-diagnostics=none .",
24
+ "biome:check": "biome check --max-diagnostics=none .",
25
+ "embed-sha": "node scripts/embed-git-sha.mjs",
26
+ "sync-skill": "node scripts/sync-skill-version.mjs",
27
+ "test-glitchtip-send": "node scripts/test-glitchtip-send.mjs",
28
+ "knip": "knip",
29
+ "verify:coverage": "node scripts/check-coverage.mjs",
30
+ "test": "bun test",
31
+ "test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-reporter=text"
32
+ },
33
+ "files": [
34
+ "src",
35
+ "package.json",
36
+ "LICENSE"
37
+ ],
38
+ "dependencies": {
39
+ "@sentry/node": "^8.55.1",
40
+ "commander": "^12.0.0",
41
+ "semver": "^7.7.1"
42
+ },
43
+ "devDependencies": {
44
+ "@biomejs/biome": "2.4.10",
45
+ "@types/bun": "latest",
46
+ "@types/semver": "^7.5.8",
47
+ "knip": "^5.46.0",
48
+ "typescript": "^5.0.0"
49
+ }
50
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env bun
2
+ import './lib/global-env.js';
3
+ import { Command } from 'commander';
4
+ import { autoReplyCommand } from './commands/auto-reply.js';
5
+ import { calendarCommand } from './commands/calendar.js';
6
+ import { counterCommand } from './commands/counter.js';
7
+ import { createEventCommand } from './commands/create-event.js';
8
+ import { delegatesCommand } from './commands/delegates.js';
9
+ import { deleteEventCommand } from './commands/delete-event.js';
10
+ import { draftsCommand } from './commands/drafts.js';
11
+ import { filesCommand } from './commands/files.js';
12
+ import { findCommand } from './commands/find.js';
13
+ import { findtimeCommand } from './commands/findtime.js';
14
+ import { foldersCommand } from './commands/folders.js';
15
+ import { forwardEventCommand } from './commands/forward-event.js';
16
+ import { graphCalendarCommand } from './commands/graph-calendar.js';
17
+ import { loginCommand } from './commands/login.js';
18
+ import { mailCommand } from './commands/mail.js';
19
+ import { oofCommand } from './commands/oof.js';
20
+ import { outlookCategoriesCommand } from './commands/outlook-categories.js';
21
+ import { outlookGraphCommand } from './commands/outlook-graph.js';
22
+ import { plannerCommand } from './commands/planner.js';
23
+ import { respondCommand } from './commands/respond.js';
24
+ import { roomsCommand } from './commands/rooms.js';
25
+ import { rulesCommand } from './commands/rules.js';
26
+ import { scheduleCommand } from './commands/schedule.js';
27
+ import { sendCommand } from './commands/send.js';
28
+ import { serveCommand } from './commands/serve.js';
29
+ import { sharepointCommand } from './commands/sharepoint.js';
30
+ import { sitePagesCommand } from './commands/site-pages.js';
31
+ import { subscribeCommand } from './commands/subscribe.js';
32
+ import { subscriptionsCommand } from './commands/subscriptions.js';
33
+ import { suggestCommand } from './commands/suggest.js';
34
+ import { todoCommand } from './commands/todo.js';
35
+ import { updateCommand } from './commands/update.js';
36
+ import { updateEventCommand } from './commands/update-event.js';
37
+ import { verifyTokenCommand } from './commands/verify-token.js';
38
+ import { whoamiCommand } from './commands/whoami.js';
39
+ import { captureCliException, flushGlitchTip, initGlitchTip } from './lib/glitchtip.js';
40
+ import { getPackageVersionSync } from './lib/package-info.js';
41
+
42
+ const program = new Command();
43
+
44
+ program.name('m365-agent-cli').description('CLI for Microsoft 365/EWS').version(getPackageVersionSync());
45
+
46
+ program.option('--read-only', 'Run in read-only mode, blocking any mutating operations');
47
+
48
+ program.addCommand(whoamiCommand);
49
+ program.addCommand(updateCommand);
50
+ program.addCommand(loginCommand);
51
+ program.addCommand(sitePagesCommand);
52
+ program.addCommand(verifyTokenCommand);
53
+ program.addCommand(autoReplyCommand);
54
+ program.addCommand(calendarCommand);
55
+ program.addCommand(findtimeCommand);
56
+ program.addCommand(respondCommand);
57
+ program.addCommand(createEventCommand);
58
+ program.addCommand(deleteEventCommand);
59
+ program.addCommand(findCommand);
60
+ program.addCommand(updateEventCommand);
61
+ program.addCommand(mailCommand);
62
+ program.addCommand(foldersCommand);
63
+ program.addCommand(sendCommand);
64
+ program.addCommand(draftsCommand);
65
+ program.addCommand(filesCommand);
66
+ program.addCommand(forwardEventCommand);
67
+ program.addCommand(counterCommand);
68
+ program.addCommand(scheduleCommand);
69
+ program.addCommand(suggestCommand);
70
+ program.addCommand(subscribeCommand);
71
+ program.addCommand(subscriptionsCommand);
72
+ program.addCommand(serveCommand);
73
+ program.addCommand(roomsCommand);
74
+ program.addCommand(oofCommand);
75
+ program.addCommand(rulesCommand);
76
+ program.addCommand(delegatesCommand);
77
+ program.addCommand(todoCommand);
78
+
79
+ program.addCommand(outlookCategoriesCommand);
80
+ program.addCommand(outlookGraphCommand);
81
+ program.addCommand(graphCalendarCommand);
82
+
83
+ program.addCommand(plannerCommand);
84
+
85
+ program.addCommand(sharepointCommand);
86
+
87
+ (async () => {
88
+ await initGlitchTip();
89
+ try {
90
+ await program.parseAsync(process.argv);
91
+ } catch (err) {
92
+ captureCliException(err);
93
+ await flushGlitchTip(2000);
94
+ process.exit(1);
95
+ }
96
+ })().catch(async (err) => {
97
+ captureCliException(err);
98
+ await flushGlitchTip(2000);
99
+ process.exit(1);
100
+ });
@@ -0,0 +1,182 @@
1
+ import { Command } from 'commander';
2
+ import { resolveAuth } from '../lib/auth.js';
3
+ import { getAutoReplyRule, setAutoReplyRule } from '../lib/ews-client.js';
4
+ import { checkReadOnly } from '../lib/utils.js';
5
+
6
+ export const autoReplyCommand = new Command('auto-reply')
7
+ .description('Manage server-side out-of-office (OOF) auto-reply templates via EWS Inbox Rules')
8
+ .option('--message <text>', 'The message text for the auto-reply template')
9
+ .option('--enable', 'Enable the auto-reply rule')
10
+ .option('--disable', 'Disable the auto-reply rule')
11
+ .option('--start <datetime>', 'Start datetime (ISO string)')
12
+ .option('--end <datetime>', 'End datetime (ISO string)')
13
+ .option('--mailbox <email>', 'Target mailbox (if different from authenticated user)')
14
+ .option('--json', 'Output as JSON')
15
+ .option('--token <token>', 'Use a specific EWS token')
16
+ .option('--identity <name>', 'Use a specific authentication identity (default: default)')
17
+ .action(async (options, cmd: any) => {
18
+ checkReadOnly(cmd);
19
+ try {
20
+ const auth = await resolveAuth({ token: options.token, identity: options.identity });
21
+ if (!auth.success || !auth.token) {
22
+ const msg = auth.error || 'Authentication failed';
23
+ if (options.json) {
24
+ console.log(JSON.stringify({ error: msg }, null, 2));
25
+ } else {
26
+ console.error(`Error: ${msg}`);
27
+ }
28
+ process.exit(1);
29
+ }
30
+
31
+ // Validate mutually exclusive --enable and --disable
32
+ if (options.enable && options.disable) {
33
+ const msg = 'Error: --enable and --disable cannot be used together.';
34
+ if (options.json) {
35
+ console.log(JSON.stringify({ error: msg }, null, 2));
36
+ } else {
37
+ console.error(msg);
38
+ }
39
+ process.exit(1);
40
+ }
41
+
42
+ // Validate date inputs
43
+ let start: Date | undefined;
44
+ let end: Date | undefined;
45
+ if (options.start) {
46
+ start = new Date(options.start);
47
+ if (!Number.isFinite(start.getTime())) {
48
+ const msg = 'Error: --start must be a valid ISO datetime string.';
49
+ if (options.json) {
50
+ console.log(JSON.stringify({ error: msg }, null, 2));
51
+ } else {
52
+ console.error(msg);
53
+ }
54
+ process.exit(1);
55
+ }
56
+ }
57
+ if (options.end) {
58
+ end = new Date(options.end);
59
+ if (!Number.isFinite(end.getTime())) {
60
+ const msg = 'Error: --end must be a valid ISO datetime string.';
61
+ if (options.json) {
62
+ console.log(JSON.stringify({ error: msg }, null, 2));
63
+ } else {
64
+ console.error(msg);
65
+ }
66
+ process.exit(1);
67
+ }
68
+ }
69
+
70
+ if (options.message || options.enable || options.disable || options.start || options.end) {
71
+ let enabled = true;
72
+ let messageText = options.message;
73
+ const currentRuleRes = await getAutoReplyRule(auth.token, options.mailbox);
74
+
75
+ // Handle error case separately from "not found"
76
+ if (!currentRuleRes.ok) {
77
+ const msg = `Failed to get current auto-reply rule: ${currentRuleRes.status} ${currentRuleRes.error?.message || ''}`;
78
+ if (options.json) {
79
+ console.log(JSON.stringify({ error: msg }, null, 2));
80
+ } else {
81
+ console.error(msg);
82
+ }
83
+ process.exit(1);
84
+ }
85
+
86
+ if (currentRuleRes.data) {
87
+ if (options.enable === undefined && options.disable === undefined) {
88
+ enabled = currentRuleRes.data.enabled;
89
+ } else {
90
+ enabled = !!options.enable;
91
+ }
92
+ if (!messageText) messageText = currentRuleRes.data.messageText;
93
+ if (!options.start && currentRuleRes.data.startTime) start = currentRuleRes.data.startTime;
94
+ if (!options.end && currentRuleRes.data.endTime) end = currentRuleRes.data.endTime;
95
+ } else {
96
+ if (options.disable) enabled = false;
97
+ if (!messageText) {
98
+ const msg = 'Error: --message is required when creating a new auto-reply rule.';
99
+ if (options.json) {
100
+ console.log(JSON.stringify({ error: msg }, null, 2));
101
+ } else {
102
+ console.error(msg);
103
+ }
104
+ process.exit(1);
105
+ }
106
+ }
107
+
108
+ if (options.json) {
109
+ console.log(JSON.stringify({ status: 'updating', enabled }, null, 2));
110
+ } else {
111
+ console.log(`Setting auto-reply rule (enabled: ${enabled})...`);
112
+ }
113
+ const result = await setAutoReplyRule(auth.token, messageText!, enabled, start, end, options.mailbox);
114
+
115
+ if (!result.ok) {
116
+ const msg = `Failed to set auto-reply rule: ${result.status} ${(result.error as any)?.message || result.error}`;
117
+ if (options.json) {
118
+ console.log(JSON.stringify({ error: msg }, null, 2));
119
+ } else {
120
+ console.error(msg);
121
+ }
122
+ process.exit(1);
123
+ }
124
+
125
+ if (options.json) {
126
+ console.log(JSON.stringify({ status: 'success' }, null, 2));
127
+ } else {
128
+ console.log('Auto-reply rule successfully set.');
129
+ }
130
+ } else {
131
+ const result = await getAutoReplyRule(auth.token, options.mailbox);
132
+
133
+ if (!result.ok) {
134
+ const msg = `Failed to get auto-reply rule: ${result.status} ${(result.error as any)?.message || result.error}`;
135
+ if (options.json) {
136
+ console.log(JSON.stringify({ error: msg }, null, 2));
137
+ } else {
138
+ console.error(msg);
139
+ }
140
+ process.exit(1);
141
+ }
142
+
143
+ if (!result.data) {
144
+ if (options.json) {
145
+ console.log(JSON.stringify({ exists: false }, null, 2));
146
+ } else {
147
+ console.log('No auto-reply template rule found.');
148
+ }
149
+ } else {
150
+ if (options.json) {
151
+ console.log(
152
+ JSON.stringify(
153
+ {
154
+ exists: true,
155
+ enabled: result.data.enabled,
156
+ startTime: result.data.startTime ? result.data.startTime.toISOString() : null,
157
+ endTime: result.data.endTime ? result.data.endTime.toISOString() : null,
158
+ messageText: result.data.messageText
159
+ },
160
+ null,
161
+ 2
162
+ )
163
+ );
164
+ } else {
165
+ console.log('Auto-Reply Template Rule:');
166
+ console.log(` Enabled: ${result.data.enabled}`);
167
+ console.log(` Start Time: ${result.data.startTime ? result.data.startTime.toISOString() : 'None'}`);
168
+ console.log(` End Time: ${result.data.endTime ? result.data.endTime.toISOString() : 'None'}`);
169
+ console.log(`\nMessage:\n${result.data.messageText}`);
170
+ }
171
+ }
172
+ }
173
+ } catch (err) {
174
+ const msg = err instanceof Error ? err.message : 'An unexpected error occurred';
175
+ if (options.json) {
176
+ console.log(JSON.stringify({ error: msg }, null, 2));
177
+ } else {
178
+ console.error('An unexpected error occurred:', msg);
179
+ }
180
+ process.exit(1);
181
+ }
182
+ });