zele 0.3.16 → 0.3.20

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 (90) hide show
  1. package/README.md +155 -36
  2. package/dist/api-utils.d.ts +14 -0
  3. package/dist/api-utils.js +20 -0
  4. package/dist/api-utils.js.map +1 -1
  5. package/dist/auth.d.ts +71 -9
  6. package/dist/auth.js +186 -10
  7. package/dist/auth.js.map +1 -1
  8. package/dist/cli-types.d.ts +4 -0
  9. package/dist/cli-types.js +6 -0
  10. package/dist/cli-types.js.map +1 -0
  11. package/dist/cli.js +1 -5
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/attachment.d.ts +2 -2
  14. package/dist/commands/attachment.js +2 -0
  15. package/dist/commands/attachment.js.map +1 -1
  16. package/dist/commands/auth-cmd.d.ts +2 -2
  17. package/dist/commands/auth-cmd.js +104 -6
  18. package/dist/commands/auth-cmd.js.map +1 -1
  19. package/dist/commands/calendar.d.ts +2 -2
  20. package/dist/commands/calendar.js.map +1 -1
  21. package/dist/commands/draft.d.ts +2 -2
  22. package/dist/commands/draft.js +58 -4
  23. package/dist/commands/draft.js.map +1 -1
  24. package/dist/commands/filter.d.ts +2 -2
  25. package/dist/commands/filter.js +7 -2
  26. package/dist/commands/filter.js.map +1 -1
  27. package/dist/commands/label.d.ts +2 -2
  28. package/dist/commands/label.js +19 -9
  29. package/dist/commands/label.js.map +1 -1
  30. package/dist/commands/mail-actions.d.ts +2 -2
  31. package/dist/commands/mail-actions.js +290 -1
  32. package/dist/commands/mail-actions.js.map +1 -1
  33. package/dist/commands/mail.d.ts +2 -2
  34. package/dist/commands/mail.js +90 -23
  35. package/dist/commands/mail.js.map +1 -1
  36. package/dist/commands/profile.d.ts +2 -2
  37. package/dist/commands/profile.js +25 -18
  38. package/dist/commands/profile.js.map +1 -1
  39. package/dist/commands/watch.d.ts +2 -2
  40. package/dist/commands/watch.js.map +1 -1
  41. package/dist/db.js +24 -0
  42. package/dist/db.js.map +1 -1
  43. package/dist/generated/internal/class.js +2 -2
  44. package/dist/generated/internal/class.js.map +1 -1
  45. package/dist/generated/internal/prismaNamespace.d.ts +2 -0
  46. package/dist/generated/internal/prismaNamespace.js +2 -0
  47. package/dist/generated/internal/prismaNamespace.js.map +1 -1
  48. package/dist/generated/internal/prismaNamespaceBrowser.d.ts +2 -0
  49. package/dist/generated/internal/prismaNamespaceBrowser.js +2 -0
  50. package/dist/generated/internal/prismaNamespaceBrowser.js.map +1 -1
  51. package/dist/generated/models/Account.d.ts +97 -1
  52. package/dist/gmail-client.d.ts +73 -3
  53. package/dist/gmail-client.js +165 -5
  54. package/dist/gmail-client.js.map +1 -1
  55. package/dist/imap-smtp-client.d.ts +306 -0
  56. package/dist/imap-smtp-client.js +1349 -0
  57. package/dist/imap-smtp-client.js.map +1 -0
  58. package/dist/mail-tui.js.map +1 -1
  59. package/dist/unsubscribe.d.ts +76 -0
  60. package/dist/unsubscribe.js +224 -0
  61. package/dist/unsubscribe.js.map +1 -0
  62. package/package.json +6 -3
  63. package/schema.prisma +7 -5
  64. package/skills/zele/SKILL.md +26 -96
  65. package/src/api-utils.ts +20 -0
  66. package/src/auth.ts +282 -14
  67. package/src/cli-types.ts +8 -0
  68. package/src/cli.ts +2 -7
  69. package/src/commands/attachment.ts +3 -2
  70. package/src/commands/auth-cmd.ts +114 -8
  71. package/src/commands/calendar.ts +2 -2
  72. package/src/commands/draft.ts +65 -6
  73. package/src/commands/filter.ts +11 -5
  74. package/src/commands/label.ts +24 -13
  75. package/src/commands/mail-actions.ts +317 -5
  76. package/src/commands/mail.ts +97 -25
  77. package/src/commands/profile.ts +29 -19
  78. package/src/commands/watch.ts +2 -2
  79. package/src/db.ts +28 -0
  80. package/src/generated/internal/class.ts +2 -2
  81. package/src/generated/internal/prismaNamespace.ts +2 -0
  82. package/src/generated/internal/prismaNamespaceBrowser.ts +2 -0
  83. package/src/generated/models/Account.ts +97 -1
  84. package/src/gmail-client.test.ts +155 -2
  85. package/src/gmail-client.ts +258 -6
  86. package/src/imap-smtp-client.ts +1560 -0
  87. package/src/mail-tui.tsx +2 -1
  88. package/src/schema.sql +2 -0
  89. package/src/unsubscribe.test.ts +487 -0
  90. package/src/unsubscribe.ts +255 -0
