zele 0.2.0 → 0.3.5

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 (158) hide show
  1. package/README.md +38 -1
  2. package/bin/zele +27 -0
  3. package/dist/api-utils.d.ts +51 -2
  4. package/dist/api-utils.js +89 -3
  5. package/dist/api-utils.js.map +1 -1
  6. package/dist/auth.d.ts +27 -6
  7. package/dist/auth.js +185 -129
  8. package/dist/auth.js.map +1 -1
  9. package/dist/calendar-client.d.ts +16 -9
  10. package/dist/calendar-client.js +163 -59
  11. package/dist/calendar-client.js.map +1 -1
  12. package/dist/cli.js +28 -1
  13. package/dist/cli.js.map +1 -1
  14. package/dist/commands/attachment.js +17 -15
  15. package/dist/commands/attachment.js.map +1 -1
  16. package/dist/commands/auth-cmd.js +20 -9
  17. package/dist/commands/auth-cmd.js.map +1 -1
  18. package/dist/commands/calendar.js +67 -78
  19. package/dist/commands/calendar.js.map +1 -1
  20. package/dist/commands/draft.js +25 -18
  21. package/dist/commands/draft.js.map +1 -1
  22. package/dist/commands/label.js +33 -45
  23. package/dist/commands/label.js.map +1 -1
  24. package/dist/commands/mail-actions.js +11 -13
  25. package/dist/commands/mail-actions.js.map +1 -1
  26. package/dist/commands/mail.js +114 -128
  27. package/dist/commands/mail.js.map +1 -1
  28. package/dist/commands/profile.js +18 -21
  29. package/dist/commands/profile.js.map +1 -1
  30. package/dist/commands/watch.d.ts +2 -0
  31. package/dist/commands/watch.js +73 -0
  32. package/dist/commands/watch.js.map +1 -0
  33. package/dist/db.js +12 -13
  34. package/dist/db.js.map +1 -1
  35. package/dist/generated/browser.d.ts +12 -27
  36. package/dist/generated/client.d.ts +13 -28
  37. package/dist/generated/client.js +1 -1
  38. package/dist/generated/commonInputTypes.d.ts +90 -26
  39. package/dist/generated/enums.d.ts +0 -4
  40. package/dist/generated/enums.js +0 -3
  41. package/dist/generated/enums.js.map +1 -1
  42. package/dist/generated/internal/class.d.ts +22 -55
  43. package/dist/generated/internal/class.js +12 -4
  44. package/dist/generated/internal/class.js.map +1 -1
  45. package/dist/generated/internal/prismaNamespace.d.ts +272 -511
  46. package/dist/generated/internal/prismaNamespace.js +54 -66
  47. package/dist/generated/internal/prismaNamespace.js.map +1 -1
  48. package/dist/generated/internal/prismaNamespaceBrowser.d.ts +60 -74
  49. package/dist/generated/internal/prismaNamespaceBrowser.js +50 -62
  50. package/dist/generated/internal/prismaNamespaceBrowser.js.map +1 -1
  51. package/dist/generated/models/Account.d.ts +1637 -0
  52. package/dist/generated/models/Account.js +2 -0
  53. package/dist/generated/models/Account.js.map +1 -0
  54. package/dist/generated/models/CalendarList.d.ts +1161 -0
  55. package/dist/generated/models/CalendarList.js +2 -0
  56. package/dist/generated/models/CalendarList.js.map +1 -0
  57. package/dist/generated/models/Label.d.ts +1161 -0
  58. package/dist/generated/models/Label.js +2 -0
  59. package/dist/generated/models/Label.js.map +1 -0
  60. package/dist/generated/models/Profile.d.ts +1269 -0
  61. package/dist/generated/models/Profile.js +2 -0
  62. package/dist/generated/models/Profile.js.map +1 -0
  63. package/dist/generated/models/SyncState.d.ts +1130 -0
  64. package/dist/generated/models/SyncState.js +2 -0
  65. package/dist/generated/models/SyncState.js.map +1 -0
  66. package/dist/generated/models/Thread.d.ts +1608 -0
  67. package/dist/generated/models/Thread.js +2 -0
  68. package/dist/generated/models/Thread.js.map +1 -0
  69. package/dist/generated/models.d.ts +6 -9
  70. package/dist/gmail-client.d.ts +119 -94
  71. package/dist/gmail-client.js +862 -315
  72. package/dist/gmail-client.js.map +1 -1
  73. package/dist/mail-tui.d.ts +1 -0
  74. package/dist/mail-tui.js +517 -0
  75. package/dist/mail-tui.js.map +1 -0
  76. package/dist/output.d.ts +6 -4
  77. package/dist/output.js +124 -17
  78. package/dist/output.js.map +1 -1
  79. package/package.json +39 -11
  80. package/schema.prisma +81 -113
  81. package/src/api-utils.ts +103 -5
  82. package/src/auth.ts +224 -143
  83. package/src/calendar-client.ts +196 -89
  84. package/src/cli.ts +32 -1
  85. package/src/commands/attachment.ts +18 -19
  86. package/src/commands/auth-cmd.ts +19 -9
  87. package/src/commands/calendar.ts +42 -85
  88. package/src/commands/draft.ts +19 -22
  89. package/src/commands/label.ts +21 -57
  90. package/src/commands/mail-actions.ts +11 -19
  91. package/src/commands/mail.ts +104 -149
  92. package/src/commands/profile.ts +12 -28
  93. package/src/commands/watch.ts +88 -0
  94. package/src/db.ts +13 -16
  95. package/src/generated/browser.ts +49 -0
  96. package/src/generated/client.ts +71 -0
  97. package/src/generated/commonInputTypes.ts +332 -0
  98. package/src/generated/enums.ts +17 -0
  99. package/src/generated/internal/class.ts +250 -0
  100. package/src/generated/internal/prismaNamespace.ts +1198 -0
  101. package/src/generated/internal/prismaNamespaceBrowser.ts +169 -0
  102. package/src/generated/models/Account.ts +1848 -0
  103. package/src/generated/models/CalendarList.ts +1331 -0
  104. package/src/generated/models/Label.ts +1331 -0
  105. package/src/generated/models/Profile.ts +1439 -0
  106. package/src/generated/models/SyncState.ts +1300 -0
  107. package/src/generated/models/Thread.ts +1787 -0
  108. package/src/generated/models.ts +17 -0
  109. package/src/gmail-client.test.ts +59 -0
  110. package/src/gmail-client.ts +1034 -422
  111. package/src/mail-tui.tsx +1061 -0
  112. package/src/output.test.ts +1093 -0
  113. package/src/output.ts +128 -20
  114. package/src/schema.sql +58 -68
  115. package/src/test-fixtures/email-html/safe-claude-event.html +28 -0
  116. package/src/test-fixtures/email-html/safe-product-announcement.html +25 -0
  117. package/src/test-fixtures/email-html/safe-tracked-links.html +27 -0
  118. package/src/test-fixtures/email-html-snapshots/safe-claude-event.html.md +9 -0
  119. package/src/test-fixtures/email-html-snapshots/safe-product-announcement.html.md +13 -0
  120. package/src/test-fixtures/email-html-snapshots/safe-tracked-links.html.md +7 -0
  121. package/AGENTS.md +0 -26
  122. package/CHANGELOG.md +0 -36
  123. package/dist/generated/models/accounts.d.ts +0 -2000
  124. package/dist/generated/models/accounts.js +0 -2
  125. package/dist/generated/models/accounts.js.map +0 -1
  126. package/dist/generated/models/calendar_events.d.ts +0 -1433
  127. package/dist/generated/models/calendar_events.js +0 -2
  128. package/dist/generated/models/calendar_events.js.map +0 -1
  129. package/dist/generated/models/calendar_lists.d.ts +0 -1131
  130. package/dist/generated/models/calendar_lists.js +0 -2
  131. package/dist/generated/models/calendar_lists.js.map +0 -1
  132. package/dist/generated/models/label_counts.d.ts +0 -1131
  133. package/dist/generated/models/label_counts.js +0 -2
  134. package/dist/generated/models/label_counts.js.map +0 -1
  135. package/dist/generated/models/labels.d.ts +0 -1131
  136. package/dist/generated/models/labels.js +0 -2
  137. package/dist/generated/models/labels.js.map +0 -1
  138. package/dist/generated/models/profiles.d.ts +0 -1131
  139. package/dist/generated/models/profiles.js +0 -2
  140. package/dist/generated/models/profiles.js.map +0 -1
  141. package/dist/generated/models/sync_states.d.ts +0 -1107
  142. package/dist/generated/models/sync_states.js +0 -2
  143. package/dist/generated/models/sync_states.js.map +0 -1
  144. package/dist/generated/models/thread_lists.d.ts +0 -1404
  145. package/dist/generated/models/thread_lists.js +0 -2
  146. package/dist/generated/models/thread_lists.js.map +0 -1
  147. package/dist/generated/models/threads.d.ts +0 -1247
  148. package/dist/generated/models/threads.js +0 -2
  149. package/dist/generated/models/threads.js.map +0 -1
  150. package/dist/gmail-cache.d.ts +0 -60
  151. package/dist/gmail-cache.js +0 -264
  152. package/dist/gmail-cache.js.map +0 -1
  153. package/docs/gogcli-gmail-implementation.md +0 -599
  154. package/scripts/test-device-code-clients.ts +0 -186
  155. package/scripts/test-micropython-scopes.ts +0 -72
  156. package/scripts/test-oauth-clients.ts +0 -257
  157. package/src/gmail-cache.ts +0 -339
  158. package/tsconfig.json +0 -16
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Thread.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Thread.js","sourceRoot":"","sources":["../../../src/generated/models/Thread.ts"],"names":[],"mappings":""}
@@ -1,10 +1,7 @@
1
- export type * from './models/accounts.js';
2
- export type * from './models/thread_lists.js';
3
- export type * from './models/threads.js';
4
- export type * from './models/labels.js';
5
- export type * from './models/label_counts.js';
6
- export type * from './models/profiles.js';
7
- export type * from './models/calendar_lists.js';
8
- export type * from './models/calendar_events.js';
9
- export type * from './models/sync_states.js';
1
+ export type * from './models/Account.js';
2
+ export type * from './models/Thread.js';
3
+ export type * from './models/Label.js';
4
+ export type * from './models/Profile.js';
5
+ export type * from './models/CalendarList.js';
6
+ export type * from './models/SyncState.js';
10
7
  export type * from './commonInputTypes.js';
