apple-mail-mcp 2.8.6 → 2.8.7
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/build/index.js +159 -95
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -20707,7 +20707,7 @@ var require_thread_stream = __commonJS({
|
|
|
20707
20707
|
var { version: version3 } = require_package2();
|
|
20708
20708
|
var { EventEmitter } = __require("events");
|
|
20709
20709
|
var { Worker } = __require("worker_threads");
|
|
20710
|
-
var { join:
|
|
20710
|
+
var { join: join6 } = __require("path");
|
|
20711
20711
|
var { pathToFileURL } = __require("url");
|
|
20712
20712
|
var { wait } = require_wait();
|
|
20713
20713
|
var {
|
|
@@ -20758,7 +20758,7 @@ var require_thread_stream = __commonJS({
|
|
|
20758
20758
|
function createWorker(stream, opts) {
|
|
20759
20759
|
const { filename, workerData } = opts;
|
|
20760
20760
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
20761
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
20761
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join6(__dirname, "lib", "worker.js");
|
|
20762
20762
|
const worker = new Worker(toExecute, {
|
|
20763
20763
|
...opts.workerOpts,
|
|
20764
20764
|
name: opts.workerOpts?.name || "thread-stream",
|
|
@@ -21224,9 +21224,9 @@ var require_transport = __commonJS({
|
|
|
21224
21224
|
"node_modules/.pnpm/pino@10.3.1/node_modules/pino/lib/transport.js"(exports, module) {
|
|
21225
21225
|
"use strict";
|
|
21226
21226
|
var { createRequire: createRequire2 } = __require("module");
|
|
21227
|
-
var { existsSync:
|
|
21227
|
+
var { existsSync: existsSync6 } = __require("node:fs");
|
|
21228
21228
|
var getCallers = require_caller();
|
|
21229
|
-
var { join:
|
|
21229
|
+
var { join: join6, isAbsolute: isAbsolute3, sep: sep2 } = __require("node:path");
|
|
21230
21230
|
var { fileURLToPath } = __require("node:url");
|
|
21231
21231
|
var sleep2 = require_atomic_sleep();
|
|
21232
21232
|
var onExit = require_on_exit_leak_free();
|
|
@@ -21298,7 +21298,7 @@ var require_transport = __commonJS({
|
|
|
21298
21298
|
return false;
|
|
21299
21299
|
}
|
|
21300
21300
|
}
|
|
21301
|
-
return isAbsolute3(path) && !
|
|
21301
|
+
return isAbsolute3(path) && !existsSync6(path);
|
|
21302
21302
|
}
|
|
21303
21303
|
function stripQuotes(value) {
|
|
21304
21304
|
const first = value[0];
|
|
@@ -21379,7 +21379,7 @@ var require_transport = __commonJS({
|
|
|
21379
21379
|
throw new Error("only one of target or targets can be specified");
|
|
21380
21380
|
}
|
|
21381
21381
|
if (targets) {
|
|
21382
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
21382
|
+
target = bundlerOverrides["pino-worker"] || join6(__dirname, "worker.js");
|
|
21383
21383
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
21384
21384
|
return {
|
|
21385
21385
|
...dest,
|
|
@@ -21397,7 +21397,7 @@ var require_transport = __commonJS({
|
|
|
21397
21397
|
});
|
|
21398
21398
|
});
|
|
21399
21399
|
} else if (pipeline) {
|
|
21400
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
21400
|
+
target = bundlerOverrides["pino-worker"] || join6(__dirname, "worker.js");
|
|
21401
21401
|
options.pipelines = [pipeline.map((dest) => {
|
|
21402
21402
|
return {
|
|
21403
21403
|
...dest,
|
|
@@ -21420,7 +21420,7 @@ var require_transport = __commonJS({
|
|
|
21420
21420
|
return origin;
|
|
21421
21421
|
}
|
|
21422
21422
|
if (origin === "pino/file") {
|
|
21423
|
-
return
|
|
21423
|
+
return join6(__dirname, "..", "file.js");
|
|
21424
21424
|
}
|
|
21425
21425
|
let fixTarget2;
|
|
21426
21426
|
for (const filePath of callers) {
|
|
@@ -22400,7 +22400,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
22400
22400
|
return circularValue;
|
|
22401
22401
|
}
|
|
22402
22402
|
let res = "";
|
|
22403
|
-
let
|
|
22403
|
+
let join6 = ",";
|
|
22404
22404
|
const originalIndentation = indentation;
|
|
22405
22405
|
if (Array.isArray(value)) {
|
|
22406
22406
|
if (value.length === 0) {
|
|
@@ -22414,7 +22414,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
22414
22414
|
indentation += spacer;
|
|
22415
22415
|
res += `
|
|
22416
22416
|
${indentation}`;
|
|
22417
|
-
|
|
22417
|
+
join6 = `,
|
|
22418
22418
|
${indentation}`;
|
|
22419
22419
|
}
|
|
22420
22420
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -22422,13 +22422,13 @@ ${indentation}`;
|
|
|
22422
22422
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
22423
22423
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
22424
22424
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
22425
|
-
res +=
|
|
22425
|
+
res += join6;
|
|
22426
22426
|
}
|
|
22427
22427
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
22428
22428
|
res += tmp !== void 0 ? tmp : "null";
|
|
22429
22429
|
if (value.length - 1 > maximumBreadth) {
|
|
22430
22430
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
22431
|
-
res += `${
|
|
22431
|
+
res += `${join6}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
22432
22432
|
}
|
|
22433
22433
|
if (spacer !== "") {
|
|
22434
22434
|
res += `
|
|
@@ -22449,7 +22449,7 @@ ${originalIndentation}`;
|
|
|
22449
22449
|
let separator = "";
|
|
22450
22450
|
if (spacer !== "") {
|
|
22451
22451
|
indentation += spacer;
|
|
22452
|
-
|
|
22452
|
+
join6 = `,
|
|
22453
22453
|
${indentation}`;
|
|
22454
22454
|
whitespace = " ";
|
|
22455
22455
|
}
|
|
@@ -22463,13 +22463,13 @@ ${indentation}`;
|
|
|
22463
22463
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
22464
22464
|
if (tmp !== void 0) {
|
|
22465
22465
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
22466
|
-
separator =
|
|
22466
|
+
separator = join6;
|
|
22467
22467
|
}
|
|
22468
22468
|
}
|
|
22469
22469
|
if (keyLength > maximumBreadth) {
|
|
22470
22470
|
const removedKeys = keyLength - maximumBreadth;
|
|
22471
22471
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
22472
|
-
separator =
|
|
22472
|
+
separator = join6;
|
|
22473
22473
|
}
|
|
22474
22474
|
if (spacer !== "" && separator.length > 1) {
|
|
22475
22475
|
res = `
|
|
@@ -22510,7 +22510,7 @@ ${originalIndentation}`;
|
|
|
22510
22510
|
}
|
|
22511
22511
|
const originalIndentation = indentation;
|
|
22512
22512
|
let res = "";
|
|
22513
|
-
let
|
|
22513
|
+
let join6 = ",";
|
|
22514
22514
|
if (Array.isArray(value)) {
|
|
22515
22515
|
if (value.length === 0) {
|
|
22516
22516
|
return "[]";
|
|
@@ -22523,7 +22523,7 @@ ${originalIndentation}`;
|
|
|
22523
22523
|
indentation += spacer;
|
|
22524
22524
|
res += `
|
|
22525
22525
|
${indentation}`;
|
|
22526
|
-
|
|
22526
|
+
join6 = `,
|
|
22527
22527
|
${indentation}`;
|
|
22528
22528
|
}
|
|
22529
22529
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -22531,13 +22531,13 @@ ${indentation}`;
|
|
|
22531
22531
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
22532
22532
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
22533
22533
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
22534
|
-
res +=
|
|
22534
|
+
res += join6;
|
|
22535
22535
|
}
|
|
22536
22536
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
22537
22537
|
res += tmp !== void 0 ? tmp : "null";
|
|
22538
22538
|
if (value.length - 1 > maximumBreadth) {
|
|
22539
22539
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
22540
|
-
res += `${
|
|
22540
|
+
res += `${join6}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
22541
22541
|
}
|
|
22542
22542
|
if (spacer !== "") {
|
|
22543
22543
|
res += `
|
|
@@ -22550,7 +22550,7 @@ ${originalIndentation}`;
|
|
|
22550
22550
|
let whitespace = "";
|
|
22551
22551
|
if (spacer !== "") {
|
|
22552
22552
|
indentation += spacer;
|
|
22553
|
-
|
|
22553
|
+
join6 = `,
|
|
22554
22554
|
${indentation}`;
|
|
22555
22555
|
whitespace = " ";
|
|
22556
22556
|
}
|
|
@@ -22559,7 +22559,7 @@ ${indentation}`;
|
|
|
22559
22559
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
22560
22560
|
if (tmp !== void 0) {
|
|
22561
22561
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
22562
|
-
separator =
|
|
22562
|
+
separator = join6;
|
|
22563
22563
|
}
|
|
22564
22564
|
}
|
|
22565
22565
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -22617,20 +22617,20 @@ ${originalIndentation}`;
|
|
|
22617
22617
|
indentation += spacer;
|
|
22618
22618
|
let res2 = `
|
|
22619
22619
|
${indentation}`;
|
|
22620
|
-
const
|
|
22620
|
+
const join7 = `,
|
|
22621
22621
|
${indentation}`;
|
|
22622
22622
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
22623
22623
|
let i = 0;
|
|
22624
22624
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
22625
22625
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
22626
22626
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
22627
|
-
res2 +=
|
|
22627
|
+
res2 += join7;
|
|
22628
22628
|
}
|
|
22629
22629
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
22630
22630
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
22631
22631
|
if (value.length - 1 > maximumBreadth) {
|
|
22632
22632
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
22633
|
-
res2 += `${
|
|
22633
|
+
res2 += `${join7}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
22634
22634
|
}
|
|
22635
22635
|
res2 += `
|
|
22636
22636
|
${originalIndentation}`;
|
|
@@ -22646,16 +22646,16 @@ ${originalIndentation}`;
|
|
|
22646
22646
|
return '"[Object]"';
|
|
22647
22647
|
}
|
|
22648
22648
|
indentation += spacer;
|
|
22649
|
-
const
|
|
22649
|
+
const join6 = `,
|
|
22650
22650
|
${indentation}`;
|
|
22651
22651
|
let res = "";
|
|
22652
22652
|
let separator = "";
|
|
22653
22653
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
22654
22654
|
if (isTypedArrayWithEntries(value)) {
|
|
22655
|
-
res += stringifyTypedArray(value,
|
|
22655
|
+
res += stringifyTypedArray(value, join6, maximumBreadth);
|
|
22656
22656
|
keys = keys.slice(value.length);
|
|
22657
22657
|
maximumPropertiesToStringify -= value.length;
|
|
22658
|
-
separator =
|
|
22658
|
+
separator = join6;
|
|
22659
22659
|
}
|
|
22660
22660
|
if (deterministic) {
|
|
22661
22661
|
keys = sort(keys, comparator);
|
|
@@ -22666,13 +22666,13 @@ ${indentation}`;
|
|
|
22666
22666
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
22667
22667
|
if (tmp !== void 0) {
|
|
22668
22668
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
22669
|
-
separator =
|
|
22669
|
+
separator = join6;
|
|
22670
22670
|
}
|
|
22671
22671
|
}
|
|
22672
22672
|
if (keyLength > maximumBreadth) {
|
|
22673
22673
|
const removedKeys = keyLength - maximumBreadth;
|
|
22674
22674
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
22675
|
-
separator =
|
|
22675
|
+
separator = join6;
|
|
22676
22676
|
}
|
|
22677
22677
|
if (separator !== "") {
|
|
22678
22678
|
res = `
|
|
@@ -76147,9 +76147,9 @@ var StdioServerTransport = class {
|
|
|
76147
76147
|
|
|
76148
76148
|
// src/services/appleMailManager.ts
|
|
76149
76149
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
76150
|
-
import { existsSync as
|
|
76151
|
-
import { isAbsolute, resolve, sep, join as
|
|
76152
|
-
import { homedir as
|
|
76150
|
+
import { existsSync as existsSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync2, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
|
|
76151
|
+
import { isAbsolute, resolve, sep, join as join4 } from "path";
|
|
76152
|
+
import { homedir as homedir3 } from "os";
|
|
76153
76153
|
|
|
76154
76154
|
// src/utils/applescript.ts
|
|
76155
76155
|
import { execSync, spawnSync } from "child_process";
|
|
@@ -76685,6 +76685,114 @@ function materializeAttachments(attachments) {
|
|
|
76685
76685
|
};
|
|
76686
76686
|
}
|
|
76687
76687
|
|
|
76688
|
+
// src/utils/contactsDb.ts
|
|
76689
|
+
import { existsSync as existsSync2, readdirSync } from "fs";
|
|
76690
|
+
import { join as join3 } from "path";
|
|
76691
|
+
import { homedir as homedir2 } from "os";
|
|
76692
|
+
function loadSqlite() {
|
|
76693
|
+
try {
|
|
76694
|
+
const mod = __require("node:sqlite");
|
|
76695
|
+
if (!mod || typeof mod.DatabaseSync !== "function") {
|
|
76696
|
+
console.error(
|
|
76697
|
+
"[contactsDb] node:sqlite unavailable (no DatabaseSync export); returning no contacts"
|
|
76698
|
+
);
|
|
76699
|
+
return null;
|
|
76700
|
+
}
|
|
76701
|
+
return mod;
|
|
76702
|
+
} catch (err) {
|
|
76703
|
+
console.error(
|
|
76704
|
+
`[contactsDb] node:sqlite not available (requires Node >= 22.5); returning no contacts: ${err instanceof Error ? err.message : String(err)}`
|
|
76705
|
+
);
|
|
76706
|
+
return null;
|
|
76707
|
+
}
|
|
76708
|
+
}
|
|
76709
|
+
function resolveContactsDbPaths(baseDir) {
|
|
76710
|
+
const root = baseDir ?? join3(homedir2(), "Library", "Application Support", "AddressBook");
|
|
76711
|
+
const paths = [];
|
|
76712
|
+
const topLevel = join3(root, "AddressBook-v22.abcddb");
|
|
76713
|
+
if (existsSync2(topLevel)) paths.push(topLevel);
|
|
76714
|
+
const sourcesDir = join3(root, "Sources");
|
|
76715
|
+
if (existsSync2(sourcesDir)) {
|
|
76716
|
+
let entries = [];
|
|
76717
|
+
try {
|
|
76718
|
+
entries = readdirSync(sourcesDir);
|
|
76719
|
+
} catch {
|
|
76720
|
+
entries = [];
|
|
76721
|
+
}
|
|
76722
|
+
for (const entry of entries) {
|
|
76723
|
+
const candidate = join3(sourcesDir, entry, "AddressBook-v22.abcddb");
|
|
76724
|
+
if (existsSync2(candidate)) paths.push(candidate);
|
|
76725
|
+
}
|
|
76726
|
+
}
|
|
76727
|
+
return paths;
|
|
76728
|
+
}
|
|
76729
|
+
var RECORD_SQL = `
|
|
76730
|
+
SELECT r.Z_PK pk,
|
|
76731
|
+
TRIM(COALESCE(r.ZFIRSTNAME,'')||' '||COALESCE(r.ZLASTNAME,'')) fullname,
|
|
76732
|
+
r.ZORGANIZATION org, r.ZNICKNAME nick
|
|
76733
|
+
FROM ZABCDRECORD r
|
|
76734
|
+
WHERE lower(TRIM(COALESCE(r.ZFIRSTNAME,'')||' '||COALESCE(r.ZLASTNAME,''))) LIKE ?
|
|
76735
|
+
OR lower(COALESCE(r.ZORGANIZATION,'')) LIKE ?
|
|
76736
|
+
OR lower(COALESCE(r.ZNICKNAME,'')) LIKE ?
|
|
76737
|
+
OR r.Z_PK IN (SELECT ZOWNER FROM ZABCDEMAILADDRESS WHERE lower(COALESCE(ZADDRESS,'')) LIKE ?)
|
|
76738
|
+
`;
|
|
76739
|
+
var EMAILS_SQL = `SELECT ZADDRESS AS v FROM ZABCDEMAILADDRESS WHERE ZOWNER = ? AND ZADDRESS IS NOT NULL`;
|
|
76740
|
+
var PHONES_SQL = `SELECT ZFULLNUMBER AS v FROM ZABCDPHONENUMBER WHERE ZOWNER = ? AND ZFULLNUMBER IS NOT NULL`;
|
|
76741
|
+
function displayName(row) {
|
|
76742
|
+
const fullname = typeof row.fullname === "string" ? row.fullname.trim() : "";
|
|
76743
|
+
if (fullname) return fullname;
|
|
76744
|
+
const org = typeof row.org === "string" ? row.org.trim() : "";
|
|
76745
|
+
if (org) return org;
|
|
76746
|
+
const nick = typeof row.nick === "string" ? row.nick.trim() : "";
|
|
76747
|
+
if (nick) return nick;
|
|
76748
|
+
return "(no name)";
|
|
76749
|
+
}
|
|
76750
|
+
function searchOneDb(sqlite, dbPath, like) {
|
|
76751
|
+
let db = null;
|
|
76752
|
+
try {
|
|
76753
|
+
db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
|
|
76754
|
+
const records = db.prepare(RECORD_SQL).all(like, like, like, like);
|
|
76755
|
+
const emailStmt = db.prepare(EMAILS_SQL);
|
|
76756
|
+
const phoneStmt = db.prepare(PHONES_SQL);
|
|
76757
|
+
const contacts = [];
|
|
76758
|
+
for (const rec of records) {
|
|
76759
|
+
const pk = rec.pk;
|
|
76760
|
+
const emails = emailStmt.all(pk).map((r) => r.v).filter((v) => typeof v === "string" && v.length > 0);
|
|
76761
|
+
const phones = phoneStmt.all(pk).map((r) => r.v).filter((v) => typeof v === "string" && v.length > 0);
|
|
76762
|
+
contacts.push({ name: displayName(rec), emails, phones });
|
|
76763
|
+
}
|
|
76764
|
+
return contacts;
|
|
76765
|
+
} catch (err) {
|
|
76766
|
+
console.error(
|
|
76767
|
+
`[contactsDb] skipping unreadable Contacts DB ${dbPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
76768
|
+
);
|
|
76769
|
+
return [];
|
|
76770
|
+
} finally {
|
|
76771
|
+
try {
|
|
76772
|
+
db?.close();
|
|
76773
|
+
} catch {
|
|
76774
|
+
}
|
|
76775
|
+
}
|
|
76776
|
+
}
|
|
76777
|
+
function searchContactsDb(query, opts) {
|
|
76778
|
+
if (!query || !query.trim()) return [];
|
|
76779
|
+
const sqlite = loadSqlite();
|
|
76780
|
+
if (!sqlite) return [];
|
|
76781
|
+
const dbPaths = opts?.dbPaths ?? resolveContactsDbPaths();
|
|
76782
|
+
const like = `%${query.toLowerCase()}%`;
|
|
76783
|
+
const seen = /* @__PURE__ */ new Set();
|
|
76784
|
+
const results = [];
|
|
76785
|
+
for (const dbPath of dbPaths) {
|
|
76786
|
+
for (const contact of searchOneDb(sqlite, dbPath, like)) {
|
|
76787
|
+
const key = `${contact.name} ${[...contact.emails].sort().join(",")}`;
|
|
76788
|
+
if (seen.has(key)) continue;
|
|
76789
|
+
seen.add(key);
|
|
76790
|
+
results.push(contact);
|
|
76791
|
+
}
|
|
76792
|
+
}
|
|
76793
|
+
return results;
|
|
76794
|
+
}
|
|
76795
|
+
|
|
76688
76796
|
// src/services/appleMailManager.ts
|
|
76689
76797
|
function getMailboxScanThreshold() {
|
|
76690
76798
|
const raw = process.env.APPLE_MAIL_MAX_SEARCH_MAILBOX;
|
|
@@ -76740,7 +76848,7 @@ function splitSearchDiagnostics(output, account) {
|
|
|
76740
76848
|
}
|
|
76741
76849
|
return { payload, diagnostics };
|
|
76742
76850
|
}
|
|
76743
|
-
var ALLOWED_SAVE_ROOTS = [
|
|
76851
|
+
var ALLOWED_SAVE_ROOTS = [homedir3(), "/tmp", "/private/tmp", "/Volumes"];
|
|
76744
76852
|
function isPathWithinAllowedRoots(resolvedPath) {
|
|
76745
76853
|
return ALLOWED_SAVE_ROOTS.some((root) => {
|
|
76746
76854
|
const base = root.endsWith(sep) ? root.slice(0, -1) : root;
|
|
@@ -76784,7 +76892,7 @@ function buildAttachmentCommands(attachments) {
|
|
|
76784
76892
|
if (!isAbsolute(filePath)) {
|
|
76785
76893
|
throw new Error(`Attachment path must be absolute: "${filePath}"`);
|
|
76786
76894
|
}
|
|
76787
|
-
if (!
|
|
76895
|
+
if (!existsSync3(filePath)) {
|
|
76788
76896
|
throw new Error(`Attachment file not found: "${filePath}"`);
|
|
76789
76897
|
}
|
|
76790
76898
|
}
|
|
@@ -78606,7 +78714,7 @@ var AppleMailManager = class {
|
|
|
78606
78714
|
let dir = null;
|
|
78607
78715
|
try {
|
|
78608
78716
|
dir = mkdtempSync2("/private/tmp/amcp-fetch-");
|
|
78609
|
-
const dest =
|
|
78717
|
+
const dest = join4(dir, attachmentName.replace(/[/\\]/g, "_"));
|
|
78610
78718
|
const ok = this.saveAttachment(id, attachmentName, dest);
|
|
78611
78719
|
if (!ok) {
|
|
78612
78720
|
return {
|
|
@@ -79040,60 +79148,16 @@ ${actionStmts.join("\n")}
|
|
|
79040
79148
|
// Contacts Integration
|
|
79041
79149
|
// ===========================================================================
|
|
79042
79150
|
/**
|
|
79043
|
-
* Search contacts by name or email.
|
|
79151
|
+
* Search contacts by name, organization, nickname, or email address.
|
|
79152
|
+
*
|
|
79153
|
+
* Reads the macOS Contacts (AddressBook) SQLite databases directly via Full
|
|
79154
|
+
* Disk Access — it does NOT drive Contacts.app over AppleScript, so it raises
|
|
79155
|
+
* no Automation / Apple-Events permission prompt (the old path would hang on
|
|
79156
|
+
* that unanswerable prompt on headless/scheduled hosts). See
|
|
79157
|
+
* {@link searchContactsDb}.
|
|
79044
79158
|
*/
|
|
79045
79159
|
searchContacts(query) {
|
|
79046
|
-
|
|
79047
|
-
const script = `
|
|
79048
|
-
tell application "Contacts"
|
|
79049
|
-
set matchedContacts to {}
|
|
79050
|
-
set foundPeople to (every person whose name contains "${safeQuery}") & (every person whose value of emails contains "${safeQuery}")
|
|
79051
|
-
|
|
79052
|
-
-- Deduplicate by tracking IDs
|
|
79053
|
-
set seenIds to {}
|
|
79054
|
-
repeat with p in foundPeople
|
|
79055
|
-
set pid to id of p
|
|
79056
|
-
if seenIds does not contain pid then
|
|
79057
|
-
set end of seenIds to pid
|
|
79058
|
-
-- Per-person try: one malformed contact (e.g. no emails) must not
|
|
79059
|
-
-- abort the whole search and return an empty list (audit finding #13).
|
|
79060
|
-
try
|
|
79061
|
-
set pName to name of p
|
|
79062
|
-
set pEmails to ""
|
|
79063
|
-
repeat with e in emails of p
|
|
79064
|
-
if pEmails is not "" then set pEmails to pEmails & ","
|
|
79065
|
-
set pEmails to pEmails & (value of e)
|
|
79066
|
-
end repeat
|
|
79067
|
-
set pPhones to ""
|
|
79068
|
-
repeat with ph in phones of p
|
|
79069
|
-
if pPhones is not "" then set pPhones to pPhones & ","
|
|
79070
|
-
set pPhones to pPhones & (value of ph)
|
|
79071
|
-
end repeat
|
|
79072
|
-
set end of matchedContacts to pName & "${FIELD_SEP}" & pEmails & "${FIELD_SEP}" & pPhones
|
|
79073
|
-
end try
|
|
79074
|
-
end if
|
|
79075
|
-
end repeat
|
|
79076
|
-
|
|
79077
|
-
set AppleScript's text item delimiters to "${RECORD_SEP}"
|
|
79078
|
-
return matchedContacts as text
|
|
79079
|
-
end tell
|
|
79080
|
-
`;
|
|
79081
|
-
const result = executeAppleScript(script);
|
|
79082
|
-
if (!result.success || !result.output.trim()) {
|
|
79083
|
-
return [];
|
|
79084
|
-
}
|
|
79085
|
-
const items = result.output.split(RECORD_SEP);
|
|
79086
|
-
const contacts = [];
|
|
79087
|
-
for (const item of items) {
|
|
79088
|
-
const parts = item.split(FIELD_SEP);
|
|
79089
|
-
if (parts.length < 3) continue;
|
|
79090
|
-
contacts.push({
|
|
79091
|
-
name: parts[0],
|
|
79092
|
-
emails: parts[1] ? parts[1].split(",").filter(Boolean) : [],
|
|
79093
|
-
phones: parts[2] ? parts[2].split(",").filter(Boolean) : []
|
|
79094
|
-
});
|
|
79095
|
-
}
|
|
79096
|
-
return contacts;
|
|
79160
|
+
return searchContactsDb(query);
|
|
79097
79161
|
}
|
|
79098
79162
|
// ===========================================================================
|
|
79099
79163
|
// Email Templates
|
|
@@ -79408,7 +79472,7 @@ import { resolve as resolvePath, join as joinPath } from "path";
|
|
|
79408
79472
|
var import_nodemailer = __toESM(require_nodemailer(), 1);
|
|
79409
79473
|
import { execFileSync } from "child_process";
|
|
79410
79474
|
import { isAbsolute as isAbsolute2 } from "path";
|
|
79411
|
-
import { existsSync as
|
|
79475
|
+
import { existsSync as existsSync4 } from "fs";
|
|
79412
79476
|
|
|
79413
79477
|
// src/utils/docsUrls.ts
|
|
79414
79478
|
var SETUP_GUIDE_URL = "https://github.com/sweetrb/apple-mail-mcp/blob/main/docs/IMAP-SETUP.md";
|
|
@@ -79484,7 +79548,7 @@ function buildAttachments(attachments) {
|
|
|
79484
79548
|
return attachments.map((a) => {
|
|
79485
79549
|
if (typeof a === "string") {
|
|
79486
79550
|
if (!isAbsolute2(a)) throw new Error(`Attachment path must be absolute: "${a}"`);
|
|
79487
|
-
if (!
|
|
79551
|
+
if (!existsSync4(a)) throw new Error(`Attachment file not found: "${a}"`);
|
|
79488
79552
|
return { path: a };
|
|
79489
79553
|
}
|
|
79490
79554
|
if (!a.filename || !a.contentBase64) {
|
|
@@ -81078,18 +81142,18 @@ var ImapIdleWatcher = class {
|
|
|
81078
81142
|
};
|
|
81079
81143
|
|
|
81080
81144
|
// src/services/fileConfig.ts
|
|
81081
|
-
import { existsSync as
|
|
81082
|
-
import { join as
|
|
81083
|
-
import { homedir as
|
|
81145
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
81146
|
+
import { join as join5 } from "path";
|
|
81147
|
+
import { homedir as homedir4 } from "os";
|
|
81084
81148
|
function fileConfigPath(env = process.env) {
|
|
81085
81149
|
const override = env.APPLE_MAIL_MCP_CONFIG_FILE;
|
|
81086
81150
|
if (override && override.trim()) return override.trim();
|
|
81087
|
-
return
|
|
81151
|
+
return join5(homedir4(), "Library", "Application Support", "apple-mail-mcp", "config.json");
|
|
81088
81152
|
}
|
|
81089
81153
|
function loadFileConfig(env = process.env, path = fileConfigPath(env)) {
|
|
81090
81154
|
const applied = [];
|
|
81091
81155
|
try {
|
|
81092
|
-
if (!
|
|
81156
|
+
if (!existsSync5(path)) return applied;
|
|
81093
81157
|
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
81094
81158
|
if (!parsed || typeof parsed !== "object") return applied;
|
|
81095
81159
|
for (const [k, v] of Object.entries(parsed)) {
|
|
@@ -82697,7 +82761,7 @@ server.registerTool(
|
|
|
82697
82761
|
server.registerTool(
|
|
82698
82762
|
"search-contacts",
|
|
82699
82763
|
{
|
|
82700
|
-
description: "Use when: looking up a person in Contacts
|
|
82764
|
+
description: "Use when: looking up a person in Contacts by name, organization, nickname, or email to find their email address(es)/phone(s) before composing or sending mail. Reads the macOS Contacts database directly (needs Full Disk Access; does NOT require Contacts.app to be running or an Automation / Apple-Events grant).\nReturns: matching contacts with their names, email addresses, and phone numbers.\nDo not use when: searching email messages (use search-messages) \u2014 this queries Contacts, not the mailbox.",
|
|
82701
82765
|
inputSchema: {
|
|
82702
82766
|
query: external_exports.string().min(1, "Search query is required")
|
|
82703
82767
|
},
|
package/package.json
CHANGED