zele 0.3.15 → 0.3.17
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 +113 -33
- package/dist/api-utils.d.ts +7 -0
- package/dist/api-utils.js +12 -0
- package/dist/api-utils.js.map +1 -1
- package/dist/auth.d.ts +71 -9
- package/dist/auth.js +187 -11
- package/dist/auth.js.map +1 -1
- package/dist/calendar-time.js +6 -0
- package/dist/calendar-time.js.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/attachment.js +2 -0
- package/dist/commands/attachment.js.map +1 -1
- package/dist/commands/auth-cmd.js +104 -6
- package/dist/commands/auth-cmd.js.map +1 -1
- package/dist/commands/calendar.js +1 -1
- package/dist/commands/calendar.js.map +1 -1
- package/dist/commands/draft.js +9 -3
- package/dist/commands/draft.js.map +1 -1
- package/dist/commands/filter.d.ts +2 -0
- package/dist/commands/filter.js +64 -0
- package/dist/commands/filter.js.map +1 -0
- package/dist/commands/label.js +19 -9
- package/dist/commands/label.js.map +1 -1
- package/dist/commands/mail-actions.js +12 -2
- package/dist/commands/mail-actions.js.map +1 -1
- package/dist/commands/mail.js +194 -84
- package/dist/commands/mail.js.map +1 -1
- package/dist/commands/profile.js +25 -18
- package/dist/commands/profile.js.map +1 -1
- package/dist/db.js +24 -0
- package/dist/db.js.map +1 -1
- package/dist/generated/internal/class.js +2 -2
- package/dist/generated/internal/class.js.map +1 -1
- package/dist/generated/internal/prismaNamespace.d.ts +2 -0
- package/dist/generated/internal/prismaNamespace.js +2 -0
- package/dist/generated/internal/prismaNamespace.js.map +1 -1
- package/dist/generated/internal/prismaNamespaceBrowser.d.ts +2 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +2 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js.map +1 -1
- package/dist/generated/models/Account.d.ts +97 -1
- package/dist/gmail-client.d.ts +42 -0
- package/dist/gmail-client.js +175 -9
- package/dist/gmail-client.js.map +1 -1
- package/dist/imap-smtp-client.d.ts +235 -0
- package/dist/imap-smtp-client.js +1225 -0
- package/dist/imap-smtp-client.js.map +1 -0
- package/dist/mail-tui.js +3 -2
- package/dist/mail-tui.js.map +1 -1
- package/dist/output.d.ts +2 -0
- package/dist/output.js +4 -0
- package/dist/output.js.map +1 -1
- package/package.json +9 -5
- package/schema.prisma +7 -5
- package/skills/zele/SKILL.md +141 -0
- package/src/api-utils.ts +13 -0
- package/src/auth.ts +283 -15
- package/src/calendar-time.test.ts +35 -0
- package/src/calendar-time.ts +5 -0
- package/src/cli.ts +6 -1
- package/src/commands/attachment.ts +1 -0
- package/src/commands/auth-cmd.ts +112 -6
- package/src/commands/calendar.ts +1 -1
- package/src/commands/draft.ts +7 -3
- package/src/commands/filter.ts +74 -0
- package/src/commands/label.ts +22 -11
- package/src/commands/mail-actions.ts +16 -3
- package/src/commands/mail.ts +207 -89
- package/src/commands/profile.ts +27 -17
- package/src/db.ts +28 -0
- package/src/generated/internal/class.ts +2 -2
- package/src/generated/internal/prismaNamespace.ts +2 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +2 -0
- package/src/generated/models/Account.ts +97 -1
- package/src/gmail-client.test.ts +155 -2
- package/src/gmail-client.ts +221 -16
- package/src/imap-smtp-client.ts +1381 -0
- package/src/mail-tui.tsx +3 -3
- package/src/output.ts +8 -1
- package/src/schema.sql +2 -0
package/src/commands/mail.ts
CHANGED
|
@@ -11,11 +11,29 @@ import React from 'react'
|
|
|
11
11
|
import { lookup as mimeLookup } from 'mrmime'
|
|
12
12
|
import { getClients, getClient, listAccounts, login } from '../auth.js'
|
|
13
13
|
import type { ThreadListResult } from '../gmail-client.js'
|
|
14
|
+
import type { GmailClient } from '../gmail-client.js'
|
|
14
15
|
import { AuthError } from '../api-utils.js'
|
|
15
16
|
import * as out from '../output.js'
|
|
16
17
|
import { handleCommandError } from '../output.js'
|
|
17
18
|
import pc from 'picocolors'
|
|
18
19
|
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Label formatting — filter out system labels already represented by flags
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
const HIDDEN_LABELS = new Set([
|
|
25
|
+
'INBOX', 'SENT', 'TRASH', 'SPAM', 'DRAFT', 'UNREAD', 'STARRED',
|
|
26
|
+
'IMPORTANT', 'CHAT', 'CATEGORY_PERSONAL', 'CATEGORY_SOCIAL',
|
|
27
|
+
'CATEGORY_PROMOTIONS', 'CATEGORY_UPDATES', 'CATEGORY_FORUMS',
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
function formatLabels(labelIds: string[], labelMap?: Map<string, string>): string {
|
|
31
|
+
const visible = labelIds
|
|
32
|
+
.filter((id) => !HIDDEN_LABELS.has(id))
|
|
33
|
+
.map((id) => labelMap?.get(id) ?? id)
|
|
34
|
+
return visible.join(', ')
|
|
35
|
+
}
|
|
36
|
+
|
|
19
37
|
// ---------------------------------------------------------------------------
|
|
20
38
|
// Register commands
|
|
21
39
|
// ---------------------------------------------------------------------------
|
|
@@ -34,8 +52,9 @@ export function registerMailCommands(cli: Goke) {
|
|
|
34
52
|
.command('mail list', 'List email threads')
|
|
35
53
|
.option('--folder [folder]', 'Folder to list (inbox, sent, trash, spam, starred, drafts, archive, all) (default: inbox)')
|
|
36
54
|
.option('--max [max]', 'Max results per page (default: 20)')
|
|
37
|
-
.option('--page <page>', 'Pagination token')
|
|
55
|
+
.option('--page <page>', 'Pagination token (requires --account, only works for a single account)')
|
|
38
56
|
.option('--label <label>', 'Filter by label name')
|
|
57
|
+
.option('--filter <filter>', 'Gmail search filter (e.g. "is:unread", "from:github", "has:attachment")')
|
|
39
58
|
.action(async (options) => {
|
|
40
59
|
const folder = options.folder ?? 'inbox'
|
|
41
60
|
const max = options.max ? Number(options.max) : 20
|
|
@@ -46,17 +65,27 @@ export function registerMailCommands(cli: Goke) {
|
|
|
46
65
|
process.exit(1)
|
|
47
66
|
}
|
|
48
67
|
|
|
49
|
-
// Fetch from all accounts concurrently
|
|
68
|
+
// Fetch threads and labels from all accounts concurrently
|
|
50
69
|
const results = await Promise.all(
|
|
51
|
-
clients.map(async ({ email, client }) => {
|
|
70
|
+
clients.map(async ({ email, client, accountType }) => {
|
|
52
71
|
const result = await client.listThreads({
|
|
53
72
|
folder,
|
|
54
73
|
maxResults: max,
|
|
55
74
|
labelIds: options.label ? [options.label] : undefined,
|
|
56
75
|
pageToken: options.page,
|
|
76
|
+
query: options.filter,
|
|
57
77
|
})
|
|
58
78
|
if (result instanceof Error) return result
|
|
59
|
-
|
|
79
|
+
|
|
80
|
+
// Labels are Google-only — skip for IMAP accounts
|
|
81
|
+
let labelMap = new Map<string, string>()
|
|
82
|
+
if (accountType === 'google') {
|
|
83
|
+
const labelsResult = await (client as GmailClient).listLabels()
|
|
84
|
+
if (!(labelsResult instanceof Error)) {
|
|
85
|
+
labelMap = new Map(labelsResult.parsed.map((l) => [l.id, l.name]))
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { email, result, labelMap }
|
|
60
89
|
}),
|
|
61
90
|
)
|
|
62
91
|
|
|
@@ -66,6 +95,10 @@ export function registerMailCommands(cli: Goke) {
|
|
|
66
95
|
return true
|
|
67
96
|
})
|
|
68
97
|
|
|
98
|
+
// Merge label maps from all accounts
|
|
99
|
+
const labelMap = new Map<string, string>()
|
|
100
|
+
for (const r of allResults) for (const [id, name] of r.labelMap) labelMap.set(id, name)
|
|
101
|
+
|
|
69
102
|
// Merge threads from all accounts, sorted by date descending, capped at max
|
|
70
103
|
const merged = allResults
|
|
71
104
|
.flatMap(({ email, result }) =>
|
|
@@ -81,16 +114,26 @@ export function registerMailCommands(cli: Goke) {
|
|
|
81
114
|
|
|
82
115
|
const showAccount = clients.length > 1
|
|
83
116
|
out.printList(
|
|
84
|
-
merged.map((t) =>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
117
|
+
merged.map((t) => {
|
|
118
|
+
const to = t.to.map((s) => out.formatSender(s)).join(', ')
|
|
119
|
+
const cc = t.cc.map((s) => out.formatSender(s)).join(', ')
|
|
120
|
+
const labels = formatLabels(t.labelIds, labelMap)
|
|
121
|
+
return {
|
|
122
|
+
...(showAccount ? { account: t.account } : {}),
|
|
123
|
+
id: t.id,
|
|
124
|
+
flags: out.formatFlags(t),
|
|
125
|
+
from: out.formatSender(t.from),
|
|
126
|
+
...(to ? { to } : {}),
|
|
127
|
+
...(cc ? { cc } : {}),
|
|
128
|
+
subject: t.subject,
|
|
129
|
+
snippet: t.snippet,
|
|
130
|
+
date: out.formatDate(t.date),
|
|
131
|
+
messages: t.messageCount,
|
|
132
|
+
...(labels ? { labels } : {}),
|
|
133
|
+
...(t.listUnsubscribe ? { unsubscribe: t.listUnsubscribe } : {}),
|
|
134
|
+
}
|
|
135
|
+
}),
|
|
136
|
+
{ summary: `${merged.length} threads (${folder})`, nextPage: allResults[0]?.result.nextPageToken },
|
|
94
137
|
)
|
|
95
138
|
})
|
|
96
139
|
|
|
@@ -101,7 +144,7 @@ export function registerMailCommands(cli: Goke) {
|
|
|
101
144
|
cli
|
|
102
145
|
.command('mail search <query>', 'Search email threads using Gmail query syntax (from:, to:, subject:, has:attachment, etc). See https://support.google.com/mail/answer/7190')
|
|
103
146
|
.option('--max [max]', 'Max results (default: 20)')
|
|
104
|
-
.option('--page <page>', 'Pagination token')
|
|
147
|
+
.option('--page <page>', 'Pagination token (requires --account, only works for a single account)')
|
|
105
148
|
.action(async (query, options) => {
|
|
106
149
|
const max = options.max ? Number(options.max) : 20
|
|
107
150
|
const clients = await getClients(options.account)
|
|
@@ -111,16 +154,24 @@ export function registerMailCommands(cli: Goke) {
|
|
|
111
154
|
process.exit(1)
|
|
112
155
|
}
|
|
113
156
|
|
|
114
|
-
// Search all accounts concurrently
|
|
157
|
+
// Search all accounts concurrently (fetch labels alongside for name resolution)
|
|
115
158
|
const results = await Promise.all(
|
|
116
|
-
clients.map(async ({ email, client }) => {
|
|
159
|
+
clients.map(async ({ email, client, accountType }) => {
|
|
117
160
|
const result = await client.listThreads({
|
|
118
161
|
query,
|
|
119
162
|
maxResults: max,
|
|
120
163
|
pageToken: options.page,
|
|
121
164
|
})
|
|
122
165
|
if (result instanceof Error) return result
|
|
123
|
-
|
|
166
|
+
|
|
167
|
+
let labelMap = new Map<string, string>()
|
|
168
|
+
if (accountType === 'google') {
|
|
169
|
+
const labelsResult = await (client as GmailClient).listLabels()
|
|
170
|
+
if (!(labelsResult instanceof Error)) {
|
|
171
|
+
labelMap = new Map(labelsResult.parsed.map((l) => [l.id, l.name]))
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { email, result, labelMap }
|
|
124
175
|
}),
|
|
125
176
|
)
|
|
126
177
|
|
|
@@ -130,6 +181,10 @@ export function registerMailCommands(cli: Goke) {
|
|
|
130
181
|
return true
|
|
131
182
|
})
|
|
132
183
|
|
|
184
|
+
// Merge label maps from all accounts
|
|
185
|
+
const labelMap = new Map<string, string>()
|
|
186
|
+
for (const r of allResults) for (const [id, name] of r.labelMap) labelMap.set(id, name)
|
|
187
|
+
|
|
133
188
|
const merged = allResults
|
|
134
189
|
.flatMap(({ email, result }) =>
|
|
135
190
|
result.threads.map((t) => ({ ...t, account: email })),
|
|
@@ -144,16 +199,26 @@ export function registerMailCommands(cli: Goke) {
|
|
|
144
199
|
|
|
145
200
|
const showAccount = clients.length > 1
|
|
146
201
|
out.printList(
|
|
147
|
-
merged.map((t) =>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
202
|
+
merged.map((t) => {
|
|
203
|
+
const to = t.to.map((s) => out.formatSender(s)).join(', ')
|
|
204
|
+
const cc = t.cc.map((s) => out.formatSender(s)).join(', ')
|
|
205
|
+
const labels = formatLabels(t.labelIds, labelMap)
|
|
206
|
+
return {
|
|
207
|
+
...(showAccount ? { account: t.account } : {}),
|
|
208
|
+
id: t.id,
|
|
209
|
+
flags: out.formatFlags(t),
|
|
210
|
+
from: out.formatSender(t.from),
|
|
211
|
+
...(to ? { to } : {}),
|
|
212
|
+
...(cc ? { cc } : {}),
|
|
213
|
+
subject: t.subject,
|
|
214
|
+
snippet: t.snippet,
|
|
215
|
+
date: out.formatDate(t.date),
|
|
216
|
+
messages: t.messageCount,
|
|
217
|
+
...(labels ? { labels } : {}),
|
|
218
|
+
...(t.listUnsubscribe ? { unsubscribe: t.listUnsubscribe } : {}),
|
|
219
|
+
}
|
|
220
|
+
}),
|
|
221
|
+
{ summary: `${merged.length} results for "${query}"`, nextPage: allResults[0]?.result.nextPageToken },
|
|
157
222
|
)
|
|
158
223
|
})
|
|
159
224
|
|
|
@@ -162,10 +227,16 @@ export function registerMailCommands(cli: Goke) {
|
|
|
162
227
|
// =========================================================================
|
|
163
228
|
|
|
164
229
|
cli
|
|
165
|
-
.command('mail read
|
|
166
|
-
.option('--raw', 'Show raw message (first message only)')
|
|
230
|
+
.command('mail read [...threadIds]', 'Read full email threads (does not mark as read)')
|
|
231
|
+
.option('--raw', 'Show raw message (first message only, single thread)')
|
|
167
232
|
.option('--raw-html', 'Show raw HTML body per message (no markdown conversion)')
|
|
168
|
-
.
|
|
233
|
+
.option('--verify', 'Show expanded email authentication details (SPF/DKIM/DMARC)')
|
|
234
|
+
.action(async (threadIds, options) => {
|
|
235
|
+
if (threadIds.length === 0) {
|
|
236
|
+
out.error('No thread IDs provided')
|
|
237
|
+
process.exit(1)
|
|
238
|
+
}
|
|
239
|
+
|
|
169
240
|
const { client } = await getClient(options.account)
|
|
170
241
|
|
|
171
242
|
if (options.raw && options.rawHtml) {
|
|
@@ -174,7 +245,11 @@ export function registerMailCommands(cli: Goke) {
|
|
|
174
245
|
}
|
|
175
246
|
|
|
176
247
|
if (options.raw) {
|
|
177
|
-
|
|
248
|
+
if (threadIds.length > 1) {
|
|
249
|
+
out.error('--raw only supports a single thread ID')
|
|
250
|
+
process.exit(1)
|
|
251
|
+
}
|
|
252
|
+
const { parsed: thread } = await client.getThread({ threadId: threadIds[0]! })
|
|
178
253
|
if (thread.messages.length === 0) {
|
|
179
254
|
out.hint('No messages in thread')
|
|
180
255
|
return
|
|
@@ -185,72 +260,114 @@ export function registerMailCommands(cli: Goke) {
|
|
|
185
260
|
return
|
|
186
261
|
}
|
|
187
262
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (options.rawHtml) {
|
|
196
|
-
thread.messages.forEach((msg, index) => {
|
|
197
|
-
console.log(msg.body)
|
|
198
|
-
if (index < thread.messages.length - 1) {
|
|
199
|
-
console.log('\n<!-- ZELE_MESSAGE_SEPARATOR -->\n')
|
|
200
|
-
}
|
|
201
|
-
})
|
|
202
|
-
return
|
|
203
|
-
}
|
|
263
|
+
// Fetch all threads concurrently, tolerating individual failures
|
|
264
|
+
const settled = await Promise.allSettled(
|
|
265
|
+
threadIds.map((id) => client.getThread({ threadId: id })),
|
|
266
|
+
)
|
|
204
267
|
|
|
205
268
|
const w = Math.min(process.stdout.columns || 72, 72)
|
|
206
269
|
const rule = pc.dim('─'.repeat(w))
|
|
270
|
+
const multi = threadIds.length > 1
|
|
207
271
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
console.log(pc.bold(`From: `) + fromStr + flagStr)
|
|
233
|
-
console.log(pc.dim(` To: ${msg.to.map((t) => t.email).join(', ')}`))
|
|
234
|
-
if (msg.cc && msg.cc.length > 0) {
|
|
235
|
-
console.log(pc.dim(` Cc: ${msg.cc.map((c) => c.email).join(', ')}`))
|
|
272
|
+
for (let i = 0; i < settled.length; i++) {
|
|
273
|
+
const result = settled[i]!
|
|
274
|
+
|
|
275
|
+
if (multi) {
|
|
276
|
+
const doubleRule = pc.bold('━'.repeat(w))
|
|
277
|
+
console.log(doubleRule)
|
|
278
|
+
console.log(pc.bold(`Thread ${i + 1}/${settled.length}`) + pc.dim(` · ${threadIds[i]}`))
|
|
279
|
+
console.log(doubleRule)
|
|
280
|
+
console.log()
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (result.status === 'rejected') {
|
|
284
|
+
out.error(`Failed to read thread ${threadIds[i]}: ${String(result.reason)}`)
|
|
285
|
+
if (multi) console.log()
|
|
286
|
+
continue
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const { parsed: thread } = result.value
|
|
290
|
+
|
|
291
|
+
if (thread.messages.length === 0) {
|
|
292
|
+
out.hint('No messages in thread')
|
|
293
|
+
if (multi) console.log()
|
|
294
|
+
continue
|
|
236
295
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return `${a.filename} (${size})`
|
|
296
|
+
|
|
297
|
+
if (options.rawHtml) {
|
|
298
|
+
thread.messages.forEach((msg, index) => {
|
|
299
|
+
console.log(msg.body)
|
|
300
|
+
if (index < thread.messages.length - 1) {
|
|
301
|
+
console.log('\n<!-- ZELE_MESSAGE_SEPARATOR -->\n')
|
|
302
|
+
}
|
|
245
303
|
})
|
|
246
|
-
console.log(
|
|
304
|
+
if (multi) console.log()
|
|
305
|
+
continue
|
|
247
306
|
}
|
|
248
307
|
|
|
249
|
-
|
|
308
|
+
// Render thread header
|
|
309
|
+
console.log(pc.bold(thread.subject))
|
|
310
|
+
const participants = new Map<string, string>()
|
|
311
|
+
for (const msg of thread.messages) {
|
|
312
|
+
participants.set(msg.from.email, msg.from.name || msg.from.email)
|
|
313
|
+
for (const r of msg.to) participants.set(r.email, r.name || r.email)
|
|
314
|
+
}
|
|
315
|
+
const participantStr = [...participants.values()].join(', ')
|
|
316
|
+
console.log(pc.dim(`${thread.messageCount} message(s) · ${participantStr}`))
|
|
317
|
+
console.log(pc.dim(`ID: ${thread.id}`))
|
|
318
|
+
console.log(rule + '\n')
|
|
319
|
+
|
|
320
|
+
// Render each message
|
|
321
|
+
for (const msg of thread.messages) {
|
|
322
|
+
const fromStr = out.formatSender(msg.from)
|
|
323
|
+
const dateStr = out.formatDate(msg.date)
|
|
324
|
+
|
|
325
|
+
const flagParts: string[] = []
|
|
326
|
+
if (msg.unread) flagParts.push(pc.yellow('[unread]'))
|
|
327
|
+
if (msg.starred) flagParts.push(pc.yellow('[starred]'))
|
|
328
|
+
const flagStr = flagParts.length > 0 ? ' ' + flagParts.join(' ') : ''
|
|
329
|
+
|
|
330
|
+
console.log(pc.bold(`From: `) + fromStr + flagStr)
|
|
331
|
+
console.log(pc.dim(` To: ${msg.to.map((t) => t.email).join(', ')}`))
|
|
332
|
+
if (msg.cc && msg.cc.length > 0) {
|
|
333
|
+
console.log(pc.dim(` Cc: ${msg.cc.map((c) => c.email).join(', ')}`))
|
|
334
|
+
}
|
|
335
|
+
console.log(pc.dim(`Date: ${dateStr}`))
|
|
250
336
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
337
|
+
if (msg.auth) {
|
|
338
|
+
const check = (verdict: string) => {
|
|
339
|
+
return verdict === 'pass'
|
|
340
|
+
? pc.green('✓')
|
|
341
|
+
: pc.red('✗')
|
|
342
|
+
}
|
|
343
|
+
const parts = [
|
|
344
|
+
`${check(msg.auth.spf)} SPF`,
|
|
345
|
+
`${check(msg.auth.dkim)} DKIM`,
|
|
346
|
+
`${check(msg.auth.dmarc)} DMARC`,
|
|
347
|
+
]
|
|
348
|
+
const label = msg.auth.authentic ? pc.green('authentic') : pc.red('UNVERIFIED')
|
|
349
|
+
console.log(`Auth: ${parts.join(' ')} (${label})`)
|
|
350
|
+
if (options.verify) {
|
|
351
|
+
console.log(pc.dim(` Raw: ${msg.auth.raw}`))
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (msg.attachments.length > 0) {
|
|
356
|
+
const attList = msg.attachments.map((a) => {
|
|
357
|
+
const size = a.size < 1024 ? `${a.size} B`
|
|
358
|
+
: a.size < 1048576 ? `${(a.size / 1024).toFixed(1)} KB`
|
|
359
|
+
: `${(a.size / 1048576).toFixed(1)} MB`
|
|
360
|
+
return `${a.filename} (${size})`
|
|
361
|
+
})
|
|
362
|
+
console.log(pc.dim(`Attachments: ${attList.join(', ')}`))
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
console.log()
|
|
366
|
+
|
|
367
|
+
const body = out.renderEmailBody(msg.body, msg.mimeType)
|
|
368
|
+
console.log(body)
|
|
369
|
+
console.log('\n' + rule + '\n')
|
|
370
|
+
}
|
|
254
371
|
}
|
|
255
372
|
})
|
|
256
373
|
|
|
@@ -326,6 +443,7 @@ export function registerMailCommands(cli: Goke) {
|
|
|
326
443
|
fromEmail: options.from,
|
|
327
444
|
attachments,
|
|
328
445
|
})
|
|
446
|
+
if (result instanceof Error) handleCommandError(result)
|
|
329
447
|
|
|
330
448
|
out.printYaml(result)
|
|
331
449
|
out.success(`Sent to ${options.to}`)
|
package/src/commands/profile.ts
CHANGED
|
@@ -5,24 +5,30 @@
|
|
|
5
5
|
|
|
6
6
|
import type { Goke } from 'goke'
|
|
7
7
|
import { getClients } from '../auth.js'
|
|
8
|
+
import type { GmailClient } from '../gmail-client.js'
|
|
8
9
|
import { AuthError } from '../api-utils.js'
|
|
9
10
|
import * as out from '../output.js'
|
|
10
11
|
|
|
11
12
|
export function registerProfileCommands(cli: Goke) {
|
|
12
13
|
cli
|
|
13
|
-
.command('profile', 'Show
|
|
14
|
+
.command('profile', 'Show account info')
|
|
14
15
|
.action(async (options) => {
|
|
15
16
|
const clients = await getClients(options.account)
|
|
16
17
|
|
|
17
18
|
// Fetch all accounts concurrently
|
|
18
19
|
const allResults = await Promise.all(
|
|
19
|
-
clients.map(async ({ client }) => {
|
|
20
|
+
clients.map(async ({ client, accountType }) => {
|
|
20
21
|
const profile = await client.getProfile()
|
|
21
22
|
if (profile instanceof Error) return profile
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
|
|
24
|
+
if (accountType === 'google') {
|
|
25
|
+
// Google accounts have aliases
|
|
26
|
+
const aliases = await (client as GmailClient).getEmailAliases()
|
|
27
|
+
if (aliases instanceof Error) return aliases
|
|
28
|
+
return { profile, aliases, accountType }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { profile, aliases: [{ email: profile.emailAddress, primary: true }], accountType }
|
|
26
32
|
}),
|
|
27
33
|
)
|
|
28
34
|
|
|
@@ -32,18 +38,22 @@ export function registerProfileCommands(cli: Goke) {
|
|
|
32
38
|
return true
|
|
33
39
|
})
|
|
34
40
|
|
|
35
|
-
for (const { profile, aliases } of results) {
|
|
36
|
-
|
|
41
|
+
for (const { profile, aliases, accountType } of results) {
|
|
42
|
+
const data: Record<string, unknown> = {
|
|
37
43
|
email: profile.emailAddress,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
type: accountType,
|
|
45
|
+
}
|
|
46
|
+
if (accountType === 'google') {
|
|
47
|
+
data.messages_total = profile.messagesTotal
|
|
48
|
+
data.threads_total = profile.threadsTotal
|
|
49
|
+
data.history_id = profile.historyId
|
|
50
|
+
}
|
|
51
|
+
data.aliases = aliases.map((a) => ({
|
|
52
|
+
email: a.email,
|
|
53
|
+
name: a.name ?? null,
|
|
54
|
+
primary: a.primary,
|
|
55
|
+
}))
|
|
56
|
+
out.printYaml(data)
|
|
47
57
|
}
|
|
48
58
|
})
|
|
49
59
|
}
|
package/src/db.ts
CHANGED
|
@@ -58,6 +58,10 @@ async function initializePrisma(): Promise<PrismaClient> {
|
|
|
58
58
|
// Run schema.sql — uses CREATE TABLE IF NOT EXISTS so it's idempotent
|
|
59
59
|
await applySchema(prisma)
|
|
60
60
|
|
|
61
|
+
// Add new columns to existing Account tables (idempotent migration).
|
|
62
|
+
// CREATE TABLE IF NOT EXISTS doesn't add columns to pre-existing tables.
|
|
63
|
+
await migrateAccountColumns(prisma)
|
|
64
|
+
|
|
61
65
|
// Secure database files (owner read/write only)
|
|
62
66
|
secureDatabase()
|
|
63
67
|
|
|
@@ -93,6 +97,30 @@ async function applySchema(prisma: PrismaClient): Promise<void> {
|
|
|
93
97
|
}
|
|
94
98
|
}
|
|
95
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Idempotent migration: add accountType and capabilities columns to Account
|
|
102
|
+
* if they don't already exist (for DBs created before IMAP/SMTP support).
|
|
103
|
+
* Also backfill existing Google accounts with their default capabilities.
|
|
104
|
+
*/
|
|
105
|
+
async function migrateAccountColumns(prisma: PrismaClient): Promise<void> {
|
|
106
|
+
const cols = await prisma.$queryRawUnsafe<Array<{ name: string }>>(`PRAGMA table_info("Account")`)
|
|
107
|
+
const colNames = new Set(cols.map((c) => c.name))
|
|
108
|
+
|
|
109
|
+
if (!colNames.has('accountType')) {
|
|
110
|
+
await prisma.$executeRawUnsafe(`ALTER TABLE "Account" ADD COLUMN "accountType" TEXT NOT NULL DEFAULT 'google'`)
|
|
111
|
+
}
|
|
112
|
+
if (!colNames.has('capabilities')) {
|
|
113
|
+
await prisma.$executeRawUnsafe(`ALTER TABLE "Account" ADD COLUMN "capabilities" TEXT NOT NULL DEFAULT ''`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Backfill: existing Google accounts should have capabilities set
|
|
117
|
+
await prisma.$executeRawUnsafe(`
|
|
118
|
+
UPDATE "Account"
|
|
119
|
+
SET "capabilities" = 'gmail,calendar,smtp'
|
|
120
|
+
WHERE "accountType" = 'google' AND ("capabilities" = '' OR "capabilities" IS NULL)
|
|
121
|
+
`)
|
|
122
|
+
}
|
|
123
|
+
|
|
96
124
|
/**
|
|
97
125
|
* Set restrictive permissions on database files.
|
|
98
126
|
* SQLite WAL mode creates additional -wal and -shm files that also need protection.
|
|
@@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = {
|
|
|
20
20
|
"clientVersion": "7.3.0",
|
|
21
21
|
"engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735",
|
|
22
22
|
"activeProvider": "sqlite",
|
|
23
|
-
"inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"./src/generated\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n}\n\n// Lifecycle status for account credentials stored in `Account`.\nenum AccountStatus {\n active\n disabled\n}\n\n// Stores one
|
|
23
|
+
"inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"./src/generated\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n}\n\n// Lifecycle status for account credentials stored in `Account`.\nenum AccountStatus {\n active\n disabled\n}\n\n// Stores one credential set per (email, appId) pair.\n// appId is the Google OAuth client ID for Google accounts, or 'imap_smtp' for IMAP/SMTP accounts.\n// accountType discriminates the credential format stored in tokens.\n// capabilities is a comma-separated list of features: \"gmail,calendar,smtp\" or \"imap,smtp\".\nmodel Account {\n email String\n appId String\n accountType String @default(\"google\") // \"google\" | \"imap_smtp\"\n capabilities String @default(\"\") // comma-separated: \"gmail,calendar,smtp\" or \"imap,smtp\" or \"imap\"\n accountStatus AccountStatus\n tokens String // JSON: OAuth2 Credentials (google) or ImapSmtpCredentials (imap_smtp)\n createdAt DateTime\n updatedAt DateTime @updatedAt\n\n threads Thread[]\n labels Label?\n profiles Profile?\n syncStates SyncState[]\n calendarLists CalendarList?\n\n @@id([email, appId])\n}\n\n// Caches hydrated thread payloads per account + thread ID.\n// Used by mail read and post-mutation cache invalidation.\n// rawData stores the raw Google gmail_v1.Schema$Thread response (format: full)\n// so the cache is resilient to changes in our own ThreadData type.\n// Indexed columns are extracted for queryability and display.\nmodel Thread {\n id Int @id @default(autoincrement())\n email String\n appId String\n threadId String\n subject String // extracted for display/search\n snippet String // extracted for display\n fromEmail String // extracted for filtering\n fromName String // extracted for display\n date String // extracted for sorting (RFC2822 from header)\n labelIds String // comma-separated, extracted for filtering\n hasUnread Boolean // extracted for filtering\n msgCount Int // extracted for display\n historyId String? // for sync\n rawData String // raw Google API response JSON (gmail_v1.Schema$Thread)\n ttlMs Int\n createdAt DateTime\n\n account Account @relation(fields: [email, appId], references: [email, appId], onDelete: Cascade)\n\n @@unique([email, appId, threadId])\n}\n\n// Caches label metadata per account (label id/name/type payload).\n// Used by label list/get and related command outputs.\n// rawData stores the raw Google gmail_v1.Schema$Label[] response.\nmodel Label {\n email String\n appId String\n rawData String // raw Google API response JSON (gmail_v1.Schema$Label[])\n ttlMs Int\n createdAt DateTime\n\n account Account @relation(fields: [email, appId], references: [email, appId], onDelete: Cascade)\n\n @@id([email, appId])\n}\n\n// Caches Gmail profile payload per account (totals/history id).\n// Used by profile command and account metadata lookups.\n// Fully flattened — no JSON blob needed, only 4 fields from Google.\nmodel Profile {\n email String\n appId String\n emailAddress String // from Gmail API\n messagesTotal Int // from Gmail API\n threadsTotal Int // from Gmail API\n historyId String // from Gmail API\n ttlMs Int\n createdAt DateTime\n\n account Account @relation(fields: [email, appId], references: [email, appId], onDelete: Cascade)\n\n @@id([email, appId])\n}\n\n// Caches calendar list per account.\n// Used by cal list to avoid fetching calendar metadata on every invocation.\n// rawData stores parsed CalendarListItem[] JSON (not raw tsdav — parsed at write time).\nmodel CalendarList {\n email String\n appId String\n rawData String // JSON blob of CalendarListItem[]\n ttlMs Int\n createdAt DateTime\n\n account Account @relation(fields: [email, appId], references: [email, appId], onDelete: Cascade)\n\n @@id([email, appId])\n}\n\n// Stores persistent per-account sync metadata as generic key/value pairs.\n// Use this for lightweight sync cursors and markers that are not cached API\n// payloads, for example `history_id` (incremental Gmail history cursor),\n// `last_full_sync_at`, or other small account-scoped checkpoints.\nmodel SyncState {\n email String\n appId String\n key String\n value String\n\n account Account @relation(fields: [email, appId], references: [email, appId], onDelete: Cascade)\n\n @@id([email, appId, key])\n}\n",
|
|
24
24
|
"runtimeDataModel": {
|
|
25
25
|
"models": {},
|
|
26
26
|
"enums": {},
|
|
@@ -28,7 +28,7 @@ const config: runtime.GetPrismaClientConfig = {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
config.runtimeDataModel = JSON.parse("{\"models\":{\"Account\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountStatus\",\"kind\":\"enum\",\"type\":\"AccountStatus\"},{\"name\":\"tokens\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"threads\",\"kind\":\"object\",\"type\":\"Thread\",\"relationName\":\"AccountToThread\"},{\"name\":\"labels\",\"kind\":\"object\",\"type\":\"Label\",\"relationName\":\"AccountToLabel\"},{\"name\":\"profiles\",\"kind\":\"object\",\"type\":\"Profile\",\"relationName\":\"AccountToProfile\"},{\"name\":\"syncStates\",\"kind\":\"object\",\"type\":\"SyncState\",\"relationName\":\"AccountToSyncState\"},{\"name\":\"calendarLists\",\"kind\":\"object\",\"type\":\"CalendarList\",\"relationName\":\"AccountToCalendarList\"}],\"dbName\":null},\"Thread\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"threadId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"subject\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"snippet\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fromEmail\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fromName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"date\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"labelIds\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hasUnread\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"msgCount\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"historyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"rawData\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"ttlMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToThread\"}],\"dbName\":null},\"Label\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"rawData\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"ttlMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToLabel\"}],\"dbName\":null},\"Profile\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailAddress\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"messagesTotal\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"threadsTotal\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"historyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"ttlMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToProfile\"}],\"dbName\":null},\"CalendarList\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"rawData\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"ttlMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToCalendarList\"}],\"dbName\":null},\"SyncState\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"key\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToSyncState\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
|
31
|
+
config.runtimeDataModel = JSON.parse("{\"models\":{\"Account\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountType\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"capabilities\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountStatus\",\"kind\":\"enum\",\"type\":\"AccountStatus\"},{\"name\":\"tokens\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"threads\",\"kind\":\"object\",\"type\":\"Thread\",\"relationName\":\"AccountToThread\"},{\"name\":\"labels\",\"kind\":\"object\",\"type\":\"Label\",\"relationName\":\"AccountToLabel\"},{\"name\":\"profiles\",\"kind\":\"object\",\"type\":\"Profile\",\"relationName\":\"AccountToProfile\"},{\"name\":\"syncStates\",\"kind\":\"object\",\"type\":\"SyncState\",\"relationName\":\"AccountToSyncState\"},{\"name\":\"calendarLists\",\"kind\":\"object\",\"type\":\"CalendarList\",\"relationName\":\"AccountToCalendarList\"}],\"dbName\":null},\"Thread\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"threadId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"subject\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"snippet\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fromEmail\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fromName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"date\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"labelIds\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hasUnread\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"msgCount\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"historyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"rawData\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"ttlMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToThread\"}],\"dbName\":null},\"Label\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"rawData\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"ttlMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToLabel\"}],\"dbName\":null},\"Profile\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailAddress\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"messagesTotal\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"threadsTotal\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"historyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"ttlMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToProfile\"}],\"dbName\":null},\"CalendarList\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"rawData\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"ttlMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToCalendarList\"}],\"dbName\":null},\"SyncState\":{\"fields\":[{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"key\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToSyncState\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
|
32
32
|
|
|
33
33
|
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
|
|
34
34
|
const { Buffer } = await import('node:buffer')
|
|
@@ -892,6 +892,8 @@ export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof
|
|
|
892
892
|
export const AccountScalarFieldEnum = {
|
|
893
893
|
email: 'email',
|
|
894
894
|
appId: 'appId',
|
|
895
|
+
accountType: 'accountType',
|
|
896
|
+
capabilities: 'capabilities',
|
|
895
897
|
accountStatus: 'accountStatus',
|
|
896
898
|
tokens: 'tokens',
|
|
897
899
|
createdAt: 'createdAt',
|
|
@@ -75,6 +75,8 @@ export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof
|
|
|
75
75
|
export const AccountScalarFieldEnum = {
|
|
76
76
|
email: 'email',
|
|
77
77
|
appId: 'appId',
|
|
78
|
+
accountType: 'accountType',
|
|
79
|
+
capabilities: 'capabilities',
|
|
78
80
|
accountStatus: 'accountStatus',
|
|
79
81
|
tokens: 'tokens',
|
|
80
82
|
createdAt: 'createdAt',
|