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,532 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolveGraphAuth } from '../lib/graph-auth.js';
|
|
3
|
+
import {
|
|
4
|
+
checkinFile,
|
|
5
|
+
createOfficeCollaborationLink,
|
|
6
|
+
type DriveItem,
|
|
7
|
+
type DriveItemReference,
|
|
8
|
+
defaultDownloadPath,
|
|
9
|
+
deleteFile,
|
|
10
|
+
downloadConvertedFile,
|
|
11
|
+
downloadFile,
|
|
12
|
+
getFileAnalytics,
|
|
13
|
+
getFileMetadata,
|
|
14
|
+
listFiles,
|
|
15
|
+
listFileVersions,
|
|
16
|
+
restoreFileVersion,
|
|
17
|
+
searchFiles,
|
|
18
|
+
shareFile,
|
|
19
|
+
uploadFile,
|
|
20
|
+
uploadLargeFile
|
|
21
|
+
} from '../lib/graph-client.js';
|
|
22
|
+
import { checkReadOnly } from '../lib/utils.js';
|
|
23
|
+
|
|
24
|
+
function parseFolderRef(folder?: string): DriveItemReference | undefined {
|
|
25
|
+
if (!folder) return undefined;
|
|
26
|
+
const trimmed = folder.trim();
|
|
27
|
+
if (!trimmed) return undefined;
|
|
28
|
+
return { id: trimmed };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatBytes(bytes?: number): string {
|
|
32
|
+
if (bytes === undefined) return '-';
|
|
33
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
34
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
35
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
36
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function renderItems(items: DriveItem[]): void {
|
|
40
|
+
if (items.length === 0) {
|
|
41
|
+
console.log('No files found.');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const item of items) {
|
|
46
|
+
const kind = item.folder ? 'DIR ' : 'FILE';
|
|
47
|
+
console.log(`${kind} ${item.id}`);
|
|
48
|
+
console.log(` Name: ${item.name}`);
|
|
49
|
+
console.log(` Size: ${formatBytes(item.size)}`);
|
|
50
|
+
if (item.webUrl) console.log(` URL: ${item.webUrl}`);
|
|
51
|
+
if (item.lastModifiedDateTime) console.log(` Modified: ${item.lastModifiedDateTime}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const filesCommand = new Command('files').description('Manage OneDrive files via Microsoft Graph');
|
|
56
|
+
|
|
57
|
+
filesCommand
|
|
58
|
+
.command('list')
|
|
59
|
+
.description('List files in OneDrive root or a folder')
|
|
60
|
+
.option('--folder <id>', 'Folder item ID')
|
|
61
|
+
.option('--json', 'Output as JSON')
|
|
62
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
63
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
64
|
+
.action(async (options: { folder?: string; json?: boolean; token?: string; identity?: string }) => {
|
|
65
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
66
|
+
if (!auth.success) {
|
|
67
|
+
console.error(`Error: ${auth.error}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const result = await listFiles(auth.token!, parseFolderRef(options.folder));
|
|
72
|
+
if (!result.ok || !result.data) {
|
|
73
|
+
console.error(`Error: ${result.error?.message || 'Failed to list files'}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (options.json) {
|
|
78
|
+
console.log(JSON.stringify({ items: result.data }, null, 2));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
renderItems(result.data);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
filesCommand
|
|
86
|
+
.command('search <query>')
|
|
87
|
+
.description('Search OneDrive files')
|
|
88
|
+
.option('--json', 'Output as JSON')
|
|
89
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
90
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
91
|
+
.action(async (query: string, options: { json?: boolean; token?: string; identity?: string }) => {
|
|
92
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
93
|
+
if (!auth.success) {
|
|
94
|
+
console.error(`Error: ${auth.error}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const result = await searchFiles(auth.token!, query);
|
|
99
|
+
if (!result.ok || !result.data) {
|
|
100
|
+
console.error(`Error: ${result.error?.message || 'Failed to search files'}`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (options.json) {
|
|
105
|
+
console.log(JSON.stringify({ items: result.data }, null, 2));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
renderItems(result.data);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
filesCommand
|
|
113
|
+
.command('meta <fileId>')
|
|
114
|
+
.description('Get file metadata')
|
|
115
|
+
.option('--json', 'Output as JSON')
|
|
116
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
117
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
118
|
+
.action(async (fileId: string, options: { json?: boolean; token?: string; identity?: string }) => {
|
|
119
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
120
|
+
if (!auth.success) {
|
|
121
|
+
console.error(`Error: ${auth.error}`);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const result = await getFileMetadata(auth.token!, fileId);
|
|
126
|
+
if (!result.ok || !result.data) {
|
|
127
|
+
console.error(`Error: ${result.error?.message || 'Failed to fetch metadata'}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (options.json) {
|
|
132
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
renderItems([result.data]);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
filesCommand
|
|
140
|
+
.command('upload <path>')
|
|
141
|
+
.description('Upload a file up to 250MB')
|
|
142
|
+
.option('--folder <id>', 'Target folder item ID')
|
|
143
|
+
.option('--json', 'Output as JSON')
|
|
144
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
145
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
146
|
+
.action(
|
|
147
|
+
async (path: string, options: { folder?: string; json?: boolean; token?: string; identity?: string }, cmd: any) => {
|
|
148
|
+
checkReadOnly(cmd);
|
|
149
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
150
|
+
if (!auth.success) {
|
|
151
|
+
console.error(`Error: ${auth.error}`);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const result = await uploadFile(auth.token!, path, parseFolderRef(options.folder));
|
|
156
|
+
if (!result.ok || !result.data) {
|
|
157
|
+
console.error(`Error: ${result.error?.message || 'Upload failed'}`);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (options.json) {
|
|
162
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
console.log(`✓ Uploaded: ${result.data.name}`);
|
|
167
|
+
console.log(` ID: ${result.data.id}`);
|
|
168
|
+
if (result.data.webUrl) console.log(` URL: ${result.data.webUrl}`);
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
filesCommand
|
|
173
|
+
.command('upload-large <path>')
|
|
174
|
+
.description('Upload a file up to 4GB using a chunked upload session')
|
|
175
|
+
.option('--folder <id>', 'Target folder item ID')
|
|
176
|
+
.option('--json', 'Output as JSON')
|
|
177
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
178
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
179
|
+
.action(
|
|
180
|
+
async (path: string, options: { folder?: string; json?: boolean; token?: string; identity?: string }, cmd: any) => {
|
|
181
|
+
checkReadOnly(cmd);
|
|
182
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
183
|
+
if (!auth.success) {
|
|
184
|
+
console.error(`Error: ${auth.error}`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const result = await uploadLargeFile(auth.token!, path, parseFolderRef(options.folder));
|
|
189
|
+
if (!result.ok || !result.data) {
|
|
190
|
+
console.error(`Error: ${result.error?.message || 'Failed to upload file'}`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (options.json) {
|
|
195
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (result.data.driveItem) {
|
|
200
|
+
console.log(`✓ Uploaded: ${result.data.driveItem.name}`);
|
|
201
|
+
console.log(` ID: ${result.data.driveItem.id}`);
|
|
202
|
+
if (result.data.driveItem.webUrl) console.log(` URL: ${result.data.driveItem.webUrl}`);
|
|
203
|
+
} else {
|
|
204
|
+
console.log('✓ Large upload session created');
|
|
205
|
+
console.log(` Upload URL: ${result.data.uploadUrl}`);
|
|
206
|
+
if (result.data.expirationDateTime) console.log(` Expires: ${result.data.expirationDateTime}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
filesCommand
|
|
212
|
+
.command('download <fileId>')
|
|
213
|
+
.description('Download a file by ID')
|
|
214
|
+
.option('--out <path>', 'Output path (defaults to ~/Downloads/<name>)')
|
|
215
|
+
.option('--json', 'Output as JSON')
|
|
216
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
217
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
218
|
+
.action(async (fileId: string, options: { out?: string; json?: boolean; token?: string; identity?: string }) => {
|
|
219
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
220
|
+
if (!auth.success) {
|
|
221
|
+
console.error(`Error: ${auth.error}`);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const meta = options.out ? undefined : await getFileMetadata(auth.token!, fileId);
|
|
226
|
+
const defaultOut = meta?.ok && meta.data ? defaultDownloadPath(meta.data.name || fileId) : undefined;
|
|
227
|
+
const result = await downloadFile(auth.token!, fileId, options.out || defaultOut, meta?.ok ? meta.data : undefined);
|
|
228
|
+
if (!result.ok || !result.data) {
|
|
229
|
+
console.error(`Error: ${result.error?.message || 'Download failed'}`);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (options.json) {
|
|
234
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
console.log(`✓ Downloaded: ${result.data.item.name}`);
|
|
239
|
+
console.log(` Saved to: ${result.data.path}`);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
filesCommand
|
|
243
|
+
.command('delete <fileId>')
|
|
244
|
+
.description('Delete a file by ID')
|
|
245
|
+
.option('--json', 'Output as JSON')
|
|
246
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
247
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
248
|
+
.action(async (fileId: string, options: { json?: boolean; token?: string; identity?: string }, cmd: any) => {
|
|
249
|
+
checkReadOnly(cmd);
|
|
250
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
251
|
+
if (!auth.success) {
|
|
252
|
+
console.error(`Error: ${auth.error}`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const result = await deleteFile(auth.token!, fileId);
|
|
257
|
+
if (!result.ok) {
|
|
258
|
+
console.error(`Error: ${result.error?.message || 'Delete failed'}`);
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (options.json) {
|
|
263
|
+
console.log(JSON.stringify({ success: true, id: fileId }, null, 2));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
console.log(`✓ Deleted file: ${fileId}`);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
filesCommand
|
|
271
|
+
.command('share <fileId>')
|
|
272
|
+
.description('Create a OneDrive sharing link or Office Online collaboration handoff')
|
|
273
|
+
.option('--type <type>', 'Link type: view or edit', 'view')
|
|
274
|
+
.option('--scope <scope>', 'Link scope: org or anonymous', 'org')
|
|
275
|
+
.option('--collab', 'Create an Office Online collaboration handoff (edit/org + webUrl)')
|
|
276
|
+
.option('--lock', 'Checkout the file before creating a collaboration link (use with --collab)')
|
|
277
|
+
.option('--json', 'Output as JSON')
|
|
278
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
279
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
280
|
+
.action(
|
|
281
|
+
async (
|
|
282
|
+
fileId: string,
|
|
283
|
+
options: {
|
|
284
|
+
type?: 'view' | 'edit';
|
|
285
|
+
scope?: 'org' | 'anonymous';
|
|
286
|
+
collab?: boolean;
|
|
287
|
+
lock?: boolean;
|
|
288
|
+
json?: boolean;
|
|
289
|
+
token?: string;
|
|
290
|
+
identity?: string;
|
|
291
|
+
},
|
|
292
|
+
cmd: any
|
|
293
|
+
) => {
|
|
294
|
+
checkReadOnly(cmd);
|
|
295
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
296
|
+
if (!auth.success) {
|
|
297
|
+
console.error(`Error: ${auth.error}`);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (options.lock && !options.collab) {
|
|
302
|
+
console.error('Error: --lock is only supported together with --collab.');
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (options.collab) {
|
|
307
|
+
const result = await createOfficeCollaborationLink(auth.token!, fileId, { lock: options.lock });
|
|
308
|
+
if (!result.ok || !result.data) {
|
|
309
|
+
console.error(`Error: ${result.error?.message || 'Collaboration link creation failed'}`);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (options.json) {
|
|
314
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
console.log('✓ Office Online collaboration handoff created');
|
|
319
|
+
console.log(` File: ${result.data.item.name}`);
|
|
320
|
+
console.log(` File ID: ${result.data.item.id}`);
|
|
321
|
+
if (result.data.link.webUrl) console.log(` Share URL: ${result.data.link.webUrl}`);
|
|
322
|
+
if (result.data.collaborationUrl) console.log(` Open in Office Online: ${result.data.collaborationUrl}`);
|
|
323
|
+
console.log(' Mode: edit / organization');
|
|
324
|
+
console.log(` Lock acquired: ${result.data.lockAcquired ? 'yes' : 'no'}`);
|
|
325
|
+
console.log(' Note: real-time co-authoring happens in Office Online after the user opens the document URL.');
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const type = options.type === 'edit' ? 'edit' : 'view';
|
|
330
|
+
const scope = options.scope === 'anonymous' ? 'anonymous' : 'organization';
|
|
331
|
+
const result = await shareFile(auth.token!, fileId, type, scope);
|
|
332
|
+
if (!result.ok || !result.data) {
|
|
333
|
+
console.error(`Error: ${result.error?.message || 'Share failed'}`);
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (options.json) {
|
|
338
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
console.log(`✓ Sharing link created (${type}/${scope})`);
|
|
343
|
+
if (result.data.webUrl) console.log(` URL: ${result.data.webUrl}`);
|
|
344
|
+
if (result.data.id) console.log(` Link ID: ${result.data.id}`);
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
filesCommand
|
|
349
|
+
.command('versions <fileId>')
|
|
350
|
+
.description('List versions of a file')
|
|
351
|
+
.option('--json', 'Output as JSON')
|
|
352
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
353
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
354
|
+
.action(async (fileId: string, options: { json?: boolean; token?: string; identity?: string }) => {
|
|
355
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
356
|
+
if (!auth.success) {
|
|
357
|
+
console.error(`Error: ${auth.error}`);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const result = await listFileVersions(auth.token!, fileId);
|
|
362
|
+
if (!result.ok || !result.data) {
|
|
363
|
+
console.error(`Error: ${result.error?.message || 'Failed to list versions'}`);
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (options.json) {
|
|
368
|
+
console.log(JSON.stringify({ versions: result.data }, null, 2));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (result.data.length === 0) {
|
|
373
|
+
console.log('No versions found.');
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
for (const v of result.data) {
|
|
378
|
+
console.log(`VERSION ${v.id}`);
|
|
379
|
+
if (v.lastModifiedDateTime) console.log(` Modified: ${v.lastModifiedDateTime}`);
|
|
380
|
+
if (v.size !== undefined) console.log(` Size: ${formatBytes(v.size)}`);
|
|
381
|
+
if (v.lastModifiedBy?.user?.displayName) console.log(` By: ${v.lastModifiedBy.user.displayName}`);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
filesCommand
|
|
386
|
+
.command('restore <fileId> <versionId>')
|
|
387
|
+
.description('Restore a specific version of a file')
|
|
388
|
+
.option('--json', 'Output as JSON')
|
|
389
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
390
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
391
|
+
.action(
|
|
392
|
+
async (
|
|
393
|
+
fileId: string,
|
|
394
|
+
versionId: string,
|
|
395
|
+
options: { json?: boolean; token?: string; identity?: string },
|
|
396
|
+
cmd: any
|
|
397
|
+
) => {
|
|
398
|
+
checkReadOnly(cmd);
|
|
399
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
400
|
+
if (!auth.success) {
|
|
401
|
+
console.error(`Error: ${auth.error}`);
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const result = await restoreFileVersion(auth.token!, fileId, versionId);
|
|
406
|
+
if (!result.ok) {
|
|
407
|
+
console.error(`Error: ${result.error?.message || 'Restore failed'}`);
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (options.json) {
|
|
412
|
+
console.log(JSON.stringify({ success: true, fileId, versionId }, null, 2));
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
console.log(`✓ Restored version ${versionId} of file ${fileId}`);
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
filesCommand
|
|
421
|
+
.command('checkin <fileId>')
|
|
422
|
+
.description('Check in a previously checked-out Office document')
|
|
423
|
+
.option('--comment <comment>', 'Optional check-in comment')
|
|
424
|
+
.option('--json', 'Output as JSON')
|
|
425
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
426
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
427
|
+
.action(
|
|
428
|
+
async (
|
|
429
|
+
fileId: string,
|
|
430
|
+
options: { comment?: string; json?: boolean; token?: string; identity?: string },
|
|
431
|
+
cmd: any
|
|
432
|
+
) => {
|
|
433
|
+
checkReadOnly(cmd);
|
|
434
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
435
|
+
if (!auth.success) {
|
|
436
|
+
console.error(`Error: ${auth.error}`);
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const result = await checkinFile(auth.token!, fileId, options.comment);
|
|
441
|
+
if (!result.ok || !result.data) {
|
|
442
|
+
console.error(`Error: ${result.error?.message || 'Check-in failed'}`);
|
|
443
|
+
process.exit(1);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (options.json) {
|
|
447
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
console.log('✓ File checked in');
|
|
452
|
+
console.log(` File: ${result.data.item.name}`);
|
|
453
|
+
console.log(` File ID: ${result.data.item.id}`);
|
|
454
|
+
if (result.data.comment) console.log(` Comment: ${result.data.comment}`);
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
filesCommand
|
|
459
|
+
.command('convert <fileId>')
|
|
460
|
+
.description('Download a file converted to a specific format (default: pdf)')
|
|
461
|
+
.option('--format <format>', 'Target format (e.g. pdf, html, glb)', 'pdf')
|
|
462
|
+
.option('--out <path>', 'Output path')
|
|
463
|
+
.option('--json', 'Output as JSON')
|
|
464
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
465
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
466
|
+
.action(
|
|
467
|
+
async (
|
|
468
|
+
fileId: string,
|
|
469
|
+
options: { format: string; out?: string; json?: boolean; token?: string; identity?: string }
|
|
470
|
+
) => {
|
|
471
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
472
|
+
if (!auth.success) {
|
|
473
|
+
console.error(`Error: ${auth.error}`);
|
|
474
|
+
process.exit(1);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const result = await downloadConvertedFile(auth.token!, fileId, options.format, options.out);
|
|
478
|
+
if (!result.ok || !result.data) {
|
|
479
|
+
console.error(`Error: ${result.error?.message || 'Conversion download failed'}`);
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (options.json) {
|
|
484
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
console.log(`✓ Converted file downloaded`);
|
|
489
|
+
console.log(` Saved to: ${result.data.path}`);
|
|
490
|
+
}
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
filesCommand
|
|
494
|
+
.command('analytics <fileId>')
|
|
495
|
+
.description('Get file analytics (access/action counts)')
|
|
496
|
+
.option('--json', 'Output as JSON')
|
|
497
|
+
.option('--token <token>', 'Use a specific Graph token')
|
|
498
|
+
.option('--identity <name>', 'Graph token cache identity (default: default)')
|
|
499
|
+
.action(async (fileId: string, options: { json?: boolean; token?: string; identity?: string }) => {
|
|
500
|
+
const auth = await resolveGraphAuth({ token: options.token, identity: options.identity });
|
|
501
|
+
if (!auth.success) {
|
|
502
|
+
console.error(`Error: ${auth.error}`);
|
|
503
|
+
process.exit(1);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const result = await getFileAnalytics(auth.token!, fileId);
|
|
507
|
+
if (!result.ok || !result.data) {
|
|
508
|
+
console.error(`Error: ${result.error?.message || 'Failed to get file analytics'}`);
|
|
509
|
+
process.exit(1);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (options.json) {
|
|
513
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
console.log(`✓ Analytics for ${fileId}:`);
|
|
518
|
+
if (result.data.allTime?.access) {
|
|
519
|
+
console.log(' All Time:');
|
|
520
|
+
console.log(` Actions: ${result.data.allTime.access.actionCount || 0}`);
|
|
521
|
+
console.log(` Actors: ${result.data.allTime.access.actorCount || 0}`);
|
|
522
|
+
}
|
|
523
|
+
if (result.data.lastSevenDays?.access) {
|
|
524
|
+
console.log(' Last 7 Days:');
|
|
525
|
+
console.log(` Actions: ${result.data.lastSevenDays.access.actionCount || 0}`);
|
|
526
|
+
console.log(` Actors: ${result.data.lastSevenDays.access.actorCount || 0}`);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (!result.data.allTime?.access && !result.data.lastSevenDays?.access) {
|
|
530
|
+
console.log(' No analytics data available.');
|
|
531
|
+
}
|
|
532
|
+
});
|