@@ -1,5 +1,7 @@
1
1
  import { type gmail_v1 } from '@googleapis/gmail';
2
2
  import type { OAuth2Client } from 'google-auth-library';
3
+ import { AuthError, ApiError, NotFoundError, EmptyThreadError, MissingDataError } from './api-utils.js';
4
+ import type { AccountId } from './auth.js';
3
5
  export interface Sender {
4
6
  name?: string;
5
7
  email: string;
@@ -25,6 +27,7 @@ export interface ParsedMessage {
25
27
  listUnsubscribe?: string;
26
28
  body: string;
27
29
  mimeType: string;
30
+ textBody: string | null;
28
31
  attachments: AttachmentMeta[];
29
32
  }
30
33
  export interface AttachmentMeta {
@@ -58,46 +61,60 @@ export interface ThreadListItem {
58
61
  }
59
62
  export interface ThreadListResult {
60
63
  threads: ThreadListItem[];
64
+ /** Raw Google gmail_v1.Schema$Thread metadata responses, parallel to threads[]. */
65
+ rawThreads: gmail_v1.Schema$Thread[];
61
66
  nextPageToken: string | null;
62
67
  resultSizeEstimate: number;
63
68
  }
69
+ /** Result from getThread() — includes both parsed data and the raw Google response. */
70
+ export interface ThreadResult {
71
+ parsed: ThreadData;
72
+ raw: gmail_v1.Schema$Thread;
73
+ }
74
+ export interface WatchEvent {
75
+ account: AccountId;
76
+ type: 'new_message';
77
+ message: ParsedMessage;
78
+ threadId: string;
79
+ }
64
80
  export declare class GmailClient {
65
81
  private gmail;
66
82
  private labelIdCache;
67
- constructor({ auth }: {
83
+ private account;
84
+ constructor({ auth, account }: {
68
85
  auth: OAuth2Client;
86
+ account?: AccountId;
69
87
  });
88
+ private get cacheEnabled();
89
+ private getCachedThread;
90
+ private cacheThreadData;
91
+ invalidateThreads(threadIds: string[]): Promise<void>;
92
+ invalidateThread(threadId: string): Promise<void>;
93
+ private getCachedLabels;
94
+ private cacheLabelsData;
95
+ invalidateLabels(): Promise<void>;
96
+ private getCachedProfile;
97
+ private cacheProfileData;
70
98
  listThreads({ query, folder, maxResults, labelIds, pageToken, }?: {
71
99
  query?: string;
72
100
  folder?: string;
73
101
  maxResults?: number;
74
102
  labelIds?: string[];
75
103
  pageToken?: string;
76
- }): Promise<ThreadListResult>;
104
+ }): Promise<ThreadListResult | AuthError | ApiError>;
77
105
  getThread({ threadId }: {
78
106
  threadId: string;
79
- }): Promise<{
80
- id: string;
81
- historyId: string | null;
82
- messages: ParsedMessage[];
83
- subject: string;
84
- snippet: string;
85
- from: Sender;
86
- date: string;
87
- labelIds: string[];
88
- hasUnread: boolean;
89
- messageCount: number;
90
- }>;
107
+ }): Promise<ThreadResult>;
91
108
  getMessage({ messageId, format, }: {
92
109
  messageId: string;
93
110
  format?: 'full' | 'metadata' | 'minimal' | 'raw';
94
111
  }): Promise<ParsedMessage | {
95
112
  id: string;
96
113
  raw: string;
97
- }>;
114
+ } | AuthError | ApiError>;
98
115
  getRawMessage({ messageId }: {
99
116
  messageId: string;
100
- }): Promise<string>;
117
+ }): Promise<string | MissingDataError | AuthError | ApiError>;
101
118
  sendMessage({ to, subject, body, cc, bcc, threadId, inReplyTo, references, attachments, fromEmail, }: {
102
119
  to: Array<{
103
120
  name?: string;
@@ -123,31 +140,32 @@ export declare class GmailClient {
123
140
  }>;
124
141
  fromEmail?: string;
125
142
  }): Promise<gmail_v1.Schema$Message>;
126
- createDraft({ to, subject, body, cc, bcc, threadId, fromEmail, attachments, }: {
127
- to: Array<{
128
- name?: string;
129
- email: string;
130
- }>;
131
- subject: string;
143
+ /**
144
+ * Reply to a thread. Handles reply-to resolution, reply-all CC computation,
145
+ * References/In-Reply-To headers, and subject prefixing.
146
+ */
147
+ replyToThread({ threadId, body, replyAll, cc, fromEmail, }: {
148
+ threadId: string;
132
149
  body: string;
150
+ replyAll?: boolean;
133
151
  cc?: Array<{
134
- name?: string;
135
152
  email: string;
136
153
  }>;
137
- bcc?: Array<{
138
- name?: string;
154
+ fromEmail?: string;
155
+ }): Promise<EmptyThreadError | AuthError | ApiError | gmail_v1.Schema$Message>;
156
+ /**
157
+ * Forward a thread. Fetches the last message, renders its body,
158
+ * builds the "Forwarded message" block, and sends.
159
+ */
160
+ forwardThread({ threadId, to, body, fromEmail, }: {
161
+ threadId: string;
162
+ to: Array<{
139
163
  email: string;
140
164
  }>;
141
- threadId?: string;
165
+ body?: string;
142
166
  fromEmail?: string;
143
- attachments?: Array<{
144
- filename: string;
145
- mimeType: string;
146
- content: Buffer;
147
- }>;
148
- }): Promise<gmail_v1.Schema$Draft>;
149
- updateDraft({ draftId, to, subject, body, cc, bcc, threadId, fromEmail, attachments, }: {
150
- draftId: string;
167
+ }): Promise<EmptyThreadError | gmail_v1.Schema$Message>;
168
+ createDraft({ to, subject, body, cc, bcc, threadId, fromEmail, attachments, }: {
151
169
  to: Array<{
152
170
  name?: string;
153
171
  email: string;
@@ -172,7 +190,7 @@ export declare class GmailClient {
172
190
  }): Promise<gmail_v1.Schema$Draft>;
173
191
  getDraft({ draftId }: {
174
192
  draftId: string;
175
- }): Promise<{
193
+ }): Promise<NotFoundError | AuthError | ApiError | {
176
194
  id: string;
177
195
  message: ParsedMessage;
178
196
  to: string[];
@@ -184,16 +202,16 @@ export declare class GmailClient {
184
202
  maxResults?: number;
185
203
  pageToken?: string;
186
204
  }): Promise<{
187
- drafts: {
205
+ drafts: Array<{
188
206
  id: string;
189
207
  threadId: string | null;
190
208
  subject: string;
191
209
  to: string[];
192
210
  date: string;
193
211
  snippet: string;
194
- }[];
212
+ }>;
195
213
  nextPageToken: string | null;
196
- }>;
214
+ } | AuthError | ApiError>;
197
215
  sendDraft({ draftId }: {
198
216
  draftId: string;
199
217
  }): Promise<gmail_v1.Schema$Message>;
@@ -202,21 +220,21 @@ export declare class GmailClient {
202
220
  }): Promise<void>;
203
221
  markAsRead({ threadIds }: {
204
222
  threadIds: string[];
205
- }): Promise<void>;
223
+ }): Promise<void | AuthError | ApiError>;
206
224
  markAsUnread({ threadIds }: {
207
225
  threadIds: string[];
208
- }): Promise<void>;
226
+ }): Promise<void | AuthError | ApiError>;
209
227
  star({ threadIds }: {
210
228
  threadIds: string[];
211
- }): Promise<void>;
229
+ }): Promise<void | AuthError | ApiError>;
212
230
  unstar({ threadIds }: {
213
231
  threadIds: string[];
214
- }): Promise<void>;
232
+ }): Promise<void | AuthError | ApiError>;
215
233
  modifyLabels({ threadIds, addLabelIds, removeLabelIds, }: {
216
234
  threadIds: string[];
217
235
  addLabelIds?: string[];
218
236
  removeLabelIds?: string[];
219
- }): Promise<void>;
237
+ }): Promise<void | AuthError | ApiError>;
220
238
  trash({ threadId }: {
221
239
  threadId: string;
222
240
  }): Promise<void>;
