@lifestreamdynamics/vault-cli 1.1.0 → 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.
@@ -0,0 +1,117 @@
1
+ import chalk from 'chalk';
2
+ import { getClientAsync } from '../client.js';
3
+ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
4
+ import { createOutput, handleError } from '../utils/output.js';
5
+ export function registerPublishVaultCommands(program) {
6
+ const pv = program.command('publish-vault').description('Manage whole-vault publishing (public sites)');
7
+ addGlobalFlags(pv.command('list')
8
+ .description('List your published vault sites'))
9
+ .action(async (_opts) => {
10
+ const flags = resolveFlags(_opts);
11
+ const out = createOutput(flags);
12
+ out.startSpinner('Fetching published vaults...');
13
+ try {
14
+ const client = await getClientAsync();
15
+ const list = await client.publishVault.listMine();
16
+ out.stopSpinner();
17
+ out.list(list.map(pv => ({ slug: pv.slug, title: pv.title, isPublished: pv.isPublished ? 'yes' : 'no', createdAt: pv.createdAt })), {
18
+ emptyMessage: 'No published vault sites found.',
19
+ columns: [
20
+ { key: 'slug', header: 'Slug' },
21
+ { key: 'title', header: 'Title' },
22
+ { key: 'isPublished', header: 'Published' },
23
+ { key: 'createdAt', header: 'Created' },
24
+ ],
25
+ textFn: (pv) => `${chalk.cyan(String(pv.slug))} — ${String(pv.title)} [${pv.isPublished === 'yes' ? chalk.green('live') : chalk.dim('draft')}]`,
26
+ });
27
+ }
28
+ catch (err) {
29
+ handleError(out, err, 'Failed to fetch published vaults');
30
+ }
31
+ });
32
+ addGlobalFlags(pv.command('publish')
33
+ .description('Publish a vault as a public site')
34
+ .argument('<vaultId>', 'Vault ID')
35
+ .requiredOption('--slug <slug>', 'URL slug for the site')
36
+ .requiredOption('--title <title>', 'Site title')
37
+ .option('--description <desc>', 'Site description')
38
+ .option('--show-sidebar', 'Show sidebar navigation')
39
+ .option('--enable-search', 'Enable search on the site')
40
+ .option('--theme <theme>', 'Site theme')
41
+ .option('--domain <domainId>', 'Custom domain ID'))
42
+ .action(async (vaultId, _opts) => {
43
+ const flags = resolveFlags(_opts);
44
+ const out = createOutput(flags);
45
+ out.startSpinner('Publishing vault...');
46
+ try {
47
+ const client = await getClientAsync();
48
+ const published = await client.publishVault.publish(vaultId, {
49
+ slug: _opts.slug,
50
+ title: _opts.title,
51
+ description: _opts.description,
52
+ showSidebar: _opts.showSidebar === true,
53
+ enableSearch: _opts.enableSearch === true,
54
+ theme: _opts.theme,
55
+ customDomainId: _opts.domain,
56
+ });
57
+ out.success(`Vault published at /${published.slug}`, { id: published.id, slug: published.slug, title: published.title });
58
+ }
59
+ catch (err) {
60
+ handleError(out, err, 'Failed to publish vault');
61
+ }
62
+ });
63
+ addGlobalFlags(pv.command('update')
64
+ .description('Update a published vault site')
65
+ .argument('<vaultId>', 'Vault ID')
66
+ .option('--slug <slug>', 'URL slug')
67
+ .option('--title <title>', 'Site title')
68
+ .option('--description <desc>', 'Site description')
69
+ .option('--show-sidebar', 'Show sidebar')
70
+ .option('--enable-search', 'Enable search')
71
+ .option('--theme <theme>', 'Site theme')
72
+ .option('--domain <domainId>', 'Custom domain ID'))
73
+ .action(async (vaultId, _opts) => {
74
+ const flags = resolveFlags(_opts);
75
+ const out = createOutput(flags);
76
+ out.startSpinner('Updating published vault...');
77
+ try {
78
+ const client = await getClientAsync();
79
+ const params = {};
80
+ if (_opts.slug)
81
+ params.slug = _opts.slug;
82
+ if (_opts.title)
83
+ params.title = _opts.title;
84
+ if (_opts.description)
85
+ params.description = _opts.description;
86
+ if (_opts.showSidebar !== undefined)
87
+ params.showSidebar = _opts.showSidebar === true;
88
+ if (_opts.enableSearch !== undefined)
89
+ params.enableSearch = _opts.enableSearch === true;
90
+ if (_opts.theme)
91
+ params.theme = _opts.theme;
92
+ if (_opts.domain)
93
+ params.customDomainId = _opts.domain;
94
+ const published = await client.publishVault.update(vaultId, params);
95
+ out.success('Published vault updated', { id: published.id, slug: published.slug });
96
+ }
97
+ catch (err) {
98
+ handleError(out, err, 'Failed to update published vault');
99
+ }
100
+ });
101
+ addGlobalFlags(pv.command('unpublish')
102
+ .description('Unpublish a vault site')
103
+ .argument('<vaultId>', 'Vault ID'))
104
+ .action(async (vaultId, _opts) => {
105
+ const flags = resolveFlags(_opts);
106
+ const out = createOutput(flags);
107
+ out.startSpinner('Unpublishing vault...');
108
+ try {
109
+ const client = await getClientAsync();
110
+ await client.publishVault.unpublish(vaultId);
111
+ out.success('Vault unpublished', { vaultId });
112
+ }
113
+ catch (err) {
114
+ handleError(out, err, 'Failed to unpublish vault');
115
+ }
116
+ });
117
+ }
@@ -135,4 +135,55 @@ export function registerPublishCommands(program) {
135
135
  handleError(out, err, 'Failed to unpublish document');
136
136
  }
