@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.
@@ -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,3 @@
1
+ export declare function saveCredentials(accountId: string, secret: string): Promise<void>;
2
+ export declare function loadCredentials(accountId: string): Promise<string | null>;
3
+ export declare function removeCredentials(accountId: string): Promise<void>;
@@ -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,9 @@
1
+ export interface OAuth2Tokens {
2
+ clientId: string;
3
+ clientSecret: string;
4
+ refreshToken: string;
5
+ accessToken?: string;
6
+ expiryDate?: number;
7
+ tokenEndpoint: string;
8
+ }
9
+ export declare function getValidAccessToken(accountId: string): Promise<string>;
@@ -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
+ }