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.
- package/README.md +38 -1
- package/bin/zele +27 -0
- package/dist/api-utils.d.ts +51 -2
- package/dist/api-utils.js +89 -3
- package/dist/api-utils.js.map +1 -1
- package/dist/auth.d.ts +27 -6
- package/dist/auth.js +185 -129
- package/dist/auth.js.map +1 -1
- package/dist/calendar-client.d.ts +16 -9
- package/dist/calendar-client.js +163 -59
- package/dist/calendar-client.js.map +1 -1
- package/dist/cli.js +28 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/attachment.js +17 -15
- package/dist/commands/attachment.js.map +1 -1
- package/dist/commands/auth-cmd.js +20 -9
- package/dist/commands/auth-cmd.js.map +1 -1
- package/dist/commands/calendar.js +67 -78
- package/dist/commands/calendar.js.map +1 -1
- package/dist/commands/draft.js +25 -18
- package/dist/commands/draft.js.map +1 -1
- package/dist/commands/label.js +33 -45
- package/dist/commands/label.js.map +1 -1
- package/dist/commands/mail-actions.js +11 -13
- package/dist/commands/mail-actions.js.map +1 -1
- package/dist/commands/mail.js +114 -128
- package/dist/commands/mail.js.map +1 -1
- package/dist/commands/profile.js +18 -21
- package/dist/commands/profile.js.map +1 -1
- package/dist/commands/watch.d.ts +2 -0
- package/dist/commands/watch.js +73 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/db.js +12 -13
- package/dist/db.js.map +1 -1
- package/dist/generated/browser.d.ts +12 -27
- package/dist/generated/client.d.ts +13 -28
- package/dist/generated/client.js +1 -1
- package/dist/generated/commonInputTypes.d.ts +90 -26
- package/dist/generated/enums.d.ts +0 -4
- package/dist/generated/enums.js +0 -3
- package/dist/generated/enums.js.map +1 -1
- package/dist/generated/internal/class.d.ts +22 -55
- package/dist/generated/internal/class.js +12 -4
- package/dist/generated/internal/class.js.map +1 -1
- package/dist/generated/internal/prismaNamespace.d.ts +272 -511
- package/dist/generated/internal/prismaNamespace.js +54 -66
- package/dist/generated/internal/prismaNamespace.js.map +1 -1
- package/dist/generated/internal/prismaNamespaceBrowser.d.ts +60 -74
- package/dist/generated/internal/prismaNamespaceBrowser.js +50 -62
- package/dist/generated/internal/prismaNamespaceBrowser.js.map +1 -1
- package/dist/generated/models/Account.d.ts +1637 -0
- package/dist/generated/models/Account.js +2 -0
- package/dist/generated/models/Account.js.map +1 -0
- package/dist/generated/models/CalendarList.d.ts +1161 -0
- package/dist/generated/models/CalendarList.js +2 -0
- package/dist/generated/models/CalendarList.js.map +1 -0
- package/dist/generated/models/Label.d.ts +1161 -0
- package/dist/generated/models/Label.js +2 -0
- package/dist/generated/models/Label.js.map +1 -0
- package/dist/generated/models/Profile.d.ts +1269 -0
- package/dist/generated/models/Profile.js +2 -0
- package/dist/generated/models/Profile.js.map +1 -0
- package/dist/generated/models/SyncState.d.ts +1130 -0
- package/dist/generated/models/SyncState.js +2 -0
- package/dist/generated/models/SyncState.js.map +1 -0
- package/dist/generated/models/Thread.d.ts +1608 -0
- package/dist/generated/models/Thread.js +2 -0
- package/dist/generated/models/Thread.js.map +1 -0
- package/dist/generated/models.d.ts +6 -9
- package/dist/gmail-client.d.ts +119 -94
- package/dist/gmail-client.js +862 -315
- package/dist/gmail-client.js.map +1 -1
- package/dist/mail-tui.d.ts +1 -0
- package/dist/mail-tui.js +517 -0
- package/dist/mail-tui.js.map +1 -0
- package/dist/output.d.ts +6 -4
- package/dist/output.js +124 -17
- package/dist/output.js.map +1 -1
- package/package.json +39 -11
- package/schema.prisma +81 -113
- package/src/api-utils.ts +103 -5
- package/src/auth.ts +224 -143
- package/src/calendar-client.ts +196 -89
- package/src/cli.ts +32 -1
- package/src/commands/attachment.ts +18 -19
- package/src/commands/auth-cmd.ts +19 -9
- package/src/commands/calendar.ts +42 -85
- package/src/commands/draft.ts +19 -22
- package/src/commands/label.ts +21 -57
- package/src/commands/mail-actions.ts +11 -19
- package/src/commands/mail.ts +104 -149
- package/src/commands/profile.ts +12 -28
- package/src/commands/watch.ts +88 -0
- package/src/db.ts +13 -16
- package/src/generated/browser.ts +49 -0
- package/src/generated/client.ts +71 -0
- package/src/generated/commonInputTypes.ts +332 -0
- package/src/generated/enums.ts +17 -0
- package/src/generated/internal/class.ts +250 -0
- package/src/generated/internal/prismaNamespace.ts +1198 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +169 -0
- package/src/generated/models/Account.ts +1848 -0
- package/src/generated/models/CalendarList.ts +1331 -0
- package/src/generated/models/Label.ts +1331 -0
- package/src/generated/models/Profile.ts +1439 -0
- package/src/generated/models/SyncState.ts +1300 -0
- package/src/generated/models/Thread.ts +1787 -0
- package/src/generated/models.ts +17 -0
- package/src/gmail-client.test.ts +59 -0
- package/src/gmail-client.ts +1034 -422
- package/src/mail-tui.tsx +1061 -0
- package/src/output.test.ts +1093 -0
- package/src/output.ts +128 -20
- package/src/schema.sql +58 -68
- package/src/test-fixtures/email-html/safe-claude-event.html +28 -0
- package/src/test-fixtures/email-html/safe-product-announcement.html +25 -0
- package/src/test-fixtures/email-html/safe-tracked-links.html +27 -0
- package/src/test-fixtures/email-html-snapshots/safe-claude-event.html.md +9 -0
- package/src/test-fixtures/email-html-snapshots/safe-product-announcement.html.md +13 -0
- package/src/test-fixtures/email-html-snapshots/safe-tracked-links.html.md +7 -0
- package/AGENTS.md +0 -26
- package/CHANGELOG.md +0 -36
- package/dist/generated/models/accounts.d.ts +0 -2000
- package/dist/generated/models/accounts.js +0 -2
- package/dist/generated/models/accounts.js.map +0 -1
- package/dist/generated/models/calendar_events.d.ts +0 -1433
- package/dist/generated/models/calendar_events.js +0 -2
- package/dist/generated/models/calendar_events.js.map +0 -1
- package/dist/generated/models/calendar_lists.d.ts +0 -1131
- package/dist/generated/models/calendar_lists.js +0 -2
- package/dist/generated/models/calendar_lists.js.map +0 -1
- package/dist/generated/models/label_counts.d.ts +0 -1131
- package/dist/generated/models/label_counts.js +0 -2
- package/dist/generated/models/label_counts.js.map +0 -1
- package/dist/generated/models/labels.d.ts +0 -1131
- package/dist/generated/models/labels.js +0 -2
- package/dist/generated/models/labels.js.map +0 -1
- package/dist/generated/models/profiles.d.ts +0 -1131
- package/dist/generated/models/profiles.js +0 -2
- package/dist/generated/models/profiles.js.map +0 -1
- package/dist/generated/models/sync_states.d.ts +0 -1107
- package/dist/generated/models/sync_states.js +0 -2
- package/dist/generated/models/sync_states.js.map +0 -1
- package/dist/generated/models/thread_lists.d.ts +0 -1404
- package/dist/generated/models/thread_lists.js +0 -2
- package/dist/generated/models/thread_lists.js.map +0 -1
- package/dist/generated/models/threads.d.ts +0 -1247
- package/dist/generated/models/threads.js +0 -2
- package/dist/generated/models/threads.js.map +0 -1
- package/dist/gmail-cache.d.ts +0 -60
- package/dist/gmail-cache.js +0 -264
- package/dist/gmail-cache.js.map +0 -1
- package/docs/gogcli-gmail-implementation.md +0 -599
- package/scripts/test-device-code-clients.ts +0 -186
- package/scripts/test-micropython-scopes.ts +0 -72
- package/scripts/test-oauth-clients.ts +0 -257
- package/src/gmail-cache.ts +0 -339
- package/tsconfig.json +0 -16
package/src/output.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Output formatting utilities for zele CLI.
|
|
2
2
|
// Handles YAML output with TTY-aware coloring, date formatting,
|
|
3
3
|
// HTML-to-markdown email conversion (turndown), and stderr hints.
|
|
4
|
-
//
|
|
4
|
+
// Data goes to stdout (console.log), hints/progress/errors to stderr (console.error).
|
|
5
|
+
// Prefer console.log/console.error over raw process.stdout/stderr when possible.
|
|
5
6
|
//
|
|
6
7
|
// All structured data is output as YAML (js-yaml). In TTY mode, keys are
|
|
7
8
|
// colored cyan and values are left at the terminal default. In non-TTY mode,
|
|
@@ -10,7 +11,9 @@
|
|
|
10
11
|
|
|
11
12
|
import yaml from 'js-yaml'
|
|
12
13
|
import TurndownService from 'turndown'
|
|
14
|
+
import { remark } from 'remark'
|
|
13
15
|
import pc from 'picocolors'
|
|
16
|
+
import EmailReplyParser from 'email-reply-parser'
|
|
14
17
|
|
|
15
18
|
// ---------------------------------------------------------------------------
|
|
16
19
|
// TTY detection (used for coloring + wrapping decisions)
|
|
@@ -42,7 +45,7 @@ turndown.addRule('tracking-pixels', {
|
|
|
42
45
|
replacement: () => '',
|
|
43
46
|
})
|
|
44
47
|
|
|
45
|
-
// Strip <style
|
|
48
|
+
// Strip <style>, <head>, <script> tags
|
|
46
49
|
turndown.addRule('strip-style', {
|
|
47
50
|
filter: ['style', 'head', 'script'],
|
|
48
51
|
replacement: () => '',
|
|
@@ -57,6 +60,79 @@ turndown.addRule('images', {
|
|
|
57
60
|
},
|
|
58
61
|
})
|
|
59
62
|
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Email-specific turndown rules
|
|
65
|
+
// Email HTML heavily relies on layout tables. These are not data tables —
|
|
66
|
+
// they're used for positioning (like a 600px centered wrapper). We unwrap
|
|
67
|
+
// them so only the cell content survives as markdown.
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
// Strip common quoted-reply wrappers from Gmail/Outlook.
|
|
71
|
+
turndown.addRule('quoted-replies', {
|
|
72
|
+
filter: (node) => {
|
|
73
|
+
const hasClass = (name: string) => {
|
|
74
|
+
const cls = node.getAttribute('class') ?? ''
|
|
75
|
+
return new RegExp(`(^|\\s)${name}(\\s|$)`).test(cls)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (node.nodeName === 'DIV') {
|
|
79
|
+
if (hasClass('gmail_quote') || hasClass('gmail_extra')) return true
|
|
80
|
+
const id = node.getAttribute('id') ?? ''
|
|
81
|
+
if (id === 'appendonsend' || id === 'divRplyFwdMsg') return true
|
|
82
|
+
}
|
|
83
|
+
if (node.nodeName === 'BLOCKQUOTE') {
|
|
84
|
+
const type = node.getAttribute('type') ?? ''
|
|
85
|
+
if (type === 'cite') return true
|
|
86
|
+
}
|
|
87
|
+
return false
|
|
88
|
+
},
|
|
89
|
+
replacement: () => '',
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// Unwrap layout tables: tables used for email layout (width, align, role="presentation")
|
|
93
|
+
// just pass through their text content instead of rendering as markdown tables.
|
|
94
|
+
turndown.addRule('layout-tables', {
|
|
95
|
+
filter: (node) => {
|
|
96
|
+
if (node.nodeName !== 'TABLE') return false
|
|
97
|
+
const role = (node.getAttribute('role') ?? '').toLowerCase()
|
|
98
|
+
if (role === 'presentation') return true
|
|
99
|
+
|
|
100
|
+
// Heuristic: tables with explicit width or align are almost always layout
|
|
101
|
+
const width = node.getAttribute('width') ?? ''
|
|
102
|
+
const align = node.getAttribute('align') ?? ''
|
|
103
|
+
if (width || align) return true
|
|
104
|
+
|
|
105
|
+
// Tables with cellpadding/cellspacing/border="0" are layout tables
|
|
106
|
+
const border = (node.getAttribute('border') ?? '').trim()
|
|
107
|
+
const cellpadding = node.getAttribute('cellpadding') ?? ''
|
|
108
|
+
const cellspacing = node.getAttribute('cellspacing') ?? ''
|
|
109
|
+
if (border === '0' || cellpadding || cellspacing) return true
|
|
110
|
+
|
|
111
|
+
return false
|
|
112
|
+
},
|
|
113
|
+
replacement: (content) => {
|
|
114
|
+
// _content already has the inner text converted by turndown
|
|
115
|
+
return content
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Strip hidden/preheader elements.
|
|
120
|
+
turndown.addRule('hidden-elements', {
|
|
121
|
+
filter: (node) => {
|
|
122
|
+
const style = node.getAttribute('style') ?? ''
|
|
123
|
+
if (/display\s*:\s*none/i.test(style)) return true
|
|
124
|
+
if (/mso-hide\s*:\s*all/i.test(style)) return true
|
|
125
|
+
if (node.hasAttribute('hidden')) return true
|
|
126
|
+
|
|
127
|
+
const cls = node.getAttribute('class') ?? ''
|
|
128
|
+
if (/(^|\s)preheader(\s|$)/.test(cls)) return true
|
|
129
|
+
if (/(^|\s)preview-text(\s|$)/.test(cls)) return true
|
|
130
|
+
|
|
131
|
+
return false
|
|
132
|
+
},
|
|
133
|
+
replacement: () => '',
|
|
134
|
+
})
|
|
135
|
+
|
|
60
136
|
// ---------------------------------------------------------------------------
|
|
61
137
|
// HTML -> Markdown conversion
|
|
62
138
|
// ---------------------------------------------------------------------------
|
|
@@ -66,11 +142,28 @@ export function htmlToMarkdown(html: string): string {
|
|
|
66
142
|
const cleaned = html
|
|
67
143
|
.replace(/<!\-\-[\s\S]*?\-\->/g, '') // HTML comments
|
|
68
144
|
.replace(/<o:p>[\s\S]*?<\/o:p>/gi, '') // Outlook tags
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
145
|
+
.replace(/<!\[if[\s\S]*?<!\[endif\]>/gi, '') // Outlook conditional comments
|
|
146
|
+
|
|
147
|
+
let md = turndown.turndown(cleaned)
|
|
148
|
+
|
|
149
|
+
// Replace non-breaking/zero-width spaces before remark sees them
|
|
150
|
+
md = md.replace(/[\u00A0\u200B\u200C\u200D\uFEFF]/g, ' ')
|
|
151
|
+
|
|
152
|
+
// Parse and re-serialize through remark for stable, normalized markdown.
|
|
153
|
+
// This collapses whitespace, fixes list indentation, and validates structure.
|
|
154
|
+
const normalized = remark().processSync(md).toString()
|
|
155
|
+
|
|
156
|
+
// Remark escapes certain characters for markdown safety. Undo escapes that
|
|
157
|
+
// hurt readability in terminal output:
|
|
158
|
+
// - brackets: only for our synthetic [image: ...] placeholders
|
|
159
|
+
// - ampersands: only inside link destination parens (URL readability)
|
|
160
|
+
return normalized
|
|
161
|
+
.replace(/\\\[image:/g, '[image:')
|
|
162
|
+
.replace(/\(([^\n)]*)\)/g, (whole, inner: string) => {
|
|
163
|
+
if (!/(https?:\/\/|mailto:)/i.test(inner)) return whole
|
|
164
|
+
return `(${inner.replace(/\\&/g, '&')})`
|
|
165
|
+
})
|
|
166
|
+
.trim()
|
|
74
167
|
}
|
|
75
168
|
|
|
76
169
|
// ---------------------------------------------------------------------------
|
|
@@ -78,12 +171,16 @@ export function htmlToMarkdown(html: string): string {
|
|
|
78
171
|
// ---------------------------------------------------------------------------
|
|
79
172
|
|
|
80
173
|
export function renderEmailBody(body: string, mimeType: string): string {
|
|
81
|
-
if (mimeType === 'text/html')
|
|
82
|
-
return htmlToMarkdown(body)
|
|
83
|
-
}
|
|
174
|
+
if (mimeType === 'text/html') return htmlToMarkdown(body)
|
|
84
175
|
return body.trim()
|
|
85
176
|
}
|
|
86
177
|
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// Quoted reply stripping (email-reply-parser)
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
export const replyParser = new EmailReplyParser()
|
|
183
|
+
|
|
87
184
|
// ---------------------------------------------------------------------------
|
|
88
185
|
// YAML output
|
|
89
186
|
// ---------------------------------------------------------------------------
|
|
@@ -214,13 +311,6 @@ export function formatEventTime(start: string, end: string, allDay = false): { s
|
|
|
214
311
|
// ---------------------------------------------------------------------------
|
|
215
312
|
|
|
216
313
|
export function formatSender(sender: { name?: string; email: string }): string {
|
|
217
|
-
if (sender.name && sender.name !== sender.email) {
|
|
218
|
-
return sender.name
|
|
219
|
-
}
|
|
220
|
-
return sender.email
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export function formatSenderFull(sender: { name?: string; email: string }): string {
|
|
224
314
|
if (sender.name && sender.name !== sender.email) {
|
|
225
315
|
return `${sender.name} <${sender.email}>`
|
|
226
316
|
}
|
|
@@ -243,13 +333,31 @@ export function formatFlags(item: { unread?: boolean; starred?: boolean }): stri
|
|
|
243
333
|
// ---------------------------------------------------------------------------
|
|
244
334
|
|
|
245
335
|
export function hint(msg: string): void {
|
|
246
|
-
|
|
336
|
+
console.error(pc.dim(`# ${msg}`))
|
|
247
337
|
}
|
|
248
338
|
|
|
249
339
|
export function success(msg: string): void {
|
|
250
|
-
|
|
340
|
+
console.error(pc.green(msg))
|
|
251
341
|
}
|
|
252
342
|
|
|
253
343
|
export function error(msg: string): void {
|
|
254
|
-
|
|
344
|
+
console.error(pc.red(msg))
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ---------------------------------------------------------------------------
|
|
348
|
+
// Centralized command error handler (errore pattern)
|
|
349
|
+
// ---------------------------------------------------------------------------
|
|
350
|
+
|
|
351
|
+
import { AuthError } from './api-utils.js'
|
|
352
|
+
|
|
353
|
+
/** Handle any error from a client method in a command context.
|
|
354
|
+
* Prints a user-friendly message to stderr and exits.
|
|
355
|
+
* AuthError gets a "Try: zele login" hint; all others print their message. */
|
|
356
|
+
export function handleCommandError(err: Error): never {
|
|
357
|
+
if (err instanceof AuthError) {
|
|
358
|
+
error(`${err.message}. Try: zele login`)
|
|
359
|
+
} else {
|
|
360
|
+
error(err.message)
|
|
361
|
+
}
|
|
362
|
+
process.exit(1)
|
|
255
363
|
}
|
package/src/schema.sql
CHANGED
|
@@ -1,84 +1,74 @@
|
|
|
1
|
-
-- Generated by
|
|
2
|
-
CREATE TABLE IF NOT EXISTS "
|
|
3
|
-
"email" TEXT NOT NULL
|
|
4
|
-
"
|
|
1
|
+
-- Generated by bun run generate:sql. Do not edit.
|
|
2
|
+
CREATE TABLE IF NOT EXISTS "Account" (
|
|
3
|
+
"email" TEXT NOT NULL,
|
|
4
|
+
"appId" TEXT NOT NULL,
|
|
5
|
+
"accountStatus" TEXT NOT NULL,
|
|
5
6
|
"tokens" TEXT NOT NULL,
|
|
6
|
-
"
|
|
7
|
-
"
|
|
7
|
+
"createdAt" DATETIME NOT NULL,
|
|
8
|
+
"updatedAt" DATETIME NOT NULL,
|
|
9
|
+
|
|
10
|
+
PRIMARY KEY ("email", "appId")
|
|
8
11
|
);
|
|
9
|
-
CREATE TABLE IF NOT EXISTS "
|
|
12
|
+
CREATE TABLE IF NOT EXISTS "Thread" (
|
|
10
13
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
11
14
|
"email" TEXT NOT NULL,
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
15
|
+
"appId" TEXT NOT NULL,
|
|
16
|
+
"threadId" TEXT NOT NULL,
|
|
17
|
+
"subject" TEXT NOT NULL,
|
|
18
|
+
"snippet" TEXT NOT NULL,
|
|
19
|
+
"fromEmail" TEXT NOT NULL,
|
|
20
|
+
"fromName" TEXT NOT NULL,
|
|
21
|
+
"date" TEXT NOT NULL,
|
|
22
|
+
"labelIds" TEXT NOT NULL,
|
|
23
|
+
"hasUnread" BOOLEAN NOT NULL,
|
|
24
|
+
"msgCount" INTEGER NOT NULL,
|
|
25
|
+
"historyId" TEXT,
|
|
26
|
+
"rawData" TEXT NOT NULL,
|
|
27
|
+
"ttlMs" INTEGER NOT NULL,
|
|
28
|
+
"createdAt" DATETIME NOT NULL,
|
|
29
|
+
CONSTRAINT "Thread_email_appId_fkey" FOREIGN KEY ("email", "appId") REFERENCES "Account" ("email", "appId") ON DELETE CASCADE ON UPDATE CASCADE
|
|
21
30
|
);
|
|
22
31
|
CREATE TABLE sqlite_sequence(name,seq);
|
|
23
|
-
CREATE TABLE IF NOT EXISTS "
|
|
24
|
-
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
32
|
+
CREATE TABLE IF NOT EXISTS "Label" (
|
|
25
33
|
"email" TEXT NOT NULL,
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
"email" TEXT NOT NULL PRIMARY KEY,
|
|
34
|
-
"data" TEXT NOT NULL,
|
|
35
|
-
"ttl_ms" INTEGER NOT NULL,
|
|
36
|
-
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
37
|
-
CONSTRAINT "labels_email_fkey" FOREIGN KEY ("email") REFERENCES "accounts" ("email") ON DELETE CASCADE ON UPDATE CASCADE
|
|
38
|
-
);
|
|
39
|
-
CREATE TABLE IF NOT EXISTS "label_counts" (
|
|
40
|
-
"email" TEXT NOT NULL PRIMARY KEY,
|
|
41
|
-
"data" TEXT NOT NULL,
|
|
42
|
-
"ttl_ms" INTEGER NOT NULL,
|
|
43
|
-
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
44
|
-
CONSTRAINT "label_counts_email_fkey" FOREIGN KEY ("email") REFERENCES "accounts" ("email") ON DELETE CASCADE ON UPDATE CASCADE
|
|
45
|
-
);
|
|
46
|
-
CREATE TABLE IF NOT EXISTS "profiles" (
|
|
47
|
-
"email" TEXT NOT NULL PRIMARY KEY,
|
|
48
|
-
"data" TEXT NOT NULL,
|
|
49
|
-
"ttl_ms" INTEGER NOT NULL,
|
|
50
|
-
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
51
|
-
CONSTRAINT "profiles_email_fkey" FOREIGN KEY ("email") REFERENCES "accounts" ("email") ON DELETE CASCADE ON UPDATE CASCADE
|
|
34
|
+
"appId" TEXT NOT NULL,
|
|
35
|
+
"rawData" TEXT NOT NULL,
|
|
36
|
+
"ttlMs" INTEGER NOT NULL,
|
|
37
|
+
"createdAt" DATETIME NOT NULL,
|
|
38
|
+
|
|
39
|
+
PRIMARY KEY ("email", "appId"),
|
|
40
|
+
CONSTRAINT "Label_email_appId_fkey" FOREIGN KEY ("email", "appId") REFERENCES "Account" ("email", "appId") ON DELETE CASCADE ON UPDATE CASCADE
|
|
52
41
|
);
|
|
53
|
-
CREATE TABLE IF NOT EXISTS "
|
|
54
|
-
"email" TEXT NOT NULL
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
|
|
42
|
+
CREATE TABLE IF NOT EXISTS "Profile" (
|
|
43
|
+
"email" TEXT NOT NULL,
|
|
44
|
+
"appId" TEXT NOT NULL,
|
|
45
|
+
"emailAddress" TEXT NOT NULL,
|
|
46
|
+
"messagesTotal" INTEGER NOT NULL,
|
|
47
|
+
"threadsTotal" INTEGER NOT NULL,
|
|
48
|
+
"historyId" TEXT NOT NULL,
|
|
49
|
+
"ttlMs" INTEGER NOT NULL,
|
|
50
|
+
"createdAt" DATETIME NOT NULL,
|
|
51
|
+
|
|
52
|
+
PRIMARY KEY ("email", "appId"),
|
|
53
|
+
CONSTRAINT "Profile_email_appId_fkey" FOREIGN KEY ("email", "appId") REFERENCES "Account" ("email", "appId") ON DELETE CASCADE ON UPDATE CASCADE
|
|
59
54
|
);
|
|
60
|
-
CREATE TABLE IF NOT EXISTS "
|
|
61
|
-
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
55
|
+
CREATE TABLE IF NOT EXISTS "CalendarList" (
|
|
62
56
|
"email" TEXT NOT NULL,
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"ttl_ms" INTEGER NOT NULL,
|
|
71
|
-
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
72
|
-
CONSTRAINT "calendar_events_email_fkey" FOREIGN KEY ("email") REFERENCES "accounts" ("email") ON DELETE CASCADE ON UPDATE CASCADE
|
|
57
|
+
"appId" TEXT NOT NULL,
|
|
58
|
+
"rawData" TEXT NOT NULL,
|
|
59
|
+
"ttlMs" INTEGER NOT NULL,
|
|
60
|
+
"createdAt" DATETIME NOT NULL,
|
|
61
|
+
|
|
62
|
+
PRIMARY KEY ("email", "appId"),
|
|
63
|
+
CONSTRAINT "CalendarList_email_appId_fkey" FOREIGN KEY ("email", "appId") REFERENCES "Account" ("email", "appId") ON DELETE CASCADE ON UPDATE CASCADE
|
|
73
64
|
);
|
|
74
|
-
CREATE TABLE IF NOT EXISTS "
|
|
65
|
+
CREATE TABLE IF NOT EXISTS "SyncState" (
|
|
75
66
|
"email" TEXT NOT NULL,
|
|
67
|
+
"appId" TEXT NOT NULL,
|
|
76
68
|
"key" TEXT NOT NULL,
|
|
77
69
|
"value" TEXT NOT NULL,
|
|
78
70
|
|
|
79
|
-
PRIMARY KEY ("email", "key"),
|
|
80
|
-
CONSTRAINT "
|
|
71
|
+
PRIMARY KEY ("email", "appId", "key"),
|
|
72
|
+
CONSTRAINT "SyncState_email_appId_fkey" FOREIGN KEY ("email", "appId") REFERENCES "Account" ("email", "appId") ON DELETE CASCADE ON UPDATE CASCADE
|
|
81
73
|
);
|
|
82
|
-
CREATE UNIQUE INDEX "
|
|
83
|
-
CREATE UNIQUE INDEX "threads_email_thread_id_key" ON "threads"("email", "thread_id");
|
|
84
|
-
CREATE UNIQUE INDEX "calendar_events_email_calendar_id_time_min_time_max_query_max_results_page_token_key" ON "calendar_events"("email", "calendar_id", "time_min", "time_max", "query", "max_results", "page_token");
|
|
74
|
+
CREATE UNIQUE INDEX "Thread_email_appId_threadId_key" ON "Thread"("email", "appId", "threadId");
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!-- Safe shared fixture derived from a real event email with redacted links and identities. -->
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<body>
|
|
5
|
+
<div style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0">
|
|
6
|
+
A host has sent you a new message.
|
|
7
|
+
</div>
|
|
8
|
+
<table role="presentation" width="100%">
|
|
9
|
+
<tr>
|
|
10
|
+
<td>
|
|
11
|
+
<img alt="CV Logo" src="https://example.com/logo.png" width="120" />
|
|
12
|
+
<p>New Event Blast</p>
|
|
13
|
+
<p>
|
|
14
|
+
Thank you for applying to Built with Opus
|
|
15
|
+
<a href="https://example.com/opus-46">4.6</a>: a Claude Code Hackathon.
|
|
16
|
+
</p>
|
|
17
|
+
<p>
|
|
18
|
+
We received an incredible number of applications and couldn't include everyone this round.
|
|
19
|
+
</p>
|
|
20
|
+
<p>
|
|
21
|
+
Stay in the loop via our
|
|
22
|
+
<a href="https://example.com/events">Community Events calendar</a>.
|
|
23
|
+
</p>
|
|
24
|
+
</td>
|
|
25
|
+
</tr>
|
|
26
|
+
</table>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!-- Safe shared fixture derived from a real product update email with redacted links. -->
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<body>
|
|
5
|
+
<table width="600" align="center" cellpadding="0" cellspacing="0">
|
|
6
|
+
<tr>
|
|
7
|
+
<td>
|
|
8
|
+
<p>Hey there,</p>
|
|
9
|
+
<p>We just shipped Product 1.0. It's stable, it's production-ready.</p>
|
|
10
|
+
<p>If you've been building with Product, thank you.</p>
|
|
11
|
+
<p>Here's what's new:</p>
|
|
12
|
+
<ul>
|
|
13
|
+
<li>Stable API in production</li>
|
|
14
|
+
<li>Improved error handling</li>
|
|
15
|
+
<li>Better state management</li>
|
|
16
|
+
</ul>
|
|
17
|
+
<p>
|
|
18
|
+
Read the full announcement:
|
|
19
|
+
<a href="https://safe.example/news/product-1">Introducing Product 1.0</a>
|
|
20
|
+
</p>
|
|
21
|
+
</td>
|
|
22
|
+
</tr>
|
|
23
|
+
</table>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- Safe shared fixture modeled after tracked-link newsletters without private tokens. -->
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<body>
|
|
5
|
+
<table role="presentation" width="100%">
|
|
6
|
+
<tr>
|
|
7
|
+
<td>
|
|
8
|
+
<h1>BentoML Joins Modular</h1>
|
|
9
|
+
<p>
|
|
10
|
+
<a href="https://tracking.example/CL0/https:%2F%2Fmodular.com%2Fblog%2Fbentoml-joins-modular/1/click-token">
|
|
11
|
+
Read the full story
|
|
12
|
+
</a>
|
|
13
|
+
</p>
|
|
14
|
+
<p>
|
|
15
|
+
<a href="https://tracking.example/CL0/https:%2F%2Fforum.modular.com%2Fama/1/click-token">
|
|
16
|
+
Join the AMA
|
|
17
|
+
</a>
|
|
18
|
+
</p>
|
|
19
|
+
<p>
|
|
20
|
+
<img alt="x" src="https://example.com/x.png" />
|
|
21
|
+
<img alt="discord" src="https://example.com/discord.png" />
|
|
22
|
+
</p>
|
|
23
|
+
</td>
|
|
24
|
+
</tr>
|
|
25
|
+
</table>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
[image: CV Logo]
|
|
2
|
+
|
|
3
|
+
New Event Blast
|
|
4
|
+
|
|
5
|
+
Thank you for applying to Built with Opus [4.6](https://example.com/opus-46): a Claude Code Hackathon.
|
|
6
|
+
|
|
7
|
+
We received an incredible number of applications and couldn't include everyone this round.
|
|
8
|
+
|
|
9
|
+
Stay in the loop via our [Community Events calendar](https://example.com/events).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Hey there,
|
|
2
|
+
|
|
3
|
+
We just shipped Product 1.0. It's stable, it's production-ready.
|
|
4
|
+
|
|
5
|
+
If you've been building with Product, thank you.
|
|
6
|
+
|
|
7
|
+
Here's what's new:
|
|
8
|
+
|
|
9
|
+
* Stable API in production
|
|
10
|
+
* Improved error handling
|
|
11
|
+
* Better state management
|
|
12
|
+
|
|
13
|
+
Read the full announcement: [Introducing Product 1.0](https://safe.example/news/product-1)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# BentoML Joins Modular
|
|
2
|
+
|
|
3
|
+
[Read the full story](https://tracking.example/CL0/https:%2F%2Fmodular.com%2Fblog%2Fbentoml-joins-modular/1/click-token)
|
|
4
|
+
|
|
5
|
+
[Join the AMA](https://tracking.example/CL0/https:%2F%2Fforum.modular.com%2Fama/1/click-token)
|
|
6
|
+
|
|
7
|
+
[image: x] [image: discord]
|
package/AGENTS.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
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
|
-
## changelog
|
|
21
|
-
|
|
22
|
-
keep `CHANGELOG.md` updated when making user-facing changes. bump the version in `package.json` and `src/cli.ts` together.
|
|
23
|
-
|
|
24
|
-
## migrations
|
|
25
|
-
|
|
26
|
-
`src/db.ts` runs `src/schema.sql` on startup (idempotent migration) so new tables/indexes are applied automatically on each CLI process start.
|
package/CHANGELOG.md
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.2.0
|
|
4
|
-
|
|
5
|
-
- **Calendar:** Add comprehensive calendar commands (`cal list`, `events`, `get`, `create`, `update`, `delete`, `respond`, `freebusy`)
|
|
6
|
-
- **Calendar:** Use CalDAV protocol instead of Google REST API for better compatibility and efficiency
|
|
7
|
-
- **Calendar:** Add local caching for calendar lists and events
|
|
8
|
-
- **Auth:** Breaking change: `auth` namespace removed. Use `zele login`, `zele logout`, `zele whoami` directly
|
|
9
|
-
- **Docs:** Add comprehensive README with install, setup, and command reference
|
|
10
|
-
- **Fixes:** Improved error logging with stack traces
|
|
11
|
-
- **Internal:** Remove focus/ooo commands (use `cal create` instead)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## 0.1.3
|
|
15
|
-
|
|
16
|
-
- Add CHANGELOG.md
|
|
17
|
-
- Add changelog guidance to AGENTS.md
|
|
18
|
-
|
|
19
|
-
## 0.1.2
|
|
20
|
-
|
|
21
|
-
- Replace monolithic `googleapis` with scoped `@googleapis/gmail` for smaller install size
|
|
22
|
-
|
|
23
|
-
## 0.1.1
|
|
24
|
-
|
|
25
|
-
- Rename package from `gtui` to `zele`
|
|
26
|
-
- Rename config directory `~/.gtui` to `~/.zele`, database `gtui.db` to `zele.db`
|
|
27
|
-
- Rename env vars `GTUI_CLIENT_ID`/`GTUI_CLIENT_SECRET` to `ZELE_CLIENT_ID`/`ZELE_CLIENT_SECRET`
|
|
28
|
-
- Style login flow with picocolors (bold steps, cyan URL, dim hints, yellow warnings)
|
|
29
|
-
|
|
30
|
-
## 0.1.0
|
|
31
|
-
|
|
32
|
-
- Initial release as `gtui`
|
|
33
|
-
- Multi-account Gmail CLI with OAuth2 authentication
|
|
34
|
-
- Commands: mail (list, search, read, send, reply, forward), drafts, labels, attachments, profile
|
|
35
|
-
- Prisma-backed SQLite cache with TTL-based expiry
|
|
36
|
-
- YAML output with TTY-aware coloring
|