@honest-magic/mail-mcp 1.0.0
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/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/cli/accounts.d.ts +7 -0
- package/dist/cli/accounts.js +171 -0
- package/dist/cli/accounts.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +48 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +658 -0
- package/dist/index.js +556 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/imap.d.ts +29 -0
- package/dist/protocol/imap.js +255 -0
- package/dist/protocol/imap.js.map +1 -0
- package/dist/protocol/smtp.d.ts +8 -0
- package/dist/protocol/smtp.js +56 -0
- package/dist/protocol/smtp.js.map +1 -0
- package/dist/security/keychain.d.ts +3 -0
- package/dist/security/keychain.js +18 -0
- package/dist/security/keychain.js.map +1 -0
- package/dist/security/oauth2.d.ts +9 -0
- package/dist/security/oauth2.js +51 -0
- package/dist/security/oauth2.js.map +1 -0
- package/dist/services/mail.d.ts +45 -0
- package/dist/services/mail.js +200 -0
- package/dist/services/mail.js.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/markdown.d.ts +1 -0
- package/dist/utils/markdown.js +11 -0
- package/dist/utils/markdown.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { ImapFlow } from 'imapflow';
|
|
2
|
+
import { loadCredentials } from '../security/keychain.js';
|
|
3
|
+
import { getValidAccessToken } from '../security/oauth2.js';
|
|
4
|
+
import { simpleParser } from 'mailparser';
|
|
5
|
+
export class ImapClient {
|
|
6
|
+
client = null;
|
|
7
|
+
account;
|
|
8
|
+
constructor(account) {
|
|
9
|
+
this.account = account;
|
|
10
|
+
}
|
|
11
|
+
async connect() {
|
|
12
|
+
let authConfig = { user: this.account.user };
|
|
13
|
+
if (this.account.authType === 'oauth2') {
|
|
14
|
+
const accessToken = await getValidAccessToken(this.account.id);
|
|
15
|
+
authConfig.accessToken = accessToken;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const password = await loadCredentials(this.account.id);
|
|
19
|
+
if (!password) {
|
|
20
|
+
throw new Error(`Credentials not found for account: ${this.account.id}`);
|
|
21
|
+
}
|
|
22
|
+
authConfig.pass = password;
|
|
23
|
+
}
|
|
24
|
+
this.client = new ImapFlow({
|
|
25
|
+
host: this.account.host,
|
|
26
|
+
port: this.account.port,
|
|
27
|
+
secure: this.account.useTLS,
|
|
28
|
+
auth: authConfig,
|
|
29
|
+
logger: false
|
|
30
|
+
});
|
|
31
|
+
await this.client.connect();
|
|
32
|
+
}
|
|
33
|
+
async disconnect() {
|
|
34
|
+
if (this.client) {
|
|
35
|
+
await this.client.logout();
|
|
36
|
+
this.client = null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async listMessages(folder = 'INBOX', count = 10) {
|
|
40
|
+
if (!this.client) {
|
|
41
|
+
throw new Error('Not connected');
|
|
42
|
+
}
|
|
43
|
+
const lock = await this.client.getMailboxLock(folder);
|
|
44
|
+
try {
|
|
45
|
+
const messages = [];
|
|
46
|
+
const mailbox = this.client.mailbox;
|
|
47
|
+
const total = (mailbox && typeof mailbox !== 'boolean') ? mailbox.exists : 0;
|
|
48
|
+
if (total === 0)
|
|
49
|
+
return [];
|
|
50
|
+
const start = Math.max(1, total - count + 1);
|
|
51
|
+
const range = `${start}:*`;
|
|
52
|
+
for await (const msg of this.client.fetch(range, { envelope: true, flags: true, internalDate: true, bodyParts: ['TEXT'] })) {
|
|
53
|
+
const textBuf = msg.bodyParts?.get('TEXT');
|
|
54
|
+
const snippet = textBuf
|
|
55
|
+
? textBuf.toString('utf-8').replace(/\s+/g, ' ').slice(0, 200).trim()
|
|
56
|
+
: '';
|
|
57
|
+
messages.push({
|
|
58
|
+
id: msg.uid.toString(),
|
|
59
|
+
uid: msg.uid,
|
|
60
|
+
subject: msg.envelope?.subject,
|
|
61
|
+
from: msg.envelope?.from?.[0]?.address || 'Unknown',
|
|
62
|
+
date: msg.envelope?.date || (msg.internalDate instanceof Date ? msg.internalDate : (msg.internalDate ? new Date(msg.internalDate) : undefined)),
|
|
63
|
+
snippet,
|
|
64
|
+
threadId: msg.threadId?.toString(),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return messages.reverse();
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
lock.release();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async searchMessages(criteria, folder = 'INBOX', count = 10) {
|
|
74
|
+
if (!this.client) {
|
|
75
|
+
throw new Error('Not connected');
|
|
76
|
+
}
|
|
77
|
+
const lock = await this.client.getMailboxLock(folder);
|
|
78
|
+
try {
|
|
79
|
+
const uids = await this.client.search(criteria, { uid: true });
|
|
80
|
+
if (!uids || typeof uids === 'boolean' || uids.length === 0)
|
|
81
|
+
return [];
|
|
82
|
+
// Take only the last 'count' messages
|
|
83
|
+
const uidsArray = uids;
|
|
84
|
+
const lastUids = uidsArray.slice(-count);
|
|
85
|
+
const messages = [];
|
|
86
|
+
for await (const msg of this.client.fetch(lastUids.join(','), { envelope: true, flags: true, internalDate: true, bodyParts: ['TEXT'] }, { uid: true })) {
|
|
87
|
+
const textBuf = msg.bodyParts?.get('TEXT');
|
|
88
|
+
const snippet = textBuf
|
|
89
|
+
? textBuf.toString('utf-8').replace(/\s+/g, ' ').slice(0, 200).trim()
|
|
90
|
+
: '';
|
|
91
|
+
messages.push({
|
|
92
|
+
id: msg.uid.toString(),
|
|
93
|
+
uid: msg.uid,
|
|
94
|
+
subject: msg.envelope?.subject,
|
|
95
|
+
from: msg.envelope?.from?.[0]?.address || 'Unknown',
|
|
96
|
+
date: msg.envelope?.date || (msg.internalDate instanceof Date ? msg.internalDate : (msg.internalDate ? new Date(msg.internalDate) : undefined)),
|
|
97
|
+
snippet,
|
|
98
|
+
threadId: msg.threadId?.toString(),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return messages.reverse();
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
lock.release();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async fetchMessageBody(uid, folder = 'INBOX') {
|
|
108
|
+
if (!this.client) {
|
|
109
|
+
throw new Error('Not connected');
|
|
110
|
+
}
|
|
111
|
+
const lock = await this.client.getMailboxLock(folder);
|
|
112
|
+
try {
|
|
113
|
+
const msg = await this.client.fetchOne(uid, { source: true, internalDate: true }, { uid: true });
|
|
114
|
+
if (!msg || !msg.source) {
|
|
115
|
+
throw new Error(`Message with UID ${uid} not found`);
|
|
116
|
+
}
|
|
117
|
+
return await simpleParser(msg.source);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
lock.release();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async fetchThreadMessages(threadId, folder = 'INBOX') {
|
|
124
|
+
if (!this.client) {
|
|
125
|
+
throw new Error('Not connected');
|
|
126
|
+
}
|
|
127
|
+
const lock = await this.client.getMailboxLock(folder);
|
|
128
|
+
try {
|
|
129
|
+
// Use GM-THRID for Gmail, fall back to References/Message-ID header search
|
|
130
|
+
let uids = [];
|
|
131
|
+
try {
|
|
132
|
+
uids = await this.client.search({ 'x-gm-thrid': threadId });
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
// x-gm-thrid not supported — fall through to header search below
|
|
136
|
+
}
|
|
137
|
+
if (!uids || uids.length === 0) {
|
|
138
|
+
try {
|
|
139
|
+
const refUids = await this.client.search({ header: { References: threadId } });
|
|
140
|
+
const rootUids = await this.client.search({ header: { 'Message-ID': threadId } });
|
|
141
|
+
uids = [...new Set([...(refUids || []), ...(rootUids || [])])];
|
|
142
|
+
}
|
|
143
|
+
catch (e2) {
|
|
144
|
+
// header search not supported — return empty
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (!uids || uids.length === 0)
|
|
149
|
+
return [];
|
|
150
|
+
const messages = [];
|
|
151
|
+
for await (const msg of this.client.fetch(uids.join(','), { envelope: true, flags: true, internalDate: true }, { uid: true })) {
|
|
152
|
+
messages.push({
|
|
153
|
+
id: msg.uid.toString(),
|
|
154
|
+
uid: msg.uid,
|
|
155
|
+
subject: msg.envelope?.subject,
|
|
156
|
+
from: msg.envelope?.from?.[0]?.address || 'Unknown',
|
|
157
|
+
date: msg.envelope?.date || (msg.internalDate instanceof Date ? msg.internalDate : (msg.internalDate ? new Date(msg.internalDate) : undefined)),
|
|
158
|
+
snippet: '',
|
|
159
|
+
threadId: msg.threadId?.toString() || threadId,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return messages.sort((a, b) => (a.date?.getTime() || 0) - (b.date?.getTime() || 0));
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
lock.release();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async appendMessage(folder, rawMessage, flags = []) {
|
|
169
|
+
if (!this.client) {
|
|
170
|
+
throw new Error('Not connected');
|
|
171
|
+
}
|
|
172
|
+
const lock = await this.client.getMailboxLock(folder);
|
|
173
|
+
try {
|
|
174
|
+
await this.client.append(folder, rawMessage, flags);
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
lock.release();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async listFolders() {
|
|
181
|
+
if (!this.client)
|
|
182
|
+
throw new Error('Not connected');
|
|
183
|
+
const folders = await this.client.list();
|
|
184
|
+
return folders.map(f => f.path);
|
|
185
|
+
}
|
|
186
|
+
async moveMessage(uid, sourceFolder, targetFolder) {
|
|
187
|
+
if (!this.client)
|
|
188
|
+
throw new Error('Not connected');
|
|
189
|
+
const lock = await this.client.getMailboxLock(sourceFolder);
|
|
190
|
+
try {
|
|
191
|
+
await this.client.messageMove(uid, targetFolder, { uid: true });
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
lock.release();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async modifyLabels(uid, folder, addLabels, removeLabels) {
|
|
198
|
+
if (!this.client)
|
|
199
|
+
throw new Error('Not connected');
|
|
200
|
+
const lock = await this.client.getMailboxLock(folder);
|
|
201
|
+
try {
|
|
202
|
+
if (addLabels.length > 0) {
|
|
203
|
+
await this.client.messageFlagsAdd(uid, addLabels, { uid: true });
|
|
204
|
+
}
|
|
205
|
+
if (removeLabels.length > 0) {
|
|
206
|
+
await this.client.messageFlagsRemove(uid, removeLabels, { uid: true });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
lock.release();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async batchMoveMessages(uids, sourceFolder, targetFolder) {
|
|
214
|
+
if (!this.client)
|
|
215
|
+
throw new Error('Not connected');
|
|
216
|
+
const sequence = uids.join(',');
|
|
217
|
+
const lock = await this.client.getMailboxLock(sourceFolder);
|
|
218
|
+
try {
|
|
219
|
+
await this.client.messageMove(sequence, targetFolder, { uid: true });
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
lock.release();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async batchDeleteMessages(uids, folder) {
|
|
226
|
+
if (!this.client)
|
|
227
|
+
throw new Error('Not connected');
|
|
228
|
+
const sequence = uids.join(',');
|
|
229
|
+
const lock = await this.client.getMailboxLock(folder);
|
|
230
|
+
try {
|
|
231
|
+
await this.client.messageDelete(sequence, { uid: true });
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
lock.release();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async batchModifyLabels(uids, folder, addLabels, removeLabels) {
|
|
238
|
+
if (!this.client)
|
|
239
|
+
throw new Error('Not connected');
|
|
240
|
+
const sequence = uids.join(',');
|
|
241
|
+
const lock = await this.client.getMailboxLock(folder);
|
|
242
|
+
try {
|
|
243
|
+
if (addLabels.length > 0) {
|
|
244
|
+
await this.client.messageFlagsAdd(sequence, addLabels, { uid: true });
|
|
245
|
+
}
|
|
246
|
+
if (removeLabels.length > 0) {
|
|
247
|
+
await this.client.messageFlagsRemove(sequence, removeLabels, { uid: true });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
lock.release();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=imap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imap.js","sourceRoot":"","sources":["../../src/protocol/imap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { EmailAccount } from '../types/index.js';
|
|
2
|
+
export declare class SmtpClient {
|
|
3
|
+
private transporter;
|
|
4
|
+
private account;
|
|
5
|
+
constructor(account: EmailAccount);
|
|
6
|
+
connect(): Promise<void>;
|
|
7
|
+
send(to: string, subject: string, body: string, isHtml?: boolean, cc?: string, bcc?: string): Promise<any>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
import { loadCredentials } from '../security/keychain.js';
|
|
3
|
+
import { getValidAccessToken } from '../security/oauth2.js';
|
|
4
|
+
export class SmtpClient {
|
|
5
|
+
transporter = null;
|
|
6
|
+
account;
|
|
7
|
+
constructor(account) {
|
|
8
|
+
this.account = account;
|
|
9
|
+
}
|
|
10
|
+
async connect() {
|
|
11
|
+
let authConfig = { user: this.account.user };
|
|
12
|
+
if (this.account.authType === 'oauth2') {
|
|
13
|
+
const accessToken = await getValidAccessToken(this.account.id);
|
|
14
|
+
authConfig.type = 'OAuth2';
|
|
15
|
+
authConfig.accessToken = accessToken;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const password = await loadCredentials(this.account.id);
|
|
19
|
+
if (!password) {
|
|
20
|
+
throw new Error(`Credentials not found for account: ${this.account.id}`);
|
|
21
|
+
}
|
|
22
|
+
authConfig.pass = password;
|
|
23
|
+
}
|
|
24
|
+
const smtpPort = this.account.smtpPort || 465;
|
|
25
|
+
this.transporter = nodemailer.createTransport({
|
|
26
|
+
host: this.account.smtpHost || this.account.host,
|
|
27
|
+
port: smtpPort,
|
|
28
|
+
secure: smtpPort === 465,
|
|
29
|
+
auth: authConfig
|
|
30
|
+
});
|
|
31
|
+
await this.transporter.verify();
|
|
32
|
+
}
|
|
33
|
+
async send(to, subject, body, isHtml = false, cc, bcc) {
|
|
34
|
+
if (!this.transporter) {
|
|
35
|
+
throw new Error('SMTP client not connected');
|
|
36
|
+
}
|
|
37
|
+
const mailOptions = {
|
|
38
|
+
from: this.account.user,
|
|
39
|
+
to,
|
|
40
|
+
subject,
|
|
41
|
+
};
|
|
42
|
+
if (cc)
|
|
43
|
+
mailOptions.cc = cc;
|
|
44
|
+
if (bcc)
|
|
45
|
+
mailOptions.bcc = bcc;
|
|
46
|
+
if (isHtml) {
|
|
47
|
+
mailOptions.html = body;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
mailOptions.text = body;
|
|
51
|
+
}
|
|
52
|
+
const info = await this.transporter.sendMail(mailOptions);
|
|
53
|
+
return info;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=smtp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smtp.js","sourceRoot":"","sources":["../../src/protocol/smtp.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,OAAO,UAAU;IACb,WAAW,GAAkC,IAAI,CAAC;IAClD,OAAO,CAAe;IAE9B,YAAY,OAAqB;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,UAAU,GAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAElD,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACvC,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC/D,UAAU,CAAC,IAAI,GAAG,QAAQ,CAAC;YAC3B,UAAU,CAAC,WAAW,GAAG,WAAW,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACxD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,sCAAsC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3E,CAAC;YACD,UAAU,CAAC,IAAI,GAAG,QAAQ,CAAC;QAC7B,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC;QAC9C,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,eAAe,CAAC;YAC5C,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI;YAChD,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,QAAQ,KAAK,GAAG;YACxB,IAAI,EAAE,UAAU;SACjB,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAU,EAAE,OAAe,EAAE,IAAY,EAAE,SAAkB,KAAK,EAAE,EAAW,EAAE,GAAY;QACtG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,WAAW,GAAQ;YACvB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YACvB,EAAE;YACF,OAAO;SACR,CAAC;QACF,IAAI,EAAE;YAAE,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC;QAC5B,IAAI,GAAG;YAAE,WAAW,CAAC,GAAG,GAAG,GAAG,CAAC;QAE/B,IAAI,MAAM,EAAE,CAAC;YACX,WAAW,CAAC,IAAI,GAAG,IAAI,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,IAAI,GAAG,IAAI,CAAC;QAC1B,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { setPassword, getPassword, deletePassword } from 'cross-keychain';
|
|
2
|
+
import { config } from '../config.js';
|
|
3
|
+
export async function saveCredentials(accountId, secret) {
|
|
4
|
+
await setPassword(config.serviceName, accountId, secret);
|
|
5
|
+
}
|
|
6
|
+
export async function loadCredentials(accountId) {
|
|
7
|
+
try {
|
|
8
|
+
return await getPassword(config.serviceName, accountId);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
console.error(`Failed to load credentials for ${accountId}:`, error);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function removeCredentials(accountId) {
|
|
16
|
+
await deletePassword(config.serviceName, accountId);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/security/keychain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,SAAiB,EAAE,MAAc;IACrE,MAAM,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,SAAiB;IACrD,IAAI,CAAC;QACH,OAAO,MAAM,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,kCAAkC,SAAS,GAAG,EAAE,KAAK,CAAC,CAAC;QACrE,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAiB;IACvD,MAAM,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { loadCredentials, saveCredentials } from './keychain.js';
|
|
2
|
+
export async function getValidAccessToken(accountId) {
|
|
3
|
+
const data = await loadCredentials(accountId);
|
|
4
|
+
if (!data) {
|
|
5
|
+
throw new Error(`No credentials found for account ${accountId}`);
|
|
6
|
+
}
|
|
7
|
+
let tokens;
|
|
8
|
+
try {
|
|
9
|
+
tokens = JSON.parse(data);
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
// Legacy plaintext password
|
|
13
|
+
return data;
|
|
14
|
+
}
|
|
15
|
+
// If we don't have OAuth2 fields, assume it's a plain password
|
|
16
|
+
if (!tokens.clientId || !tokens.refreshToken) {
|
|
17
|
+
return data;
|
|
18
|
+
}
|
|
19
|
+
// Check if access token is valid (with 1 minute buffer)
|
|
20
|
+
if (tokens.accessToken && tokens.expiryDate && Date.now() + 60000 < tokens.expiryDate) {
|
|
21
|
+
return tokens.accessToken;
|
|
22
|
+
}
|
|
23
|
+
// Refresh token
|
|
24
|
+
const response = await fetch(tokens.tokenEndpoint, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
28
|
+
},
|
|
29
|
+
body: new URLSearchParams({
|
|
30
|
+
client_id: tokens.clientId,
|
|
31
|
+
client_secret: tokens.clientSecret,
|
|
32
|
+
refresh_token: tokens.refreshToken,
|
|
33
|
+
grant_type: 'refresh_token',
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const errText = await response.text();
|
|
38
|
+
throw new Error(`Failed to refresh token: ${response.status} ${errText}`);
|
|
39
|
+
}
|
|
40
|
+
const result = await response.json();
|
|
41
|
+
tokens.accessToken = result.access_token;
|
|
42
|
+
if (result.expires_in) {
|
|
43
|
+
tokens.expiryDate = Date.now() + result.expires_in * 1000;
|
|
44
|
+
}
|
|
45
|
+
if (result.refresh_token) {
|
|
46
|
+
tokens.refreshToken = result.refresh_token;
|
|
47
|
+
}
|
|
48
|
+
await saveCredentials(accountId, JSON.stringify(tokens));
|
|
49
|
+
return tokens.accessToken;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=oauth2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth2.js","sourceRoot":"","sources":["../../src/security/oauth2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAWjE,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,SAAiB;IACzD,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,oCAAoC,SAAS,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,MAAoB,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,4BAA4B;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,+DAA+D;IAC/D,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wDAAwD;IACxD,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QACtF,OAAO,MAAM,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,gBAAgB;IAChB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE;QACjD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,mCAAmC;SACpD;QACD,IAAI,EAAE,IAAI,eAAe,CAAC;YACxB,SAAS,EAAE,MAAM,CAAC,QAAQ;YAC1B,aAAa,EAAE,MAAM,CAAC,YAAY;YAClC,aAAa,EAAE,MAAM,CAAC,YAAY;YAClC,UAAU,EAAE,eAAe;SAC5B,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAErC,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC;IACzC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;IAC5D,CAAC;IACD,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACzB,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,aAAa,CAAC;IAC7C,CAAC;IAED,MAAM,eAAe,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAEzD,OAAO,MAAM,CAAC,WAAY,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { MessageMetadata } from '../protocol/imap.js';
|
|
2
|
+
import { EmailAccount } from '../types/index.js';
|
|
3
|
+
export declare class MailService {
|
|
4
|
+
private readonly readOnly;
|
|
5
|
+
private imapClient;
|
|
6
|
+
private smtpClient;
|
|
7
|
+
private account;
|
|
8
|
+
private smtpConnected;
|
|
9
|
+
constructor(account: EmailAccount, readOnly?: boolean);
|
|
10
|
+
connect(): Promise<void>;
|
|
11
|
+
private ensureSmtp;
|
|
12
|
+
disconnect(): Promise<void>;
|
|
13
|
+
listEmails(folder?: string, count?: number): Promise<MessageMetadata[]>;
|
|
14
|
+
searchEmails(query: {
|
|
15
|
+
from?: string;
|
|
16
|
+
subject?: string;
|
|
17
|
+
since?: string;
|
|
18
|
+
before?: string;
|
|
19
|
+
keywords?: string;
|
|
20
|
+
}, folder?: string, count?: number): Promise<MessageMetadata[]>;
|
|
21
|
+
sendEmail(to: string, subject: string, body: string, isHtml?: boolean, cc?: string, bcc?: string): Promise<any>;
|
|
22
|
+
createDraft(to: string, subject: string, body: string, isHtml?: boolean, cc?: string, bcc?: string): Promise<void>;
|
|
23
|
+
readEmail(uid: string, folder?: string): Promise<string>;
|
|
24
|
+
getThread(threadId: string, folder?: string): Promise<MessageMetadata[]>;
|
|
25
|
+
downloadAttachment(uid: string, filename: string, folder?: string): Promise<{
|
|
26
|
+
content: Buffer;
|
|
27
|
+
contentType: string;
|
|
28
|
+
}>;
|
|
29
|
+
extractAttachmentText(uid: string, filename: string, folder?: string): Promise<string>;
|
|
30
|
+
listFolders(): Promise<string[]>;
|
|
31
|
+
moveMessage(uid: string, sourceFolder: string, targetFolder: string): Promise<void>;
|
|
32
|
+
modifyLabels(uid: string, folder: string, addLabels: string[], removeLabels: string[]): Promise<void>;
|
|
33
|
+
batchOperations(uids: string[], folder: string, operation: {
|
|
34
|
+
type: 'move';
|
|
35
|
+
targetFolder: string;
|
|
36
|
+
} | {
|
|
37
|
+
type: 'delete';
|
|
38
|
+
} | {
|
|
39
|
+
type: 'label';
|
|
40
|
+
addLabels?: string[];
|
|
41
|
+
removeLabels?: string[];
|
|
42
|
+
}): Promise<{
|
|
43
|
+
processed: number;
|
|
44
|
+
}>;
|
|
45
|
+
}
|