@@ -225,25 +243,17 @@ export declare class GmailClient {
225
243
  }): Promise<void>;
226
244
  archive({ threadIds }: {
227
245
  threadIds: string[];
228
- }): Promise<void>;
229
- deleteMessage({ messageId }: {
230
- messageId: string;
231
- }): Promise<void>;
246
+ }): Promise<void | AuthError | ApiError>;
247
+ /** Invalidate thread cache after a thread mutation. */
248
+ private invalidateAfterThreadMutation;
232
249
  /** Moves all spam threads to trash. Does not permanently delete. */
233
250
  trashAllSpam(): Promise<{
234
251
  count: number;
235
- }>;
252
+ } | AuthError | ApiError>;
236
253
  listLabels(): Promise<{
237
- id: string;
238
- name: string;
239
- type: "system" | "user";
240
- messageListVisibility: string | null;
241
- labelListVisibility: string | null;
242
- color: {
243
- backgroundColor: string;
244
- textColor: string;
245
- } | null;
246
- }[]>;
254
+ parsed: ReturnType<typeof GmailClient.parseRawLabels>;
255
+ raw: gmail_v1.Schema$Label[];
256
+ } | AuthError | ApiError>;
247
257
  getLabel({ labelId }: {
248
258
  labelId: string;
249
259
  }): Promise<{
@@ -269,45 +279,58 @@ export declare class GmailClient {
269
279
  id: string;
270
280
  name: string;
271
281
  }>;
272
- updateLabel({ labelId, name, color, }: {
273
- labelId: string;
274
- name?: string;
275
- color?: {
276
- backgroundColor: string;
277
- textColor: string;
278
- };
279
- }): Promise<void>;
280
282
  deleteLabel({ labelId }: {
281
283
  labelId: string;
282
284
  }): Promise<void>;
283
285
  getLabelCounts(): Promise<{
284
- label: string;
285
- count: number;
286
- }[]>;
286
+ parsed: Array<{
287
+ label: string;
288
+ count: number;
289
+ }>;
290
+ raw: gmail_v1.Schema$Label[];
291
+ archiveEstimate: number | null;
292
+ } | AuthError | ApiError>;
287
293
  getAttachment({ messageId, attachmentId, }: {
288
294
  messageId: string;
289
295
  attachmentId: string;
290
296
  }): Promise<string>;