137
137
  });
138
+ const subdomain = publish.command('subdomain').description('Subdomain management for published vaults');
139
+ addGlobalFlags(subdomain.command('get')
140
+ .description('Get the subdomain for a published vault')
141
+ .argument('<vaultId>', 'Vault ID'))
142
+ .action(async (vaultId, _opts) => {
143
+ const flags = resolveFlags(_opts);
144
+ const out = createOutput(flags);
145
+ out.startSpinner('Fetching subdomain...');
146
+ try {
147
+ const client = await getClientAsync();
148
+ const result = await client.publish.getSubdomain(vaultId);
149
+ out.stopSpinner();
150
+ out.record({ subdomain: result.subdomain });
151
+ }
152
+ catch (err) {
153
+ handleError(out, err, 'Failed to fetch subdomain');
154
+ }
155
+ });
156
+ addGlobalFlags(subdomain.command('set')
157
+ .description('Set a subdomain for a published vault')
158
+ .argument('<vaultId>', 'Vault ID')
159
+ .argument('<subdomain>', 'Subdomain to assign'))
160
+ .action(async (vaultId, subdomainArg, _opts) => {
161
+ const flags = resolveFlags(_opts);
162
+ const out = createOutput(flags);
163
+ out.startSpinner('Setting subdomain...');
164
+ try {
165
+ const client = await getClientAsync();
166
+ const result = await client.publish.setSubdomain(vaultId, subdomainArg);
167
+ out.success(`Subdomain set: ${result.subdomain}`, { subdomain: result.subdomain });
168
+ }
169
+ catch (err) {
170
+ handleError(out, err, 'Failed to set subdomain');
171
+ }
172
+ });
173
+ addGlobalFlags(subdomain.command('delete')
174
+ .description('Remove the subdomain for a published vault')
175
+ .argument('<vaultId>', 'Vault ID'))
176
+ .action(async (vaultId, _opts) => {
177
+ const flags = resolveFlags(_opts);
178
+ const out = createOutput(flags);
179
+ out.startSpinner('Removing subdomain...');
180
+ try {
181
+ const client = await getClientAsync();
182
+ const result = await client.publish.deleteSubdomain(vaultId);
183
+ out.success(result.message, { message: result.message });
184
+ }
185
+ catch (err) {
186
+ handleError(out, err, 'Failed to delete subdomain');
187
+ }
188
+ });
138
189
  }
