zele 0.0.0 → 0.1.1

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 (109) hide show
  1. package/AGENTS.md +22 -0
  2. package/dist/auth.d.ts +34 -0
  3. package/dist/auth.js +294 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +38 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/attachment.d.ts +2 -0
  9. package/dist/commands/attachment.js +85 -0
  10. package/dist/commands/attachment.js.map +1 -0
  11. package/dist/commands/auth-cmd.d.ts +2 -0
  12. package/dist/commands/auth-cmd.js +72 -0
  13. package/dist/commands/auth-cmd.js.map +1 -0
  14. package/dist/commands/draft.d.ts +2 -0
  15. package/dist/commands/draft.js +168 -0
  16. package/dist/commands/draft.js.map +1 -0
  17. package/dist/commands/label.d.ts +2 -0
  18. package/dist/commands/label.js +168 -0
  19. package/dist/commands/label.js.map +1 -0
  20. package/dist/commands/mail-actions.d.ts +2 -0
  21. package/dist/commands/mail-actions.js +86 -0
  22. package/dist/commands/mail-actions.js.map +1 -0
  23. package/dist/commands/mail.d.ts +2 -0
  24. package/dist/commands/mail.js +369 -0
  25. package/dist/commands/mail.js.map +1 -0
  26. package/dist/commands/profile.d.ts +2 -0
  27. package/dist/commands/profile.js +53 -0
  28. package/dist/commands/profile.js.map +1 -0
  29. package/dist/db.d.ts +11 -0
  30. package/dist/db.js +83 -0
  31. package/dist/db.js.map +1 -0
  32. package/dist/email-utils.d.ts +15 -0
  33. package/dist/email-utils.js +53 -0
  34. package/dist/email-utils.js.map +1 -0
  35. package/dist/generated/browser.d.ts +39 -0
  36. package/dist/generated/browser.js +18 -0
  37. package/dist/generated/browser.js.map +1 -0
  38. package/dist/generated/client.d.ts +56 -0
  39. package/dist/generated/client.js +33 -0
  40. package/dist/generated/client.js.map +1 -0
  41. package/dist/generated/commonInputTypes.d.ts +226 -0
  42. package/dist/generated/commonInputTypes.js +11 -0
  43. package/dist/generated/commonInputTypes.js.map +1 -0
  44. package/dist/generated/enums.d.ts +9 -0
  45. package/dist/generated/enums.js +17 -0
  46. package/dist/generated/enums.js.map +1 -0
  47. package/dist/generated/internal/class.d.ts +192 -0
  48. package/dist/generated/internal/class.js +42 -0
  49. package/dist/generated/internal/class.js.map +1 -0
  50. package/dist/generated/internal/prismaNamespace.d.ts +1021 -0
  51. package/dist/generated/internal/prismaNamespace.js +139 -0
  52. package/dist/generated/internal/prismaNamespace.js.map +1 -0
  53. package/dist/generated/internal/prismaNamespaceBrowser.d.ts +103 -0
  54. package/dist/generated/internal/prismaNamespaceBrowser.js +110 -0
  55. package/dist/generated/internal/prismaNamespaceBrowser.js.map +1 -0
  56. package/dist/generated/models/accounts.d.ts +1719 -0
  57. package/dist/generated/models/accounts.js +2 -0
  58. package/dist/generated/models/accounts.js.map +1 -0
  59. package/dist/generated/models/label_counts.d.ts +1131 -0
  60. package/dist/generated/models/label_counts.js +2 -0
  61. package/dist/generated/models/label_counts.js.map +1 -0
  62. package/dist/generated/models/labels.d.ts +1131 -0
  63. package/dist/generated/models/labels.js +2 -0
  64. package/dist/generated/models/labels.js.map +1 -0
  65. package/dist/generated/models/profiles.d.ts +1131 -0
  66. package/dist/generated/models/profiles.js +2 -0
  67. package/dist/generated/models/profiles.js.map +1 -0
  68. package/dist/generated/models/sync_states.d.ts +1107 -0
  69. package/dist/generated/models/sync_states.js +2 -0
  70. package/dist/generated/models/sync_states.js.map +1 -0
  71. package/dist/generated/models/thread_lists.d.ts +1404 -0
  72. package/dist/generated/models/thread_lists.js +2 -0
  73. package/dist/generated/models/thread_lists.js.map +1 -0
  74. package/dist/generated/models/threads.d.ts +1247 -0
  75. package/dist/generated/models/threads.js +2 -0
  76. package/dist/generated/models/threads.js.map +1 -0
  77. package/dist/generated/models.d.ts +8 -0
  78. package/dist/generated/models.js +2 -0
  79. package/dist/generated/models.js.map +1 -0
  80. package/dist/gmail-cache.d.ts +38 -0
  81. package/dist/gmail-cache.js +188 -0
  82. package/dist/gmail-cache.js.map +1 -0
  83. package/dist/gmail-client.d.ts +344 -0
  84. package/dist/gmail-client.js +974 -0
  85. package/dist/gmail-client.js.map +1 -0
  86. package/dist/output.d.ts +30 -0
  87. package/dist/output.js +177 -0
  88. package/dist/output.js.map +1 -0
  89. package/docs/gogcli-gmail-implementation.md +599 -0
  90. package/package.json +42 -18
  91. package/schema.prisma +125 -0
  92. package/src/auth.ts +364 -0
  93. package/src/cli.ts +51 -0
  94. package/src/commands/attachment.ts +106 -0
  95. package/src/commands/auth-cmd.ts +89 -0
  96. package/src/commands/draft.ts +208 -0
  97. package/src/commands/label.ts +220 -0
  98. package/src/commands/mail-actions.ts +119 -0
  99. package/src/commands/mail.ts +450 -0
  100. package/src/commands/profile.ts +65 -0
  101. package/src/db.ts +101 -0
  102. package/src/email-utils.ts +63 -0
  103. package/src/gmail-cache.ts +243 -0
  104. package/src/gmail-client.ts +1438 -0
  105. package/src/output.ts +204 -0
  106. package/src/schema.sql +62 -0
  107. package/tsconfig.json +16 -0
  108. package/src/index.test.ts +0 -9
  109. package/src/index.ts +0 -0