291
- getMessageAttachments({ messageId }: {
292
- messageId: string;
293
- }): Promise<{
294
- filename: string;
295
- mimeType: string;
296
- size: number;
297
- attachmentId: string;
298
- data: string;
299
- }[]>;
300
297
  getProfile(): Promise<{
301
298
  emailAddress: string;
302
299
  messagesTotal: number;
303
300
  threadsTotal: number;
304
301
  historyId: string;
305
- }>;
306
- getEmailAliases(): Promise<{
302
+ } | AuthError | ApiError>;
303
+ /** Parse a raw gmail_v1.Schema$Thread (format: full) into ThreadData. */
304
+ static parseRawThread(raw: gmail_v1.Schema$Thread): ThreadData;
305
+ /** Parse a raw gmail_v1.Schema$Message into ParsedMessage. */
306
+ static parseRawMessage(message: gmail_v1.Schema$Message): ParsedMessage;
307
+ /** Parse raw gmail_v1.Schema$Thread (format: metadata) into ThreadListItem. */
308
+ static parseRawThreadListItem(raw: gmail_v1.Schema$Thread): ThreadListItem;
309
+ /** Parse raw gmail_v1.Schema$Label[] from labels.list into our label objects. */
310
+ static parseRawLabels(rawLabels: gmail_v1.Schema$Label[]): {
311
+ id: string;
312
+ name: string;
313
+ type: "system" | "user";
314
+ messageListVisibility: string | null;
315
+ labelListVisibility: string | null;
316
+ color: {
317
+ backgroundColor: string;
318
+ textColor: string;
319
+ } | null;
320
+ }[];
321
+ /** Parse raw gmail_v1.Schema$Label[] (with counts) into label count objects. */
322
+ static parseRawLabelCounts(rawLabels: gmail_v1.Schema$Label[], archiveEstimate: number | null): {
323
+ label: string;
324
+ count: number;
325
+ }[];
326
+ private static extractBodyStatic;
327
+ private static findBodyPartStatic;
328
+ private static extractAttachmentMetaStatic;
329
+ getEmailAliases(): Promise<Array<{
307
330
  email: string;
308
331
  name?: string;
309
332
  primary: boolean;
310
- }[]>;
333
+ }> | AuthError | ApiError>;
311
334
  listHistory({ startHistoryId, labelId, historyTypes, }: {
312
335
  startHistoryId: string;
313
336
  labelId?: string;
@@ -315,21 +338,23 @@ export declare class GmailClient {
315
338
  }): Promise<{
316
339
  history: gmail_v1.Schema$History[];
317
340
  historyId: string;
318
- }>;
319
- watch({ topicName, labelIds, }: {
320
- topicName: string;
321
- labelIds?: string[];
322
- }): Promise<{
323
- historyId: string;
324
- expiration: string;
325
- }>;
326
- stopWatch(): Promise<void>;
341
+ } | AuthError | ApiError>;
342
+ /**
343
+ * Poll for new messages using the Gmail History API.
344
+ * Yields WatchEvent objects as new messages arrive.
345
+ * Handles history seeding, expiry re-seeding, and client-side query filtering.
346
+ * Persists historyId in the DB so it survives across CLI invocations.
347
+ */
348
+ watchInbox({ folder, intervalMs, query, once, }?: {
349
+ folder?: string;
350
+ intervalMs?: number;
351
+ query?: string;
352
+ once?: boolean;
353
+ }): AsyncGenerator<WatchEvent>;
354
+ /** Single poll tick from pre-fetched history — yields WatchEvents for new messages. */
355
+ private pollOnceFromHistory;
327
356
  private parseMessage;
328
357
  private parseThreadListItem;
329
- private extractBody;
330
- private findBodyPart;
331
- private extractAttachmentMeta;
332
- private findAttachmentParts;
333
358
  private buildMimeMessage;
334
359
  /** Look up a label ID by name. Returns null if not found (never creates). */
335
360
  private lookupLabelId;