@@ -319,4 +319,100 @@ EXAMPLES
319
319
  handleError(out, err, 'Failed to create team vault');
320
320
  }
321
321
  });
322
+ const teamCalendar = teams.command('calendar').description('Team calendar operations');
323
+ addGlobalFlags(teamCalendar.command('view')
324
+ .description('View team calendar')
325
+ .argument('<teamId>', 'Team ID')
326
+ .requiredOption('--start <date>', 'Start date (YYYY-MM-DD)')
327
+ .requiredOption('--end <date>', 'End date (YYYY-MM-DD)')
328
+ .option('--types <types>', 'Event types to include'))
329
+ .action(async (teamId, _opts) => {
330
+ const flags = resolveFlags(_opts);
331
+ const out = createOutput(flags);
332
+ out.startSpinner('Fetching team calendar...');
333
+ try {
334
+ const client = await getClientAsync();
335
+ const cal = await client.teams.getCalendar(teamId, {
336
+ start: _opts.start,
337
+ end: _opts.end,
338
+ types: _opts.types,
339
+ });
340
+ out.stopSpinner();
341
+ if (flags.output === 'json') {
342
+ out.raw(JSON.stringify(cal, null, 2) + '\n');
343
+ }
344
+ else {
345
+ for (const [date, day] of Object.entries(cal.days ?? {})) {
346
+ if (day && day.count) {
347
+ process.stdout.write(`${chalk.cyan(date)}: ${day.count} events\n`);
348
+ }
349
+ }
350
+ }
351
+ }
352
+ catch (err) {
353
+ handleError(out, err, 'Failed to fetch team calendar');
354
+ }
355
+ });
356
+ addGlobalFlags(teamCalendar.command('activity')
357
+ .description('View team calendar activity')
358
+ .argument('<teamId>', 'Team ID')
359
+ .requiredOption('--start <date>', 'Start date (YYYY-MM-DD)')
360
+ .requiredOption('--end <date>', 'End date (YYYY-MM-DD)'))
361
+ .action(async (teamId, _opts) => {
362
+ const flags = resolveFlags(_opts);
363
+ const out = createOutput(flags);
364
+ out.startSpinner('Fetching team calendar activity...');
365
+ try {
366
+ const client = await getClientAsync();
367
+ const activity = await client.teams.getCalendarActivity(teamId, {
368
+ start: _opts.start,
369
+ end: _opts.end,
370
+ });
371
+ out.stopSpinner();
372
+ if (flags.output === 'json') {
373
+ out.raw(JSON.stringify(activity, null, 2) + '\n');
374
+ }
375
+ else {
376
+ for (const day of activity.days ?? []) {
377
+ if (day.count) {
378
+ process.stdout.write(`${chalk.cyan(day.date)}: ${day.count} activities\n`);
379
+ }
380
+ }
381
+ }
382
+ }
383
+ catch (err) {
384
+ handleError(out, err, 'Failed to fetch team calendar activity');
385
+ }
386
+ });
387
+ addGlobalFlags(teamCalendar.command('events')
388
+ .description('List team calendar events')
389
+ .argument('<teamId>', 'Team ID')
390
+ .option('--start <date>', 'Start date (YYYY-MM-DD)')
391
+ .option('--end <date>', 'End date (YYYY-MM-DD)'))
392
+ .action(async (teamId, _opts) => {
393
+ const flags = resolveFlags(_opts);
394
+ const out = createOutput(flags);
395
+ out.startSpinner('Fetching team calendar events...');
396
+ try {
397
+ const client = await getClientAsync();
398
+ const events = await client.teams.getCalendarEvents(teamId, {
399
+ start: _opts.start,
400
+ end: _opts.end,
401
+ });
402
+ out.stopSpinner();
403
+ out.list(events.map(e => ({ id: e.id, title: e.title, startDate: e.startDate, endDate: e.endDate ?? '' })), {
404
+ emptyMessage: 'No events found.',
405
+ columns: [
406
+ { key: 'id', header: 'ID' },
407
+ { key: 'title', header: 'Title' },
408
+ { key: 'startDate', header: 'Start' },
409
+ { key: 'endDate', header: 'End' },
410
+ ],
411
+ textFn: (e) => `${chalk.cyan(String(e.title))} — ${String(e.startDate)}`,
412
+ });
413
+ }
414
+ catch (err) {
415
+ handleError(out, err, 'Failed to fetch team calendar events');
416
+ }
417
+ });
322
418
  }
