apple-mail-mcp 2.8.2 → 2.8.3

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.
Files changed (65) hide show
  1. package/README.md +5 -5
  2. package/build/cli.js +12083 -138
  3. package/build/index.js +82684 -1687
  4. package/package.json +3 -4
  5. package/build/cli.d.ts +0 -24
  6. package/build/cli.d.ts.map +0 -1
  7. package/build/index.d.ts +0 -23
  8. package/build/index.d.ts.map +0 -1
  9. package/build/services/appleMailManager.d.ts +0 -680
  10. package/build/services/appleMailManager.d.ts.map +0 -1
  11. package/build/services/appleMailManager.js +0 -3143
  12. package/build/services/fileConfig.d.ts +0 -7
  13. package/build/services/fileConfig.d.ts.map +0 -1
  14. package/build/services/fileConfig.js +0 -52
  15. package/build/services/imapClient.d.ts +0 -312
  16. package/build/services/imapClient.d.ts.map +0 -1
  17. package/build/services/imapClient.js +0 -1023
  18. package/build/services/imapIdle.d.ts +0 -58
  19. package/build/services/imapIdle.d.ts.map +0 -1
  20. package/build/services/imapIdle.js +0 -151
  21. package/build/services/imapMultiAccount.d.ts +0 -124
  22. package/build/services/imapMultiAccount.d.ts.map +0 -1
  23. package/build/services/imapMultiAccount.js +0 -253
  24. package/build/services/messageRouter.d.ts +0 -24
  25. package/build/services/messageRouter.d.ts.map +0 -1
  26. package/build/services/messageRouter.js +0 -31
  27. package/build/services/replyForward.d.ts +0 -87
  28. package/build/services/replyForward.d.ts.map +0 -1
  29. package/build/services/replyForward.js +0 -150
  30. package/build/services/smtpMailer.d.ts +0 -160
  31. package/build/services/smtpMailer.d.ts.map +0 -1
  32. package/build/services/smtpMailer.js +0 -268
  33. package/build/services/templateStore.d.ts +0 -18
  34. package/build/services/templateStore.d.ts.map +0 -1
  35. package/build/services/templateStore.js +0 -91
  36. package/build/tools/doctor.d.ts +0 -23
  37. package/build/tools/doctor.d.ts.map +0 -1
  38. package/build/tools/doctor.js +0 -74
  39. package/build/tools/resourcesAndPrompts.d.ts +0 -14
  40. package/build/tools/resourcesAndPrompts.d.ts.map +0 -1
  41. package/build/tools/resourcesAndPrompts.js +0 -109
  42. package/build/tools/respond.d.ts +0 -48
  43. package/build/tools/respond.d.ts.map +0 -1
  44. package/build/tools/respond.js +0 -95
  45. package/build/tools/thread.d.ts +0 -19
  46. package/build/tools/thread.d.ts.map +0 -1
  47. package/build/tools/thread.js +0 -32
  48. package/build/types.d.ts +0 -434
  49. package/build/types.d.ts.map +0 -1
  50. package/build/types.js +0 -13
  51. package/build/utils/applescript.d.ts +0 -45
  52. package/build/utils/applescript.d.ts.map +0 -1
  53. package/build/utils/applescript.js +0 -446
  54. package/build/utils/attachmentMaterialize.d.ts +0 -9
  55. package/build/utils/attachmentMaterialize.d.ts.map +0 -1
  56. package/build/utils/attachmentMaterialize.js +0 -38
  57. package/build/utils/mimeParse.d.ts +0 -62
  58. package/build/utils/mimeParse.d.ts.map +0 -1
  59. package/build/utils/mimeParse.js +0 -317
  60. package/build/utils/orphan.d.ts +0 -25
  61. package/build/utils/orphan.d.ts.map +0 -1
  62. package/build/utils/orphan.js +0 -26
  63. package/build/utils/serialize.d.ts +0 -30
  64. package/build/utils/serialize.d.ts.map +0 -1
  65. package/build/utils/serialize.js +0 -41
