@hasna/connectors 1.3.22 → 1.3.23

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 (63) hide show
  1. package/bin/index.js +109 -59
  2. package/bin/mcp.js +236 -182
  3. package/bin/serve.js +77 -33
  4. package/connectors/connect-dropbox/src/api/bulk.ts +285 -0
  5. package/connectors/connect-dropbox/src/api/index.ts +4 -0
  6. package/connectors/connect-dropbox/src/cli/index.ts +151 -0
  7. package/connectors/connect-github/src/api/bulk.ts +328 -0
  8. package/connectors/connect-github/src/api/index.ts +4 -0
  9. package/connectors/connect-github/src/cli/index.ts +145 -0
  10. package/connectors/connect-gmail/src/api/history.ts +82 -0
  11. package/connectors/connect-gmail/src/api/index.ts +12 -0
  12. package/connectors/connect-gmail/src/api/settings.ts +280 -0
  13. package/connectors/connect-gmail/src/api/watch.ts +40 -0
  14. package/connectors/connect-gmail/src/cli/index.ts +237 -0
  15. package/connectors/connect-googleads/src/api/bulk.ts +354 -0
  16. package/connectors/connect-googleads/src/api/index.ts +4 -0
  17. package/connectors/connect-googleads/src/cli/index.ts +192 -0
  18. package/connectors/connect-googlecalendar/src/api/acl.ts +66 -0
  19. package/connectors/connect-googlecalendar/src/api/bulk.ts +452 -0
  20. package/connectors/connect-googlecalendar/src/api/freebusy.ts +35 -0
  21. package/connectors/connect-googlecalendar/src/api/index.ts +9 -0
  22. package/connectors/connect-googlecalendar/src/cli/index.ts +522 -0
  23. package/connectors/connect-googlecalendar/src/types/index.ts +90 -0
  24. package/connectors/connect-googlecontacts/src/api/bulk.ts +365 -0
  25. package/connectors/connect-googlecontacts/src/api/groups.ts +164 -0
  26. package/connectors/connect-googlecontacts/src/api/index.ts +8 -0
  27. package/connectors/connect-googlecontacts/src/cli/index.ts +340 -0
  28. package/connectors/connect-googledocs/src/api/bulk.ts +301 -0
  29. package/connectors/connect-googledocs/src/api/index.ts +4 -0
  30. package/connectors/connect-googledocs/src/cli/index.ts +187 -0
  31. package/connectors/connect-googledrive/src/api/bulk.ts +505 -0
  32. package/connectors/connect-googledrive/src/api/changes.ts +139 -0
  33. package/connectors/connect-googledrive/src/api/client.ts +34 -0
  34. package/connectors/connect-googledrive/src/api/index.ts +12 -0
  35. package/connectors/connect-googledrive/src/api/revisions.ts +132 -0
  36. package/connectors/connect-googledrive/src/cli/index.ts +624 -0
  37. package/connectors/connect-googledrive/src/index.ts +2 -0
  38. package/connectors/connect-googlephotos/src/api/bulk.ts +455 -0
  39. package/connectors/connect-googlephotos/src/api/index.ts +8 -0
  40. package/connectors/connect-googlephotos/src/cli/index.ts +185 -0
  41. package/connectors/connect-googletasks/src/api/bulk.ts +359 -0
  42. package/connectors/connect-googletasks/src/api/index.ts +1 -0
  43. package/connectors/connect-googletasks/src/cli/index.ts +178 -0
  44. package/connectors/connect-linear/src/api/bulk.ts +219 -0
  45. package/connectors/connect-linear/src/api/index.ts +4 -0
  46. package/connectors/connect-linear/src/cli/index.ts +116 -0
  47. package/connectors/connect-shopify/src/api/bulk.ts +288 -0
  48. package/connectors/connect-shopify/src/api/index.ts +4 -0
  49. package/connectors/connect-shopify/src/cli/index.ts +108 -0
  50. package/connectors/connect-slack/src/api/bulk.ts +237 -0
  51. package/connectors/connect-slack/src/api/index.ts +4 -0
  52. package/connectors/connect-slack/src/cli/index.ts +157 -0
  53. package/connectors/connect-stripe/src/api/bulk.ts +488 -0
  54. package/connectors/connect-stripe/src/api/index.ts +4 -0
  55. package/connectors/connect-stripe/src/cli/index.ts +206 -0
  56. package/dist/index.js +75 -33
  57. package/dist/lib/runner.d.ts +15 -5
  58. package/package.json +2 -2
  59. package/connectors/connect-cloudflare/bun.lock +0 -32
  60. package/connectors/connect-gmail/bin/index.js +0 -7027
  61. package/connectors/connect-gmail/dist/cli/index.js +0 -7035
  62. package/connectors/connect-gmail/dist/index.js +0 -2174
  63. package/dist/server/server.test.d.ts +0 -1
