apple-mail-mcp 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -6
- package/build/index.js +25 -4
- package/build/services/appleMailManager.d.ts +24 -3
- package/build/services/appleMailManager.d.ts.map +1 -1
- package/build/services/appleMailManager.js +55 -18
- package/build/services/imapClient.d.ts +33 -0
- package/build/services/imapClient.d.ts.map +1 -1
- package/build/services/imapClient.js +83 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -279,9 +279,15 @@ an account is configured for IMAP, `search-messages` and `list-messages` instead
|
|
|
279
279
|
run a **server-side IMAP search** ([#43](https://github.com/sweetrb/apple-mail-mcp/issues/43)) —
|
|
280
280
|
typically sub-second and correct on the same mailbox where AppleScript times out.
|
|
281
281
|
This is **opt-in and additive**: any account without IMAP configured behaves
|
|
282
|
-
exactly as before (AppleScript).
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
exactly as before (AppleScript). When an account is IMAP-configured,
|
|
283
|
+
`search-messages`/`list-messages` (read) and `create-mailbox`/`rename-mailbox`/
|
|
284
|
+
`delete-mailbox` (folder ops) route to IMAP. The folder ops are the key win for
|
|
285
|
+
server accounts: IMAP's `CREATE`/`RENAME`/`DELETE` succeed on exactly the
|
|
286
|
+
iCloud/Gmail/Workspace/Exchange mailboxes where Mail.app's AppleScript bridge
|
|
287
|
+
can't (#42). `get-message` and message-level mutations (mark/flag/move/delete-
|
|
288
|
+
message) stay on AppleScript for now — they key off a message id, and the IMAP
|
|
289
|
+
read rows report **UIDs** (a different, per-mailbox namespace), so routing them
|
|
290
|
+
safely needs a UID-aware design (tracked on #43).
|
|
285
291
|
|
|
286
292
|
Routing is conservative: only a call whose explicit `account` matches the
|
|
287
293
|
configured IMAP account goes to IMAP; everything else falls through to
|
|
@@ -303,9 +309,15 @@ config. Gmail label semantics: common names (`All Mail`, `Sent`, `Trash`,
|
|
|
303
309
|
`Spam`, `Important`, …) map to their `[Gmail]/…` IMAP paths automatically.
|
|
304
310
|
|
|
305
311
|
> Note: each call currently opens its own IMAP connection (no pooling yet), so
|
|
306
|
-
> expect a few seconds of connection overhead per call. Phase 2
|
|
307
|
-
>
|
|
308
|
-
> [#42](https://github.com/sweetrb/apple-mail-mcp/issues/42)
|
|
312
|
+
> expect a few seconds of connection overhead per call. Phase 2 added the folder
|
|
313
|
+
> ops (create/rename/delete-mailbox) — resolving the IMAP slice of
|
|
314
|
+
> [#42](https://github.com/sweetrb/apple-mail-mcp/issues/42). IMAP-backed
|
|
315
|
+
> message-level mutations are still future work (see #43).
|
|
316
|
+
>
|
|
317
|
+
> **iCloud:** set `APPLE_MAIL_MCP_IMAP_HOST=imap.mail.me.com`, `APPLE_MAIL_MCP_IMAP_USER`
|
|
318
|
+
> to your iCloud address, `APPLE_MAIL_MCP_IMAP_ACCOUNT` to the Mail account name
|
|
319
|
+
> (e.g. `iCloud`), and use an **app-specific password** (from appleid.apple.com)
|
|
320
|
+
> stored in the Keychain.
|
|
309
321
|
|
|
310
322
|
---
|
|
311
323
|
|
package/build/index.js
CHANGED
|
@@ -25,7 +25,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
25
25
|
import { z } from "zod";
|
|
26
26
|
import { AppleMailManager } from "./services/appleMailManager.js";
|
|
27
27
|
import { sendViaSmtp } from "./services/smtpMailer.js";
|
|
28
|
-
import { isImapAccount, imapSearchMessages, imapListMessages } from "./services/imapClient.js";
|
|
28
|
+
import { isImapAccount, imapSearchMessages, imapListMessages, imapCreateMailbox, imapDeleteMailbox, imapRenameMailbox, } from "./services/imapClient.js";
|
|
29
29
|
import { createSerialGate } from "./utils/serialize.js";
|
|
30
30
|
// =============================================================================
|
|
31
31
|
// Shared Validation Schemas
|
|
@@ -582,7 +582,15 @@ server.tool("get-unread-count", {
|
|
|
582
582
|
server.tool("create-mailbox", {
|
|
583
583
|
name: z.string().min(1, "Mailbox name is required"),
|
|
584
584
|
account: z.string().optional().describe("Account to create the mailbox in"),
|
|
585
|
-
}, withErrorHandling(({ name, account }) => {
|
|
585
|
+
}, withErrorHandling(async ({ name, account }) => {
|
|
586
|
+
// IMAP backend (issue #43, Phase 2): server-side folder op when this account
|
|
587
|
+
// is IMAP-configured; otherwise AppleScript.
|
|
588
|
+
if (isImapAccount(account)) {
|
|
589
|
+
const r = await imapCreateMailbox(name);
|
|
590
|
+
if (!r.success)
|
|
591
|
+
return errorResponse(r.error || `Failed to create mailbox "${name}"`);
|
|
592
|
+
return successResponse(r.info || `Mailbox "${name}" created`);
|
|
593
|
+
}
|
|
586
594
|
const success = mailManager.createMailbox(name, account);
|
|
587
595
|
if (!success) {
|
|
588
596
|
return errorResponse(`Failed to create mailbox "${name}"`);
|
|
@@ -593,7 +601,13 @@ server.tool("create-mailbox", {
|
|
|
593
601
|
server.tool("delete-mailbox", {
|
|
594
602
|
name: z.string().min(1, "Mailbox name is required"),
|
|
595
603
|
account: z.string().optional().describe("Account containing the mailbox"),
|
|
596
|
-
}, withErrorHandling(({ name, account }) => {
|
|
604
|
+
}, withErrorHandling(async ({ name, account }) => {
|
|
605
|
+
if (isImapAccount(account)) {
|
|
606
|
+
const r = await imapDeleteMailbox(name);
|
|
607
|
+
if (!r.success)
|
|
608
|
+
return errorResponse(r.error || `Failed to delete mailbox "${name}"`);
|
|
609
|
+
return successResponse(r.info || `Mailbox "${name}" deleted`);
|
|
610
|
+
}
|
|
597
611
|
const { success, error } = mailManager.deleteMailbox(name, account);
|
|
598
612
|
if (!success) {
|
|
599
613
|
return errorResponse(error || `Failed to delete mailbox "${name}"`);
|
|
@@ -605,7 +619,14 @@ server.tool("rename-mailbox", {
|
|
|
605
619
|
oldName: z.string().min(1, "Current mailbox name is required"),
|
|
606
620
|
newName: z.string().min(1, "New mailbox name is required"),
|
|
607
621
|
account: z.string().optional().describe("Account containing the mailbox"),
|
|
608
|
-
}, withErrorHandling(({ oldName, newName, account }) => {
|
|
622
|
+
}, withErrorHandling(async ({ oldName, newName, account }) => {
|
|
623
|
+
if (isImapAccount(account)) {
|
|
624
|
+
const r = await imapRenameMailbox(oldName, newName);
|
|
625
|
+
if (!r.success) {
|
|
626
|
+
return errorResponse(r.error || `Failed to rename mailbox "${oldName}" to "${newName}"`);
|
|
627
|
+
}
|
|
628
|
+
return successResponse(r.info || `Mailbox renamed from "${oldName}" to "${newName}"`);
|
|
629
|
+
}
|
|
609
630
|
const { success, error } = mailManager.renameMailbox(oldName, newName, account);
|
|
610
631
|
if (!success) {
|
|
611
632
|
return errorResponse(error || `Failed to rename mailbox "${oldName}" to "${newName}"`);
|
|
@@ -54,6 +54,25 @@ export declare function isPathWithinAllowedRoots(resolvedPath: string): boolean;
|
|
|
54
54
|
* Exported for unit testing.
|
|
55
55
|
*/
|
|
56
56
|
export declare function describeMailboxOpError(op: "delete" | "rename", raw: string): string;
|
|
57
|
+
/** Env var to pin the default account (matched by account name or email). */
|
|
58
|
+
export declare const DEFAULT_ACCOUNT_ENV = "APPLE_MAIL_MCP_DEFAULT_ACCOUNT";
|
|
59
|
+
/**
|
|
60
|
+
* Choose the account to use when a tool call omits `account`.
|
|
61
|
+
*
|
|
62
|
+
* Priority: explicit `override` (by name or email) → Mail's default-send
|
|
63
|
+
* account *if enabled* → first enabled account → first account → null. The key
|
|
64
|
+
* guarantee (issue #47): a **disabled** account is never chosen implicitly — it
|
|
65
|
+
* can only be selected via an explicit override (deliberate user intent) or as
|
|
66
|
+
* a last resort when no account is enabled. This prevents operations silently
|
|
67
|
+
* landing in a configured-but-disabled account (e.g. an unused iCloud account
|
|
68
|
+
* that's still addressable via AppleScript).
|
|
69
|
+
*
|
|
70
|
+
* Pure/exported for unit testing.
|
|
71
|
+
*/
|
|
72
|
+
export declare function chooseDefaultAccount(accounts: Account[], opts?: {
|
|
73
|
+
override?: string;
|
|
74
|
+
defaultSendEmail?: string;
|
|
75
|
+
}): string | null;
|
|
57
76
|
export declare function escapeForAppleScript(text: string): string;
|
|
58
77
|
/**
|
|
59
78
|
* Emits AppleScript that builds a date into the variable `varName` from numeric
|
|
@@ -135,9 +154,11 @@ export declare class AppleMailManager {
|
|
|
135
154
|
*/
|
|
136
155
|
private invalidateCache;
|
|
137
156
|
/**
|
|
138
|
-
* Resolves the account to use for an operation.
|
|
139
|
-
*
|
|
140
|
-
*
|
|
157
|
+
* Resolves the account to use for an operation when the caller omits one.
|
|
158
|
+
*
|
|
159
|
+
* Order (see chooseDefaultAccount): the APPLE_MAIL_MCP_DEFAULT_ACCOUNT env
|
|
160
|
+
* override → Mail.app's configured default-send account (if enabled) → the
|
|
161
|
+
* first enabled account. A disabled account is never chosen implicitly (#47).
|
|
141
162
|
*/
|
|
142
163
|
private resolveAccount;
|
|
143
164
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appleMailManager.d.ts","sourceRoot":"","sources":["../../src/services/appleMailManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,OAAO,EACP,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,SAAS,EAET,oBAAoB,EACpB,UAAU,EACV,qBAAqB,EACrB,QAAQ,EACR,OAAO,EACP,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACb,MAAM,YAAY,CAAC;AA2DpB;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAK7F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,iBAAiB,CAAA;CAAE,CAsCrD;AAqBD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAKtE;AAWD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAOnF;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAczD;AAiED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,GAAG,MAAM,CAWrE;AA2CD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAoB5E;AAED,qBAAa,gBAAgB;IAC3B;;OAEG;IACH,OAAO,CAAC,cAAc,CAAuB;IAE7C;;;;OAIG;IACH,OAAO,CAAC,KAAK,CAGX;IAEF,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;IAEvC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAUzB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAW7B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAKvB
|
|
1
|
+
{"version":3,"file":"appleMailManager.d.ts","sourceRoot":"","sources":["../../src/services/appleMailManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -181,6 +181,41 @@ export function describeMailboxOpError(op, raw) {
|
|
|
181
181
|
}
|
|
182
182
|
return trimmed || `Failed to ${op} mailbox`;
|
|
183
183
|
}
|
|
184
|
+
/** Env var to pin the default account (matched by account name or email). */
|
|
185
|
+
export const DEFAULT_ACCOUNT_ENV = "APPLE_MAIL_MCP_DEFAULT_ACCOUNT";
|
|
186
|
+
/**
|
|
187
|
+
* Choose the account to use when a tool call omits `account`.
|
|
188
|
+
*
|
|
189
|
+
* Priority: explicit `override` (by name or email) → Mail's default-send
|
|
190
|
+
* account *if enabled* → first enabled account → first account → null. The key
|
|
191
|
+
* guarantee (issue #47): a **disabled** account is never chosen implicitly — it
|
|
192
|
+
* can only be selected via an explicit override (deliberate user intent) or as
|
|
193
|
+
* a last resort when no account is enabled. This prevents operations silently
|
|
194
|
+
* landing in a configured-but-disabled account (e.g. an unused iCloud account
|
|
195
|
+
* that's still addressable via AppleScript).
|
|
196
|
+
*
|
|
197
|
+
* Pure/exported for unit testing.
|
|
198
|
+
*/
|
|
199
|
+
export function chooseDefaultAccount(accounts, opts = {}) {
|
|
200
|
+
const norm = (s) => s.trim().toLowerCase();
|
|
201
|
+
const override = opts.override?.trim();
|
|
202
|
+
if (override) {
|
|
203
|
+
const o = norm(override);
|
|
204
|
+
const m = accounts.find((a) => norm(a.name) === o || norm(a.email) === o);
|
|
205
|
+
if (m)
|
|
206
|
+
return m.name; // honor an explicit pin even if that account is disabled
|
|
207
|
+
}
|
|
208
|
+
if (opts.defaultSendEmail) {
|
|
209
|
+
const e = norm(opts.defaultSendEmail);
|
|
210
|
+
const m = accounts.find((a) => norm(a.email) === e);
|
|
211
|
+
if (m && m.enabled)
|
|
212
|
+
return m.name;
|
|
213
|
+
}
|
|
214
|
+
const firstEnabled = accounts.find((a) => a.enabled);
|
|
215
|
+
if (firstEnabled)
|
|
216
|
+
return firstEnabled.name;
|
|
217
|
+
return accounts[0]?.name ?? null;
|
|
218
|
+
}
|
|
184
219
|
export function escapeForAppleScript(text) {
|
|
185
220
|
if (!text)
|
|
186
221
|
return "";
|
|
@@ -396,16 +431,20 @@ export class AppleMailManager {
|
|
|
396
431
|
this.cache.mailboxNames.clear();
|
|
397
432
|
}
|
|
398
433
|
/**
|
|
399
|
-
* Resolves the account to use for an operation.
|
|
400
|
-
*
|
|
401
|
-
*
|
|
434
|
+
* Resolves the account to use for an operation when the caller omits one.
|
|
435
|
+
*
|
|
436
|
+
* Order (see chooseDefaultAccount): the APPLE_MAIL_MCP_DEFAULT_ACCOUNT env
|
|
437
|
+
* override → Mail.app's configured default-send account (if enabled) → the
|
|
438
|
+
* first enabled account. A disabled account is never chosen implicitly (#47).
|
|
402
439
|
*/
|
|
403
440
|
resolveAccount(account) {
|
|
404
441
|
if (account)
|
|
405
442
|
return account;
|
|
406
443
|
if (this.defaultAccount)
|
|
407
444
|
return this.defaultAccount;
|
|
408
|
-
|
|
445
|
+
const accounts = this.getCachedAccounts();
|
|
446
|
+
// Mail.app's default send account (inspect a throwaway outgoing message).
|
|
447
|
+
let defaultSendEmail;
|
|
409
448
|
const defaultResult = executeAppleScript(buildAppLevelScript(`
|
|
410
449
|
set newMsg to make new outgoing message
|
|
411
450
|
set fromAddr to sender of newMsg
|
|
@@ -413,24 +452,22 @@ export class AppleMailManager {
|
|
|
413
452
|
return fromAddr
|
|
414
453
|
`));
|
|
415
454
|
if (defaultResult.success && defaultResult.output.trim()) {
|
|
416
|
-
// sender returns "Name <email>" —
|
|
455
|
+
// sender returns "Name <email>" — pull out the address
|
|
417
456
|
const senderOutput = defaultResult.output.trim();
|
|
418
457
|
const emailMatch = senderOutput.match(/<([^>]+)>/);
|
|
419
|
-
|
|
420
|
-
const accounts = this.getCachedAccounts();
|
|
421
|
-
const matchedAccount = accounts.find((a) => a.email.toLowerCase() === defaultEmail.toLowerCase());
|
|
422
|
-
if (matchedAccount) {
|
|
423
|
-
this.defaultAccount = matchedAccount.name;
|
|
424
|
-
return this.defaultAccount;
|
|
425
|
-
}
|
|
458
|
+
defaultSendEmail = emailMatch ? emailMatch[1] : senderOutput;
|
|
426
459
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
460
|
+
const chosen = chooseDefaultAccount(accounts, {
|
|
461
|
+
override: process.env[DEFAULT_ACCOUNT_ENV],
|
|
462
|
+
defaultSendEmail,
|
|
463
|
+
});
|
|
464
|
+
if (chosen) {
|
|
465
|
+
this.defaultAccount = chosen;
|
|
466
|
+
return chosen;
|
|
432
467
|
}
|
|
433
|
-
return
|
|
468
|
+
// No accounts at all — return something rather than throw; downstream
|
|
469
|
+
// AppleScript will surface a clear "account not found".
|
|
470
|
+
return accounts[0]?.name ?? "iCloud";
|
|
434
471
|
}
|
|
435
472
|
/**
|
|
436
473
|
* Resolves a mailbox name to its actual name in the account.
|
|
@@ -46,6 +46,10 @@ interface ImapMessage {
|
|
|
46
46
|
interface MailboxLock {
|
|
47
47
|
release: () => void;
|
|
48
48
|
}
|
|
49
|
+
interface ImapMailboxListing {
|
|
50
|
+
path: string;
|
|
51
|
+
name: string;
|
|
52
|
+
}
|
|
49
53
|
export interface ImapClientLike {
|
|
50
54
|
connect(): Promise<void>;
|
|
51
55
|
getMailboxLock(path: string): Promise<MailboxLock>;
|
|
@@ -55,6 +59,18 @@ export interface ImapClientLike {
|
|
|
55
59
|
fetch(range: string, query: Record<string, unknown>, opts: {
|
|
56
60
|
uid: true;
|
|
57
61
|
}): AsyncIterable<ImapMessage>;
|
|
62
|
+
list(): Promise<ImapMailboxListing[]>;
|
|
63
|
+
mailboxCreate(path: string): Promise<{
|
|
64
|
+
path: string;
|
|
65
|
+
created: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
mailboxRename(path: string, newPath: string): Promise<{
|
|
68
|
+
path: string;
|
|
69
|
+
newPath: string;
|
|
70
|
+
}>;
|
|
71
|
+
mailboxDelete(path: string): Promise<{
|
|
72
|
+
path: string;
|
|
73
|
+
}>;
|
|
58
74
|
logout(): Promise<void>;
|
|
59
75
|
}
|
|
60
76
|
export type ImapConnect = (cfg: ImapConfig) => Promise<ImapClientLike>;
|
|
@@ -71,5 +87,22 @@ export declare function imapListMessages(args: ImapSearchArgs, deps?: {
|
|
|
71
87
|
connect?: ImapConnect;
|
|
72
88
|
config?: ImapConfig;
|
|
73
89
|
}): Promise<string>;
|
|
90
|
+
export interface ImapOpResult {
|
|
91
|
+
success: boolean;
|
|
92
|
+
error?: string;
|
|
93
|
+
info?: string;
|
|
94
|
+
}
|
|
95
|
+
export declare function imapCreateMailbox(name: string, deps?: {
|
|
96
|
+
connect?: ImapConnect;
|
|
97
|
+
config?: ImapConfig;
|
|
98
|
+
}): Promise<ImapOpResult>;
|
|
99
|
+
export declare function imapDeleteMailbox(name: string, deps?: {
|
|
100
|
+
connect?: ImapConnect;
|
|
101
|
+
config?: ImapConfig;
|
|
102
|
+
}): Promise<ImapOpResult>;
|
|
103
|
+
export declare function imapRenameMailbox(oldName: string, newName: string, deps?: {
|
|
104
|
+
connect?: ImapConnect;
|
|
105
|
+
config?: ImapConfig;
|
|
106
|
+
}): Promise<ImapOpResult>;
|
|
74
107
|
export {};
|
|
75
108
|
//# sourceMappingURL=imapClient.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imapClient.d.ts","sourceRoot":"","sources":["../../src/services/imapClient.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"imapClient.d.ts","sourceRoot":"","sources":["../../src/services/imapClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -175,3 +175,86 @@ export function imapSearchMessages(args, deps = {}) {
|
|
|
175
175
|
export function imapListMessages(args, deps = {}) {
|
|
176
176
|
return run(args, true, deps);
|
|
177
177
|
}
|
|
178
|
+
function errText(e) {
|
|
179
|
+
return e instanceof Error ? e.message : String(e);
|
|
180
|
+
}
|
|
181
|
+
/** Connect, run `fn`, always log out. */
|
|
182
|
+
async function withClient(deps, fn) {
|
|
183
|
+
const cfg = deps.config ?? resolveImapConfig();
|
|
184
|
+
const client = await (deps.connect ?? defaultConnect)(cfg);
|
|
185
|
+
try {
|
|
186
|
+
return await fn(client, cfg);
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
await client.logout().catch(() => undefined);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Resolve a user-supplied mailbox name to an actual server path by listing the
|
|
194
|
+
* mailboxes and matching on full path, then leaf name (case-insensitive).
|
|
195
|
+
* Returns null when no such mailbox exists.
|
|
196
|
+
*/
|
|
197
|
+
async function findMailboxPath(client, name) {
|
|
198
|
+
const wanted = name.trim().toLowerCase();
|
|
199
|
+
const boxes = await client.list();
|
|
200
|
+
const byPath = boxes.find((b) => b.path.toLowerCase() === wanted);
|
|
201
|
+
if (byPath)
|
|
202
|
+
return byPath.path;
|
|
203
|
+
const byName = boxes.find((b) => b.name.toLowerCase() === wanted);
|
|
204
|
+
return byName ? byName.path : null;
|
|
205
|
+
}
|
|
206
|
+
export function imapCreateMailbox(name, deps = {}) {
|
|
207
|
+
return withClient(deps, async (client) => {
|
|
208
|
+
try {
|
|
209
|
+
const res = await client.mailboxCreate(name);
|
|
210
|
+
return res.created
|
|
211
|
+
? { success: true, info: `Created mailbox "${res.path}".` }
|
|
212
|
+
: { success: true, info: `Mailbox "${res.path}" already existed.` };
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
return { success: false, error: `IMAP create failed for "${name}": ${errText(e)}` };
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
export function imapDeleteMailbox(name, deps = {}) {
|
|
220
|
+
return withClient(deps, async (client, cfg) => {
|
|
221
|
+
const path = await findMailboxPath(client, name);
|
|
222
|
+
if (!path) {
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
error: `Mailbox "${name}" not found on IMAP account ${cfg.accountLabel}.`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
await client.mailboxDelete(path);
|
|
230
|
+
return {
|
|
231
|
+
success: true,
|
|
232
|
+
info: `Deleted mailbox "${path}" via IMAP (account ${cfg.accountLabel}).`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
return { success: false, error: `IMAP delete failed for "${path}": ${errText(e)}` };
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
export function imapRenameMailbox(oldName, newName, deps = {}) {
|
|
241
|
+
return withClient(deps, async (client, cfg) => {
|
|
242
|
+
const path = await findMailboxPath(client, oldName);
|
|
243
|
+
if (!path) {
|
|
244
|
+
return {
|
|
245
|
+
success: false,
|
|
246
|
+
error: `Mailbox "${oldName}" not found on IMAP account ${cfg.accountLabel}.`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const res = await client.mailboxRename(path, newName);
|
|
251
|
+
return { success: true, info: `Renamed "${res.path}" to "${res.newPath}" via IMAP.` };
|
|
252
|
+
}
|
|
253
|
+
catch (e) {
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
error: `IMAP rename failed for "${path}" -> "${newName}": ${errText(e)}`,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|