zele 0.3.17 → 0.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -12
- package/dist/api-utils.d.ts +10 -0
- package/dist/api-utils.js +14 -0
- package/dist/api-utils.js.map +1 -1
- package/dist/cli-types.d.ts +4 -0
- package/dist/cli-types.js +6 -0
- package/dist/cli-types.js.map +1 -0
- package/dist/cli.js +1 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/attachment.d.ts +2 -2
- package/dist/commands/attachment.js.map +1 -1
- package/dist/commands/auth-cmd.d.ts +2 -2
- package/dist/commands/auth-cmd.js +58 -52
- package/dist/commands/auth-cmd.js.map +1 -1
- package/dist/commands/calendar.d.ts +2 -2
- package/dist/commands/calendar.js +13 -14
- package/dist/commands/calendar.js.map +1 -1
- package/dist/commands/draft.d.ts +2 -2
- package/dist/commands/draft.js +62 -15
- package/dist/commands/draft.js.map +1 -1
- package/dist/commands/filter.d.ts +2 -2
- package/dist/commands/filter.js.map +1 -1
- package/dist/commands/label.d.ts +2 -2
- package/dist/commands/label.js +5 -6
- package/dist/commands/label.js.map +1 -1
- package/dist/commands/mail-actions.d.ts +2 -2
- package/dist/commands/mail-actions.js +290 -1
- package/dist/commands/mail-actions.js.map +1 -1
- package/dist/commands/mail.d.ts +2 -2
- package/dist/commands/mail.js +50 -10
- package/dist/commands/mail.js.map +1 -1
- package/dist/commands/profile.d.ts +2 -2
- package/dist/commands/profile.js.map +1 -1
- package/dist/commands/watch.d.ts +2 -2
- package/dist/commands/watch.js +2 -2
- package/dist/commands/watch.js.map +1 -1
- package/dist/gmail-client.d.ts +59 -3
- package/dist/gmail-client.js +119 -5
- package/dist/gmail-client.js.map +1 -1
- package/dist/imap-smtp-client.d.ts +75 -4
- package/dist/imap-smtp-client.js +131 -7
- package/dist/imap-smtp-client.js.map +1 -1
- package/dist/unsubscribe.d.ts +76 -0
- package/dist/unsubscribe.js +224 -0
- package/dist/unsubscribe.js.map +1 -0
- package/package.json +3 -2
- package/skills/zele/SKILL.md +32 -124
- package/src/api-utils.ts +14 -0
- package/src/cli-types.ts +8 -0
- package/src/cli.ts +2 -7
- package/src/commands/attachment.ts +2 -2
- package/src/commands/auth-cmd.ts +66 -56
- package/src/commands/calendar.ts +15 -16
- package/src/commands/draft.ts +71 -17
- package/src/commands/filter.ts +2 -2
- package/src/commands/label.ts +7 -8
- package/src/commands/mail-actions.ts +315 -4
- package/src/commands/mail.ts +54 -12
- package/src/commands/profile.ts +2 -2
- package/src/commands/watch.ts +4 -4
- package/src/gmail-client.ts +193 -6
- package/src/imap-smtp-client.ts +186 -7
- package/src/unsubscribe.test.ts +487 -0
- package/src/unsubscribe.ts +255 -0
package/src/commands/auth-cmd.ts
CHANGED
|
@@ -2,71 +2,83 @@
|
|
|
2
2
|
// Manages authentication for zele (Google OAuth and IMAP/SMTP credentials).
|
|
3
3
|
// Supports multiple accounts: login adds accounts, logout removes one.
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { ZeleCli } from '../cli-types.js'
|
|
6
6
|
import { z } from 'zod'
|
|
7
7
|
import pc from 'picocolors'
|
|
8
|
+
import * as clack from '@clack/prompts'
|
|
8
9
|
import { login, loginImap, logout, listAccounts, getAuthStatuses } from '../auth.js'
|
|
9
10
|
import { closePrisma } from '../db.js'
|
|
10
11
|
import * as out from '../output.js'
|
|
11
12
|
import { handleCommandError } from '../output.js'
|
|
12
13
|
|
|
13
|
-
export function registerAuthCommands(cli:
|
|
14
|
+
export function registerAuthCommands(cli: ZeleCli) {
|
|
14
15
|
cli
|
|
15
16
|
.command('login', 'Authenticate with Google (opens browser) or show IMAP/SMTP login instructions')
|
|
16
|
-
.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
.option(
|
|
18
|
+
'--method <method>',
|
|
19
|
+
z.enum(['google', 'imap']).optional().describe('Authentication method (google or imap)'),
|
|
20
|
+
)
|
|
21
|
+
.action(async (options) => {
|
|
22
|
+
let method = options.method
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
if (!method) {
|
|
25
|
+
if (!process.stdin.isTTY) {
|
|
26
|
+
out.error('Run non-interactively with: zele login --method google|imap')
|
|
27
|
+
process.exit(1)
|
|
28
|
+
}
|
|
25
29
|
|
|
26
|
-
const
|
|
27
|
-
|
|
30
|
+
const choice = await clack.select({
|
|
31
|
+
message: 'Choose authentication method',
|
|
32
|
+
options: [
|
|
33
|
+
{ value: 'google', label: 'Google', hint: 'opens browser for OAuth' },
|
|
34
|
+
{ value: 'imap', label: 'Other', hint: 'IMAP/SMTP with password' },
|
|
35
|
+
],
|
|
28
36
|
})
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (choice === '2') {
|
|
34
|
-
console.error(pc.bold('\nTo add an IMAP/SMTP account, run:\n'))
|
|
35
|
-
console.error(pc.dim(' # Fastmail'))
|
|
36
|
-
console.error(` zele login imap \\`)
|
|
37
|
-
console.error(` --email you@fastmail.com \\`)
|
|
38
|
-
console.error(` --imap-host imap.fastmail.com --imap-port 993 \\`)
|
|
39
|
-
console.error(` --smtp-host smtp.fastmail.com --smtp-port 465 \\`)
|
|
40
|
-
console.error(` --password "your-app-password"`)
|
|
41
|
-
console.error()
|
|
42
|
-
console.error(pc.dim(' # Gmail (app password)'))
|
|
43
|
-
console.error(` zele login imap \\`)
|
|
44
|
-
console.error(` --email you@gmail.com \\`)
|
|
45
|
-
console.error(` --imap-host imap.gmail.com --imap-port 993 \\`)
|
|
46
|
-
console.error(` --smtp-host smtp.gmail.com --smtp-port 465 \\`)
|
|
47
|
-
console.error(` --password "your-app-password"`)
|
|
48
|
-
console.error()
|
|
49
|
-
console.error(pc.dim(' # Outlook/Hotmail'))
|
|
50
|
-
console.error(` zele login imap \\`)
|
|
51
|
-
console.error(` --email you@outlook.com \\`)
|
|
52
|
-
console.error(` --imap-host outlook.office365.com --imap-port 993 \\`)
|
|
53
|
-
console.error(` --smtp-host smtp-mail.outlook.com --smtp-port 587 \\`)
|
|
54
|
-
console.error(` --password "your-password"`)
|
|
55
|
-
console.error()
|
|
56
|
-
console.error(pc.dim(' # Generic (any IMAP/SMTP provider)'))
|
|
57
|
-
console.error(` zele login imap \\`)
|
|
58
|
-
console.error(` --email you@example.com \\`)
|
|
59
|
-
console.error(` --imap-host imap.example.com --imap-port 993 \\`)
|
|
60
|
-
console.error(` --smtp-host smtp.example.com --smtp-port 465 \\`)
|
|
61
|
-
console.error(` --password "your-password"`)
|
|
62
|
-
console.error()
|
|
63
|
-
console.error(pc.dim('Omit --smtp-host for read-only (IMAP only, no sending).'))
|
|
64
|
-
console.error(pc.dim('Use --imap-user/--smtp-user if the login username differs from your email.'))
|
|
65
|
-
return
|
|
37
|
+
|
|
38
|
+
if (clack.isCancel(choice)) {
|
|
39
|
+
out.hint('Cancelled')
|
|
40
|
+
process.exit(0)
|
|
66
41
|
}
|
|
42
|
+
|
|
43
|
+
method = choice
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (method === 'imap') {
|
|
47
|
+
console.error(pc.bold('\nTo add an IMAP/SMTP account, run:\n'))
|
|
48
|
+
console.error(pc.dim(' # Fastmail'))
|
|
49
|
+
console.error(` zele login imap \\`)
|
|
50
|
+
console.error(` --email you@fastmail.com \\`)
|
|
51
|
+
console.error(` --imap-host imap.fastmail.com --imap-port 993 \\`)
|
|
52
|
+
console.error(` --smtp-host smtp.fastmail.com --smtp-port 465 \\`)
|
|
53
|
+
console.error(` --password "your-app-password"`)
|
|
54
|
+
console.error()
|
|
55
|
+
console.error(pc.dim(' # Gmail (app password)'))
|
|
56
|
+
console.error(` zele login imap \\`)
|
|
57
|
+
console.error(` --email you@gmail.com \\`)
|
|
58
|
+
console.error(` --imap-host imap.gmail.com --imap-port 993 \\`)
|
|
59
|
+
console.error(` --smtp-host smtp.gmail.com --smtp-port 465 \\`)
|
|
60
|
+
console.error(` --password "your-app-password"`)
|
|
61
|
+
console.error()
|
|
62
|
+
console.error(pc.dim(' # Outlook/Hotmail'))
|
|
63
|
+
console.error(` zele login imap \\`)
|
|
64
|
+
console.error(` --email you@outlook.com \\`)
|
|
65
|
+
console.error(` --imap-host outlook.office365.com --imap-port 993 \\`)
|
|
66
|
+
console.error(` --smtp-host smtp-mail.outlook.com --smtp-port 587 \\`)
|
|
67
|
+
console.error(` --password "your-password"`)
|
|
68
|
+
console.error()
|
|
69
|
+
console.error(pc.dim(' # Generic (any IMAP/SMTP provider)'))
|
|
70
|
+
console.error(` zele login imap \\`)
|
|
71
|
+
console.error(` --email you@example.com \\`)
|
|
72
|
+
console.error(` --imap-host imap.example.com --imap-port 993 \\`)
|
|
73
|
+
console.error(` --smtp-host smtp.example.com --smtp-port 465 \\`)
|
|
74
|
+
console.error(` --password "your-password"`)
|
|
75
|
+
console.error()
|
|
76
|
+
console.error(pc.dim('Omit --smtp-host for read-only (IMAP only, no sending).'))
|
|
77
|
+
console.error(pc.dim('Use --imap-user/--smtp-user if the login username differs from your email.'))
|
|
78
|
+
return
|
|
67
79
|
}
|
|
68
80
|
|
|
69
|
-
//
|
|
81
|
+
// Google OAuth flow
|
|
70
82
|
const result = await login()
|
|
71
83
|
if (result instanceof Error) handleCommandError(result)
|
|
72
84
|
const { email } = result
|
|
@@ -162,14 +174,12 @@ export function registerAuthCommands(cli: Goke) {
|
|
|
162
174
|
process.exit(1)
|
|
163
175
|
}
|
|
164
176
|
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
rl.question(`Remove credentials for ${targetEmail}? [y/N] `, resolve)
|
|
177
|
+
const confirmed = await clack.confirm({
|
|
178
|
+
message: `Remove credentials for ${targetEmail}?`,
|
|
179
|
+
initialValue: false,
|
|
169
180
|
})
|
|
170
|
-
rl.close()
|
|
171
181
|
|
|
172
|
-
if (
|
|
182
|
+
if (clack.isCancel(confirmed) || !confirmed) {
|
|
173
183
|
out.hint('Cancelled')
|
|
174
184
|
return
|
|
175
185
|
}
|
package/src/commands/calendar.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// Cache is handled by the client — commands just call methods and use data.
|
|
4
4
|
// Multi-account: list/events fetch all accounts concurrently and merge by start time.
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { ZeleCli } from '../cli-types.js'
|
|
7
7
|
import { z } from 'zod'
|
|
8
|
-
import
|
|
8
|
+
import * as clack from '@clack/prompts'
|
|
9
9
|
import { getCalendarClients, getCalendarClient } from '../auth.js'
|
|
10
10
|
import type { CalendarClient, CalendarEvent, CalendarListItem, EventListResult } from '../calendar-client.js'
|
|
11
11
|
import { AuthError } from '../api-utils.js'
|
|
@@ -17,7 +17,7 @@ import { resolveTimeRange, parseTimeExpression, parseDuration, isDateOnly } from
|
|
|
17
17
|
// Register commands
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
|
|
20
|
-
export function registerCalendarCommands(cli:
|
|
20
|
+
export function registerCalendarCommands(cli: ZeleCli) {
|
|
21
21
|
// =========================================================================
|
|
22
22
|
// cal list
|
|
23
23
|
// =========================================================================
|
|
@@ -76,11 +76,11 @@ export function registerCalendarCommands(cli: Goke) {
|
|
|
76
76
|
.option('--week', 'Show this week')
|
|
77
77
|
.option('--days <days>', z.number().describe('Show next N days'))
|
|
78
78
|
.option('--all', 'Fetch from all calendars')
|
|
79
|
-
.option('--
|
|
80
|
-
.option('--
|
|
79
|
+
.option('--filter <filter>', 'Free text search')
|
|
80
|
+
.option('--limit [limit]', 'Max results (default: 20)')
|
|
81
81
|
.option('--page <page>', 'Pagination token (requires --account, only works for a single account)')
|
|
82
82
|
.action(async (options) => {
|
|
83
|
-
const
|
|
83
|
+
const limit = options.limit ? Number(options.limit) : 20
|
|
84
84
|
const calendarId = options.calendar ?? 'primary'
|
|
85
85
|
|
|
86
86
|
if (options.all && options.calendar) {
|
|
@@ -122,8 +122,8 @@ export function registerCalendarCommands(cli: Goke) {
|
|
|
122
122
|
calendarId: cal.id,
|
|
123
123
|
timeMin,
|
|
124
124
|
timeMax,
|
|
125
|
-
query: options.
|
|
126
|
-
maxResults:
|
|
125
|
+
query: options.filter,
|
|
126
|
+
maxResults: limit,
|
|
127
127
|
})
|
|
128
128
|
if (r instanceof Error) return r
|
|
129
129
|
return r.events.map((e) => ({ ...e, calendarId: cal.id }))
|
|
@@ -145,8 +145,8 @@ export function registerCalendarCommands(cli: Goke) {
|
|
|
145
145
|
calendarId,
|
|
146
146
|
timeMin,
|
|
147
147
|
timeMax,
|
|
148
|
-
query: options.
|
|
149
|
-
maxResults:
|
|
148
|
+
query: options.filter,
|
|
149
|
+
maxResults: limit,
|
|
150
150
|
pageToken: options.page,
|
|
151
151
|
})
|
|
152
152
|
if (r instanceof Error) return r
|
|
@@ -171,7 +171,7 @@ export function registerCalendarCommands(cli: Goke) {
|
|
|
171
171
|
result.events.map((e) => ({ ...e, account: email })),
|
|
172
172
|
)
|
|
173
173
|
.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime())
|
|
174
|
-
.slice(0,
|
|
174
|
+
.slice(0, limit)
|
|
175
175
|
|
|
176
176
|
if (merged.length === 0) {
|
|
177
177
|
out.printList([], { summary: 'No events found' })
|
|
@@ -418,13 +418,12 @@ export function registerCalendarCommands(cli: Goke) {
|
|
|
418
418
|
const { client } = await getCalendarClient(options.account)
|
|
419
419
|
|
|
420
420
|
if (!options.force && process.stdin.isTTY) {
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
421
|
+
const confirmed = await clack.confirm({
|
|
422
|
+
message: `Delete event ${eventId}?`,
|
|
423
|
+
initialValue: false,
|
|
424
424
|
})
|
|
425
|
-
rl.close()
|
|
426
425
|
|
|
427
|
-
if (
|
|
426
|
+
if (clack.isCancel(confirmed) || !confirmed) {
|
|
428
427
|
out.hint('Cancelled')
|
|
429
428
|
return
|
|
430
429
|
}
|
package/src/commands/draft.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
// Cache invalidation is handled by the client (sendDraft invalidates threadLists).
|
|
4
4
|
// Multi-account: list fetches all accounts concurrently and merges by date.
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { ZeleCli } from '../cli-types.js'
|
|
7
7
|
import { z } from 'zod'
|
|
8
8
|
import fs from 'node:fs'
|
|
9
|
+
import * as clack from '@clack/prompts'
|
|
9
10
|
import { getClients, getClient } from '../auth.js'
|
|
10
11
|
import type { GmailClient } from '../gmail-client.js'
|
|
11
12
|
import type { ImapSmtpClient } from '../imap-smtp-client.js'
|
|
@@ -14,16 +15,16 @@ import * as out from '../output.js'
|
|
|
14
15
|
import { handleCommandError } from '../output.js'
|
|
15
16
|
import pc from 'picocolors'
|
|
16
17
|
|
|
17
|
-
export function registerDraftCommands(cli:
|
|
18
|
+
export function registerDraftCommands(cli: ZeleCli) {
|
|
18
19
|
// =========================================================================
|
|
19
20
|
// draft list
|
|
20
21
|
// =========================================================================
|
|
21
22
|
|
|
22
23
|
cli
|
|
23
24
|
.command('draft list', 'List drafts')
|
|
24
|
-
.option('--
|
|
25
|
+
.option('--limit <limit>', z.number().default(20).describe('Max results'))
|
|
25
26
|
.option('--page <page>', z.string().describe('Pagination token (requires --account, only works for a single account)'))
|
|
26
|
-
.option('--
|
|
27
|
+
.option('--filter <filter>', z.string().describe('Search query'))
|
|
27
28
|
.action(async (options) => {
|
|
28
29
|
const clients = await getClients(options.account)
|
|
29
30
|
|
|
@@ -36,8 +37,8 @@ export function registerDraftCommands(cli: Goke) {
|
|
|
36
37
|
const results = await Promise.all(
|
|
37
38
|
clients.map(async ({ email, client }) => {
|
|
38
39
|
const result = await client.listDrafts({
|
|
39
|
-
query: options.
|
|
40
|
-
maxResults: options.
|
|
40
|
+
query: options.filter,
|
|
41
|
+
maxResults: options.limit,
|
|
41
42
|
pageToken: options.page,
|
|
42
43
|
})
|
|
43
44
|
if (result instanceof Error) return result
|
|
@@ -51,13 +52,13 @@ export function registerDraftCommands(cli: Goke) {
|
|
|
51
52
|
return true
|
|
52
53
|
})
|
|
53
54
|
|
|
54
|
-
// Merge drafts from all accounts, sorted by date descending, capped at
|
|
55
|
+
// Merge drafts from all accounts, sorted by date descending, capped at limit
|
|
55
56
|
const merged = allResults
|
|
56
57
|
.flatMap(({ email, result }) =>
|
|
57
58
|
result.drafts.map((d) => ({ ...d, account: email })),
|
|
58
59
|
)
|
|
59
60
|
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
|
|
60
|
-
.slice(0, options.
|
|
61
|
+
.slice(0, options.limit)
|
|
61
62
|
|
|
62
63
|
if (merged.length === 0) {
|
|
63
64
|
out.printList([], { summary: 'No drafts found' })
|
|
@@ -89,14 +90,17 @@ export function registerDraftCommands(cli: Goke) {
|
|
|
89
90
|
const draft = await client.getDraft({ draftId })
|
|
90
91
|
if (draft instanceof Error) handleCommandError(draft)
|
|
91
92
|
|
|
93
|
+
const fmtRecipients = (list: Array<{ name?: string; email: string }>) =>
|
|
94
|
+
list.map((r) => r.name && r.name !== r.email ? `${r.name} <${r.email}>` : r.email).join(', ')
|
|
95
|
+
|
|
92
96
|
console.log(pc.bold(`Draft: ${draft.message.subject}`))
|
|
93
97
|
console.log(pc.dim(`Draft ID: ${draft.id}`))
|
|
94
|
-
console.log(`To: ${draft.to
|
|
98
|
+
console.log(`To: ${fmtRecipients(draft.to) || '(none)'}`)
|
|
95
99
|
if (draft.cc.length > 0) {
|
|
96
|
-
console.log(`Cc: ${draft.cc
|
|
100
|
+
console.log(`Cc: ${fmtRecipients(draft.cc)}`)
|
|
97
101
|
}
|
|
98
102
|
if (draft.bcc.length > 0) {
|
|
99
|
-
console.log(`Bcc: ${draft.bcc
|
|
103
|
+
console.log(`Bcc: ${fmtRecipients(draft.bcc)}`)
|
|
100
104
|
}
|
|
101
105
|
console.log()
|
|
102
106
|
|
|
@@ -161,6 +165,58 @@ export function registerDraftCommands(cli: Goke) {
|
|
|
161
165
|
out.success('Draft created')
|
|
162
166
|
})
|
|
163
167
|
|
|
168
|
+
// =========================================================================
|
|
169
|
+
// draft update
|
|
170
|
+
// =========================================================================
|
|
171
|
+
|
|
172
|
+
cli
|
|
173
|
+
.command('draft update <draftId>', 'Update an existing draft')
|
|
174
|
+
.option('--to <to>', z.string().describe('New recipient email(s), comma-separated'))
|
|
175
|
+
.option('--subject <subject>', z.string().describe('New subject'))
|
|
176
|
+
.option('--body <body>', z.string().describe('New body text'))
|
|
177
|
+
.option('--body-file <bodyFile>', z.string().describe('Read new body from file (use - for stdin)'))
|
|
178
|
+
.option('--cc <cc>', z.string().describe('New CC recipients (comma-separated)'))
|
|
179
|
+
.option('--bcc <bcc>', z.string().describe('New BCC recipients (comma-separated)'))
|
|
180
|
+
.option('--from <from>', z.string().describe('Send-as alias email'))
|
|
181
|
+
.action(async (draftId, options) => {
|
|
182
|
+
const { client } = await getClient(options.account)
|
|
183
|
+
|
|
184
|
+
// Fetch existing draft to merge unchanged fields
|
|
185
|
+
const existing = await client.getDraft({ draftId })
|
|
186
|
+
if (existing instanceof Error) handleCommandError(existing)
|
|
187
|
+
|
|
188
|
+
let body = options.body
|
|
189
|
+
if (options.bodyFile) {
|
|
190
|
+
if (options.bodyFile === '-') {
|
|
191
|
+
const chunks: Buffer[] = []
|
|
192
|
+
for await (const chunk of process.stdin) {
|
|
193
|
+
chunks.push(chunk)
|
|
194
|
+
}
|
|
195
|
+
body = Buffer.concat(chunks).toString('utf-8')
|
|
196
|
+
} else {
|
|
197
|
+
body = fs.readFileSync(options.bodyFile, 'utf-8')
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const parseEmails = (str: string) =>
|
|
202
|
+
str.split(',').map((e) => e.trim()).filter(Boolean).map((email) => ({ email }))
|
|
203
|
+
|
|
204
|
+
const result = await client.updateDraft({
|
|
205
|
+
draftId,
|
|
206
|
+
to: options.to ? parseEmails(options.to) : existing.to,
|
|
207
|
+
subject: options.subject ?? existing.message.subject,
|
|
208
|
+
body: body ?? existing.message.body,
|
|
209
|
+
cc: options.cc ? parseEmails(options.cc) : existing.cc,
|
|
210
|
+
bcc: options.bcc ? parseEmails(options.bcc) : existing.bcc,
|
|
211
|
+
threadId: existing.message.threadId || undefined,
|
|
212
|
+
fromEmail: options.from ?? existing.message.from.email,
|
|
213
|
+
})
|
|
214
|
+
if (result instanceof Error) handleCommandError(result)
|
|
215
|
+
|
|
216
|
+
out.printYaml(result)
|
|
217
|
+
out.success('Draft updated')
|
|
218
|
+
})
|
|
219
|
+
|
|
164
220
|
// =========================================================================
|
|
165
221
|
// draft send
|
|
166
222
|
// =========================================================================
|
|
@@ -185,14 +241,12 @@ export function registerDraftCommands(cli: Goke) {
|
|
|
185
241
|
.option('--force', 'Skip confirmation')
|
|
186
242
|
.action(async (draftId, options) => {
|
|
187
243
|
if (!options.force && process.stdin.isTTY) {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
rl.question(`Delete draft ${draftId}? [y/N] `, resolve)
|
|
244
|
+
const confirmed = await clack.confirm({
|
|
245
|
+
message: `Delete draft ${draftId}?`,
|
|
246
|
+
initialValue: false,
|
|
192
247
|
})
|
|
193
|
-
rl.close()
|
|
194
248
|
|
|
195
|
-
if (
|
|
249
|
+
if (clack.isCancel(confirmed) || !confirmed) {
|
|
196
250
|
out.hint('Cancelled')
|
|
197
251
|
return
|
|
198
252
|
}
|
package/src/commands/filter.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// Filter commands: list, create, delete Gmail filters.
|
|
2
2
|
// Multi-account support via getClients/getClient like label.ts.
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { ZeleCli } from '../cli-types.js'
|
|
5
5
|
import { getClients } from '../auth.js'
|
|
6
6
|
import { AuthError, UnsupportedError, isScopeError } from '../api-utils.js'
|
|
7
7
|
import type { GmailClient } from '../gmail-client.js'
|
|
8
8
|
import * as out from '../output.js'
|
|
9
9
|
import { handleCommandError } from '../output.js'
|
|
10
10
|
|
|
11
|
-
export function registerFilterCommands(cli:
|
|
11
|
+
export function registerFilterCommands(cli: ZeleCli) {
|
|
12
12
|
// =========================================================================
|
|
13
13
|
// filter list
|
|
14
14
|
// =========================================================================
|
package/src/commands/label.ts
CHANGED
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
// Cache is handled by the client — commands just call methods and use data.
|
|
4
4
|
// Multi-account: list and counts fetch all accounts concurrently and merge.
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { ZeleCli } from '../cli-types.js'
|
|
7
7
|
import { z } from 'zod'
|
|
8
|
+
import * as clack from '@clack/prompts'
|
|
8
9
|
import { getClients, getGmailClient } from '../auth.js'
|
|
9
10
|
import { AuthError, UnsupportedError } from '../api-utils.js'
|
|
10
11
|
import type { GmailClient } from '../gmail-client.js'
|
|
11
12
|
import * as out from '../output.js'
|
|
12
13
|
import { handleCommandError } from '../output.js'
|
|
13
14
|
|
|
14
|
-
export function registerLabelCommands(cli:
|
|
15
|
+
export function registerLabelCommands(cli: ZeleCli) {
|
|
15
16
|
// =========================================================================
|
|
16
17
|
// label list
|
|
17
18
|
// =========================================================================
|
|
@@ -122,14 +123,12 @@ export function registerLabelCommands(cli: Goke) {
|
|
|
122
123
|
.option('--force', 'Skip confirmation')
|
|
123
124
|
.action(async (labelId, options) => {
|
|
124
125
|
if (!options.force && process.stdin.isTTY) {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
rl.question(`Delete label ${labelId}? [y/N] `, resolve)
|
|
126
|
+
const confirmed = await clack.confirm({
|
|
127
|
+
message: `Delete label ${labelId}?`,
|
|
128
|
+
initialValue: false,
|
|
129
129
|
})
|
|
130
|
-
rl.close()
|
|
131
130
|
|
|
132
|
-
if (
|
|
131
|
+
if (clack.isCancel(confirmed) || !confirmed) {
|
|
133
132
|
out.hint('Cancelled')
|
|
134
133
|
return
|
|
135
134
|
}
|