@@ -1,446 +0,0 @@
1
- /**
2
- * AppleScript Execution Utilities
3
- *
4
- * This module provides a safe interface for executing AppleScript commands
5
- * on macOS. It handles script execution, error capture, and result parsing.
6
- *
7
- * @module utils/applescript
8
- */
9
- import { execSync, spawnSync } from "child_process";
10
- /**
11
- * Default execution timeout for AppleScript commands in milliseconds.
12
- * 30 seconds is sufficient for most operations, including complex
13
- * searches on large mailboxes. Can be overridden per-call.
14
- */
15
- const DEFAULT_TIMEOUT_MS = 30000;
16
- /**
17
- * Maximum stdout/stderr buffer (bytes) for an osascript invocation.
18
- *
19
- * Node's `execSync` defaults to a 1 MB `maxBuffer`; exceeding it throws
20
- * `ENOBUFS`, which we surface as a failure and callers convert to `null`. Mail
21
- * operations routinely exceed 1 MB — `getRawSource` reads the entire raw MIME
22
- * (a 20 MB attachment is explicitly anticipated), `getMessageContent` returns
23
- * the full source, and large `search`/`list` result sets accumulate — so the
24
- * default silently breaks exactly the large / attachment-bearing messages where
25
- * it matters most (issue #27). Default to 64 MB; override with
26
- * `APPLE_MAIL_MCP_MAX_BUFFER` (bytes).
27
- */
28
- const DEFAULT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
29
- /**
30
- * Resolve the osascript output buffer size, honoring the
31
- * `APPLE_MAIL_MCP_MAX_BUFFER` environment override (bytes).
32
- */
33
- function getMaxBuffer() {
34
- const raw = process.env.APPLE_MAIL_MCP_MAX_BUFFER;
35
- if (raw !== undefined) {
36
- const n = Number(raw);
37
- if (Number.isFinite(n) && n > 0)
38
- return n;
39
- }
40
- return DEFAULT_MAX_BUFFER_BYTES;
41
- }
42
- /**
43
- * Default retry configuration.
44
- * - 1 attempt means no retries (default behavior)
45
- * - Use maxRetries: 3 for exponential backoff with 1s/2s delays
46
- */
47
- const DEFAULT_MAX_RETRIES = 1;
48
- const DEFAULT_RETRY_DELAY_MS = 1000;
49
- /**
50
- * Check if debug/verbose logging is enabled.
51
- * Set DEBUG=1 or DEBUG=true or VERBOSE=1 to enable.
52
- */
53
- const isDebugEnabled = () => {
54
- const debug = process.env.DEBUG;
55
- const verbose = process.env.VERBOSE;
56
- return debug === "1" || debug === "true" || verbose === "1" || verbose === "true";
57
- };
58
- /**
59
- * Log a debug message if debug mode is enabled.
60
- *
61
- * @param message - The message to log
62
- * @param data - Optional additional data to log
63
- */
64
- function debugLog(message, data) {
65
- if (!isDebugEnabled())
66
- return;
67
- const timestamp = new Date().toISOString();
68
- if (data !== undefined) {
69
- console.error(`[DEBUG ${timestamp}] ${message}`, data);
70
- }
71
- else {
72
- console.error(`[DEBUG ${timestamp}] ${message}`);
73
- }
74
- }
75
- /**
76
- * Escapes a string for safe inclusion in a shell command.
77
- *
78
- * When passing AppleScript to osascript via shell, we need to handle
79
- * the interaction between shell quoting and AppleScript string literals.
80
- * This function escapes single quotes since we wrap the script in single quotes.
81
- *
82
- * @param script - The raw AppleScript code
83
- * @returns Shell-safe version of the script
84
- *
85
- * @example
86
- * // Input: tell app "Notes" to get note "Rob's Note"
87
- * // Output: tell app "Notes" to get note "Rob'\''s Note"
88
- */
89
- function escapeForShell(script) {
90
- // Replace single quotes with: end quote, escaped quote, start quote
91
- // This is the standard shell escaping pattern for single-quoted strings
92
- return script.replace(/'/g, "'\\''");
93
- }
94
- /**
95
- * Headroom (ms) between the in-AppleScript `with timeout` and the outer
96
- * osascript process timeout. The script-level timeout must fire *first* so
97
- * Mail.app aborts the operation from inside its own AppleScript dispatch —
98
- * cleanly releasing the event queue — before Node SIGKILLs the osascript
99
- * process. Killing osascript alone does not stop work already dispatched into
100
- * Mail.app, which is what wedges Mail.app for subsequent calls (issue #11).
101
- */
102
- const SCRIPT_TIMEOUT_HEADROOM_MS = 5000;
103
- /**
104
- * Wraps a script body in an AppleScript `with timeout` block so any Apple
105
- * Event that honors timeouts (most Mail.app operations do) aborts cleanly
106
- * rather than holding Mail.app's single-threaded AppleScript dispatch queue
107
- * open indefinitely.
108
- *
109
- * The timeout is set a few seconds below the osascript process timeout so the
110
- * in-Mail abort wins the race against the outer process kill.
111
- *
112
- * @param script - The AppleScript body
113
- * @param processTimeoutMs - The outer osascript process timeout
114
- * @returns The script wrapped in `with timeout … end timeout`
115
- */
116
- function wrapWithTimeout(script, processTimeoutMs) {
117
- const seconds = Math.max(1, Math.ceil((processTimeoutMs - SCRIPT_TIMEOUT_HEADROOM_MS) / 1000));
118
- return `with timeout of ${seconds} seconds\n${script}\nend timeout`;
119
- }
120
- /**
121
- * Checks if an error is a timeout error from execSync.
122
- *
123
- * Node.js throws errors with specific properties when a child process
124
- * is killed due to timeout.
125
- *
126
- * @param error - The caught error object
127
- * @returns True if this was a timeout error
128
- */
129
- function isTimeoutError(error) {
130
- if (error instanceof Error) {
131
- const execError = error;
132
- // executeAppleScript kills a timed-out osascript with SIGKILL; execSync also
133
- // sets killed=true on its own timeout. Treat any killed osascript
134
- // (SIGTERM/SIGKILL) as a timeout/abnormal exit rather than leaking the raw
135
- // "Command failed" string downstream.
136
- return (execError.killed === true || execError.signal === "SIGTERM" || execError.signal === "SIGKILL");
137
- }
138
- return false;
139
- }
140
- /**
141
- * Error patterns that indicate transient failures worth retrying.
142
- * These typically occur when Mail.app is busy or temporarily unresponsive.
143
- */
144
- const RETRYABLE_ERROR_PATTERNS = [
145
- /timed? out/i,
146
- /not responding/i,
147
- /connection.*invalid/i,
148
- /lost connection/i,
149
- /busy/i,
150
- ];
151
- /**
152
- * Checks if an error message indicates a transient failure that should be retried.
153
- *
154
- * @param errorMessage - The error message to check
155
- * @returns True if this error is worth retrying
156
- */
157
- function isRetryableError(errorMessage) {
158
- return RETRYABLE_ERROR_PATTERNS.some((pattern) => pattern.test(errorMessage));
159
- }
160
- /**
161
- * Synchronous sleep using the system's sleep command.
162
- * Used between retry attempts for exponential backoff.
163
- *
164
- * This is more efficient than a busy-wait loop as it doesn't
165
- * consume CPU cycles during the delay.
166
- *
167
- * Uses spawnSync instead of execSync to avoid interference with
168
- * execSync mocks in tests.
169
- *
170
- * @param ms - Milliseconds to sleep
171
- */
172
- function sleep(ms) {
173
- // Use system sleep command with fractional seconds support
174
- // This avoids CPU-spinning busy wait while keeping the code synchronous
175
- const seconds = ms / 1000;
176
- const result = spawnSync("sleep", [seconds.toString()], { stdio: "ignore" });
177
- if (result.error) {
178
- // Fallback to busy-wait if sleep command fails (shouldn't happen on macOS)
179
- const end = Date.now() + ms;
180
- while (Date.now() < end) {
181
- // Busy wait fallback
182
- }
183
- }
184
- }
185
- /**
186
- * User-friendly error messages mapped from common AppleScript errors.
187
- * Each entry maps a pattern (regex or string) to a user-friendly message.
188
- */
189
- const ERROR_MAPPINGS = [
190
- // Permission errors
191
- {
192
- pattern: /not authorized|not permitted|access.*denied/i,
193
- message: "Permission denied. Grant automation access in System Preferences > Privacy & Security > Automation.",
194
- },
195
- // Application not running
196
- {
197
- pattern: /application isn't running|not running/i,
198
- message: "Mail.app is not responding. Try opening Mail.app manually.",
199
- },
200
- // Connection errors
201
- {
202
- pattern: /connection is invalid|lost connection/i,
203
- message: "Lost connection to Mail.app. The app may have crashed or been restarted.",
204
- },
205
- // Message not found
206
- {
207
- pattern: /can't get message/i,
208
- message: "Message not found. The message may have been deleted or moved.",
209
- },
210
- // Mailbox not found
211
- {
212
- pattern: /can't get mailbox "([^"]+)"/i,
213
- message: 'Mailbox "$1" not found. Use list-mailboxes to see available mailboxes.',
214
- },
215
- // Account not found
216
- {
217
- pattern: /can't get account "([^"]+)"/i,
218
- message: 'Account "$1" not found. Use list-accounts to see available accounts.',
219
- },
220
- // Send failed
221
- {
222
- pattern: /couldn't send|send failed|cannot send/i,
223
- message: "Failed to send email. Check your network connection and Mail.app settings.",
224
- },
225
- // Offline
226
- {
227
- pattern: /offline|no connection/i,
228
- message: "Mail.app is offline. Check your network connection.",
229
- },
230
- // Cannot delete (various reasons)
231
- {
232
- pattern: /can't delete|cannot delete/i,
233
- message: "Cannot delete. The message may be locked or in use.",
234
- },
235
- // Syntax/script errors (usually programming bugs)
236
- {
237
- pattern: /syntax error|expected/i,
238
- message: "Internal error. Please report this issue.",
239
- },
240
- ];
241
- /**
242
- * Parses error output from osascript to extract meaningful error messages.
243
- *
244
- * osascript errors typically include execution error numbers and descriptions.
245
- * This function attempts to extract the human-readable portion and map it
246
- * to a user-friendly message with helpful suggestions.
247
- *
248
- * @param errorOutput - Raw error string from execSync
249
- * @returns User-friendly error message with suggested action
250
- */
251
- function parseErrorMessage(errorOutput) {
252
- // First, extract the core error message from AppleScript format
253
- let coreError = errorOutput;
254
- // Check for execution error format: "execution error: Message (-1234)"
255
- const executionError = errorOutput.match(/execution error: (.+?)(?:\s*\(-?\d+\))?$/m);
256
- if (executionError) {
257
- coreError = executionError[1].trim();
258
- }
259
- // Try to match against known error patterns for user-friendly messages
260
- for (const { pattern, message } of ERROR_MAPPINGS) {
261
- const match = coreError.match(pattern);
262
- if (match) {
263
- // Replace $1, $2, etc. with captured groups
264
- let result = message;
265
- for (let i = 1; i < match.length; i++) {
266
- result = result.replace(`$${i}`, match[i] || "");
267
- }
268
- return result;
269
- }
270
- }
271
- // Fall back to basic "Can't get X" parsing
272
- const notFoundError = coreError.match(/Can't get (.+?)\./);
273
- if (notFoundError) {
274
- return `Not found: ${notFoundError[1]}`;
275
- }
276
- // Never leak Node's raw `Command failed: osascript -e '<entire script>'` to the
277
- // user — it means osascript exited abnormally with no parseable AppleScript
278
- // error (killed under load, Mail.app relaunching, or Automation denied).
279
- if (/^Command failed:\s*osascript/.test(coreError.trim())) {
280
- return ("Mail.app scripting failed (osascript exited abnormally). Mail may be " +
281
- "unresponsive or relaunching, or Automation permission was denied — check " +
282
- "System Settings > Privacy & Security > Automation, and try again.");
283
- }
284
- // Return cleaned version of original error
285
- return coreError.trim() || "Unknown AppleScript error";
286
- }
287
- /**
288
- * Executes an AppleScript command and returns a structured result.
289
- *
290
- * This function serves as the bridge between TypeScript and macOS AppleScript.
291
- * It handles the complexity of shell escaping, execution, and error handling
292
- * so that calling code can work with clean TypeScript interfaces.
293
- *
294
- * The script is executed synchronously via the `osascript` command-line tool.
295
- * Multi-line scripts are supported and preserved (important for AppleScript
296
- * tell blocks and repeat loops).
297
- *
298
- * @param script - The AppleScript code to execute
299
- * @param options - Optional execution settings (timeout, etc.)
300
- * @returns A result object with success status and output or error message
301
- *
302
- * @example
303
- * ```typescript
304
- * // Basic usage with default timeout (30 seconds)
305
- * const result = executeAppleScript(`
306
- * tell application "Notes"
307
- * get name of every note
308
- * end tell
309
- * `);
310
- *
311
- * // With custom timeout for complex operations
312
- * const result = executeAppleScript(complexScript, { timeoutMs: 60000 });
313
- *
314
- * if (result.success) {
315
- * console.log("Notes:", result.output);
316
- * } else {
317
- * console.error("Failed:", result.error);
318
- * }
319
- * ```
320
- */
321
- export function executeAppleScript(script, options = {}) {
322
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
323
- const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
324
- const retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
325
- // Validate input - empty scripts are likely programmer errors
326
- if (!script || !script.trim()) {
327
- return {
328
- success: false,
329
- output: "",
330
- error: "Cannot execute empty AppleScript",
331
- };
332
- }
333
- // Prepare the script:
334
- // 1. Trim leading/trailing whitespace (cosmetic)
335
- // 2. Preserve internal newlines (required for AppleScript syntax)
336
- // 3. Wrap in `with timeout` so a stuck Mail.app operation aborts from
337
- // inside its own dispatch before the process timeout kills osascript (#11)
338
- // 4. Escape for shell execution
339
- const preparedScript = escapeForShell(wrapWithTimeout(script.trim(), timeoutMs));
340
- // Build the osascript command
341
- // We use single quotes to wrap the script, which is why we escape
342
- // single quotes within the script itself
343
- const command = `osascript -e '${preparedScript}'`;
344
- // Debug: Log the script being executed
345
- debugLog("Executing AppleScript", {
346
- scriptPreview: script.trim().substring(0, 200) + (script.length > 200 ? "..." : ""),
347
- timeout: timeoutMs,
348
- maxRetries,
349
- });
350
- let lastError = null;
351
- const startTime = Date.now();
352
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
353
- const attemptStart = Date.now();
354
- try {
355
- // Execute synchronously - MCP tools are inherently synchronous
356
- // and Apple Notes operations are fast enough that async isn't needed
357
- const output = execSync(command, {
358
- encoding: "utf8",
359
- timeout: timeoutMs,
360
- // SIGKILL (not the default SIGTERM): a wedged osascript blocked on an
361
- // unresponsive Mail.app can ignore SIGTERM, leaking processes that pile
362
- // up and worsen the contention. SIGKILL guarantees the process is reaped
363
- // when the timeout fires. (#11)
364
- killSignal: "SIGKILL",
365
- // Raise the output cap well above Node's 1 MB default so large message
366
- // sources / attachment payloads aren't truncated into an ENOBUFS
367
- // failure. (#27)
368
- maxBuffer: getMaxBuffer(),
369
- // Capture stderr separately to get error details
370
- stdio: ["pipe", "pipe", "pipe"],
371
- });
372
- const duration = Date.now() - attemptStart;
373
- debugLog("AppleScript succeeded", {
374
- attempt,
375
- duration: `${duration}ms`,
376
- outputLength: output.length,
377
- outputPreview: output.substring(0, 100) + (output.length > 100 ? "..." : ""),
378
- });
379
- return {
380
- success: true,
381
- output: output.trim(),
382
- };
383
- }
384
- catch (error) {
385
- // execSync throws on non-zero exit codes
386
- // The error object contains stderr output with AppleScript error details
387
- const attemptDuration = Date.now() - attemptStart;
388
- let errorMessage;
389
- let isTimeout = false;
390
- let rawError;
391
- // Check for timeout first - provide specific message
392
- if (isTimeoutError(error)) {
393
- isTimeout = true;
394
- const timeoutSecs = Math.round(timeoutMs / 1000);
395
- errorMessage = `Operation timed out after ${timeoutSecs} seconds. Mail.app may be unresponsive or the operation involves too many messages.`;
396
- }
397
- else if (error instanceof Error) {
398
- rawError = error.message;
399
- // Node's ExecException includes stderr in the message
400
- errorMessage = parseErrorMessage(error.message);
401
- }
402
- else if (typeof error === "string") {
403
- rawError = error;
404
- errorMessage = parseErrorMessage(error);
405
- }
406
- else {
407
- errorMessage = "AppleScript execution failed with unknown error";
408
- }
409
- // Debug: Log error details
410
- debugLog("AppleScript failed", {
411
- attempt,
412
- duration: `${attemptDuration}ms`,
413
- totalElapsed: `${Date.now() - startTime}ms`,
414
- isTimeout,
415
- errorMessage,
416
- rawError: rawError?.substring(0, 500),
417
- });
418
- lastError = {
419
- success: false,
420
- output: "",
421
- error: errorMessage,
422
- };
423
- // Check if we should retry
424
- const canRetry = isTimeout || isRetryableError(errorMessage);
425
- const hasAttemptsLeft = attempt < maxRetries;
426
- if (canRetry && hasAttemptsLeft) {
427
- const delayMs = retryDelayMs * Math.pow(2, attempt - 1);
428
- console.error(`AppleScript retry: Attempt ${attempt}/${maxRetries} failed with "${errorMessage}". Retrying in ${delayMs}ms...`);
429
- sleep(delayMs);
430
- // Continue to next attempt
431
- }
432
- else {
433
- // Log final error and return
434
- if (isTimeout) {
435
- console.error(`AppleScript timeout: ${errorMessage}`);
436
- }
437
- else {
438
- console.error(`AppleScript error: ${errorMessage}`);
439
- }
440
- return lastError;
441
- }
442
- }
443
- }
444
- // Return the last error (all retries exhausted - shouldn't reach here normally)
445
- return lastError;
446
- }
@@ -1,9 +0,0 @@
1
- import type { AttachmentInput } from "../types.js";
2
- export interface MaterializedAttachments {
3
- /** Absolute file paths ready to hand to the AppleScript attachment builder. */
4
- paths: string[];
5
- /** Remove any temp files created; safe to call always. */
6
- cleanup: () => void;
7
- }
8
- export declare function materializeAttachments(attachments?: AttachmentInput[]): MaterializedAttachments;
9
- //# sourceMappingURL=attachmentMaterialize.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"attachmentMaterialize.d.ts","sourceRoot":"","sources":["../../src/utils/attachmentMaterialize.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,uBAAuB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,eAAe,EAAE,GAAG,uBAAuB,CAsB/F"}
@@ -1,38 +0,0 @@
1
- /**
2
- * Materialize inline (base64) attachments to temp files (B4).
3
- *
4
- * The AppleScript send/draft path attaches files by POSIX path, so inline
5
- * base64 content is written to a throwaway temp dir first and cleaned up after
6
- * the operation. Plain string entries (existing absolute paths) pass through.
7
- *
8
- * @module utils/attachmentMaterialize
9
- */
10
- import { writeFileSync, rmSync, mkdtempSync } from "fs";
11
- import { join } from "path";
12
- import { tmpdir } from "os";
13
- export function materializeAttachments(attachments) {
14
- if (!attachments || attachments.length === 0) {
15
- return { paths: [], cleanup: () => undefined };
16
- }
17
- let dir = null;
18
- const paths = attachments.map((a) => {
19
- if (typeof a === "string")
20
- return a;
21
- if (!a.filename || !a.contentBase64) {
22
- throw new Error("Inline attachment requires both filename and contentBase64.");
23
- }
24
- if (!dir)
25
- dir = mkdtempSync(join(tmpdir(), "amcp-att-"));
26
- const safeName = a.filename.replace(/[/\\]/g, "_");
27
- const p = join(dir, safeName);
28
- writeFileSync(p, Buffer.from(a.contentBase64, "base64"));
29
- return p;
30
- });
31
- return {
32
- paths,
33
- cleanup: () => {
34
- if (dir)
35
- rmSync(dir, { recursive: true, force: true });
36
- },
37
- };
38
- }
@@ -1,62 +0,0 @@
1
- /**
2
- * MIME Source Parser for Attachment Extraction
3
- *
4
- * Parses raw email MIME source to extract attachment metadata and content.
5
- * Used as a fallback when AppleScript's `mail attachments` returns empty
6
- * (which happens across all account types: iCloud, Google, Exchange).
7
- *
8
- * @module utils/mimeParse
9
- */
10
- export interface MimeAttachmentInfo {
11
- /** Filename from Content-Disposition or Content-Type name parameter */
12
- name: string;
13
- /** MIME type from Content-Type header */
14
- mimeType: string;
15
- /** Size in bytes from Content-Disposition size parameter, or estimated from body */
16
- size: number;
17
- }
18
- export interface MimeAttachmentData extends MimeAttachmentInfo {
19
- /** Decoded binary content */
20
- data: Buffer;
21
- }
22
- /**
23
- * Parse MIME source and return metadata for all file attachments.
24
- * Skips inline dispositions (signature images, etc.). Descends into
25
- * nested multipart/* containers.
26
- *
27
- * @param source - Raw MIME source of the email
28
- * @returns Array of attachment metadata (name, mimeType, size)
29
- */
30
- export declare function parseMimeAttachments(source: string): MimeAttachmentInfo[];
31
- /**
32
- * Extract the decoded `text/html` body from raw MIME source.
33
- *
34
- * Used by get-message's `preferHtml` path so it returns the actual HTML body
35
- * rather than the entire raw MIME blob (headers + base64 attachments), which is
36
- * both wrong and enormous (#32). Handles both multipart messages (walks leaf
37
- * parts, descending into nested multipart/* containers) and a non-multipart
38
- * message whose top-level Content-Type is text/html. Bodies are decoded per
39
- * Content-Transfer-Encoding (base64 / quoted-printable / raw) and returned as
40
- * UTF-8 text.
41
- *
42
- * @param source - Raw MIME source of the email
43
- * @returns The decoded HTML body, or null if the message has no text/html part
44
- */
45
- export declare function extractHtmlBody(source: string): string | null;
46
- /**
47
- * Extract the decoded `text/plain` body from raw MIME source. Mirror of
48
- * extractHtmlBody; used by the IMAP get-message path (#43 Phase 3) to render a
49
- * message fetched by UID. Returns null when there's no text/plain part.
50
- */
51
- export declare function extractTextBody(source: string): string | null;
52
- /**
53
- * Extract and decode a specific attachment from MIME source by filename.
54
- * Supports base64, quoted-printable, and 7bit/8bit/binary transfer encodings.
55
- * Descends into nested multipart/* containers.
56
- *
57
- * @param source - Raw MIME source of the email
58
- * @param attachmentName - Filename to extract
59
- * @returns Decoded attachment data, or null if not found
60
- */
61
- export declare function extractMimeAttachment(source: string, attachmentName: string): MimeAttachmentData | null;
62
- //# sourceMappingURL=mimeParse.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mimeParse.d.ts","sourceRoot":"","sources":["../../src/utils/mimeParse.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAmB,SAAQ,kBAAkB;IAC5D,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAqMD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAyBzE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuB7D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuB7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,kBAAkB,GAAG,IAAI,CAwB3B"}