@@ -1,7 +1,11 @@
1
- // Mail action commands: star, unstar, archive, trash, untrash, mark read/unread, spam, unspam, label modify.
1
+ // Mail action commands: star, unstar, archive, trash, untrash, mark read/unread,
2
+ // spam, unspam, label modify, unsubscribe.
2
3
  // Bulk operations on threads — cache invalidation is handled by the client methods.
3
4
  import { z } from 'zod';
5
+ import * as errore from 'errore';
4
6
  import { getClient } from '../auth.js';
7
+ import { UnsubscribeUnavailableError, UnsubscribeFailedError } from '../api-utils.js';
8
+ import { planUnsubscribe, } from '../unsubscribe.js';
5
9
  import * as out from '../output.js';
6
10
  import { handleCommandError } from '../output.js';
7
11
  // ---------------------------------------------------------------------------
@@ -90,5 +94,290 @@ export function registerMailActionCommands(cli) {
90
94
  out.printYaml(result);
91
95
  out.success(`Trashed ${result.count} spam thread(s)`);
92
96
  });
97
+ // =========================================================================
98
+ // mail unsubscribe — RFC 2369 + RFC 8058
99
+ // =========================================================================
100
+ //
101
+ // Reads List-Unsubscribe and List-Unsubscribe-Post from the latest non-draft
102
+ // message in a thread, then picks a mechanism:
103
+ // 1. RFC 8058 one-click (HTTPS POST with `List-Unsubscribe=One-Click`)
104
+ // 2. RFC 2369 mailto: (send the canonical unsubscribe email)
105
+ // 3. RFC 2369 http(s): landing page (manual — print URL only)
106
+ //
107
+ // The decision logic lives in ../unsubscribe.ts so it can be unit-tested
108
+ // with inline snapshots. This command is the thin executor: fetch thread,
109
+ // build plan, optionally dry-run, otherwise perform the chosen mechanism.
110
+ cli
111
+ .command('mail unsubscribe <threadId>', 'Unsubscribe from a mailing list thread (RFC 2369 / RFC 8058)')
112
+ .option('--via <via>', z.enum(['auto', 'one-click', 'mailto', 'url']).describe('Mechanism to use (default: auto — prefers one-click if DKIM passes, then mailto, then url)'))
113
+ .option('--dry-run', 'Print the unsubscribe plan without executing anything')
114
+ .option('--require-dkim', 'Refuse one-click unless the message has DKIM=pass')
115
+ .option('--then <then>', z.enum(['nothing', 'archive', 'trash']).describe('Follow-up action on the thread after unsubscribing (default: nothing)'))
116
+ .action(async (threadId, options) => {
117
+ const via = options.via ?? 'auto';
118
+ const then = options.then ?? 'nothing';
119
+ const { client } = await getClient(options.account);
120
+ const { parsed: thread } = await client.getThread({ threadId });
121
+ const nonDraft = thread.messages.filter((m) => !m.isDraft);
122
+ const latest = nonDraft[nonDraft.length - 1] ?? thread.messages[thread.messages.length - 1];
123
+ if (!latest) {
124
+ handleCommandError(new UnsubscribeUnavailableError({ threadId }));
125
+ }
126
+ // DKIM gating: per RFC 8058 §4 the signed headers SHOULD include
127
+ // List-Unsubscribe and List-Unsubscribe-Post. We can't verify the
128
+ // `h=` tag here, so we approximate with the MTA's DKIM verdict
129
+ // (`auth.dkim === 'pass'`). SPF and DMARC aren't relevant for this
130
+ // specific header-signing check, so we don't require `auth.authentic`.
131
+ const dkimPass = latest.auth ? latest.auth.dkim === 'pass' : null;
132
+ const plan = planUnsubscribe({
133
+ listUnsubscribe: latest.listUnsubscribe,
134
+ listUnsubscribePost: latest.listUnsubscribePost,
135
+ dkimAuthentic: dkimPass,
136
+ });
137
+ if (plan.mechanisms.length === 0) {
138
+ handleCommandError(new UnsubscribeUnavailableError({ threadId }));
139
+ }
140
+ const chosen = pickMechanism(plan, via, dkimPass);
141
+ if (chosen instanceof Error)
142
+ handleCommandError(chosen);
143
+ if (options.requireDkim && chosen.kind === 'one-click' && dkimPass !== true) {
144
+ handleCommandError(new UnsubscribeFailedError({
145
+ mechanism: 'one-click',
146
+ reason: dkimPass === false
147
+ ? 'DKIM did not pass and --require-dkim was set'
148
+ : 'DKIM status unknown and --require-dkim was set',
149
+ }));
150
+ }
151
+ if (options.dryRun) {
152
+ out.printYaml({
153
+ action: 'Unsubscribe (dry-run)',
154
+ thread_id: threadId,
155
+ chosen: describeMechanism(chosen),
156
+ plan: describePlan(plan),
157
+ });
158
+ return;
159
+ }
160
+ // Execute the chosen mechanism.
161
+ if (chosen.kind === 'one-click') {
162
+ const res = await oneClickPost(chosen.url);
163
+ if (res instanceof Error)
164
+ handleCommandError(res);
165
+ }
166
+ else if (chosen.kind === 'mailto') {
167
+ const sendResult = await client.sendMessage({
168
+ to: [{ email: chosen.mailto.to }],
169
+ subject: chosen.mailto.subject ?? 'unsubscribe',
170
+ body: chosen.mailto.body ?? 'unsubscribe',
171
+ cc: chosen.mailto.cc?.map((email) => ({ email })),
172
+ });
173
+ if (sendResult instanceof Error) {
174
+ handleCommandError(new UnsubscribeFailedError({
175
+ mechanism: 'mailto',
176
+ reason: sendResult.message,
177
+ cause: sendResult,
178
+ }));
179
+ }
180
+ }
181
+ else {
182
+ // url: cannot be executed programmatically — surface the URL and exit
183
+ // without marking success (nothing was actually done).
184
+ out.printYaml({
185
+ action: 'Unsubscribe (manual required)',
186
+ thread_id: threadId,
187
+ url: chosen.url,
188
+ note: 'Only a landing-page URL is available. Open it in a browser to complete unsubscription.',
189
+ plan: describePlan(plan),
190
+ });
191
+ out.hint('Open the printed URL in a browser to finish unsubscribing.');
192
+ return;
193
+ }
194
+ // Unsubscribe action itself succeeded at this point. Report that
195
+ // BEFORE attempting the follow-up so that a follow-up failure can't
196
+ // hide an already-completed (and irreversible) unsubscribe.
197
+ out.printYaml({
198
+ action: 'Unsubscribed',
199
+ thread_id: threadId,
200
+ mechanism: describeMechanism(chosen),
201
+ then,
202
+ plan: describePlan(plan),
203
+ });
204
+ out.success(`Unsubscribed via ${chosen.kind}`);
205
+ // Optional follow-up action on the thread. Failure here is non-fatal
206
+ // for the unsubscribe itself — warn and exit non-zero so scripts can
207
+ // still notice, but don't hide the success.
208
+ if (then === 'archive') {
209
+ const r = await client.archive({ threadIds: [threadId] });
210
+ if (r instanceof Error) {
211
+ out.error(`Unsubscribed, but follow-up archive failed: ${r.message}`);
212
+ process.exit(1);
213
+ }
214
+ }
215
+ else if (then === 'trash') {
216
+ const r = await client.trash({ threadId });
217
+ if (r instanceof Error) {
218
+ out.error(`Unsubscribed, but follow-up trash failed: ${r.message}`);
219
+ process.exit(1);
220
+ }
221
+ }
222
+ });
223
+ }
224
+ // ---------------------------------------------------------------------------
225
+ // Unsubscribe helpers
226
+ // ---------------------------------------------------------------------------
227
+ /** Pick a mechanism from a plan based on the --via flag.
228
+ *
229
+ * In `auto` mode we only use one-click if DKIM is known to pass, so a
230
+ * spoofed List-Unsubscribe-Post header on an unauthenticated message
231
+ * can't trigger a background POST. Users can still force it with
232
+ * `--via one-click` (and can combine with `--require-dkim` to re-gate). */
233
+ function pickMechanism(plan, via, dkimPass) {
234
+ if (via === 'auto') {
235
+ for (const m of plan.mechanisms) {
236
+ if (m.kind === 'one-click' && dkimPass !== true)
237
+ continue;
238
+ return m;
239
+ }
240
+ // Nothing usable in auto mode — the only remaining case is a plan made
241
+ // up entirely of one-click entries on a message we couldn't verify.
242
+ if (plan.mechanisms.length === 0) {
243
+ return new UnsubscribeFailedError({ mechanism: 'auto', reason: 'no mechanisms available' });
244
+ }
245
+ return new UnsubscribeFailedError({
246
+ mechanism: 'auto',
247
+ reason: 'only one-click is advertised, but DKIM did not pass. Re-run with --via one-click to force it.',
248
+ });
249
+ }
250
+ const match = plan.mechanisms.find((m) => m.kind === via);
251
+ if (match)
252
+ return match;
253
+ return new UnsubscribeFailedError({
254
+ mechanism: via,
255
+ reason: `no ${via} mechanism advertised by the sender`,
256
+ });
257
+ }
258
+ /** Reject URLs that point at localhost, loopback, or RFC1918 / link-local /
259
+ * ULA ranges. This is a best-effort SSRF guard — it won't catch DNS
260
+ * rebinding or hostnames that resolve to private IPs, but it stops the
261
+ * obvious cases of `https://127.0.0.1/unsubscribe` hidden in a spoofed
262
+ * List-Unsubscribe header. */
263
+ function isPrivateOrLoopbackHost(hostname) {
264
+ const h = hostname.toLowerCase().replace(/^\[|\]$/g, '');
265
+ if (h === 'localhost' || h.endsWith('.localhost') || h === 'ip6-localhost')
266
+ return true;
267
+ // IPv4 literal
268
+ const v4 = h.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
269
+ if (v4) {
270
+ const [a, b] = [Number(v4[1]), Number(v4[2])];
271
+ if (a === 10)
272
+ return true;
273
+ if (a === 127)
274
+ return true;
275
+ if (a === 0)
276
+ return true;
277
+ if (a === 169 && b === 254)
278
+ return true; // link-local
279
+ if (a === 172 && b >= 16 && b <= 31)
280
+ return true;
281
+ if (a === 192 && b === 168)
282
+ return true;
283
+ return false;
284
+ }
285
+ // IPv6 literal — reject loopback (::1), link-local (fe80::/10), ULA (fc00::/7).
286
+ if (h === '::1' || h === '0:0:0:0:0:0:0:1')
287
+ return true;
288
+ if (/^fe[89ab][0-9a-f]:/i.test(h))
289
+ return true;
290
+ if (/^f[cd][0-9a-f]{2}:/i.test(h))
291
+ return true;
292
+ return false;
293
+ }
294
+ /** Build an RFC 8058 one-click POST request and validate the response.
295
+ * Returns void on success, UnsubscribeFailedError on any failure. */
296
+ async function oneClickPost(url) {
297
+ // Re-validate the URL at the executor boundary (not just the planner),
298
+ // in case a future code path constructs a mechanism without going
299
+ // through planUnsubscribe.
300
+ let parsed;
301
+ try {
302
+ parsed = new URL(url);
303
+ }
304
+ catch (err) {
305
+ return new UnsubscribeFailedError({
306
+ mechanism: 'one-click',
307
+ reason: `invalid URL: ${String(err)}`,
308
+ cause: err,
309
+ });
310
+ }
311
+ if (parsed.protocol !== 'https:') {
312
+ return new UnsubscribeFailedError({
313
+ mechanism: 'one-click',
314
+ reason: `refusing to POST to ${parsed.protocol} URL (RFC 8058 requires https)`,
315
+ });
316
+ }
317
+ if (isPrivateOrLoopbackHost(parsed.hostname)) {
318
+ return new UnsubscribeFailedError({
319
+ mechanism: 'one-click',
320
+ reason: `refusing to POST to private/loopback host ${parsed.hostname} (SSRF guard)`,
321
+ });
322
+ }
323
+ // RFC 8058 §3.1: senders MUST NOT return redirects, so we refuse to follow.
324
+ // `redirect: 'manual'` lets us see 3xx status codes instead of auto-following.
325
+ // 10-second timeout keeps a slow/unreachable endpoint from hanging the CLI.
326
+ const res = await errore.tryAsync({
327
+ try: () => fetch(parsed, {
328
+ method: 'POST',
329
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
330
+ body: 'List-Unsubscribe=One-Click',
331
+ redirect: 'manual',
332
+ signal: AbortSignal.timeout(10_000),
333
+ }),
334
+ catch: (err) => new UnsubscribeFailedError({
335
+ mechanism: 'one-click',
336
+ reason: String(err),
337
+ cause: err,
338
+ }),
339
+ });
340
+ if (res instanceof Error)
341
+ return res;
342
+ if (res.status >= 200 && res.status < 300)
343
+ return undefined;
344
+ if (res.status >= 300 && res.status < 400) {
345
+ // RFC 8058 §3.1 says senders MUST NOT redirect, but many widely-deployed
346
+ // senders (ConvertKit, SendGrid, Mailchimp) redirect to a "you have been
347
+ // unsubscribed" confirmation page after processing the POST body. A POST
348
+ // that was going to be rejected would return 4xx, not 3xx — the server
349
+ // has to read and act on the body before deciding to redirect — so we
350
+ // treat 3xx as success with a warning printed to stderr.
351
+ const location = res.headers.get('location');
352
+ out.hint(`one-click endpoint returned HTTP ${res.status} redirect${location ? ` → ${location}` : ''} (RFC 8058 §3.1 forbids this, but many senders do it anyway). Treating as success.`);
353
+ return undefined;
354
+ }
355
+ return new UnsubscribeFailedError({
356
+ mechanism: 'one-click',
357
+ reason: `HTTP ${res.status} ${res.statusText}`,
358
+ });
359
+ }
360
+ /** YAML-friendly representation of a mechanism for output. */
361
+ function describeMechanism(m) {
362
+ if (m.kind === 'one-click')
363
+ return { kind: 'one-click', url: m.url };
364
+ if (m.kind === 'url')
365
+ return { kind: 'url', url: m.url };
366
+ return {
367
+ kind: 'mailto',
368
+ to: m.mailto.to,
369
+ ...(m.mailto.subject ? { subject: m.mailto.subject } : {}),
370
+ ...(m.mailto.body ? { body: m.mailto.body } : {}),
371
+ ...(m.mailto.cc && m.mailto.cc.length > 0 ? { cc: m.mailto.cc } : {}),
372
+ };
373
+ }
374
+ /** YAML-friendly representation of a full plan. */
375
+ function describePlan(plan) {
376
+ return {
377
+ mechanisms: plan.mechanisms.map(describeMechanism),
378
+ has_one_click: plan.hasOneClick,
379
+ dkim_authentic: plan.dkimAuthentic,
380
+ ...(plan.warnings.length > 0 ? { warnings: plan.warnings } : {}),
381
+ };
93
382
  }