package/AGENTS.md ADDED
@@ -0,0 +1,22 @@
1
+ ## development
2
+
3
+ to run the cli locally use `tsx src/cli.ts`
4
+
5
+ ## goke typing
6
+
7
+ do not add manual type annotations to `.action(async ...)` parameters in goke commands; rely on goke option inference.
8
+
9
+ ## database
10
+
11
+ zele uses a single SQLite database at `~/.zele/zele.db` as the source of truth for CLI state.
12
+
13
+ all persistent state is stored in this DB via Prisma models:
14
+ - `accounts`: OAuth tokens per email account
15
+ - `thread_lists` + `threads`: cached mail list/read payloads
16
+ - `labels` + `label_counts`: cached label metadata and unread counters
17
+ - `profiles`: cached account profile data
18
+ - `sync_states`: misc per-account sync metadata (for example history IDs)
19
+
20
+ ## migrations
21
+
22
+ `src/db.ts` runs `src/schema.sql` on startup (idempotent migration) so new tables/indexes are applied automatically on each CLI process start.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import { OAuth2Client } from 'google-auth-library';
2
+ import { GmailClient } from './gmail-client.js';
3
+ export declare function createOAuth2Client(): OAuth2Client;
4
+ /**
5
+ * Run the full browser OAuth flow and save the account to the DB.
6
+ * Returns the email and an authenticated GmailClient.
7
+ */
8
+ export declare function login(): Promise<{
9
+ email: string;
10
+ client: GmailClient;
11
+ }>;
12
+ export declare function logout(email: string): Promise<void>;
13
+ export declare function listAccounts(): Promise<string[]>;
14
+ /**
15
+ * Get authenticated GmailClient instances for all accounts (or filtered by email list).
16
+ * If no accounts are registered, throws with a helpful message.
17
+ */
18
+ export declare function getClients(accounts?: string[]): Promise<Array<{
19
+ email: string;
20
+ client: GmailClient;
21
+ }>>;
22
+ /**
23
+ * Get a single authenticated GmailClient. Errors if multiple accounts exist
24
+ * and no --account filter was provided.
25
+ */
26
+ export declare function getClient(accounts?: string[]): Promise<{
27
+ email: string;
28
+ client: GmailClient;
29
+ }>;
30
+ export interface AuthStatus {
31
+ email: string;
32
+ expiresAt?: Date;
33
+ }
34
+ export declare function getAuthStatuses(): Promise<AuthStatus[]>;
package/dist/auth.js ADDED
@@ -0,0 +1,294 @@
1
+ // OAuth2 authentication module for zele.
2
+ // Multi-account support: tokens are stored in the Prisma-managed SQLite DB
3
+ // (accounts table) keyed by email. Supports login (browser OAuth), per-account
4
+ // token refresh, and helpers to get authenticated GmailClient instances for
5
+ // one or all accounts.
6
+ // Migration: on first use, if legacy ~/.zele/tokens.json exists, it is
7
+ // imported into the DB and renamed to tokens.json.bak.
8
+ import http from 'node:http';
9
+ import readline from 'node:readline';
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import os from 'node:os';
13
+ import { OAuth2Client } from 'google-auth-library';
14
+ import fkill from 'fkill';
15
+ import pc from 'picocolors';
16
+ import { getPrisma } from './db.js';
17
+ import { GmailClient } from './gmail-client.js';
18
+ // ---------------------------------------------------------------------------
19
+ // Config
20
+ // ---------------------------------------------------------------------------
21
+ const ZELE_DIR = path.join(os.homedir(), '.zele');
22
+ const LEGACY_TOKENS_FILE = path.join(ZELE_DIR, 'tokens.json');
23
+ const CLIENT_ID = process.env.ZELE_CLIENT_ID ??
24
+ '406964657835-aq8lmia8j95dhl1a2bvharmfk3t1hgqj.apps.googleusercontent.com';
25
+ const CLIENT_SECRET = process.env.ZELE_CLIENT_SECRET ?? 'kSmqreRr0qwBWJgbf5Y-PjSU';
26
+ const REDIRECT_PORT = 8089;
27
+ const SCOPES = [
28
+ 'https://mail.google.com/', // Gmail (full)
29
+ 'https://www.googleapis.com/auth/calendar', // Calendar (full)
30
+ 'https://www.googleapis.com/auth/userinfo.email', // Email identity
31
+ ];
32
+ // ---------------------------------------------------------------------------
33
+ // OAuth2 client factory
34
+ // ---------------------------------------------------------------------------
35
+ export function createOAuth2Client() {
36
+ return new OAuth2Client({
37
+ clientId: CLIENT_ID,
38
+ clientSecret: CLIENT_SECRET,
39
+ redirectUri: `http://localhost:${REDIRECT_PORT}`,
40
+ });
41
+ }
42
+ // ---------------------------------------------------------------------------
43
+ // Legacy migration: tokens.json → DB
44
+ // ---------------------------------------------------------------------------
45
+ async function migrateLegacyTokens() {
46
+ if (!fs.existsSync(LEGACY_TOKENS_FILE))
47
+ return;
48
+ const prisma = await getPrisma();
49
+ const count = await prisma.accounts.count();
50
+ if (count > 0) {
51
+ // DB already has accounts — skip migration
52
+ return;
53
+ }
54
+ try {
55
+ const data = fs.readFileSync(LEGACY_TOKENS_FILE, 'utf-8');
56
+ const tokens = JSON.parse(data);
57
+ // We need to discover the email for this token
58
+ const oauth2Client = createOAuth2Client();
59
+ oauth2Client.setCredentials(tokens);
60
+ // Refresh if expired
61
+ if (tokens.expiry_date && tokens.expiry_date < Date.now()) {
62
+ const { credentials } = await oauth2Client.refreshAccessToken();
63
+ oauth2Client.setCredentials(credentials);
64
+ Object.assign(tokens, credentials);
65
+ }
66
+ const client = new GmailClient({ auth: oauth2Client });
67
+ const profile = await client.getProfile();
68
+ const email = profile.emailAddress;
69
+ await prisma.accounts.create({
70
+ data: {
71
+ email,
72
+ tokens: JSON.stringify(tokens),
73
+ updated_at: new Date(),
74
+ },
75
+ });
76
+ // Rename old file so we don't migrate again
77
+ fs.renameSync(LEGACY_TOKENS_FILE, LEGACY_TOKENS_FILE + '.bak');
78
+ process.stderr.write(pc.green(`Migrated legacy tokens for ${email}`) + '\n');
79
+ }
80
+ catch (err) {
81
+ process.stderr.write(pc.yellow(`Warning: legacy token migration failed: ${err}`) + '\n');
82
+ }
83
+ }
84
+ // ---------------------------------------------------------------------------
85
+ // Browser OAuth flow
86
+ // ---------------------------------------------------------------------------
87
+ function extractCodeFromInput(input) {
88
+ const trimmed = input.trim();
89
+ if (!trimmed)
90
+ return null;
91
+ try {
92
+ const url = new URL(trimmed);
93
+ const code = url.searchParams.get('code');
94
+ if (code)
95
+ return code;
96
+ }
97
+ catch {
98
+ // Not a URL
99
+ }
100
+ if (trimmed.length > 10 && !trimmed.includes(' ')) {
101
+ return trimmed;
102
+ }
103
+ return null;
104
+ }
105
+ async function getAuthCodeFromBrowser(oauth2Client) {
106
+ const authUrl = oauth2Client.generateAuthUrl({
107
+ access_type: 'offline',
108
+ scope: SCOPES,
109
+ prompt: 'consent',
110
+ });
111
+ await fkill(`:${REDIRECT_PORT}`, { force: true, silent: true }).catch(() => { });
112
+ process.stderr.write('\n' + pc.bold('1.') + ' Open this URL to authorize:\n\n');
113
+ process.stderr.write(' ' + pc.cyan(pc.underline(authUrl)) + '\n\n');
114
+ process.stderr.write(pc.bold('2.') + ' If running locally, the browser will redirect automatically.\n');
115
+ process.stderr.write(pc.dim(' If running remotely, the redirect page won\'t load — that\'s fine.') + '\n');
116
+ process.stderr.write(pc.dim(' Just copy the URL from your browser\'s address bar and paste it below.') + '\n\n');
117
+ return new Promise((resolve, reject) => {
118
+ let resolved = false;
119
+ let server = null;
120
+ let rl = null;
121
+ function finish(code) {
122
+ if (resolved)
123
+ return;
124
+ resolved = true;
125
+ server?.close();
126
+ if (rl) {
127
+ rl.close();
128
+ process.stdin.unref();
129
+ }
130
+ resolve(code);
131
+ }
132
+ function fail(err) {
133
+ if (resolved)
134
+ return;
135
+ resolved = true;
136
+ server?.close();
137
+ rl?.close();
138
+ reject(err);
139
+ }
140
+ server = http.createServer((req, res) => {
141
+ const url = new URL(req.url, `http://localhost:${REDIRECT_PORT}`);
142
+ const code = url.searchParams.get('code');
143
+ const error = url.searchParams.get('error');
144
+ if (error) {
145
+ res.writeHead(400, { 'Content-Type': 'text/html' });
146
+ res.end(`<h1>Error: ${error}</h1>`);
147
+ fail(new Error(error));
148
+ return;
149
+ }
150
+ if (code) {
151
+ res.writeHead(200, { 'Content-Type': 'text/html' });
152
+ res.end('<h1>Success! You can close this window.</h1>');
153
+ finish(code);
154
+ return;
155
+ }
156
+ res.writeHead(400, { 'Content-Type': 'text/html' });
157
+ res.end('<h1>No authorization code received</h1>');
158
+ });
159
+ server.listen(REDIRECT_PORT);
160
+ if (process.stdin.isTTY) {
161
+ rl = readline.createInterface({ input: process.stdin, output: process.stderr });
162
+ rl.question(pc.dim('Paste redirect URL here (or wait for auto-redirect): '), (answer) => {
163
+ const code = extractCodeFromInput(answer);
164
+ if (code) {
165
+ finish(code);
166
+ }
167
+ else {
168
+ process.stderr.write(pc.yellow('Could not extract authorization code from input.') + '\n');
169
+ process.stderr.write(pc.dim('Waiting for browser redirect...') + '\n');
170
+ }
171
+ });
172
+ }
173
+ });
174
+ }
175
+ // ---------------------------------------------------------------------------
176
+ // Login: browser OAuth → save tokens to DB
177
+ // ---------------------------------------------------------------------------
178
+ /**
179
+ * Run the full browser OAuth flow and save the account to the DB.
180
+ * Returns the email and an authenticated GmailClient.
181
+ */
182
+ export async function login() {
183
+ const oauth2Client = createOAuth2Client();
184
+ const code = await getAuthCodeFromBrowser(oauth2Client);
185
+ process.stderr.write(pc.dim('Got authorization code, exchanging for tokens...') + '\n');
186
+ const { tokens } = await oauth2Client.getToken(code);
187
+ oauth2Client.setCredentials(tokens);
188
+ // Discover email
189
+ const client = new GmailClient({ auth: oauth2Client });
190
+ const profile = await client.getProfile();
191
+ const email = profile.emailAddress;
192
+ // Upsert account in DB
193
+ const prisma = await getPrisma();
194
+ await prisma.accounts.upsert({
195
+ where: { email },
196
+ create: { email, tokens: JSON.stringify(tokens), updated_at: new Date() },
197
+ update: { tokens: JSON.stringify(tokens), updated_at: new Date() },
198
+ });
199
+ return { email, client };
200
+ }
201
+ // ---------------------------------------------------------------------------
202
+ // Logout: remove account from DB
203
+ // ---------------------------------------------------------------------------
204
+ export async function logout(email) {
205
+ const prisma = await getPrisma();
206
+ await prisma.accounts.delete({ where: { email } });
207
+ }
208
+ // ---------------------------------------------------------------------------
209
+ // Account listing
210
+ // ---------------------------------------------------------------------------
211
+ export async function listAccounts() {
212
+ await migrateLegacyTokens();
213
+ const prisma = await getPrisma();
214
+ const rows = await prisma.accounts.findMany({ select: { email: true } });
215
+ return rows.map((r) => r.email);
216
+ }
217
+ // ---------------------------------------------------------------------------
218
+ // Get authenticated clients
219
+ // ---------------------------------------------------------------------------
220
+ /**
221
+ * Create an authenticated OAuth2Client for a known account.
222
+ * Loads tokens from DB, refreshes if expired, saves refreshed tokens back.
223
+ */
224
+ async function authenticateAccount(email) {
225
+ const prisma = await getPrisma();
226
+ const row = await prisma.accounts.findUnique({ where: { email } });
227
+ if (!row) {
228
+ throw new Error(`No account found for ${email}. Run: zele auth login`);
229
+ }
230
+ const tokens = JSON.parse(row.tokens);
231
+ const oauth2Client = createOAuth2Client();
232
+ oauth2Client.setCredentials(tokens);
233
+ // Refresh if expired — merge to preserve refresh_token which Google
234
+ // often omits from refresh responses
235
+ if (tokens.expiry_date && tokens.expiry_date < Date.now()) {
236
+ process.stderr.write(pc.dim(`Token expired for ${email}, refreshing...`) + '\n');
237
+ const { credentials } = await oauth2Client.refreshAccessToken();
238
+ const merged = { ...tokens, ...credentials };
239
+ oauth2Client.setCredentials(merged);
240
+ await prisma.accounts.update({
241
+ where: { email },
242
+ data: { tokens: JSON.stringify(merged), updated_at: new Date() },
243
+ });
244
+ }
245
+ return oauth2Client;
246
+ }
247
+ /**
248
+ * Get authenticated GmailClient instances for all accounts (or filtered by email list).
249
+ * If no accounts are registered, throws with a helpful message.
250
+ */
251
+ export async function getClients(accounts) {
252
+ await migrateLegacyTokens();
253
+ const allEmails = await listAccounts();
254
+ if (allEmails.length === 0) {
255
+ throw new Error('No accounts registered. Run: zele auth login');
256
+ }
257
+ const emails = accounts && accounts.length > 0
258
+ ? allEmails.filter((e) => accounts.includes(e))
259
+ : allEmails;
260
+ if (emails.length === 0) {
261
+ const available = allEmails.join(', ');
262
+ throw new Error(`No matching accounts. Available: ${available}`);
263
+ }
264
+ const results = await Promise.all(emails.map(async (email) => {
265
+ const auth = await authenticateAccount(email);
266
+ return { email, client: new GmailClient({ auth }) };
267
+ }));
268
+ return results;
269
+ }
270
+ /**
271
+ * Get a single authenticated GmailClient. Errors if multiple accounts exist
272
+ * and no --account filter was provided.
273
+ */
274
+ export async function getClient(accounts) {
275
+ const clients = await getClients(accounts);
276
+ if (clients.length === 1) {
277
+ return clients[0];
278
+ }
279
+ const emails = clients.map((c) => c.email).join('\n ');
280
+ throw new Error(`Multiple accounts matched. Specify --account:\n ${emails}`);
281
+ }
282
+ export async function getAuthStatuses() {
283
+ await migrateLegacyTokens();
284
+ const prisma = await getPrisma();
285
+ const rows = await prisma.accounts.findMany();
286
+ return rows.map((row) => {
287
+ const tokens = JSON.parse(row.tokens);
288
+ return {
289
+ email: row.email,
290
+ expiresAt: tokens.expiry_date ? new Date(tokens.expiry_date) : undefined,
291
+ };
292
+ });
293
+ }
294
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ // zele — Gmail CLI built on goke.
3
+ // Entry point: registers all commands, global options, help, and version.
4
+ // Uses goke for command parsing with zod schemas for type-safe options.
5
+ import { goke } from 'goke';
6
+ import { z } from 'zod';
7
+ import { registerAuthCommands } from './commands/auth-cmd.js';
8
+ import { registerMailCommands } from './commands/mail.js';
9
+ import { registerMailActionCommands } from './commands/mail-actions.js';
10
+ import { registerDraftCommands } from './commands/draft.js';
11
+ import { registerLabelCommands } from './commands/label.js';
12
+ import { registerAttachmentCommands } from './commands/attachment.js';
13
+ import { registerProfileCommands } from './commands/profile.js';
14
+ const cli = goke('zele');
15
+ // ---------------------------------------------------------------------------
16
+ // Global options
17
+ // ---------------------------------------------------------------------------
18
+ cli.option('--account <account>', z.array(z.string()).describe('Filter by email account (repeatable)'));
19
+ // ---------------------------------------------------------------------------
20
+ // Register all command modules
21
+ // ---------------------------------------------------------------------------
22
+ registerAuthCommands(cli);
23
+ registerMailCommands(cli);
24
+ registerMailActionCommands(cli);
25
+ registerDraftCommands(cli);
26
+ registerLabelCommands(cli);
27
+ registerAttachmentCommands(cli);
28
+ registerProfileCommands(cli);
29
+ // ---------------------------------------------------------------------------
30
+ // Help & version
31
+ // ---------------------------------------------------------------------------
32
+ cli.help();
33
+ cli.version('0.1.1');
34
+ // ---------------------------------------------------------------------------
35
+ // Parse & run
36
+ // ---------------------------------------------------------------------------
37
+ cli.parse();
38
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,kCAAkC;AAClC,0EAA0E;AAC1E,wEAAwE;AAExE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAE/D,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAA;AAExB,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,GAAG,CAAC,MAAM,CACR,qBAAqB,EACrB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,sCAAsC,CAAC,CACrE,CAAA;AAED,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E,oBAAoB,CAAC,GAAG,CAAC,CAAA;AACzB,oBAAoB,CAAC,GAAG,CAAC,CAAA;AACzB,0BAA0B,CAAC,GAAG,CAAC,CAAA;AAC/B,qBAAqB,CAAC,GAAG,CAAC,CAAA;AAC1B,qBAAqB,CAAC,GAAG,CAAC,CAAA;AAC1B,0BAA0B,CAAC,GAAG,CAAC,CAAA;AAC/B,uBAAuB,CAAC,GAAG,CAAC,CAAA;AAE5B,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,GAAG,CAAC,IAAI,EAAE,CAAA;AACV,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;AAEpB,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,GAAG,CAAC,KAAK,EAAE,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { Goke } from 'goke';
2
+ export declare function registerAttachmentCommands(cli: Goke): void;
@@ -0,0 +1,85 @@
1
+ // Attachment commands: list, get (download).
2
+ // Lists attachments for a message and downloads them to disk.
3
+ // Skips re-download if file already exists with same size (like gogcli).
4
+ import { z } from 'zod';
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { getClient } from '../auth.js';
8
+ import * as out from '../output.js';
9
+ export function registerAttachmentCommands(cli) {
10
+ // =========================================================================
11
+ // attachment list
12
+ // =========================================================================
13
+ cli
14
+ .command('attachment list <messageId>', 'List attachments for a message')
15
+ .action(async (messageId, options) => {
16
+ const { client } = await getClient(options.account);
17
+ const msg = await client.getMessage({ messageId });
18
+ if ('raw' in msg) {
19
+ out.error('Cannot list attachments for raw messages');
20
+ process.exit(1);
21
+ }
22
+ const attachments = msg.attachments;
23
+ if (attachments.length === 0) {
24
+ out.hint('No attachments');
25
+ return;
26
+ }
27
+ out.printList(attachments.map((a) => ({
28
+ attachment_id: a.attachmentId,
29
+ filename: a.filename,
30
+ type: a.mimeType,
31
+ size: formatSize(a.size),
32
+ })));
33
+ out.hint(`${attachments.length} attachment(s)`);
34
+ });
35
+ // =========================================================================
36
+ // attachment get
37
+ // =========================================================================
38
+ cli
39
+ .command('attachment get <messageId> <attachmentId>', 'Download an attachment')
40
+ .option('--out-dir <outDir>', z.string().default('.').describe('Output directory'))
41
+ .option('--filename <filename>', z.string().describe('Override filename'))
42
+ .action(async (messageId, attachmentId, options) => {
43
+ const { client } = await getClient(options.account);
44
+ // Get attachment metadata first
45
+ const msg = await client.getMessage({ messageId });
46
+ if ('raw' in msg) {
47
+ out.error('Cannot get attachments for raw messages');
48
+ process.exit(1);
49
+ }
50
+ const meta = msg.attachments.find((a) => a.attachmentId === attachmentId);
51
+ const filename = options.filename ?? meta?.filename ?? `${messageId}_${attachmentId.slice(0, 8)}`;
52
+ const outPath = path.resolve(options.outDir, filename);
53
+ // Check if file already exists with same size (skip re-download)
54
+ if (fs.existsSync(outPath) && meta) {
55
+ const stat = fs.statSync(outPath);
56
+ if (stat.size === meta.size) {
57
+ out.printYaml({ path: outPath, cached: true, size: stat.size });
58
+ out.hint(`Cached: ${outPath}`);
59
+ return;
60
+ }
61
+ }
62
+ // Download
63
+ const base64Data = await client.getAttachment({ messageId, attachmentId });
64
+ const buffer = Buffer.from(base64Data, 'base64');
65
+ // Ensure output directory exists
66
+ const dir = path.dirname(outPath);
67
+ if (!fs.existsSync(dir)) {
68
+ fs.mkdirSync(dir, { recursive: true });
69
+ }
70
+ fs.writeFileSync(outPath, buffer);
71
+ out.printYaml({ path: outPath, cached: false, size: buffer.length });
72
+ out.success(`Saved: ${outPath} (${formatSize(buffer.length)})`);
73
+ });
74
+ }
75
+ // ---------------------------------------------------------------------------
76
+ // Helpers
77
+ // ---------------------------------------------------------------------------
78
+ function formatSize(bytes) {
79
+ if (bytes < 1024)
80
+ return `${bytes} B`;
81
+ if (bytes < 1024 * 1024)
82
+ return `${(bytes / 1024).toFixed(1)} KB`;
83
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
84
+ }
85
+ //# sourceMappingURL=attachment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment.js","sourceRoot":"","sources":["../../src/commands/attachment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Goke } from 'goke';
2
+ export declare function registerAuthCommands(cli: Goke): void;
@@ -0,0 +1,72 @@
1
+ // Auth commands: login, logout, status.
2
+ // Manages OAuth2 authentication for zele.
3
+ // Supports multiple accounts: login adds accounts, logout removes one.
4
+ import { login, logout, listAccounts, getAuthStatuses } from '../auth.js';
5
+ import * as out from '../output.js';
6
+ export function registerAuthCommands(cli) {
7
+ cli
8
+ .command('auth login', 'Authenticate with Google (opens browser)')
9
+ .action(async () => {
10
+ const { email } = await login();
11
+ out.success(`Authenticated as ${email}`);
12
+ });
13
+ cli
14
+ .command('auth logout [email]', 'Remove stored credentials for an account')
15
+ .option('--force', 'Skip confirmation')
16
+ .action(async (email, options) => {
17
+ const accounts = await listAccounts();
18
+ if (accounts.length === 0) {
19
+ out.hint('No accounts currently authenticated');
20
+ return;
21
+ }
22
+ // If no email specified and multiple accounts: error with list
23
+ if (!email && accounts.length > 1) {
24
+ out.error('Multiple accounts logged in. Specify which to remove:');
25
+ for (const a of accounts) {
26
+ process.stderr.write(` ${a}\n`);
27
+ }
28
+ process.exit(1);
29
+ }
30
+ // If no email and only one account, use that one
31
+ const targetEmail = email ?? accounts[0];
32
+ if (!accounts.includes(targetEmail)) {
33
+ out.error(`Account not found: ${targetEmail}`);
34
+ out.hint(`Logged in accounts: ${accounts.join(', ')}`);
35
+ process.exit(1);
36
+ }
37
+ if (!options.force) {
38
+ if (!process.stdin.isTTY) {
39
+ out.error('Use --force to logout non-interactively');
40
+ process.exit(1);
41
+ }
42
+ const readline = await import('node:readline');
43
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
44
+ const answer = await new Promise((resolve) => {
45
+ rl.question(`Remove credentials for ${targetEmail}? [y/N] `, resolve);
46
+ });
47
+ rl.close();
48
+ if (answer.toLowerCase() !== 'y') {
49
+ out.hint('Cancelled');
50
+ return;
51
+ }
52
+ }
53
+ await logout(targetEmail);
54
+ out.success(`Credentials removed for ${targetEmail}`);
55
+ });
56
+ cli
57
+ .command('auth status', 'Show authentication status')
58
+ .action(async () => {
59
+ const statuses = await getAuthStatuses();
60
+ if (statuses.length === 0) {
61
+ out.hint('Not authenticated. Run: zele auth login');
62
+ return;
63
+ }
64
+ out.printList(statuses.map((s) => ({
65
+ email: s.email,
66
+ status: 'Authenticated',
67
+ expires: s.expiresAt?.toISOString() ?? 'unknown',
68
+ })));
69
+ out.hint(`${statuses.length} account(s)`);
70
+ });
71
+ }
72
+ //# sourceMappingURL=auth-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-cmd.js","sourceRoot":"","sources":["../../src/commands/auth-cmd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Goke } from 'goke';
2
+ export declare function registerDraftCommands(cli: Goke): void;