@@ -0,0 +1,280 @@
1
+ import type { GmailClient } from './client';
2
+
3
+ // ============================================
4
+ // Settings API Types
5
+ // ============================================
6
+
7
+ export interface GetSettingsOptions {
8
+ format?: 'full' | 'minimal';
9
+ }
10
+
11
+ export interface GmailSettings {
12
+ autoForwardAddresses?: AutoForwardingAddress[];
13
+ displayName: string;
14
+ emailAddress: string;
15
+ hasAGoogleAppsDomain?: boolean;
16
+ language: string;
17
+ replyToAddresses?: string[];
18
+ signatures: Signature[];
19
+ vacationResponder: VacationResponder;
20
+ autoDeleteChats?: AutoDeleteChatsValue[];
21
+ keyboardShortcuts?: boolean;
22
+ locale: string;
23
+ markImportantEmailsAsRead?: boolean;
24
+ useExternalCalendarInvites?: boolean;
25
+ conversationView?: boolean;
26
+ defaultSnippets?: boolean;
27
+ externalBodyContent?: boolean;
28
+ imagesInEmails?: ImagesInEmailsValue;
29
+ unreadMessageIcon?: UnreadMessageIconValue;
30
+ indicators?: string[];
31
+ dateStampFormat?: number;
32
+ defaultInboxStyle?: string;
33
+ dotsInEmails?: string;
34
+ autoAdvance?: AutoAdvanceValue;
35
+ allowCustomFrom?: boolean;
36
+ fontSize?: string;
37
+ defaultCccPref?: boolean;
38
+ autoCreateContacts?: boolean;
39
+ allowMailToLinks?: boolean;
40
+ replyFromAddress: ReplyFromAddress;
41
+ }
42
+
43
+ export interface AutoForwardingAddress {
44
+ forwardingEmailAddress: string;
45
+ verificationStatus: string;
46
+ disposition: string;
47
+ }
48
+
49
+ export interface Signature {
50
+ sendAsEmail: string;
51
+ signature: string;
52
+ isDefault: boolean;
53
+ }
54
+
55
+ export interface VacationResponder {
56
+ enableAutoReply: boolean;
57
+ responseBodyHtml: string;
58
+ responseBodyPlainText: string;
59
+ subject: string;
60
+ startTime: string;
61
+ endTime: string;
62
+ restrictToContacts: boolean;
63
+ restrictToDomain: boolean;
64
+ }
65
+
66
+ export interface ReplyFromAddress {
67
+ treatAsAlias: boolean;
68
+ sendAsEmail: string;
69
+ }
70
+
71
+ export type AutoDeleteChatsValue = 'autoDeleteForever' | 'autoDeleteNever' | 'autoDeleteAfter30Days';
72
+ export type ImagesInEmailsValue = 'displayExternal' | 'alwaysDisplay';
73
+ export type UnreadMessageIconValue = 'number' | 'dot' | 'nothing';
74
+ export type AutoAdvanceValue = 'goToNext' | 'goToPrev' | 'goToInbox';
75
+
76
+ export interface AutoForwarding {
77
+ enabled: boolean;
78
+ emailAddress: string;
79
+ disposition: 'leaveInInbox' | 'archive' | 'trash' | 'markRead' | 'leaveInInboxAndMarkRead';
80
+ }
81
+
82
+ export interface PopSettings {
83
+ accessWindow: 'disabled' | 'fromNowOn' | 'allMail';
84
+ disposition: 'archive' | 'trash' | 'leaveInInbox';
85
+ }
86
+
87
+ export interface ImapSettings {
88
+ enabled: boolean;
89
+ autoExpunge: boolean;
90
+ expungeBehavior: 'archive' | 'trash' | 'markRead';
91
+ maxFolderSize: number;
92
+ }
93
+
94
+ export interface LanguageSettings {
95
+ displayLanguage: string;
96
+ writeLanguage: string;
97
+ }
98
+
99
+ export interface SendAsEmail {
100
+ sendAsEmail: string;
101
+ displayName: string;
102
+ replyToAddress: string;
103
+ signature: string;
104
+ isPrimary: boolean;
105
+ isDefault: boolean;
106
+ treatAsAlias: boolean;
107
+ smtpMsa?: {
108
+ host: string;
109
+ port: number;
110
+ username: string;
111
+ securityMode: 'none' | 'ssl' | 'starttls';
112
+ };
113
+ verificationStatus: 'accepted' | 'pending';
114
+ }
115
+
116
+ export interface CreateSendAsOptions {
117
+ sendAsEmail: string;
118
+ displayName?: string;
119
+ replyToAddress?: string;
120
+ signature?: string;
121
+ treatAsAlias?: boolean;
122
+ isDefault?: boolean;
123
+ }
124
+
125
+ export interface UpdateSendAsOptions {
126
+ displayName?: string;
127
+ replyToAddress?: string;
128
+ signature?: string;
129
+ treatAsAlias?: boolean;
130
+ isDefault?: boolean;
131
+ }
132
+
133
+ export interface CreateSmtpMsaOptions {
134
+ host: string;
135
+ port: number;
136
+ username: string;
137
+ password: string;
138
+ securityMode: 'none' | 'ssl' | 'starttls';
139
+ }
140
+
141
+ /**
142
+ * Settings API module - Gmail account settings, send-as, POP, IMAP, forwarding
143
+ */
144
+ export class SettingsApi {
145
+ constructor(private readonly client: GmailClient) {}
146
+
147
+ /**
148
+ * Get user's Gmail settings
149
+ */
150
+ async get(options: GetSettingsOptions = {}): Promise<GmailSettings> {
151
+ const params: Record<string, string> = {};
152
+ if (options.format) params.format = options.format;
153
+ return this.client.get<GmailSettings>('/v1/users/me/settings', params);
154
+ }
155
+
156
+ /**
157
+ * Get auto-forwarding settings
158
+ */
159
+ async getAutoForwarding(): Promise<AutoForwarding> {
160
+ return this.client.get<AutoForwarding>('/v1/users/me/settings/autoForwarding');
161
+ }
162
+
163
+ /**
164
+ * Update auto-forwarding settings
165
+ */
166
+ async updateAutoForwarding(forwarding: Omit<AutoForwarding, 'emailAddress'> & { emailAddress: string }): Promise<AutoForwarding> {
167
+ return this.client.put<AutoForwarding>('/v1/users/me/settings/autoForwarding', forwarding as unknown as unknown as Record<string, unknown>);
168
+ }
169
+
170
+ /**
171
+ * Get POP settings
172
+ */
173
+ async getPop(): Promise<PopSettings> {
174
+ return this.client.get<PopSettings>('/v1/users/me/settings/pop');
175
+ }
176
+
177
+ /**
178
+ * Update POP settings
179
+ */
180
+ async updatePop(pop: PopSettings): Promise<PopSettings> {
181
+ return this.client.put<PopSettings>('/v1/users/me/settings/pop', pop as unknown as Record<string, unknown>);
182
+ }
183
+
184
+ /**
185
+ * Get IMAP settings
186
+ */
187
+ async getImap(): Promise<ImapSettings> {
188
+ return this.client.get<ImapSettings>('/v1/users/me/settings/imap');
189
+ }
190
+
191
+ /**
192
+ * Update IMAP settings
193
+ */
194
+ async updateImap(imap: ImapSettings): Promise<ImapSettings> {
195
+ return this.client.put<ImapSettings>('/v1/users/me/settings/imap', imap as unknown as Record<string, unknown>);
196
+ }
197
+
198
+ /**
199
+ * Get language settings
200
+ */
201
+ async getLanguage(): Promise<LanguageSettings> {
202
+ return this.client.get<LanguageSettings>('/v1/users/me/settings/language');
203
+ }
204
+
205
+ /**
206
+ * Update language settings
207
+ */
208
+ async updateLanguage(language: LanguageSettings): Promise<LanguageSettings> {
209
+ return this.client.put<LanguageSettings>('/v1/users/me/settings/language', language as unknown as Record<string, unknown>);
210
+ }
211
+
212
+ /**
213
+ * List all send-as addresses
214
+ */
215
+ async listSendAs(): Promise<{ sendAs: SendAsEmail[] }> {
216
+ return this.client.get('/v1/users/me/settings/sendAs');
217
+ }
218
+
219
+ /**
220
+ * Get a specific send-as address
221
+ */
222
+ async getSendAs(sendAsEmail: string): Promise<SendAsEmail> {
223
+ const encoded = encodeURIComponent(sendAsEmail);
224
+ return this.client.get<SendAsEmail>(`/v1/users/me/settings/sendAs/${encoded}`);
225
+ }
226
+
227
+ /**
228
+ * Create a new send-as alias
229
+ */
230
+ async createSendAs(options: CreateSendAsOptions): Promise<SendAsEmail> {
231
+ const body: Record<string, unknown> = { sendAsEmail: options.sendAsEmail };
232
+ if (options.displayName !== undefined) body.displayName = options.displayName;
233
+ if (options.replyToAddress !== undefined) body.replyToAddress = options.replyToAddress;
234
+ if (options.signature !== undefined) body.signature = options.signature;
235
+ if (options.treatAsAlias !== undefined) body.treatAsAlias = options.treatAsAlias;
236
+ if (options.isDefault !== undefined) body.isDefault = options.isDefault;
237
+
238
+ return this.client.post<SendAsEmail>('/v1/users/me/settings/sendAs', body);
239
+ }
240
+
241
+ /**
242
+ * Update a send-as alias
243
+ */
244
+ async updateSendAs(sendAsEmail: string, options: UpdateSendAsOptions): Promise<SendAsEmail> {
245
+ const encoded = encodeURIComponent(sendAsEmail);
246
+ return this.client.put<SendAsEmail>(`/v1/users/me/settings/sendAs/${encoded}`, options as unknown as Record<string, unknown>);
247
+ }
248
+
249
+ /**
250
+ * Delete a send-as alias
251
+ */
252
+ async deleteSendAs(sendAsEmail: string): Promise<void> {
253
+ const encoded = encodeURIComponent(sendAsEmail);
254
+ await this.client.delete(`/v1/users/me/settings/sendAs/${encoded}`);
255
+ }
256
+
257
+ /**
258
+ * Set SMTP MSA for a send-as alias (for third-party SMTP)
259
+ */
260
+ async setSendAsSmtpMsa(sendAsEmail: string, smtp: CreateSmtpMsaOptions): Promise<SendAsEmail> {
261
+ const encoded = encodeURIComponent(sendAsEmail);
262
+ return this.client.put<SendAsEmail>(`/v1/users/me/settings/sendAs/${encoded}/smtpMsa`, smtp as unknown as Record<string, unknown>);
263
+ }
264
+
265
+ /**
266
+ * Delete SMTP MSA for a send-as alias (revert to Gmail's servers)
267
+ */
268
+ async deleteSendAsSmtpMsa(sendAsEmail: string): Promise<SendAsEmail> {
269
+ const encoded = encodeURIComponent(sendAsEmail);
270
+ return this.client.delete<SendAsEmail>(`/v1/users/me/settings/sendAs/${encoded}/smtpMsa`);
271
+ }
272
+
273
+ /**
274
+ * Request verification for a send-as alias
275
+ */
276
+ async verifySendAs(sendAsEmail: string): Promise<void> {
277
+ const encoded = encodeURIComponent(sendAsEmail);
278
+ await this.client.post(`/v1/users/me/settings/sendAs/${encoded}/verify`);
279
+ }
280
+ }
@@ -0,0 +1,40 @@
1
+ import type { GmailClient } from './client';
2
+
3
+ // ============================================
4
+ // Watch API Types
5
+ // ============================================
6
+
7
+ export interface WatchRequest {
8
+ labelIds?: string[];
9
+ labelFilterAction?: 'include' | 'exclude';
10
+ topicName: string;
11
+ }
12
+
13
+ export interface WatchResponse {
14
+ historyId: string;
15
+ expiration: string;
16
+ }
17
+
18
+ /**
19
+ * Watch API module - Set up push notifications for mailbox changes
20
+ */
21
+ export class WatchApi {
22
+ constructor(private readonly client: GmailClient) {}
23
+
24
+ /**
25
+ * Set up or update a push notifications watch
26
+ *
27
+ * @param request - Watch configuration including Pub/Sub topic and optional label filters
28
+ * @returns Watch response with historyId and expiration timestamp
29
+ */
30
+ async start(request: WatchRequest): Promise<WatchResponse> {
31
+ return this.client.post<WatchResponse>('/v1/users/me/watch', request as unknown as Record<string, unknown>);
32
+ }
33
+
34
+ /**
35
+ * Stop receiving push notifications on the current watch
36
+ */
37
+ async stop(): Promise<void> {
38
+ await this.client.post('/v1/users/me/stop');
39
+ }
40
+ }
@@ -2419,5 +2419,242 @@ bulkCmd
2419
2419
  info(' bulk mark-read "is:unread older_than:7d" --batch');