@@ -45,4 +45,335 @@ export function registerUserCommands(program) {
45
45
  handleError(out, err, 'Failed to fetch storage usage');
46
46
  }
47
47
  });
48
+ // user me
49
+ addGlobalFlags(user.command('me')
50
+ .description('Show your user profile'))
51
+ .action(async (_opts) => {
52
+ const flags = resolveFlags(_opts);
53
+ const out = createOutput(flags);
54
+ out.startSpinner('Fetching profile...');
55
+ try {
56
+ const client = await getClientAsync();
57
+ const me = await client.user.me();
58
+ out.stopSpinner();
59
+ out.record({ id: me.id, email: me.email, name: me.name, role: me.role, createdAt: me.createdAt });
60
+ }
61
+ catch (err) {
62
+ handleError(out, err, 'Failed to fetch profile');
63
+ }
64
+ });
65
+ // user password
66
+ addGlobalFlags(user.command('password')
67
+ .description('Change your password')
68
+ .requiredOption('--current <pwd>', 'Current password')
69
+ .requiredOption('--new <pwd>', 'New password'))
70
+ .action(async (_opts) => {
71
+ const flags = resolveFlags(_opts);
72
+ const out = createOutput(flags);
73
+ out.startSpinner('Changing password...');
74
+ try {
75
+ const client = await getClientAsync();
76
+ await client.user.changePassword({ currentPassword: _opts.current, newPassword: _opts.new });
77
+ out.success('Password changed successfully', { changed: true });
78
+ }
79
+ catch (err) {
80
+ handleError(out, err, 'Failed to change password');
81
+ }
82
+ });
83
+ // user email
84
+ addGlobalFlags(user.command('email')
85
+ .description('Request email address change')
86
+ .requiredOption('--new <email>', 'New email address')
87
+ .requiredOption('--password <pwd>', 'Current password'))
88
+ .action(async (_opts) => {
89
+ const flags = resolveFlags(_opts);
90
+ const out = createOutput(flags);
91
+ out.startSpinner('Requesting email change...');
92
+ try {
93
+ const client = await getClientAsync();
94
+ const result = await client.user.requestEmailChange({ newEmail: _opts.new, password: _opts.password });
95
+ out.success(result.message, { message: result.message });
96
+ }
97
+ catch (err) {
98
+ handleError(out, err, 'Failed to request email change');
99
+ }
100
+ });
101
+ // user email-verify
102
+ addGlobalFlags(user.command('email-verify')
103
+ .description('Confirm email change with verification token')
104
+ .requiredOption('--token <token>', 'Verification token from email'))
105
+ .action(async (_opts) => {
106
+ const flags = resolveFlags(_opts);
107
+ const out = createOutput(flags);
108
+ out.startSpinner('Verifying email change...');
109
+ try {
110
+ const client = await getClientAsync();
111
+ const result = await client.user.confirmEmailChange(_opts.token);
112
+ out.success(result.message, { message: result.message });
113
+ }
114
+ catch (err) {
115
+ handleError(out, err, 'Failed to verify email change');
116
+ }
117
+ });
118
+ // user profile
119
+ addGlobalFlags(user.command('profile')
120
+ .description('Update your profile')
121
+ .option('--name <name>', 'Display name')
122
+ .option('--slug <slug>', 'Profile URL slug'))
123
+ .action(async (_opts) => {
124
+ const flags = resolveFlags(_opts);
125
+ const out = createOutput(flags);
126
+ out.startSpinner('Updating profile...');
127
+ try {
128
+ const client = await getClientAsync();
129
+ const result = await client.user.updateProfile({ name: _opts.name, slug: _opts.slug });
130
+ out.success(result.message, { message: result.message });
131
+ }
132
+ catch (err) {
133
+ handleError(out, err, 'Failed to update profile');
134
+ }
135
+ });
136
+ // user delete
137
+ addGlobalFlags(user.command('delete')
138
+ .description('Request account deletion')
139
+ .requiredOption('--password <pwd>', 'Current password')
140
+ .option('--reason <reason>', 'Reason for deletion')
141
+ .option('--export-data', 'Request data export before deletion'))
142
+ .action(async (_opts) => {
143
+ const flags = resolveFlags(_opts);
144
+ const out = createOutput(flags);
145
+ out.startSpinner('Requesting account deletion...');
146
+ try {
147
+ const client = await getClientAsync();
148
+ const result = await client.user.requestAccountDeletion({
149
+ password: _opts.password,
150
+ reason: _opts.reason,
151
+ exportData: _opts.exportData === true,
152
+ });
153
+ out.success(result.message, { message: result.message, scheduledAt: result.scheduledAt });
154
+ }
155
+ catch (err) {
156
+ handleError(out, err, 'Failed to request account deletion');
157
+ }
158
+ });
159
+ // user delete-cancel
160
+ addGlobalFlags(user.command('delete-cancel')
161
+ .description('Cancel a pending account deletion'))
162
+ .action(async (_opts) => {
163
+ const flags = resolveFlags(_opts);
164
+ const out = createOutput(flags);
165
+ out.startSpinner('Cancelling account deletion...');
166
+ try {
167
+ const client = await getClientAsync();
168
+ const result = await client.user.cancelAccountDeletion();
169
+ out.success(result.message, { message: result.message });
170
+ }
171
+ catch (err) {
172
+ handleError(out, err, 'Failed to cancel account deletion');
173
+ }
174
+ });
175
+ // user sessions subgroup
176
+ const sessions = user.command('sessions').description('Session management');
177
+ addGlobalFlags(sessions.command('list')
178
+ .description('List active sessions'))
179
+ .action(async (_opts) => {
180
+ const flags = resolveFlags(_opts);
181
+ const out = createOutput(flags);
182
+ out.startSpinner('Fetching sessions...');
183
+ try {
184
+ const client = await getClientAsync();
185
+ const list = await client.user.getSessions();
186
+ out.stopSpinner();
187
+ out.list(list.map(s => ({ id: s.id, current: s.current ? 'yes' : 'no', ip: s.ipAddress || '', createdAt: s.createdAt, lastSeen: s.lastSeenAt })), {
188
+ emptyMessage: 'No sessions found.',
189
+ columns: [
190
+ { key: 'id', header: 'ID' },
191
+ { key: 'current', header: 'Current' },
192
+ { key: 'ip', header: 'IP' },
193
+ { key: 'createdAt', header: 'Created' },
194
+ { key: 'lastSeen', header: 'Last Seen' },
195
+ ],
196
+ textFn: (s) => `${String(s.id)} ${s.current === 'yes' ? chalk.green('[current]') : ''} — ${String(s.ip)}`,
197
+ });
198
+ }
199
+ catch (err) {
200
+ handleError(out, err, 'Failed to fetch sessions');
201
+ }
202
+ });
203
+ addGlobalFlags(sessions.command('revoke')
204
+ .description('Revoke a session')
205
+ .argument('<sessionId>', 'Session ID'))
206
+ .action(async (sessionId, _opts) => {
207
+ const flags = resolveFlags(_opts);
208
+ const out = createOutput(flags);
209
+ out.startSpinner('Revoking session...');
210
+ try {
211
+ const client = await getClientAsync();
212
+ const result = await client.user.revokeSession(sessionId);
213
+ out.success(result.message, { message: result.message });
214
+ }
215
+ catch (err) {
216
+ handleError(out, err, 'Failed to revoke session');
217
+ }
218
+ });
219
+ addGlobalFlags(sessions.command('revoke-all')
220
+ .description('Revoke all sessions'))
221
+ .action(async (_opts) => {
222
+ const flags = resolveFlags(_opts);
223
+ const out = createOutput(flags);
224
+ out.startSpinner('Revoking all sessions...');
225
+ try {
226
+ const client = await getClientAsync();
227
+ const result = await client.user.revokeAllSessions();
228
+ out.success(result.message, { message: result.message });
229
+ }
230
+ catch (err) {
231
+ handleError(out, err, 'Failed to revoke sessions');
232
+ }
233
+ });
234
+ // user export subgroup
235
+ const userExport = user.command('export').description('Data export management');
236
+ addGlobalFlags(userExport.command('create')
237
+ .description('Request a data export')
238
+ .option('--format <format>', 'Export format (json)', 'json'))
239
+ .action(async (_opts) => {
240
+ const flags = resolveFlags(_opts);
241
+ const out = createOutput(flags);
242
+ out.startSpinner('Creating data export...');
243
+ try {
244
+ const client = await getClientAsync();
245
+ const exp = await client.user.requestDataExport(_opts.format);
246
+ out.success('Data export requested', { id: exp.id, status: exp.status, format: exp.format });
247
+ }
248
+ catch (err) {
249
+ handleError(out, err, 'Failed to create data export');
250
+ }
251
+ });
252
+ addGlobalFlags(userExport.command('get')
253
+ .description('Get a data export status')
254
+ .argument('<exportId>', 'Export ID'))
255
+ .action(async (exportId, _opts) => {
256
+ const flags = resolveFlags(_opts);
257
+ const out = createOutput(flags);
258
+ out.startSpinner('Fetching export status...');
259
+ try {
260
+ const client = await getClientAsync();
261
+ const exp = await client.user.getDataExport(exportId);
262
+ out.stopSpinner();
263
+ out.record({ id: exp.id, status: exp.status, format: exp.format, createdAt: exp.createdAt, completedAt: exp.completedAt, downloadUrl: exp.downloadUrl });
264
+ }
265
+ catch (err) {
266
+ handleError(out, err, 'Failed to fetch export status');
267
+ }
268
+ });
269
+ // user consents subgroup
270
+ const consents = user.command('consents').description('Consent management');
271
+ addGlobalFlags(consents.command('list')
272
+ .description('List consent records'))
273
+ .action(async (_opts) => {
274
+ const flags = resolveFlags(_opts);
275
+ const out = createOutput(flags);
276
+ out.startSpinner('Fetching consents...');
277
+ try {
278
+ const client = await getClientAsync();
279
+ const list = await client.user.getConsents();
280
+ out.stopSpinner();
281
+ out.list(list.map(c => ({ type: c.consentType, version: c.version, granted: c.granted ? 'yes' : 'no', recordedAt: c.recordedAt })), {
282
+ emptyMessage: 'No consents found.',
283
+ columns: [
284
+ { key: 'type', header: 'Type' },
285
+ { key: 'version', header: 'Version' },
286
+ { key: 'granted', header: 'Granted' },
287
+ { key: 'recordedAt', header: 'Recorded' },
288
+ ],
289
+ textFn: (c) => `${String(c.type)} v${String(c.version)}: ${c.granted === 'yes' ? chalk.green('granted') : chalk.red('denied')}`,
290
+ });
291
+ }
292
+ catch (err) {
293
+ handleError(out, err, 'Failed to fetch consents');
294
+ }
295
+ });
296
+ addGlobalFlags(consents.command('set')
297
+ .description('Record a consent decision')
298
+ .requiredOption('--type <t>', 'Consent type')
299
+ .requiredOption('--version <v>', 'Policy version')
300
+ .option('--granted', 'Grant consent')
301
+ .option('--no-granted', 'Deny consent'))
302
+ .action(async (_opts) => {
303
+ const flags = resolveFlags(_opts);
304
+ const out = createOutput(flags);
305
+ out.startSpinner('Recording consent...');
306
+ try {
307
+ const client = await getClientAsync();
308
+ const result = await client.user.recordConsent({
309
+ consentType: _opts.type,
310
+ version: _opts.version,
311
+ granted: _opts.granted !== false,
312
+ });
313
+ out.success(result.message, { message: result.message });
314
+ }
315
+ catch (err) {
316
+ handleError(out, err, 'Failed to record consent');
317
+ }
318
+ });
319
+ // user invitations subgroup
320
+ const invitations = user.command('invitations').description('Team invitation management');
321
+ addGlobalFlags(invitations.command('list')
322
+ .description('List pending team invitations'))
323
+ .action(async (_opts) => {
324
+ const flags = resolveFlags(_opts);
325
+ const out = createOutput(flags);
326
+ out.startSpinner('Fetching invitations...');
327
+ try {
328
+ const client = await getClientAsync();
329
+ const list = await client.user.listTeamInvitations();
330
+ out.stopSpinner();
331
+ out.list(list.map(i => ({ id: i.id, team: i.teamName, role: i.role, invitedBy: i.invitedBy, expiresAt: i.expiresAt })), {
332
+ emptyMessage: 'No pending invitations.',
333
+ columns: [
334
+ { key: 'id', header: 'ID' },
335
+ { key: 'team', header: 'Team' },
336
+ { key: 'role', header: 'Role' },
337
+ { key: 'invitedBy', header: 'Invited By' },
338
+ { key: 'expiresAt', header: 'Expires' },
339
+ ],
340
+ textFn: (i) => `${chalk.cyan(String(i.team))} [${String(i.role)}] — invited by ${String(i.invitedBy)}`,
341
+ });
342
+ }
343
+ catch (err) {
344
+ handleError(out, err, 'Failed to fetch invitations');
345
+ }
346
+ });
347
+ addGlobalFlags(invitations.command('accept')
348
+ .description('Accept a team invitation')
349
+ .argument('<id>', 'Invitation ID'))
350
+ .action(async (id, _opts) => {
351
+ const flags = resolveFlags(_opts);
352
+ const out = createOutput(flags);
353
+ out.startSpinner('Accepting invitation...');
354
+ try {
355
+ const client = await getClientAsync();
356
+ const result = await client.user.acceptTeamInvitation(id);
357
+ out.success(result.message, { message: result.message });
358
+ }
359
+ catch (err) {
360
+ handleError(out, err, 'Failed to accept invitation');
361
+ }
362
+ });
363
+ addGlobalFlags(invitations.command('decline')
364
+ .description('Decline a team invitation')
365
+ .argument('<id>', 'Invitation ID'))
366
+ .action(async (id, _opts) => {
367
+ const flags = resolveFlags(_opts);
368
+ const out = createOutput(flags);
369
+ out.startSpinner('Declining invitation...');
370
+ try {
371
+ const client = await getClientAsync();
372
+ const result = await client.user.declineTeamInvitation(id);
373
+ out.success(result.message, { message: result.message });
374
+ }
375
+ catch (err) {
376
+ handleError(out, err, 'Failed to decline invitation');
377
+ }
378
+ });
48
379
  }