94
383
  //# sourceMappingURL=mail-actions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mail-actions.js","sourceRoot":"","sources":["../../src/commands/mail-actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mail-actions.js","sourceRoot":"","sources":["../../src/commands/mail-actions.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import type { Goke } from 'goke';
2
- export declare function registerMailCommands(cli: Goke): void;
1
+ import type { ZeleCli } from '../cli-types.js';
2
+ export declare function registerMailCommands(cli: ZeleCli): void;
@@ -8,6 +8,7 @@ import path from 'node:path';
8
8
  import { lookup as mimeLookup } from 'mrmime';
9
9
  import { getClients, getClient } from '../auth.js';
10
10
  import { AuthError } from '../api-utils.js';
11
+ import { hasUnsubscribeMechanism, hasOneClickUnsubscribe } from '../unsubscribe.js';
11
12
  import * as out from '../output.js';
12
13
  import { handleCommandError } from '../output.js';
13
14
  import pc from 'picocolors';
@@ -43,7 +44,9 @@ export function registerMailCommands(cli) {
43
44
  .option('--label <label>', 'Filter by label name')
44
45
  .option('--filter <filter>', 'Gmail search filter (e.g. "is:unread", "from:github", "has:attachment")')
45
46
  .action(async (options) => {
46
- const folder = options.folder ?? 'inbox';
47
+ // `options.folder` / `options.max` are `string | undefined` now.
48
+ // `''` (bare flag) falls back to the default via `||`.
49
+ const folder = options.folder || 'inbox';
47
50
  const max = options.max ? Number(options.max) : 20;
48
51
  const clients = await getClients(options.account);
49
52
  if (options.page && clients.length > 1) {
@@ -51,20 +54,24 @@ export function registerMailCommands(cli) {
51
54
  process.exit(1);
52
55
  }
53
56
  // Fetch threads and labels from all accounts concurrently
54
- const results = await Promise.all(clients.map(async ({ email, client }) => {
55
- const [result, labelsResult] = await Promise.all([
56
- client.listThreads({
57
- folder,
58
- maxResults: max,
59
- labelIds: options.label ? [options.label] : undefined,
60
- pageToken: options.page,
61
- query: options.filter,
62
- }),
63
- client.listLabels(),
64
- ]);
57
+ const results = await Promise.all(clients.map(async ({ email, client, accountType }) => {
58
+ const result = await client.listThreads({
59
+ folder,
60
+ maxResults: max,
61
+ labelIds: options.label ? [options.label] : undefined,
62
+ pageToken: options.page,
63
+ query: options.filter,
64
+ });
65
65
  if (result instanceof Error)
66
66
  return result;
67
- const labelMap = labelsResult instanceof Error ? new Map() : new Map(labelsResult.parsed.map((l) => [l.id, l.name]));
67
+ // Labels are Google-only skip for IMAP accounts
68
+ let labelMap = new Map();
69
+ if (accountType === 'google') {
70
+ const labelsResult = await client.listLabels();
71
+ if (!(labelsResult instanceof Error)) {
72
+ labelMap = new Map(labelsResult.parsed.map((l) => [l.id, l.name]));
73
+ }
74
+ }
68
75
  return { email, result, labelMap };
69
76
  }));
70
77
  const allResults = results.filter((r) => {
@@ -97,6 +104,8 @@ export function registerMailCommands(cli) {
97
104
  const to = t.to.map((s) => out.formatSender(s)).join(', ');
98
105
  const cc = t.cc.map((s) => out.formatSender(s)).join(', ');
99
106
  const labels = formatLabels(t.labelIds, labelMap);
107
+ const canUnsubscribe = hasUnsubscribeMechanism(t.listUnsubscribe);
108
+ const oneClick = hasOneClickUnsubscribe(t.listUnsubscribe, t.listUnsubscribePost);
100
109
  return {
101
110
  ...(showAccount ? { account: t.account } : {}),
102
111
  id: t.id,
@@ -109,6 +118,8 @@ export function registerMailCommands(cli) {
109
118
  date: out.formatDate(t.date),
110
119
  messages: t.messageCount,
111
120
  ...(labels ? { labels } : {}),
121
+ ...(canUnsubscribe ? { can_unsubscribe: true } : {}),
122
+ ...(oneClick ? { one_click: true } : {}),
112
123
  ...(t.listUnsubscribe ? { unsubscribe: t.listUnsubscribe } : {}),
113
124
  };
114
125
  }), { summary: `${merged.length} threads (${folder})`, nextPage: allResults[0]?.result.nextPageToken });
@@ -128,18 +139,21 @@ export function registerMailCommands(cli) {
128
139
  process.exit(1);
129
140
  }
130
141
  // Search all accounts concurrently (fetch labels alongside for name resolution)
131
- const results = await Promise.all(clients.map(async ({ email, client }) => {
132
- const [result, labelsResult] = await Promise.all([
133
- client.listThreads({
134
- query,
135
- maxResults: max,
136
- pageToken: options.page,
137
- }),
138
- client.listLabels(),
139
- ]);
142
+ const results = await Promise.all(clients.map(async ({ email, client, accountType }) => {
143
+ const result = await client.listThreads({
144
+ query,
145
+ maxResults: max,
146
+ pageToken: options.page,
147
+ });
140
148
  if (result instanceof Error)
141
149
  return result;
142
- const labelMap = labelsResult instanceof Error ? new Map() : new Map(labelsResult.parsed.map((l) => [l.id, l.name]));
150
+ let labelMap = new Map();
151
+ if (accountType === 'google') {
152
+ const labelsResult = await client.listLabels();
153
+ if (!(labelsResult instanceof Error)) {
154
+ labelMap = new Map(labelsResult.parsed.map((l) => [l.id, l.name]));
155
+ }
156
+ }
143
157
  return { email, result, labelMap };
144
158
  }));
145
159
  const allResults = results.filter((r) => {
@@ -171,6 +185,8 @@ export function registerMailCommands(cli) {
171
185
  const to = t.to.map((s) => out.formatSender(s)).join(', ');
172
186
  const cc = t.cc.map((s) => out.formatSender(s)).join(', ');
173
187
  const labels = formatLabels(t.labelIds, labelMap);
188
+ const canUnsubscribe = hasUnsubscribeMechanism(t.listUnsubscribe);
189
+ const oneClick = hasOneClickUnsubscribe(t.listUnsubscribe, t.listUnsubscribePost);
174
190
  return {
175
191
  ...(showAccount ? { account: t.account } : {}),
176
192
  id: t.id,
@@ -183,6 +199,8 @@ export function registerMailCommands(cli) {
183
199
  date: out.formatDate(t.date),
184
200
  messages: t.messageCount,
185
201
  ...(labels ? { labels } : {}),
202
+ ...(canUnsubscribe ? { can_unsubscribe: true } : {}),
203
+ ...(oneClick ? { one_click: true } : {}),
186
204
  ...(t.listUnsubscribe ? { unsubscribe: t.listUnsubscribe } : {}),
187
205
  };
188
206
  }), { summary: `${merged.length} results for "${query}"`, nextPage: allResults[0]?.result.nextPageToken });
@@ -194,6 +212,7 @@ export function registerMailCommands(cli) {
194
212
  .command('mail read [...threadIds]', 'Read full email threads (does not mark as read)')
195
213
  .option('--raw', 'Show raw message (first message only, single thread)')
196
214
  .option('--raw-html', 'Show raw HTML body per message (no markdown conversion)')
215
+ .option('--verify', 'Show expanded email authentication details (SPF/DKIM/DMARC)')
197
216
  .action(async (threadIds, options) => {
198
217
  if (threadIds.length === 0) {
199
218
  out.error('No thread IDs provided');
@@ -286,6 +305,23 @@ export function registerMailCommands(cli) {
286
305
  console.log(pc.dim(` Cc: ${msg.cc.map((c) => c.email).join(', ')}`));
287
306
  }
288
307
  console.log(pc.dim(`Date: ${dateStr}`));
308
+ if (msg.auth) {
309
+ const check = (verdict) => {
310
+ return verdict === 'pass'
311
+ ? pc.green('✓')
312
+ : pc.red('✗');
313
+ };
314
+ const parts = [
315
+ `${check(msg.auth.spf)} SPF`,
316
+ `${check(msg.auth.dkim)} DKIM`,
317
+ `${check(msg.auth.dmarc)} DMARC`,
318
+ ];
319
+ const label = msg.auth.authentic ? pc.green('authentic') : pc.red('UNVERIFIED');
320
+ console.log(`Auth: ${parts.join(' ')} (${label})`);
321
+ if (options.verify) {
322
+ console.log(pc.dim(` Raw: ${msg.auth.raw}`));
323
+ }
324
+ }
289
325
  if (msg.attachments.length > 0) {
290
326
  const attList = msg.attachments.map((a) => {
291
327
  const size = a.size < 1024 ? `${a.size} B`
@@ -367,6 +403,8 @@ export function registerMailCommands(cli) {
367
403
  fromEmail: options.from,
368
404
  attachments,
369
405
  });
406
+ if (result instanceof Error)
407
+ handleCommandError(result);
370
408
  out.printYaml(result);
371
409
  out.success(`Sent to ${options.to}`);
372
410
  });
@@ -380,6 +418,7 @@ export function registerMailCommands(cli) {
380
418
  .option('--cc <cc>', z.string().describe('Additional CC recipients'))
381
419
  .option('--all', 'Reply all (include all original recipients)')
382
420
  .option('--from <from>', z.string().describe('Send-as alias email'))
421
+ .option('--draft', 'Save as draft instead of sending')
383
422
  .action(async (threadId, options) => {
384
423
  let body = options.body ?? '';
385
424
  if (options.bodyFile) {
@@ -402,6 +441,20 @@ export function registerMailCommands(cli) {
402
441
  const cc = options.cc
403
442
  ? options.cc.split(',').map((e) => ({ email: e.trim() })).filter((e) => e.email)
404
443
  : undefined;
444
+ if (options.draft) {
445
+ const result = await client.createDraftReply({
446
+ threadId,
447
+ body,
448
+ replyAll: options.all,
449
+ cc,
450
+ fromEmail: options.from,
451
+ });
452
+ if (result instanceof Error)
453
+ handleCommandError(result);
454
+ out.printYaml(result);
455
+ out.success('Reply draft created');
456
+ return;
457
+ }
405
458
  const result = await client.replyToThread({
406
459
  threadId,
407
460
  body,
@@ -422,6 +475,7 @@ export function registerMailCommands(cli) {
422
475
  .option('--to <to>', z.string().describe('Forward recipient(s), comma-separated'))
423
476
  .option('--body <body>', z.string().describe('Optional message to prepend'))
424
477
  .option('--from <from>', z.string().describe('Send-as alias email'))
478
+ .option('--draft', 'Save as draft instead of sending')
425
479
  .action(async (threadId, options) => {
426
480
  if (!options.to) {
427
481
  out.error('--to is required');
@@ -432,6 +486,19 @@ export function registerMailCommands(cli) {
432
486
  .map((e) => ({ email: e.trim() }))
433
487
  .filter((e) => e.email);
434
488
  const { client } = await getClient(options.account);
489
+ if (options.draft) {
490
+ const result = await client.createDraftForward({
491
+ threadId,
492
+ to: recipients,
493
+ body: options.body,
494
+ fromEmail: options.from,
495
+ });
496
+ if (result instanceof Error)
497
+ handleCommandError(result);
498
+ out.printYaml(result);
499
+ out.success('Forward draft created');
500
+ return;
501
+ }
435
502
  const result = await client.forwardThread({
436
503
  threadId,
437
504
  to: recipients,