2420
2420
  });
2421
2421
 
2422
+ // ============================================
2423
+ // History Commands
2424
+ // ============================================
2425
+ const historyCmd = program
2426
+ .command('history')
2427
+ .description('Mailbox history commands');
2428
+
2429
+ historyCmd
2430
+ .command('list')
2431
+ .description('List mailbox change history')
2432
+ .requiredOption('--start-history-id <id>', 'History ID to start from')
2433
+ .option('--types <types>', 'History types: messageAdded,messageChanged,messageDeleted,labelAdded,labelRemoved')
2434
+ .option('--label-id <id>', 'Filter by label ID')
2435
+ .option('-n, --max <number>', 'Maximum history records to return', '50')
2436
+ .action(async (opts) => {
2437
+ try {
2438
+ const gmail = requireAuth();
2439
+ const historyTypes = opts.types ? opts.types.split(',').map((t: string) => t.trim()) : undefined;
2440
+ const result = await gmail.history.list({
2441
+ startHistoryId: opts.startHistoryId,
2442
+ historyTypes,
2443
+ labelId: opts.labelId,
2444
+ maxResults: parseInt(opts.max),
2445
+ });
2446
+
2447
+ if (!result.history || result.history.length === 0) {
2448
+ info('No history records found');
2449
+ return;
2450
+ }
2451
+
2452
+ success(`Found ${result.history.length} history record(s):`);
2453
+ const output = result.history.map(h => ({
2454
+ id: h.id,
2455
+ messagesAdded: h.messagesAdded?.length || 0,
2456
+ messagesDeleted: h.messagesDeleted?.length || 0,
2457
+ labelsAdded: h.labelsAdded?.length || 0,
2458
+ labelsRemoved: h.labelsRemoved?.length || 0,
2459
+ messagesChanged: h.messageChanged?.length || 0,
2460
+ }));
2461
+ print(output, getFormat(historyCmd));
2462
+ info(`Current historyId: ${result.historyId}`);
2463
+ } catch (err) {
2464
+ error(String(err));
2465
+ process.exit(1);
2466
+ }
2467
+ });
2468
+
2469
+ // ============================================
2470
+ // Gmail Settings Commands (API settings, not CLI config)
2471
+ // ============================================
2472
+ const gmailSettingsCmd = program
2473
+ .command('gmail-settings')
2474
+ .description('Gmail account settings commands');
2475
+
2476
+ gmailSettingsCmd
2477
+ .command('get')
2478
+ .description('Get Gmail account settings')
2479
+ .action(async () => {
2480
+ try {
2481
+ const gmail = requireAuth();
2482
+ const settings = await gmail.settings.get();
2483
+ print({
2484
+ displayName: settings.displayName,
2485
+ emailAddress: settings.emailAddress,
2486
+ language: settings.language,
2487
+ locale: settings.locale,
2488
+ autoDeleteChats: settings.autoDeleteChats,
2489
+ vacationResponder: {
2490
+ enabled: settings.vacationResponder.enableAutoReply,
2491
+ subject: settings.vacationResponder.subject,
2492
+ restrictToContacts: settings.vacationResponder.restrictToContacts,
2493
+ restrictToDomain: settings.vacationResponder.restrictToDomain,
2494
+ },
2495
+ signature: settings.signatures?.find(s => s.isDefault)?.signature || 'None',
2496
+ }, getFormat(gmailSettingsCmd));
2497
+ } catch (err) {
2498
+ error(String(err));
2499
+ process.exit(1);
2500
+ }
2501
+ });
2502
+
2503
+ gmailSettingsCmd
2504
+ .command('forwarding')
2505
+ .description('Get auto-forwarding settings')
2506
+ .action(async () => {
2507
+ try {
2508
+ const gmail = requireAuth();
2509
+ const forwarding = await gmail.settings.getAutoForwarding();
2510
+ print(forwarding, getFormat(gmailSettingsCmd));
2511
+ } catch (err) {
2512
+ error(String(err));
2513
+ process.exit(1);
2514
+ }
2515
+ });
2516
+
2517
+ gmailSettingsCmd
2518
+ .command('imap')
2519
+ .description('Get IMAP settings')
2520
+ .action(async () => {
2521
+ try {
2522
+ const gmail = requireAuth();
2523
+ const imap = await gmail.settings.getImap();
2524
+ print(imap, getFormat(gmailSettingsCmd));
2525
+ } catch (err) {
2526
+ error(String(err));
2527
+ process.exit(1);
2528
+ }
2529
+ });
2530
+
2531
+ gmailSettingsCmd
2532
+ .command('pop')
2533
+ .description('Get POP settings')
2534
+ .action(async () => {
2535
+ try {
2536
+ const gmail = requireAuth();
2537
+ const pop = await gmail.settings.getPop();
2538
+ print(pop, getFormat(gmailSettingsCmd));
2539
+ } catch (err) {
2540
+ error(String(err));
2541
+ process.exit(1);
2542
+ }
2543
+ });
2544
+
2545
+ gmailSettingsCmd
2546
+ .command('send-as')
2547
+ .description('List send-as aliases')
2548
+ .action(async () => {
2549
+ try {
2550
+ const gmail = requireAuth();
2551
+ const result = await gmail.settings.listSendAs();
2552
+
2553
+ if (!result.sendAs || result.sendAs.length === 0) {
2554
+ info('No send-as aliases found');
2555
+ return;
2556
+ }
2557
+
2558
+ success(`Found ${result.sendAs.length} send-as address(es):`);
2559
+ const output = result.sendAs.map(s => ({
2560
+ email: s.sendAsEmail,
2561
+ displayName: s.displayName,
2562
+ isDefault: s.isDefault,
2563
+ isPrimary: s.isPrimary,
2564
+ verified: s.verificationStatus,
2565
+ }));
2566
+ print(output, getFormat(gmailSettingsCmd));
2567
+ } catch (err) {
2568
+ error(String(err));
2569
+ process.exit(1);
2570
+ }
2571
+ });
2572
+
2573
+ gmailSettingsCmd
2574
+ .command('add-send-as')
2575
+ .description('Add a send-as alias')
2576
+ .requiredOption('--email <email>', 'Send-as email address')
2577
+ .option('--display-name <name>', 'Display name')
2578
+ .option('--reply-to <email>', 'Reply-to address')
2579
+ .option('--signature <text>', 'Email signature')
2580
+ .option('--set-default', 'Make this the default send-as')
2581
+ .action(async (opts) => {
2582
+ try {
2583
+ const gmail = requireAuth();
2584
+ const sendAs = await gmail.settings.createSendAs({
2585
+ sendAsEmail: opts.email,
2586
+ displayName: opts.displayName,
2587
+ replyToAddress: opts.replyTo,
2588
+ signature: opts.signature,
2589
+ isDefault: opts.setDefault,
2590
+ });
2591
+ success(`Send-as alias created: ${sendAs.sendAsEmail}`);
2592
+ info(`Verification status: ${sendAs.verificationStatus}`);
2593
+ } catch (err) {
2594
+ error(String(err));
2595
+ process.exit(1);
2596
+ }
2597
+ });
2598
+
2599
+ gmailSettingsCmd
2600
+ .command('delete-send-as <email>')
2601
+ .description('Delete a send-as alias')
2602
+ .action(async (email: string) => {
2603
+ try {
2604
+ const gmail = requireAuth();
2605
+ await gmail.settings.deleteSendAs(email);
2606
+ success(`Send-as alias deleted: ${email}`);
2607
+ } catch (err) {
2608
+ error(String(err));
2609
+ process.exit(1);
2610
+ }
2611
+ });
2612
+
2613
+ // ============================================
2614
+ // Watch Commands
2615
+ // ============================================
2616
+ const watchCmd = program
2617
+ .command('watch')
2618
+ .description('Push notification watch commands');
2619
+
2620
+ watchCmd
2621
+ .command('start')
2622
+ .description('Start a push notification watch')
2623
+ .requiredOption('--topic <topic>', 'Pub/Sub topic name (e.g., projects/my-project/topics/my-topic)')
2624
+ .option('--label-ids <ids>', 'Comma-separated label IDs to watch')
2625
+ .option('--label-filter <action>', 'Label filter: include or exclude', 'include')
2626
+ .action(async (opts) => {
2627
+ try {
2628
+ const gmail = requireAuth();
2629
+ const labelIds = opts.labelIds ? opts.labelIds.split(',').map((l: string) => l.trim()) : undefined;
2630
+ const result = await gmail.watch.start({
2631
+ topicName: opts.topic,
2632
+ labelIds,
2633
+ labelFilterAction: opts.labelFilter,
2634
+ });
2635
+
2636
+ success('Watch started!');
2637
+ info(`History ID: ${result.historyId}`);
2638
+ info(`Expires: ${new Date(parseInt(result.historyId) > 1e12 ? parseInt(result.historyId) : Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()}`);
2639
+ } catch (err) {
2640
+ error(String(err));
2641
+ process.exit(1);
2642
+ }
2643
+ });
2644
+
2645
+ watchCmd
2646
+ .command('stop')
2647
+ .description('Stop push notification watch')
2648
+ .action(async () => {
2649
+ try {
2650
+ const gmail = requireAuth();
2651
+ await gmail.watch.stop();
2652
+ success('Watch stopped');
2653
+ } catch (err) {
2654
+ error(String(err));
2655
+ process.exit(1);
2656
+ }
2657
+ });
2658
+
2422
2659
  // Parse and execute
2423
2660
  program.parse();