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.
- package/LICENSE +22 -0
- package/README.md +916 -0
- package/package.json +50 -0
- package/src/cli.ts +100 -0
- package/src/commands/auto-reply.ts +182 -0
- package/src/commands/calendar.ts +576 -0
- package/src/commands/counter.ts +87 -0
- package/src/commands/create-event.ts +544 -0
- package/src/commands/delegates.ts +286 -0
- package/src/commands/delete-event.ts +321 -0
- package/src/commands/drafts.ts +502 -0
- package/src/commands/files.ts +532 -0
- package/src/commands/find.ts +195 -0
- package/src/commands/findtime.ts +270 -0
- package/src/commands/folders.ts +177 -0
- package/src/commands/forward-event.ts +49 -0
- package/src/commands/graph-calendar.ts +217 -0
- package/src/commands/login.ts +195 -0
- package/src/commands/mail.ts +950 -0
- package/src/commands/oof.ts +263 -0
- package/src/commands/outlook-categories.ts +173 -0
- package/src/commands/outlook-graph.ts +880 -0
- package/src/commands/planner.ts +1678 -0
- package/src/commands/respond.ts +291 -0
- package/src/commands/rooms.ts +210 -0
- package/src/commands/rules.ts +511 -0
- package/src/commands/schedule.ts +109 -0
- package/src/commands/send.ts +204 -0
- package/src/commands/serve.ts +14 -0
- package/src/commands/sharepoint.ts +179 -0
- package/src/commands/site-pages.ts +163 -0
- package/src/commands/subscribe.ts +103 -0
- package/src/commands/subscriptions.ts +29 -0
- package/src/commands/suggest.ts +155 -0
- package/src/commands/todo.ts +2092 -0
- package/src/commands/update-event.ts +608 -0
- package/src/commands/update.ts +88 -0
- package/src/commands/verify-token.ts +62 -0
- package/src/commands/whoami.ts +74 -0
- package/src/index.ts +190 -0
- package/src/lib/atomic-write.ts +20 -0
- package/src/lib/attach-link-spec.test.ts +24 -0
- package/src/lib/attach-link-spec.ts +70 -0
- package/src/lib/attachments.ts +79 -0
- package/src/lib/auth.ts +192 -0
- package/src/lib/calendar-range.test.ts +41 -0
- package/src/lib/calendar-range.ts +103 -0
- package/src/lib/dates.test.ts +74 -0
- package/src/lib/dates.ts +137 -0
- package/src/lib/delegate-client.test.ts +74 -0
- package/src/lib/delegate-client.ts +322 -0
- package/src/lib/ews-client.ts +3418 -0
- package/src/lib/git-commit.ts +4 -0
- package/src/lib/glitchtip-eligibility.ts +220 -0
- package/src/lib/glitchtip.ts +253 -0
- package/src/lib/global-env.ts +3 -0
- package/src/lib/graph-auth.ts +223 -0
- package/src/lib/graph-calendar-client.test.ts +118 -0
- package/src/lib/graph-calendar-client.ts +112 -0
- package/src/lib/graph-client.test.ts +107 -0
- package/src/lib/graph-client.ts +1058 -0
- package/src/lib/graph-constants.ts +12 -0
- package/src/lib/graph-directory.ts +116 -0
- package/src/lib/graph-event.ts +134 -0
- package/src/lib/graph-schedule.ts +173 -0
- package/src/lib/graph-subscriptions.ts +94 -0
- package/src/lib/graph-user-path.ts +13 -0
- package/src/lib/jwt-utils.ts +34 -0
- package/src/lib/markdown.test.ts +21 -0
- package/src/lib/markdown.ts +174 -0
- package/src/lib/mime-type.ts +106 -0
- package/src/lib/oof-client.test.ts +59 -0
- package/src/lib/oof-client.ts +122 -0
- package/src/lib/outlook-graph-client.test.ts +146 -0
- package/src/lib/outlook-graph-client.ts +649 -0
- package/src/lib/outlook-master-categories.ts +145 -0
- package/src/lib/package-info.ts +59 -0
- package/src/lib/places-client.ts +144 -0
- package/src/lib/planner-client.ts +1226 -0
- package/src/lib/rules-client.ts +178 -0
- package/src/lib/sharepoint-client.ts +101 -0
- package/src/lib/site-pages-client.ts +73 -0
- package/src/lib/todo-client.test.ts +298 -0
- package/src/lib/todo-client.ts +1309 -0
- package/src/lib/url-validation.ts +40 -0
- package/src/lib/utils.ts +45 -0
- package/src/lib/webhook-server.ts +51 -0
- package/src/test/auth.test.ts +104 -0
- package/src/test/cli.integration.test.ts +1083 -0
- package/src/test/ews-client.test.ts +268 -0
- package/src/test/mocks/index.ts +375 -0
- package/src/test/mocks/responses.ts +861 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolveGraphAuth } from '../lib/graph-auth.js';
|
|
3
|
+
import { type DateTimeTimeZone, getMailboxSettings, type OofStatus, setMailboxSettings } from '../lib/oof-client.js';
|
|
4
|
+
import { checkReadOnly } from '../lib/utils.js';
|
|
5
|
+
|
|
6
|
+
function formatStatus(status: OofStatus): string {
|
|
7
|
+
switch (status) {
|
|
8
|
+
case 'alwaysEnabled':
|
|
9
|
+
return 'Always On';
|
|
10
|
+
case 'scheduled':
|
|
11
|
+
return 'Scheduled';
|
|
12
|
+
case 'disabled':
|
|
13
|
+
return 'Disabled';
|
|
14
|
+
default:
|
|
15
|
+
return status;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseIsoDateTime(value: string): string {
|
|
20
|
+
const d = new Date(value);
|
|
21
|
+
if (!Number.isFinite(d.getTime())) {
|
|
22
|
+
throw new Error(`Invalid ISO datetime: ${value}`);
|
|
23
|
+
}
|
|
24
|
+
return d.toISOString();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const oofCommand = new Command('oof')
|
|
28
|
+
.description('Get or set out-of-office (automatic reply) settings via Microsoft Graph')
|
|
29
|
+
.option('--status <status>', 'OOF status: always, scheduled, disabled')
|
|
30
|
+
.option('--internal-message <text>', 'Auto-reply message for internal senders')
|
|
31
|
+
.option('--external-message <text>', 'Auto-reply message for external senders')
|
|
32
|
+
.option('--start <datetime>', 'Scheduled start datetime (ISO 8601, e.g. 2025-12-01T00:00:00)')
|
|
33
|
+
.option('--end <datetime>', 'Scheduled end datetime (ISO 8601, e.g. 2025-12-15T23:59:59)')
|
|
34
|
+
.option('--json', 'Output as JSON')
|
|
35
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
36
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
37
|
+
.option('--user <email>', 'Target user or shared mailbox (Graph delegation)')
|
|
38
|
+
.action(async (options: any, cmd: any) => {
|
|
39
|
+
const authResult = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
40
|
+
if (!authResult.success || !authResult.token) {
|
|
41
|
+
const msg = authResult.error || 'Graph authentication failed';
|
|
42
|
+
if (options.json) {
|
|
43
|
+
console.log(JSON.stringify({ error: msg }, null, 2));
|
|
44
|
+
} else {
|
|
45
|
+
console.error(`Error: ${msg}`);
|
|
46
|
+
}
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const token = authResult.token;
|
|
51
|
+
|
|
52
|
+
// --- READ mode: no write options provided ---
|
|
53
|
+
if (
|
|
54
|
+
!options.status &&
|
|
55
|
+
options.internalMessage === undefined &&
|
|
56
|
+
options.externalMessage === undefined &&
|
|
57
|
+
!options.start &&
|
|
58
|
+
!options.end
|
|
59
|
+
) {
|
|
60
|
+
const res = await getMailboxSettings(token, options.user);
|
|
61
|
+
if (!res.ok || !res.data) {
|
|
62
|
+
const msg = res.error?.message || 'Failed to retrieve mailbox settings';
|
|
63
|
+
if (options.json) {
|
|
64
|
+
console.log(JSON.stringify({ error: msg }, null, 2));
|
|
65
|
+
} else {
|
|
66
|
+
console.error(`Error: ${msg}`);
|
|
67
|
+
}
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const oof = res.data.automaticRepliesSetting;
|
|
72
|
+
if (!oof) {
|
|
73
|
+
if (options.json) {
|
|
74
|
+
console.log(JSON.stringify({ status: 'disabled', automaticRepliesSetting: null }, null, 2));
|
|
75
|
+
} else {
|
|
76
|
+
console.log('Out-of-office is disabled and no message is configured.');
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (options.json) {
|
|
82
|
+
console.log(
|
|
83
|
+
JSON.stringify(
|
|
84
|
+
{
|
|
85
|
+
status: oof.status,
|
|
86
|
+
scheduledStartDateTime: oof.scheduledStartDateTime ?? null,
|
|
87
|
+
scheduledEndDateTime: oof.scheduledEndDateTime ?? null,
|
|
88
|
+
internalReplyMessage: oof.internalReplyMessage ?? null,
|
|
89
|
+
externalReplyMessage: oof.externalReplyMessage ?? null
|
|
90
|
+
},
|
|
91
|
+
null,
|
|
92
|
+
2
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
console.log('Out-of-Office Settings:');
|
|
97
|
+
console.log(` Status: ${formatStatus(oof.status)}`);
|
|
98
|
+
if (oof.status === 'scheduled') {
|
|
99
|
+
const startStr = oof.scheduledStartDateTime
|
|
100
|
+
? typeof oof.scheduledStartDateTime === 'string'
|
|
101
|
+
? oof.scheduledStartDateTime
|
|
102
|
+
: `${oof.scheduledStartDateTime.dateTime} (${oof.scheduledStartDateTime.timeZone})`
|
|
103
|
+
: '?';
|
|
104
|
+
const endStr = oof.scheduledEndDateTime
|
|
105
|
+
? typeof oof.scheduledEndDateTime === 'string'
|
|
106
|
+
? oof.scheduledEndDateTime
|
|
107
|
+
: `${oof.scheduledEndDateTime.dateTime} (${oof.scheduledEndDateTime.timeZone})`
|
|
108
|
+
: '?';
|
|
109
|
+
console.log(` Scheduled: ${startStr} → ${endStr}`);
|
|
110
|
+
}
|
|
111
|
+
if (oof.internalReplyMessage) {
|
|
112
|
+
console.log(`\n Internal Reply:\n ${oof.internalReplyMessage}`);
|
|
113
|
+
}
|
|
114
|
+
if (oof.externalReplyMessage) {
|
|
115
|
+
console.log(`\n External Reply:\n ${oof.externalReplyMessage}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// --- WRITE mode: validate inputs ---
|
|
122
|
+
checkReadOnly(cmd);
|
|
123
|
+
const errors: string[] = [];
|
|
124
|
+
|
|
125
|
+
let status: OofStatus | undefined;
|
|
126
|
+
if (options.status) {
|
|
127
|
+
const raw = options.status.toLowerCase();
|
|
128
|
+
if (raw === 'always' || raw === 'alwaysenabled') {
|
|
129
|
+
status = 'alwaysEnabled';
|
|
130
|
+
} else if (raw === 'scheduled') {
|
|
131
|
+
status = 'scheduled';
|
|
132
|
+
} else if (raw === 'disabled') {
|
|
133
|
+
status = 'disabled';
|
|
134
|
+
} else {
|
|
135
|
+
errors.push('--status must be one of: always, scheduled, disabled');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let scheduledStartDateTime: string | DateTimeTimeZone | undefined;
|
|
140
|
+
let scheduledEndDateTime: string | DateTimeTimeZone | undefined;
|
|
141
|
+
|
|
142
|
+
if (options.start) {
|
|
143
|
+
try {
|
|
144
|
+
scheduledStartDateTime = parseIsoDateTime(options.start);
|
|
145
|
+
} catch {
|
|
146
|
+
errors.push('--start must be a valid ISO 8601 datetime (e.g. 2025-12-01T00:00:00)');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (options.end) {
|
|
151
|
+
try {
|
|
152
|
+
scheduledEndDateTime = parseIsoDateTime(options.end);
|
|
153
|
+
} catch {
|
|
154
|
+
errors.push('--end must be a valid ISO 8601 datetime (e.g. 2025-12-15T23:59:59)');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// If start/end are provided without explicit --status, default to scheduled
|
|
159
|
+
if ((scheduledStartDateTime || scheduledEndDateTime) && !status) {
|
|
160
|
+
status = 'scheduled';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (errors.length > 0) {
|
|
164
|
+
for (const e of errors) {
|
|
165
|
+
if (options.json) {
|
|
166
|
+
console.log(JSON.stringify({ error: e }, null, 2));
|
|
167
|
+
} else {
|
|
168
|
+
console.error(`Error: ${e}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Fetch existing settings if we are not explicitly overriding status,
|
|
175
|
+
// because Graph requires status in the PATCH payload or resets it to disabled.
|
|
176
|
+
let statusWasUndefined = false;
|
|
177
|
+
if (!status && (options.internalMessage !== undefined || options.externalMessage !== undefined)) {
|
|
178
|
+
statusWasUndefined = true;
|
|
179
|
+
const currentRes = await getMailboxSettings(token, options.user);
|
|
180
|
+
if (currentRes.ok && currentRes.data?.automaticRepliesSetting) {
|
|
181
|
+
status = currentRes.data.automaticRepliesSetting.status;
|
|
182
|
+
if (status === 'scheduled') {
|
|
183
|
+
scheduledStartDateTime =
|
|
184
|
+
scheduledStartDateTime ?? currentRes.data.automaticRepliesSetting.scheduledStartDateTime;
|
|
185
|
+
scheduledEndDateTime = scheduledEndDateTime ?? currentRes.data.automaticRepliesSetting.scheduledEndDateTime;
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
status = 'disabled'; // fallback if we couldn't fetch
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// --- Apply updates ---
|
|
193
|
+
const patchResult = await setMailboxSettings(
|
|
194
|
+
token,
|
|
195
|
+
{
|
|
196
|
+
status,
|
|
197
|
+
internalReplyMessage: options.internalMessage,
|
|
198
|
+
externalReplyMessage: options.externalMessage,
|
|
199
|
+
scheduledStartDateTime,
|
|
200
|
+
scheduledEndDateTime
|
|
201
|
+
},
|
|
202
|
+
options.user
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
if (!patchResult.ok) {
|
|
206
|
+
const msg = patchResult.error?.message || 'Failed to update mailbox settings';
|
|
207
|
+
if (options.json) {
|
|
208
|
+
console.log(JSON.stringify({ error: msg }, null, 2));
|
|
209
|
+
} else {
|
|
210
|
+
console.error(`Error: ${msg}`);
|
|
211
|
+
}
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (options.json) {
|
|
216
|
+
const normalizeDateTime = (dt: string | DateTimeTimeZone | undefined): string | null => {
|
|
217
|
+
if (!dt) return null;
|
|
218
|
+
if (typeof dt === 'string') return dt;
|
|
219
|
+
return dt.dateTime;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const responseBody: any = {
|
|
223
|
+
status: 'success',
|
|
224
|
+
automaticRepliesSetting: {
|
|
225
|
+
scheduledStartDateTime: normalizeDateTime(scheduledStartDateTime),
|
|
226
|
+
scheduledEndDateTime: normalizeDateTime(scheduledEndDateTime),
|
|
227
|
+
internalReplyMessage: options.internalMessage ?? null,
|
|
228
|
+
externalReplyMessage: options.externalMessage ?? null
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
if (status !== undefined) {
|
|
232
|
+
responseBody.automaticRepliesSetting.status = status;
|
|
233
|
+
}
|
|
234
|
+
console.log(JSON.stringify(responseBody, null, 2));
|
|
235
|
+
} else {
|
|
236
|
+
console.log('Out-of-office settings updated.');
|
|
237
|
+
if (statusWasUndefined && status !== undefined) {
|
|
238
|
+
console.log(` Status: ${formatStatus(status)} (unchanged)`);
|
|
239
|
+
} else if (status !== undefined) {
|
|
240
|
+
console.log(` Status: ${formatStatus(status)}`);
|
|
241
|
+
} else {
|
|
242
|
+
console.log(` Status: (unchanged)`);
|
|
243
|
+
}
|
|
244
|
+
if (status === 'scheduled' || (status === undefined && (scheduledStartDateTime || scheduledEndDateTime))) {
|
|
245
|
+
if (scheduledStartDateTime) {
|
|
246
|
+
const startStr =
|
|
247
|
+
typeof scheduledStartDateTime === 'string'
|
|
248
|
+
? scheduledStartDateTime
|
|
249
|
+
: `${scheduledStartDateTime.dateTime} (${scheduledStartDateTime.timeZone})`;
|
|
250
|
+
console.log(` Start: ${startStr}`);
|
|
251
|
+
}
|
|
252
|
+
if (scheduledEndDateTime) {
|
|
253
|
+
const endStr =
|
|
254
|
+
typeof scheduledEndDateTime === 'string'
|
|
255
|
+
? scheduledEndDateTime
|
|
256
|
+
: `${scheduledEndDateTime.dateTime} (${scheduledEndDateTime.timeZone})`;
|
|
257
|
+
console.log(` End: ${endStr}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (options.internalMessage) console.log(` Internal message: ${options.internalMessage}`);
|
|
261
|
+
if (options.externalMessage) console.log(` External message: ${options.externalMessage}`);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolveGraphAuth } from '../lib/graph-auth.js';
|
|
3
|
+
import {
|
|
4
|
+
createOutlookMasterCategory,
|
|
5
|
+
deleteOutlookMasterCategory,
|
|
6
|
+
isValidOutlookCategoryColor,
|
|
7
|
+
listOutlookMasterCategories,
|
|
8
|
+
OUTLOOK_CATEGORY_COLOR_PRESETS,
|
|
9
|
+
updateOutlookMasterCategory
|
|
10
|
+
} from '../lib/outlook-master-categories.js';
|
|
11
|
+
import { checkReadOnly } from '../lib/utils.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Mailbox master category list (display names + preset colors). Same names are used on
|
|
15
|
+
* mail/calendar items when you set categories via EWS (`--category`); To Do uses separate string categories.
|
|
16
|
+
*/
|
|
17
|
+
export const outlookCategoriesCommand = new Command('outlook-categories').description(
|
|
18
|
+
'List, create, update, or delete Outlook mailbox master categories (names and colors; Graph)'
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
outlookCategoriesCommand
|
|
22
|
+
.command('list')
|
|
23
|
+
.description('Show master categories for the signed-in mailbox')
|
|
24
|
+
.option('--json', 'Output as JSON')
|
|
25
|
+
.option('--token <token>', 'Use a specific token')
|
|
26
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
27
|
+
.option('--user <email>', 'Target user (Graph delegation)')
|
|
28
|
+
.action(async (opts: { json?: boolean; token?: string; identity?: string; user?: string }) => {
|
|
29
|
+
const auth = await resolveGraphAuth({ token: opts.token, identity: opts.identity });
|
|
30
|
+
if (!auth.success) {
|
|
31
|
+
console.error(`Auth error: ${auth.error}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const result = await listOutlookMasterCategories(auth.token!, opts.user);
|
|
35
|
+
if (!result.ok || !result.data) {
|
|
36
|
+
console.error(`Error: ${result.error?.message}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
if (opts.json) {
|
|
40
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (result.data.length === 0) {
|
|
44
|
+
console.log('No master categories defined.');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log(`\nOutlook master categories (${result.data.length}):\n`);
|
|
48
|
+
for (const c of result.data) {
|
|
49
|
+
console.log(` ${c.displayName}`);
|
|
50
|
+
console.log(` color: ${c.color} id: ${c.id}`);
|
|
51
|
+
}
|
|
52
|
+
console.log('');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
outlookCategoriesCommand
|
|
56
|
+
.command('create')
|
|
57
|
+
.description('Add a category to the mailbox master list (requires MailboxSettings.ReadWrite)')
|
|
58
|
+
.requiredOption('--name <text>', 'Display name (unique in the list)')
|
|
59
|
+
.requiredOption('--color <preset>', `Color: ${OUTLOOK_CATEGORY_COLOR_PRESETS.join(', ')}`)
|
|
60
|
+
.option('--json', 'Output as JSON')
|
|
61
|
+
.option('--token <token>', 'Use a specific token')
|
|
62
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
63
|
+
.option('--user <email>', 'Target user (Graph delegation)')
|
|
64
|
+
.action(
|
|
65
|
+
async (
|
|
66
|
+
opts: { name: string; color: string; json?: boolean; token?: string; identity?: string; user?: string },
|
|
67
|
+
cmd: any
|
|
68
|
+
) => {
|
|
69
|
+
checkReadOnly(cmd);
|
|
70
|
+
const color = opts.color.trim();
|
|
71
|
+
if (!isValidOutlookCategoryColor(color)) {
|
|
72
|
+
console.error(`Invalid --color "${color}". Use one of: ${OUTLOOK_CATEGORY_COLOR_PRESETS.join(', ')}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
const auth = await resolveGraphAuth({ token: opts.token, identity: opts.identity });
|
|
76
|
+
if (!auth.success) {
|
|
77
|
+
console.error(`Auth error: ${auth.error}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
const result = await createOutlookMasterCategory(auth.token!, opts.name, color, opts.user);
|
|
81
|
+
if (!result.ok || !result.data) {
|
|
82
|
+
console.error(`Error: ${result.error?.message}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
if (opts.json) console.log(JSON.stringify(result.data, null, 2));
|
|
86
|
+
else {
|
|
87
|
+
console.log(`\nCreated: ${result.data.displayName} (${result.data.color})`);
|
|
88
|
+
console.log(` id: ${result.data.id}\n`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
outlookCategoriesCommand
|
|
94
|
+
.command('update')
|
|
95
|
+
.description('Rename or recolor a master category by id (from list)')
|
|
96
|
+
.requiredOption('--id <id>', 'Category id')
|
|
97
|
+
.option('--name <text>', 'New display name')
|
|
98
|
+
.option('--color <preset>', `New color: ${OUTLOOK_CATEGORY_COLOR_PRESETS.slice(0, 5).join(', ')}, ... preset24`)
|
|
99
|
+
.option('--json', 'Output as JSON')
|
|
100
|
+
.option('--token <token>', 'Use a specific token')
|
|
101
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
102
|
+
.option('--user <email>', 'Target user (Graph delegation)')
|
|
103
|
+
.action(
|
|
104
|
+
async (
|
|
105
|
+
opts: {
|
|
106
|
+
id: string;
|
|
107
|
+
name?: string;
|
|
108
|
+
color?: string;
|
|
109
|
+
json?: boolean;
|
|
110
|
+
token?: string;
|
|
111
|
+
identity?: string;
|
|
112
|
+
user?: string;
|
|
113
|
+
},
|
|
114
|
+
cmd: any
|
|
115
|
+
) => {
|
|
116
|
+
checkReadOnly(cmd);
|
|
117
|
+
if (!opts.name && !opts.color) {
|
|
118
|
+
console.error('Error: specify --name and/or --color');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
if (opts.color !== undefined && !isValidOutlookCategoryColor(opts.color.trim())) {
|
|
122
|
+
console.error(`Invalid --color. Use one of: ${OUTLOOK_CATEGORY_COLOR_PRESETS.join(', ')}`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
const auth = await resolveGraphAuth({ token: opts.token, identity: opts.identity });
|
|
126
|
+
if (!auth.success) {
|
|
127
|
+
console.error(`Auth error: ${auth.error}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
const result = await updateOutlookMasterCategory(
|
|
131
|
+
auth.token!,
|
|
132
|
+
opts.id.trim(),
|
|
133
|
+
{
|
|
134
|
+
displayName: opts.name,
|
|
135
|
+
color: opts.color?.trim()
|
|
136
|
+
},
|
|
137
|
+
opts.user
|
|
138
|
+
);
|
|
139
|
+
if (!result.ok || !result.data) {
|
|
140
|
+
console.error(`Error: ${result.error?.message}`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
if (opts.json) console.log(JSON.stringify(result.data, null, 2));
|
|
144
|
+
else {
|
|
145
|
+
console.log(`\nUpdated: ${result.data.displayName} (${result.data.color})`);
|
|
146
|
+
console.log(` id: ${result.data.id}\n`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
outlookCategoriesCommand
|
|
152
|
+
.command('delete')
|
|
153
|
+
.description('Remove a category from the master list (does not remove labels from existing items)')
|
|
154
|
+
.requiredOption('--id <id>', 'Category id')
|
|
155
|
+
.option('--json', 'Output as JSON')
|
|
156
|
+
.option('--token <token>', 'Use a specific token')
|
|
157
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
158
|
+
.option('--user <email>', 'Target user (Graph delegation)')
|
|
159
|
+
.action(async (opts: { id: string; json?: boolean; token?: string; identity?: string; user?: string }, cmd: any) => {
|
|
160
|
+
checkReadOnly(cmd);
|
|
161
|
+
const auth = await resolveGraphAuth({ token: opts.token, identity: opts.identity });
|
|
162
|
+
if (!auth.success) {
|
|
163
|
+
console.error(`Auth error: ${auth.error}`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
const result = await deleteOutlookMasterCategory(auth.token!, opts.id.trim(), opts.user);
|
|
167
|
+
if (!result.ok) {
|
|
168
|
+
console.error(`Error: ${result.error?.message}`);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
if (opts.json) console.log(JSON.stringify({ success: true }, null, 2));
|
|
172
|
+
else console.log(`\nDeleted category id: ${opts.id.trim()}\n`);
|
|
173
|
+
});
|