apple-notes-mcp 2.9.16 → 2.9.19
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 +80 -2
- package/build/index.js +485 -152
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -417,11 +417,11 @@ var require_codegen = __commonJS({
|
|
|
417
417
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
418
418
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
419
419
|
}
|
|
420
|
-
optimizeNames(names,
|
|
420
|
+
optimizeNames(names, constants10) {
|
|
421
421
|
if (!names[this.name.str])
|
|
422
422
|
return;
|
|
423
423
|
if (this.rhs)
|
|
424
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
424
|
+
this.rhs = optimizeExpr(this.rhs, names, constants10);
|
|
425
425
|
return this;
|
|
426
426
|
}
|
|
427
427
|
get names() {
|
|
@@ -438,10 +438,10 @@ var require_codegen = __commonJS({
|
|
|
438
438
|
render({ _n }) {
|
|
439
439
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
440
440
|
}
|
|
441
|
-
optimizeNames(names,
|
|
441
|
+
optimizeNames(names, constants10) {
|
|
442
442
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
443
443
|
return;
|
|
444
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
444
|
+
this.rhs = optimizeExpr(this.rhs, names, constants10);
|
|
445
445
|
return this;
|
|
446
446
|
}
|
|
447
447
|
get names() {
|
|
@@ -502,8 +502,8 @@ var require_codegen = __commonJS({
|
|
|
502
502
|
optimizeNodes() {
|
|
503
503
|
return `${this.code}` ? this : void 0;
|
|
504
504
|
}
|
|
505
|
-
optimizeNames(names,
|
|
506
|
-
this.code = optimizeExpr(this.code, names,
|
|
505
|
+
optimizeNames(names, constants10) {
|
|
506
|
+
this.code = optimizeExpr(this.code, names, constants10);
|
|
507
507
|
return this;
|
|
508
508
|
}
|
|
509
509
|
get names() {
|
|
@@ -532,12 +532,12 @@ var require_codegen = __commonJS({
|
|
|
532
532
|
}
|
|
533
533
|
return nodes.length > 0 ? this : void 0;
|
|
534
534
|
}
|
|
535
|
-
optimizeNames(names,
|
|
535
|
+
optimizeNames(names, constants10) {
|
|
536
536
|
const { nodes } = this;
|
|
537
537
|
let i = nodes.length;
|
|
538
538
|
while (i--) {
|
|
539
539
|
const n = nodes[i];
|
|
540
|
-
if (n.optimizeNames(names,
|
|
540
|
+
if (n.optimizeNames(names, constants10))
|
|
541
541
|
continue;
|
|
542
542
|
subtractNames(names, n.names);
|
|
543
543
|
nodes.splice(i, 1);
|
|
@@ -590,12 +590,12 @@ var require_codegen = __commonJS({
|
|
|
590
590
|
return void 0;
|
|
591
591
|
return this;
|
|
592
592
|
}
|
|
593
|
-
optimizeNames(names,
|
|
593
|
+
optimizeNames(names, constants10) {
|
|
594
594
|
var _a;
|
|
595
|
-
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
596
|
-
if (!(super.optimizeNames(names,
|
|
595
|
+
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants10);
|
|
596
|
+
if (!(super.optimizeNames(names, constants10) || this.else))
|
|
597
597
|
return;
|
|
598
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
598
|
+
this.condition = optimizeExpr(this.condition, names, constants10);
|
|
599
599
|
return this;
|
|
600
600
|
}
|
|
601
601
|
get names() {
|
|
@@ -618,10 +618,10 @@ var require_codegen = __commonJS({
|
|
|
618
618
|
render(opts) {
|
|
619
619
|
return `for(${this.iteration})` + super.render(opts);
|
|
620
620
|
}
|
|
621
|
-
optimizeNames(names,
|
|
622
|
-
if (!super.optimizeNames(names,
|
|
621
|
+
optimizeNames(names, constants10) {
|
|
622
|
+
if (!super.optimizeNames(names, constants10))
|
|
623
623
|
return;
|
|
624
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
624
|
+
this.iteration = optimizeExpr(this.iteration, names, constants10);
|
|
625
625
|
return this;
|
|
626
626
|
}
|
|
627
627
|
get names() {
|
|
@@ -657,10 +657,10 @@ var require_codegen = __commonJS({
|
|
|
657
657
|
render(opts) {
|
|
658
658
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
659
659
|
}
|
|
660
|
-
optimizeNames(names,
|
|
661
|
-
if (!super.optimizeNames(names,
|
|
660
|
+
optimizeNames(names, constants10) {
|
|
661
|
+
if (!super.optimizeNames(names, constants10))
|
|
662
662
|
return;
|
|
663
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
663
|
+
this.iterable = optimizeExpr(this.iterable, names, constants10);
|
|
664
664
|
return this;
|
|
665
665
|
}
|
|
666
666
|
get names() {
|
|
@@ -702,11 +702,11 @@ var require_codegen = __commonJS({
|
|
|
702
702
|
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
|
|
703
703
|
return this;
|
|
704
704
|
}
|
|
705
|
-
optimizeNames(names,
|
|
705
|
+
optimizeNames(names, constants10) {
|
|
706
706
|
var _a, _b;
|
|
707
|
-
super.optimizeNames(names,
|
|
708
|
-
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
709
|
-
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names,
|
|
707
|
+
super.optimizeNames(names, constants10);
|
|
708
|
+
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants10);
|
|
709
|
+
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants10);
|
|
710
710
|
return this;
|
|
711
711
|
}
|
|
712
712
|
get names() {
|
|
@@ -1007,7 +1007,7 @@ var require_codegen = __commonJS({
|
|
|
1007
1007
|
function addExprNames(names, from) {
|
|
1008
1008
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
1009
1009
|
}
|
|
1010
|
-
function optimizeExpr(expr, names,
|
|
1010
|
+
function optimizeExpr(expr, names, constants10) {
|
|
1011
1011
|
if (expr instanceof code_1.Name)
|
|
1012
1012
|
return replaceName(expr);
|
|
1013
1013
|
if (!canOptimize(expr))
|
|
@@ -1022,14 +1022,14 @@ var require_codegen = __commonJS({
|
|
|
1022
1022
|
return items;
|
|
1023
1023
|
}, []));
|
|
1024
1024
|
function replaceName(n) {
|
|
1025
|
-
const c =
|
|
1025
|
+
const c = constants10[n.str];
|
|
1026
1026
|
if (c === void 0 || names[n.str] !== 1)
|
|
1027
1027
|
return n;
|
|
1028
1028
|
delete names[n.str];
|
|
1029
1029
|
return c;
|
|
1030
1030
|
}
|
|
1031
1031
|
function canOptimize(e) {
|
|
1032
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
1032
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants10[c.str] !== void 0);
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
1035
|
function subtractNames(names, from) {
|
|
@@ -7422,7 +7422,7 @@ var require_DOMException = __commonJS({
|
|
|
7422
7422
|
"INVALID_NODE_TYPE_ERR (24): the supplied node is invalid or has an invalid ancestor for this operation",
|
|
7423
7423
|
"DATA_CLONE_ERR (25): the object can not be cloned."
|
|
7424
7424
|
];
|
|
7425
|
-
var
|
|
7425
|
+
var constants10 = {
|
|
7426
7426
|
INDEX_SIZE_ERR,
|
|
7427
7427
|
DOMSTRING_SIZE_ERR: 2,
|
|
7428
7428
|
// historical
|
|
@@ -7461,8 +7461,8 @@ var require_DOMException = __commonJS({
|
|
|
7461
7461
|
this.name = names[code];
|
|
7462
7462
|
}
|
|
7463
7463
|
DOMException.prototype.__proto__ = Error.prototype;
|
|
7464
|
-
for (c in
|
|
7465
|
-
v = { value:
|
|
7464
|
+
for (c in constants10) {
|
|
7465
|
+
v = { value: constants10[c] };
|
|
7466
7466
|
Object.defineProperty(DOMException, c, v);
|
|
7467
7467
|
Object.defineProperty(DOMException.prototype, c, v);
|
|
7468
7468
|
}
|
|
@@ -24465,14 +24465,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
24465
24465
|
} else if (node.nodeType === 1) {
|
|
24466
24466
|
replacement = replacementForNode.call(self, node);
|
|
24467
24467
|
}
|
|
24468
|
-
return
|
|
24468
|
+
return join31(output, replacement);
|
|
24469
24469
|
}, "");
|
|
24470
24470
|
}
|
|
24471
24471
|
function postProcess(output) {
|
|
24472
24472
|
var self = this;
|
|
24473
24473
|
this.rules.forEach(function(rule) {
|
|
24474
24474
|
if (typeof rule.append === "function") {
|
|
24475
|
-
output =
|
|
24475
|
+
output = join31(output, rule.append(self.options));
|
|
24476
24476
|
}
|
|
24477
24477
|
});
|
|
24478
24478
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -24484,7 +24484,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
24484
24484
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
24485
24485
|
return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
|
|
24486
24486
|
}
|
|
24487
|
-
function
|
|
24487
|
+
function join31(output, replacement) {
|
|
24488
24488
|
var s1 = trimTrailingNewlines(output);
|
|
24489
24489
|
var s2 = trimLeadingNewlines(replacement);
|
|
24490
24490
|
var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
|
@@ -42435,7 +42435,7 @@ function readSmartFolderIds(read = readSmartFolders) {
|
|
|
42435
42435
|
}
|
|
42436
42436
|
function smartFolderDestinationError(folderPath) {
|
|
42437
42437
|
return new CodedError(
|
|
42438
|
-
`Refused: "${folderPath}" is a smart folder. Smart folders only gather notes by their rules and cannot hold notes or folders; choose an ordinary folder (list-folders). Nothing was changed`,
|
|
42438
|
+
`Refused: "${folderPath}" is a smart folder. Smart folders only gather notes by their rules and cannot hold notes or folders; choose an ordinary folder (list-folders marks smart folders smartFolder: true). Nothing was changed`,
|
|
42439
42439
|
{
|
|
42440
42440
|
code: "unsupported",
|
|
42441
42441
|
committed: false,
|
|
@@ -43881,6 +43881,9 @@ var AppleNotesManager = class {
|
|
|
43881
43881
|
/**
|
|
43882
43882
|
* Lists all folders in an account with full hierarchical paths.
|
|
43883
43883
|
*
|
|
43884
|
+
* Smart folders are included (AppleScript lists them like ordinary folders)
|
|
43885
|
+
* and carry `smartFolder: true` when the NoteStore database is readable.
|
|
43886
|
+
*
|
|
43884
43887
|
* Each folder's `name` field contains the full path (e.g., "Work/Clients/Omnia")
|
|
43885
43888
|
* so that duplicate folder names (e.g., multiple "Archive" folders) are
|
|
43886
43889
|
* distinguishable and can be used directly in other operations.
|
|
@@ -43939,11 +43942,13 @@ var AppleNotesManager = class {
|
|
|
43939
43942
|
}
|
|
43940
43943
|
return safeName;
|
|
43941
43944
|
};
|
|
43945
|
+
const smart = new Set(entries2.some((entry) => entry.id) ? this.smartFolderIds() : []);
|
|
43942
43946
|
return entries2.map((entry) => ({
|
|
43943
43947
|
id: entry.id,
|
|
43944
43948
|
name: buildPath(entry),
|
|
43945
43949
|
account: entry.account || targetAccount || "",
|
|
43946
|
-
shared: entry.shared
|
|
43950
|
+
shared: entry.shared,
|
|
43951
|
+
...smart.has(entry.id) ? { smartFolder: true } : {}
|
|
43947
43952
|
}));
|
|
43948
43953
|
}
|
|
43949
43954
|
/**
|
|
@@ -48265,6 +48270,7 @@ function comparableVisibleText(html) {
|
|
|
48265
48270
|
}
|
|
48266
48271
|
|
|
48267
48272
|
// src/services/backgroundNotes.ts
|
|
48273
|
+
var capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
48268
48274
|
var BACKGROUND_SHORTCUT = "Apple Notes MCP - Background Operations v5";
|
|
48269
48275
|
var backgroundShortcutName = () => process.env.APPLE_NOTES_MCP_BACKGROUND_SHORTCUT || BACKGROUND_SHORTCUT;
|
|
48270
48276
|
var backgroundStatus = () => nativeTagsStatus(backgroundShortcutName());
|
|
@@ -48375,10 +48381,14 @@ function validateAppendContent(content, format, options = {}) {
|
|
|
48375
48381
|
);
|
|
48376
48382
|
}
|
|
48377
48383
|
}
|
|
48384
|
+
var BRIDGE_REFUSED = "refused the request without running it: its Find Notes step did not find exactly one note with this exact title whose body contains the scope text. Notes search can lag behind a note created or edited moments ago, so wait a minute and retry, or pass a longer, more distinctive scopeText";
|
|
48378
48385
|
function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
48379
48386
|
if (!status.installed)
|
|
48380
|
-
throw
|
|
48381
|
-
|
|
48387
|
+
throw Object.assign(
|
|
48388
|
+
new Error(
|
|
48389
|
+
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
48390
|
+
),
|
|
48391
|
+
{ shortcut: status.shortcut, notInstalled: true }
|
|
48382
48392
|
);
|
|
48383
48393
|
const directory = mkdtempSync4(join17(tmpdir4(), "apple-notes-background-"));
|
|
48384
48394
|
try {
|
|
@@ -48401,16 +48411,31 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
48401
48411
|
}),
|
|
48402
48412
|
{ mode: 384 }
|
|
48403
48413
|
);
|
|
48414
|
+
let output;
|
|
48404
48415
|
try {
|
|
48405
|
-
execFileSync16(
|
|
48406
|
-
|
|
48407
|
-
|
|
48408
|
-
|
|
48409
|
-
|
|
48410
|
-
|
|
48416
|
+
output = execFileSync16(
|
|
48417
|
+
"/usr/bin/shortcuts",
|
|
48418
|
+
["run", status.identifier, "--input-path", file],
|
|
48419
|
+
{
|
|
48420
|
+
encoding: "utf8",
|
|
48421
|
+
timeout: callTimeoutMs() ?? 6e4,
|
|
48422
|
+
maxBuffer: 1024 * 1024,
|
|
48423
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
48424
|
+
}
|
|
48425
|
+
);
|
|
48411
48426
|
} catch (error2) {
|
|
48412
48427
|
throw Object.assign(error2, { shortcut: status.shortcut });
|
|
48413
48428
|
}
|
|
48429
|
+
if (/_REFUSED\b/.test(String(output ?? "")))
|
|
48430
|
+
throw Object.assign(
|
|
48431
|
+
new Error(
|
|
48432
|
+
status.shortcut === markdownShortcutName() ? "refused the request without running it" : BRIDGE_REFUSED
|
|
48433
|
+
),
|
|
48434
|
+
{
|
|
48435
|
+
shortcut: status.shortcut,
|
|
48436
|
+
refused: true
|
|
48437
|
+
}
|
|
48438
|
+
);
|
|
48414
48439
|
} finally {
|
|
48415
48440
|
rmSync4(directory, { recursive: true, force: true });
|
|
48416
48441
|
}
|
|
@@ -48480,6 +48505,7 @@ function assertPreserved(before, after, options = {}) {
|
|
|
48480
48505
|
function describeTransportFailure(error2) {
|
|
48481
48506
|
const detail = error2;
|
|
48482
48507
|
const named = detail?.shortcut ? `the "${detail.shortcut}" Shortcut` : "the background Shortcut";
|
|
48508
|
+
if (detail?.refused) return `${named} ${detail.message}`;
|
|
48483
48509
|
return detail?.code === "ETIMEDOUT" ? `Shortcuts timed out waiting for ${named}. ${shortcutConsentHint(detail.shortcut)}` : `${named} failed: ${String(detail?.stderr || detail?.message || "no output").trim().slice(0, 400)}`;
|
|
48484
48510
|
}
|
|
48485
48511
|
function mutateBackground(request, operation, data, verify, deps) {
|
|
@@ -48497,16 +48523,30 @@ function mutateBackground(request, operation, data, verify, deps) {
|
|
|
48497
48523
|
throw new Error("Note revision changed during preflight");
|
|
48498
48524
|
let transportUncertain = false;
|
|
48499
48525
|
let transportMessage = "";
|
|
48526
|
+
let transportError;
|
|
48500
48527
|
try {
|
|
48501
48528
|
deps.run({ ...data, operation, title: before.title, scopeText: request.scopeText });
|
|
48502
48529
|
} catch (error2) {
|
|
48503
48530
|
transportUncertain = true;
|
|
48531
|
+
transportError = error2 ?? {};
|
|
48504
48532
|
transportMessage = describeTransportFailure(error2);
|
|
48505
48533
|
}
|
|
48506
48534
|
const after = deps.read(request.id);
|
|
48507
48535
|
try {
|
|
48508
48536
|
verify(before, after);
|
|
48509
48537
|
} catch (error2) {
|
|
48538
|
+
const unchanged = after.hash === before.hash && after.pinned === before.pinned;
|
|
48539
|
+
if (transportError?.notInstalled && unchanged)
|
|
48540
|
+
throw new CodedError(
|
|
48541
|
+
`${transportError.message}. The Shortcut never ran, so nothing was written; run apple-notes-mcp setup, then retry.`,
|
|
48542
|
+
{ code: "shortcut_not_installed", committed: false, indeterminate: false }
|
|
48543
|
+
);
|
|
48544
|
+
const exitedWithError = typeof transportError?.status === "number" && transportError.status !== 0 && !transportError.signal && transportError.code !== "ETIMEDOUT";
|
|
48545
|
+
if (transportError && (transportError.refused || exitedWithError) && unchanged)
|
|
48546
|
+
throw new CodedError(
|
|
48547
|
+
`${capitalize(transportMessage)}. The note is unchanged, so nothing was written; it is safe to retry once the cause is fixed.`,
|
|
48548
|
+
{ code: "operation_failed", committed: false, indeterminate: false }
|
|
48549
|
+
);
|
|
48510
48550
|
throw new Error(
|
|
48511
48551
|
`Operation outcome uncertain; read exact note before any retry: ${error2 instanceof Error ? error2.message : "readback failed"}${transportMessage ? "; " + transportMessage : ""}`
|
|
48512
48552
|
);
|
|
@@ -51666,16 +51706,16 @@ var NOTE_PLACEHOLDERS = [
|
|
|
51666
51706
|
"exportStem"
|
|
51667
51707
|
];
|
|
51668
51708
|
var PLACEHOLDER_MODIFIERS = ["raw", "yaml"];
|
|
51669
|
-
var wrap2 = (before, after = "",
|
|
51709
|
+
var wrap2 = (before, after = "", join31) => ({
|
|
51670
51710
|
mode: "wrap",
|
|
51671
51711
|
before,
|
|
51672
51712
|
after,
|
|
51673
|
-
...
|
|
51713
|
+
...join31 ? { join: join31 } : {}
|
|
51674
51714
|
});
|
|
51675
|
-
var pattern = (value,
|
|
51715
|
+
var pattern = (value, join31) => ({
|
|
51676
51716
|
mode: "pattern",
|
|
51677
51717
|
value,
|
|
51678
|
-
...
|
|
51718
|
+
...join31 ? { join: join31 } : {}
|
|
51679
51719
|
});
|
|
51680
51720
|
var STANDARD = {
|
|
51681
51721
|
schemaVersion: 1,
|
|
@@ -53056,6 +53096,22 @@ function exportWithTemplate(request, deps, chosen, notes, skipped, { output, ass
|
|
|
53056
53096
|
|
|
53057
53097
|
// src/tools/directOperations.ts
|
|
53058
53098
|
import { createHash as createHash3 } from "node:crypto";
|
|
53099
|
+
import {
|
|
53100
|
+
closeSync as closeSync7,
|
|
53101
|
+
constants as constants7,
|
|
53102
|
+
fstatSync as fstatSync7,
|
|
53103
|
+
mkdtempSync as mkdtempSync6,
|
|
53104
|
+
openSync as openSync7,
|
|
53105
|
+
readFileSync as readFileSync4,
|
|
53106
|
+
readSync as readSync5,
|
|
53107
|
+
rmSync as rmSync6,
|
|
53108
|
+
writeFileSync as writeFileSync5
|
|
53109
|
+
} from "node:fs";
|
|
53110
|
+
import { tmpdir as tmpdir6 } from "node:os";
|
|
53111
|
+
import { basename as basename6, extname as extname7, isAbsolute as isAbsolute4, join as join24 } from "node:path";
|
|
53112
|
+
|
|
53113
|
+
// src/utils/pasteboardFreeze.ts
|
|
53114
|
+
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
53059
53115
|
import {
|
|
53060
53116
|
closeSync as closeSync6,
|
|
53061
53117
|
constants as constants6,
|
|
@@ -53064,10 +53120,224 @@ import {
|
|
|
53064
53120
|
openSync as openSync6,
|
|
53065
53121
|
readFileSync as readFileSync3,
|
|
53066
53122
|
rmSync as rmSync5,
|
|
53123
|
+
statSync as statSync4,
|
|
53067
53124
|
writeFileSync as writeFileSync4
|
|
53068
53125
|
} from "node:fs";
|
|
53069
53126
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
53070
|
-
import { basename as basename5, extname as extname6,
|
|
53127
|
+
import { basename as basename5, extname as extname6, join as join23 } from "node:path";
|
|
53128
|
+
var PASTEBOARD_NAME_ENV = "APPLE_NOTES_MCP_PASTEBOARD_NAME";
|
|
53129
|
+
var MAX_PASTEBOARD_BYTES = 64 * 1024 * 1024;
|
|
53130
|
+
var PASTEBOARD_DATA_TYPES = [
|
|
53131
|
+
{ type: "public.png", ext: "png", label: "image" },
|
|
53132
|
+
{ type: "public.jpeg", ext: "jpg", label: "image" },
|
|
53133
|
+
{ type: "public.heic", ext: "heic", label: "image" },
|
|
53134
|
+
{ type: "com.compuserve.gif", ext: "gif", label: "image" },
|
|
53135
|
+
{ type: "public.tiff", ext: "tiff", label: "image" },
|
|
53136
|
+
{ type: "com.adobe.pdf", ext: "pdf", label: "document" }
|
|
53137
|
+
];
|
|
53138
|
+
var PASTEBOARD_ACCESS_BEHAVIORS = {
|
|
53139
|
+
0: "default",
|
|
53140
|
+
1: "ask",
|
|
53141
|
+
2: "alwaysAllow",
|
|
53142
|
+
3: "alwaysDeny"
|
|
53143
|
+
};
|
|
53144
|
+
var PASTEBOARD_FREEZE_JXA = `
|
|
53145
|
+
ObjC.import("AppKit");
|
|
53146
|
+
function run(argv) {
|
|
53147
|
+
var dir = argv[0], name = argv[1], limit = Number(argv[2]), prefs = JSON.parse(argv[3]);
|
|
53148
|
+
var allowAlert = argv[4] === "1";
|
|
53149
|
+
var pb = name ? $.NSPasteboard.pasteboardWithName(name) : $.NSPasteboard.generalPasteboard;
|
|
53150
|
+
if (!pb || pb.isNil()) return JSON.stringify({ status: "error", code: "pasteboard_unavailable" });
|
|
53151
|
+
if (!name && pb.respondsToSelector("accessBehavior")) {
|
|
53152
|
+
var behavior = Number(pb.accessBehavior);
|
|
53153
|
+
if (behavior === 3 || (behavior !== 2 && !allowAlert))
|
|
53154
|
+
return JSON.stringify({ status: "error", code: "pasteboard_access_denied", accessBehavior: behavior });
|
|
53155
|
+
}
|
|
53156
|
+
var before = pb.changeCount;
|
|
53157
|
+
var types = ObjC.deepUnwrap(pb.types) || [];
|
|
53158
|
+
if (types.length === 0) return JSON.stringify({ status: "error", code: "pasteboard_empty" });
|
|
53159
|
+
var result = null;
|
|
53160
|
+
var items = pb.pasteboardItems;
|
|
53161
|
+
var itemCount = items && !items.isNil() ? Number(items.count) : 0;
|
|
53162
|
+
var fileUrls = [];
|
|
53163
|
+
for (var k = 0; k < itemCount; k++) {
|
|
53164
|
+
var item = items.objectAtIndex(k);
|
|
53165
|
+
var itemTypes = ObjC.deepUnwrap(item.types) || [];
|
|
53166
|
+
if (itemTypes.indexOf("public.file-url") < 0) continue;
|
|
53167
|
+
var value = ObjC.unwrap(item.stringForType("public.file-url"));
|
|
53168
|
+
if (value) fileUrls.push(value);
|
|
53169
|
+
}
|
|
53170
|
+
if (fileUrls.length > 1)
|
|
53171
|
+
return JSON.stringify({ status: "error", code: "multiple_files", count: fileUrls.length });
|
|
53172
|
+
if (fileUrls.length === 1) {
|
|
53173
|
+
var url = $.NSURL.URLWithString(fileUrls[0]);
|
|
53174
|
+
if (url && !url.isNil() && url.isFileURL) {
|
|
53175
|
+
result = { status: "ok", kind: "file", type: "public.file-url", path: ObjC.unwrap(url.path) };
|
|
53176
|
+
}
|
|
53177
|
+
}
|
|
53178
|
+
if (!result) {
|
|
53179
|
+
for (var i = 0; i < prefs.length && !result; i++) {
|
|
53180
|
+
if (types.indexOf(prefs[i][0]) < 0) continue;
|
|
53181
|
+
var data = pb.dataForType(prefs[i][0]);
|
|
53182
|
+
if (!data || data.isNil() || data.length === 0) continue;
|
|
53183
|
+
if (data.length > limit) return JSON.stringify({ status: "error", code: "too_large", bytes: data.length });
|
|
53184
|
+
var path = dir + "/pasteboard." + prefs[i][1];
|
|
53185
|
+
if (!data.writeToFileAtomically(path, true))
|
|
53186
|
+
return JSON.stringify({ status: "error", code: "write_failed" });
|
|
53187
|
+
result = { status: "ok", kind: "data", type: prefs[i][0], path: path, bytes: data.length };
|
|
53188
|
+
}
|
|
53189
|
+
}
|
|
53190
|
+
if (pb.changeCount !== before) return JSON.stringify({ status: "error", code: "pasteboard_changed" });
|
|
53191
|
+
return JSON.stringify(result || { status: "error", code: "unsupported_content", types: types.slice(0, 20) });
|
|
53192
|
+
}
|
|
53193
|
+
`;
|
|
53194
|
+
var ENVELOPE_CODES = {
|
|
53195
|
+
pasteboard_access_denied: "permission_denied",
|
|
53196
|
+
pasteboard_unavailable: "operation_failed",
|
|
53197
|
+
pasteboard_timeout: "operation_failed",
|
|
53198
|
+
pasteboard_empty: "validation_error",
|
|
53199
|
+
pasteboard_changed: "operation_failed",
|
|
53200
|
+
unsupported_content: "validation_error",
|
|
53201
|
+
multiple_files: "validation_error",
|
|
53202
|
+
too_large: "validation_error",
|
|
53203
|
+
write_failed: "operation_failed",
|
|
53204
|
+
file_unreadable: "validation_error"
|
|
53205
|
+
};
|
|
53206
|
+
var PasteboardError = class extends CodedError {
|
|
53207
|
+
constructor(code, message, details = {}) {
|
|
53208
|
+
super(message, {
|
|
53209
|
+
...details,
|
|
53210
|
+
code: ENVELOPE_CODES[code],
|
|
53211
|
+
pasteboardCode: code,
|
|
53212
|
+
committed: false
|
|
53213
|
+
});
|
|
53214
|
+
this.code = code;
|
|
53215
|
+
this.name = "PasteboardError";
|
|
53216
|
+
}
|
|
53217
|
+
code;
|
|
53218
|
+
};
|
|
53219
|
+
var SUPPORTED = "an image (PNG, JPEG, HEIC, GIF, TIFF), a PDF, or one copied file";
|
|
53220
|
+
var MESSAGES = {
|
|
53221
|
+
pasteboard_access_denied: "Reading the pasteboard now would make macOS ask whether to allow the paste, so nothing was read. Call again with allowPasteAlert: true to let macOS show its paste alert, or allow pasting for the app that runs this server in System Settings, where macOS lists it after its first paste alert.",
|
|
53222
|
+
pasteboard_unavailable: "The pasteboard is not reachable from this process. The MCP host must run in your logged-in GUI session (not over SSH or as a background daemon).",
|
|
53223
|
+
pasteboard_timeout: "Reading the pasteboard timed out; nothing was attached. If macOS showed a paste alert, answer it and try again.",
|
|
53224
|
+
pasteboard_empty: "The pasteboard is empty. Copy an image or a file first.",
|
|
53225
|
+
pasteboard_changed: "The pasteboard changed while it was being read. Try again.",
|
|
53226
|
+
unsupported_content: `The pasteboard holds no supported content: copy ${SUPPORTED}. Text belongs in append-to-note, not in an attachment.`,
|
|
53227
|
+
multiple_files: "The pasteboard holds more than one copied file. Copy exactly one file, or attach each file with add-attachment.",
|
|
53228
|
+
too_large: "The pasteboard contents exceed the 64 MiB attachment limit.",
|
|
53229
|
+
write_failed: "Could not write the pasteboard contents to a temporary file.",
|
|
53230
|
+
file_unreadable: "The copied file could not be read as a regular file of at most 64 MiB."
|
|
53231
|
+
};
|
|
53232
|
+
function replyError(code, reply) {
|
|
53233
|
+
if (code === "pasteboard_access_denied") {
|
|
53234
|
+
const raw = typeof reply.accessBehavior === "number" ? reply.accessBehavior : void 0;
|
|
53235
|
+
const accessBehavior = raw !== void 0 && PASTEBOARD_ACCESS_BEHAVIORS[raw] || "unknown";
|
|
53236
|
+
const message = accessBehavior === "alwaysDeny" ? "macOS is set to deny pasteboard access to the app that runs this server, so nothing was read. Change it to allow in System Settings, then try again." : MESSAGES.pasteboard_access_denied;
|
|
53237
|
+
return new PasteboardError(code, message, { accessBehavior });
|
|
53238
|
+
}
|
|
53239
|
+
if (code === "multiple_files") {
|
|
53240
|
+
const count = typeof reply.count === "number" ? reply.count : void 0;
|
|
53241
|
+
return new PasteboardError(
|
|
53242
|
+
code,
|
|
53243
|
+
count ? `The pasteboard holds ${count} copied files. Copy exactly one file, or attach each file with add-attachment.` : MESSAGES[code],
|
|
53244
|
+
count ? { count } : {}
|
|
53245
|
+
);
|
|
53246
|
+
}
|
|
53247
|
+
if (code === "unsupported_content" && Array.isArray(reply.types)) {
|
|
53248
|
+
const types = reply.types.filter((t) => typeof t === "string").slice(0, 20);
|
|
53249
|
+
if (types.length)
|
|
53250
|
+
return new PasteboardError(
|
|
53251
|
+
code,
|
|
53252
|
+
`${MESSAGES[code]} Pasteboard types found: ${types.join(", ")}.`,
|
|
53253
|
+
{ types }
|
|
53254
|
+
);
|
|
53255
|
+
}
|
|
53256
|
+
if (code === "too_large" && typeof reply.bytes === "number")
|
|
53257
|
+
return new PasteboardError(code, MESSAGES[code], { bytes: reply.bytes });
|
|
53258
|
+
return new PasteboardError(code, MESSAGES[code]);
|
|
53259
|
+
}
|
|
53260
|
+
function pasteboardTimeoutMs() {
|
|
53261
|
+
return callTimeoutMs() ?? envPositiveNumber("APPLE_NOTES_MCP_TIMEOUT_MS") ?? 3e4;
|
|
53262
|
+
}
|
|
53263
|
+
function defaultRunJxa(args) {
|
|
53264
|
+
return execFileSync19("osascript", ["-l", "JavaScript", "-e", PASTEBOARD_FREEZE_JXA, ...args], {
|
|
53265
|
+
encoding: "utf8",
|
|
53266
|
+
timeout: pasteboardTimeoutMs(),
|
|
53267
|
+
maxBuffer: 1024 * 1024,
|
|
53268
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
53269
|
+
});
|
|
53270
|
+
}
|
|
53271
|
+
function pasteboardFilename(requested, frozenFilename) {
|
|
53272
|
+
if (requested === void 0) return void 0;
|
|
53273
|
+
const ext = extname6(frozenFilename);
|
|
53274
|
+
return ext && !extname6(requested) ? requested + ext : requested;
|
|
53275
|
+
}
|
|
53276
|
+
function readRegularFile(path10) {
|
|
53277
|
+
let descriptor;
|
|
53278
|
+
try {
|
|
53279
|
+
descriptor = openSync6(path10, constants6.O_RDONLY | constants6.O_NOFOLLOW);
|
|
53280
|
+
} catch {
|
|
53281
|
+
throw new PasteboardError("file_unreadable", MESSAGES.file_unreadable);
|
|
53282
|
+
}
|
|
53283
|
+
try {
|
|
53284
|
+
const stat = fstatSync6(descriptor);
|
|
53285
|
+
if (!stat.isFile() || stat.size === 0 || stat.size > MAX_PASTEBOARD_BYTES)
|
|
53286
|
+
throw new PasteboardError("file_unreadable", MESSAGES.file_unreadable);
|
|
53287
|
+
return readFileSync3(descriptor);
|
|
53288
|
+
} finally {
|
|
53289
|
+
closeSync6(descriptor);
|
|
53290
|
+
}
|
|
53291
|
+
}
|
|
53292
|
+
function freezePasteboard(options = {}) {
|
|
53293
|
+
const directory = mkdtempSync5(join23(tmpdir5(), "notes-pasteboard-"));
|
|
53294
|
+
const cleanup = () => rmSync5(directory, { recursive: true, force: true });
|
|
53295
|
+
try {
|
|
53296
|
+
const prefs = JSON.stringify(PASTEBOARD_DATA_TYPES.map((t) => [t.type, t.ext]));
|
|
53297
|
+
let raw;
|
|
53298
|
+
try {
|
|
53299
|
+
raw = (options.runJxa ?? defaultRunJxa)([
|
|
53300
|
+
directory,
|
|
53301
|
+
options.pasteboardName ?? "",
|
|
53302
|
+
String(MAX_PASTEBOARD_BYTES),
|
|
53303
|
+
prefs,
|
|
53304
|
+
options.allowPasteAlert ? "1" : "0"
|
|
53305
|
+
]);
|
|
53306
|
+
} catch (error2) {
|
|
53307
|
+
throw error2?.code === "ETIMEDOUT" ? new PasteboardError("pasteboard_timeout", MESSAGES.pasteboard_timeout) : new PasteboardError("pasteboard_unavailable", MESSAGES.pasteboard_unavailable);
|
|
53308
|
+
}
|
|
53309
|
+
let reply;
|
|
53310
|
+
try {
|
|
53311
|
+
reply = JSON.parse(raw.trim());
|
|
53312
|
+
} catch {
|
|
53313
|
+
throw new PasteboardError("pasteboard_unavailable", MESSAGES.pasteboard_unavailable);
|
|
53314
|
+
}
|
|
53315
|
+
if (reply.status !== "ok" || !reply.path || !reply.type) {
|
|
53316
|
+
const code = reply.code && reply.code in MESSAGES ? reply.code : "pasteboard_unavailable";
|
|
53317
|
+
throw replyError(code, reply);
|
|
53318
|
+
}
|
|
53319
|
+
if (reply.kind === "file") {
|
|
53320
|
+
const bytes = readRegularFile(reply.path);
|
|
53321
|
+
const filename2 = basename5(reply.path);
|
|
53322
|
+
const path11 = join23(directory, filename2);
|
|
53323
|
+
writeFileSync4(path11, bytes, { mode: 384 });
|
|
53324
|
+
return { kind: "file", type: reply.type, path: path11, filename: filename2, bytes: bytes.length, cleanup };
|
|
53325
|
+
}
|
|
53326
|
+
if (join23(directory, basename5(reply.path)) !== reply.path)
|
|
53327
|
+
throw new PasteboardError("write_failed", MESSAGES.write_failed);
|
|
53328
|
+
const label = PASTEBOARD_DATA_TYPES.find((t) => t.type === reply.type)?.label ?? "item";
|
|
53329
|
+
const filename = `Pasted ${label}${extname6(reply.path)}`;
|
|
53330
|
+
const path10 = join23(directory, filename);
|
|
53331
|
+
writeFileSync4(path10, readFileSync3(reply.path), { mode: 384 });
|
|
53332
|
+
rmSync5(reply.path, { force: true });
|
|
53333
|
+
return { kind: "data", type: reply.type, path: path10, filename, bytes: statSync4(path10).size, cleanup };
|
|
53334
|
+
} catch (error2) {
|
|
53335
|
+
cleanup();
|
|
53336
|
+
throw error2;
|
|
53337
|
+
}
|
|
53338
|
+
}
|
|
53339
|
+
|
|
53340
|
+
// src/tools/directOperations.ts
|
|
53071
53341
|
var noteId = exactIdInput(
|
|
53072
53342
|
"ICNote",
|
|
53073
53343
|
/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
|
|
@@ -53112,17 +53382,41 @@ function assertExistingContentPreserved(before, after) {
|
|
|
53112
53382
|
}
|
|
53113
53383
|
function localAttachment(path10) {
|
|
53114
53384
|
if (!isAbsolute4(path10)) throw new Error("An absolute local file path is required");
|
|
53115
|
-
const descriptor =
|
|
53385
|
+
const descriptor = openSync7(path10, constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
53116
53386
|
try {
|
|
53117
|
-
const stat =
|
|
53118
|
-
if (!stat.isFile() || stat.size === 0 || stat.size >
|
|
53387
|
+
const stat = fstatSync7(descriptor);
|
|
53388
|
+
if (!stat.isFile() || stat.size === 0 || stat.size > MAX_ADD_ATTACHMENT_BYTES)
|
|
53119
53389
|
throw new Error("Attachment must be a nonempty regular file of at most 64 MiB");
|
|
53120
|
-
const bytes =
|
|
53390
|
+
const bytes = readFileSync4(descriptor);
|
|
53121
53391
|
if (bytes.length !== stat.size)
|
|
53122
53392
|
throw new Error("Attachment changed while it was being read; try again");
|
|
53123
53393
|
return bytes;
|
|
53124
53394
|
} finally {
|
|
53125
|
-
|
|
53395
|
+
closeSync7(descriptor);
|
|
53396
|
+
}
|
|
53397
|
+
}
|
|
53398
|
+
var MAX_ADD_ATTACHMENT_BYTES = 64 * 1024 * 1024;
|
|
53399
|
+
function fileMatches(path10, size, expected) {
|
|
53400
|
+
let descriptor;
|
|
53401
|
+
try {
|
|
53402
|
+
descriptor = openSync7(path10, constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
53403
|
+
const stat = fstatSync7(descriptor);
|
|
53404
|
+
if (!stat.isFile() || stat.size !== size) return false;
|
|
53405
|
+
const hash = createHash3("sha256");
|
|
53406
|
+
const chunk = Buffer.allocUnsafe(1024 * 1024);
|
|
53407
|
+
let total = 0;
|
|
53408
|
+
for (; ; ) {
|
|
53409
|
+
const read = readSync5(descriptor, chunk, 0, chunk.length, null);
|
|
53410
|
+
if (read === 0) break;
|
|
53411
|
+
total += read;
|
|
53412
|
+
if (total > size) return false;
|
|
53413
|
+
hash.update(chunk.subarray(0, read));
|
|
53414
|
+
}
|
|
53415
|
+
return total === size && hash.digest("hex") === expected;
|
|
53416
|
+
} catch {
|
|
53417
|
+
return false;
|
|
53418
|
+
} finally {
|
|
53419
|
+
if (descriptor !== void 0) closeSync7(descriptor);
|
|
53126
53420
|
}
|
|
53127
53421
|
}
|
|
53128
53422
|
function registerDirectOperations(server2, manager) {
|
|
@@ -53235,19 +53529,67 @@ function registerDirectOperations(server2, manager) {
|
|
|
53235
53529
|
}
|
|
53236
53530
|
}
|
|
53237
53531
|
);
|
|
53532
|
+
tool(
|
|
53533
|
+
"add-attachment-from-pasteboard",
|
|
53534
|
+
"Use when: the user copied an image, a PDF, or a file (screenshot, Copy Image, Finder Copy) and wants it attached to an exact note.\nReturns: the add-attachment result (attachment id, bytes, name, content hash) plus source: what was taken from the pasteboard (kind, type, default filename).\nDo not use when: the pasteboard holds text (use append-to-note), several copied files (refused; use add-attachment per file), or you have a file path (use add-attachment).\nSafety: checks the note and revision before reading the pasteboard; reads nothing when macOS would show its paste alert unless allowPasteAlert is true (error pasteboardCode pasteboard_access_denied); reads the pasteboard once and freezes its bytes into a private temporary file before attaching, never writes to the pasteboard, then runs add-attachment's checks: fresh rich revision, at most 64 MiB, no insertion retry, existing content and exact bytes verified. Needs the MCP host to run in the logged-in GUI session.",
|
|
53535
|
+
{
|
|
53536
|
+
id: noteId,
|
|
53537
|
+
expectedContentHash: revision,
|
|
53538
|
+
filename: attachmentInput.filename.describe(
|
|
53539
|
+
`Name the attachment gets in Notes (default: the copied file's name, or "Pasted image.png" / "Pasted document.pdf"). Without an extension, the pasted type's extension is added; with one, it must match the pasted type. Same rules as add-attachment otherwise.`
|
|
53540
|
+
),
|
|
53541
|
+
allowPasteAlert: external_exports.boolean().optional().describe(
|
|
53542
|
+
"Read the pasteboard even if macOS will show its paste alert (macOS 15.4+ paste privacy). Default false: when pasting is not already always allowed, the tool reads nothing and returns pasteboardCode pasteboard_access_denied. Set true only after the user agrees to answer the alert. Never overrides a Deny setting."
|
|
53543
|
+
)
|
|
53544
|
+
},
|
|
53545
|
+
({ id: id2, expectedContentHash, filename, allowPasteAlert }) => {
|
|
53546
|
+
if (filename !== void 0) checkAttachmentFilename(filename);
|
|
53547
|
+
const before = readSnapshot(manager, id2);
|
|
53548
|
+
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
53549
|
+
const frozen = freezePasteboard({
|
|
53550
|
+
pasteboardName: process.env[PASTEBOARD_NAME_ENV]?.trim() || void 0,
|
|
53551
|
+
allowPasteAlert: allowPasteAlert === true
|
|
53552
|
+
});
|
|
53553
|
+
try {
|
|
53554
|
+
const source = {
|
|
53555
|
+
kind: frozen.kind,
|
|
53556
|
+
type: frozen.type,
|
|
53557
|
+
filename: frozen.filename
|
|
53558
|
+
};
|
|
53559
|
+
return {
|
|
53560
|
+
...attachFile(
|
|
53561
|
+
manager,
|
|
53562
|
+
{
|
|
53563
|
+
id: id2,
|
|
53564
|
+
expectedContentHash,
|
|
53565
|
+
path: frozen.path,
|
|
53566
|
+
filename: pasteboardFilename(filename, frozen.filename)
|
|
53567
|
+
},
|
|
53568
|
+
before
|
|
53569
|
+
),
|
|
53570
|
+
source
|
|
53571
|
+
};
|
|
53572
|
+
} finally {
|
|
53573
|
+
frozen.cleanup();
|
|
53574
|
+
}
|
|
53575
|
+
}
|
|
53576
|
+
);
|
|
53238
53577
|
}
|
|
53239
|
-
function
|
|
53240
|
-
const source = basename5(path10);
|
|
53241
|
-
if (filename === void 0) return source;
|
|
53578
|
+
function checkAttachmentFilename(filename) {
|
|
53242
53579
|
if (Buffer.byteLength(filename, "utf8") > 255)
|
|
53243
53580
|
throw new Error("filename must be at most 255 bytes");
|
|
53244
53581
|
if (filename !== filename.trim() || filename === "" || filename.startsWith(".") || /[/:\\\p{Cc}]/u.test(filename))
|
|
53245
53582
|
throw new Error(
|
|
53246
53583
|
"filename must be one path component with no slash, colon, backslash, control character, leading dot, or surrounding spaces"
|
|
53247
53584
|
);
|
|
53248
|
-
|
|
53585
|
+
}
|
|
53586
|
+
function attachmentName(path10, filename) {
|
|
53587
|
+
const source = basename6(path10);
|
|
53588
|
+
if (filename === void 0) return source;
|
|
53589
|
+
checkAttachmentFilename(filename);
|
|
53590
|
+
if (extname7(filename).toLowerCase() !== extname7(source).toLowerCase())
|
|
53249
53591
|
throw new Error(
|
|
53250
|
-
`filename must keep the source file's extension (${
|
|
53592
|
+
`filename must keep the source file's extension (${extname7(source) || "none"})`
|
|
53251
53593
|
);
|
|
53252
53594
|
return filename;
|
|
53253
53595
|
}
|
|
@@ -53283,36 +53625,25 @@ function storedInsertion(manager, id2, before, bytes, returnedId) {
|
|
|
53283
53625
|
if (returnedId && /\/ICAttachment\/p\d+$/.test(returnedId) && returnedId !== attachmentId)
|
|
53284
53626
|
throw new Error(UNCERTAIN);
|
|
53285
53627
|
const expected = sha256(bytes);
|
|
53286
|
-
const matches = row.assetPaths.some((path10) =>
|
|
53287
|
-
let descriptor;
|
|
53288
|
-
try {
|
|
53289
|
-
descriptor = openSync6(path10, constants6.O_RDONLY | constants6.O_NOFOLLOW);
|
|
53290
|
-
const stat = fstatSync6(descriptor);
|
|
53291
|
-
return stat.isFile() && stat.size === bytes.length && sha256(readFileSync3(descriptor)) === expected;
|
|
53292
|
-
} catch {
|
|
53293
|
-
return false;
|
|
53294
|
-
} finally {
|
|
53295
|
-
if (descriptor !== void 0) closeSync6(descriptor);
|
|
53296
|
-
}
|
|
53297
|
-
});
|
|
53628
|
+
const matches = row.assetPaths.some((path10) => fileMatches(path10, bytes.length, expected));
|
|
53298
53629
|
if (!matches)
|
|
53299
53630
|
throw new Error(
|
|
53300
53631
|
`Notes' database shows new attachment ${attachmentId} on this note, but its file bytes could not be verified; read the exact note and do not attach the file again`
|
|
53301
53632
|
);
|
|
53302
53633
|
return { attachmentId, name: row.filename };
|
|
53303
53634
|
}
|
|
53304
|
-
function attachFile(manager, args) {
|
|
53635
|
+
function attachFile(manager, args, checked) {
|
|
53305
53636
|
const { id: id2, expectedContentHash, path: path10 } = args;
|
|
53306
53637
|
const name = attachmentName(path10, args.filename);
|
|
53307
|
-
const before = readSnapshot(manager, id2);
|
|
53638
|
+
const before = checked ?? readSnapshot(manager, id2);
|
|
53308
53639
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
53309
53640
|
const bytes = localAttachment(path10);
|
|
53310
53641
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
53311
53642
|
const beforeStored = storedAttachmentIds(manager, id2);
|
|
53312
|
-
const directory =
|
|
53313
|
-
const temporaryFile =
|
|
53643
|
+
const directory = mkdtempSync6(join24(tmpdir6(), "notes-attachment-add-"));
|
|
53644
|
+
const temporaryFile = join24(directory, name);
|
|
53314
53645
|
try {
|
|
53315
|
-
|
|
53646
|
+
writeFileSync5(temporaryFile, bytes, { mode: 384 });
|
|
53316
53647
|
if (readSnapshot(manager, id2).hash !== before.hash) throw new Error("Note revision changed");
|
|
53317
53648
|
let returnedId;
|
|
53318
53649
|
let transportUncertain = false;
|
|
@@ -53338,9 +53669,9 @@ function attachFile(manager, args) {
|
|
|
53338
53669
|
if (inserted.length === 1 && !(persistentReturnedId && returnedId !== inserted[0].id)) {
|
|
53339
53670
|
attachmentId = inserted[0].id;
|
|
53340
53671
|
reportedName = inserted[0].name;
|
|
53341
|
-
const
|
|
53342
|
-
const
|
|
53343
|
-
if (!
|
|
53672
|
+
const verifyPath = join24(directory, "verify", "attachment.bin");
|
|
53673
|
+
const saved = manager.saveAttachmentById(id2, attachmentId, verifyPath);
|
|
53674
|
+
if (!saved.success || !fileMatches(saved.savedPath ?? verifyPath, bytes.length, sha256(bytes)))
|
|
53344
53675
|
throw new Error("Attachment bytes were not verified; read the exact note before retrying");
|
|
53345
53676
|
} else {
|
|
53346
53677
|
const stored = inserted.length === 0 && beforeStored ? storedInsertion(manager, id2, beforeStored, bytes, returnedId) : null;
|
|
@@ -53372,7 +53703,7 @@ function attachFile(manager, args) {
|
|
|
53372
53703
|
} : {}
|
|
53373
53704
|
};
|
|
53374
53705
|
} finally {
|
|
53375
|
-
|
|
53706
|
+
rmSync6(directory, { recursive: true, force: true });
|
|
53376
53707
|
}
|
|
53377
53708
|
}
|
|
53378
53709
|
|
|
@@ -53380,7 +53711,7 @@ function attachFile(manager, args) {
|
|
|
53380
53711
|
import { createHash as createHash4 } from "node:crypto";
|
|
53381
53712
|
|
|
53382
53713
|
// src/utils/folderStore.ts
|
|
53383
|
-
import { execFileSync as
|
|
53714
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
53384
53715
|
import * as os9 from "os";
|
|
53385
53716
|
import * as path9 from "path";
|
|
53386
53717
|
var NOTE_STORE_PATH = path9.join(
|
|
@@ -53444,7 +53775,7 @@ function runSqlite7(dbPath2, sql, pk) {
|
|
|
53444
53775
|
const args = ["-readonly"];
|
|
53445
53776
|
if (pk !== void 0) args.push("-cmd", `.parameter set @pk ${pk}`);
|
|
53446
53777
|
args.push(dbPath2, sql);
|
|
53447
|
-
return
|
|
53778
|
+
return execFileSync20("sqlite3", args, {
|
|
53448
53779
|
encoding: "utf8",
|
|
53449
53780
|
timeout: 5e3,
|
|
53450
53781
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -53702,21 +54033,21 @@ function registerFolderDelete(server2, manager, deps = defaultDeps) {
|
|
|
53702
54033
|
// src/services/templateStore.ts
|
|
53703
54034
|
import { randomBytes } from "node:crypto";
|
|
53704
54035
|
import {
|
|
53705
|
-
closeSync as
|
|
53706
|
-
constants as
|
|
53707
|
-
fstatSync as
|
|
54036
|
+
closeSync as closeSync8,
|
|
54037
|
+
constants as constants8,
|
|
54038
|
+
fstatSync as fstatSync8,
|
|
53708
54039
|
linkSync,
|
|
53709
54040
|
lstatSync as lstatSync5,
|
|
53710
54041
|
mkdirSync as mkdirSync7,
|
|
53711
|
-
openSync as
|
|
54042
|
+
openSync as openSync8,
|
|
53712
54043
|
readdirSync as readdirSync4,
|
|
53713
|
-
readSync as
|
|
54044
|
+
readSync as readSync6,
|
|
53714
54045
|
renameSync,
|
|
53715
54046
|
unlinkSync as unlinkSync3,
|
|
53716
54047
|
writeSync as writeSync5
|
|
53717
54048
|
} from "node:fs";
|
|
53718
54049
|
import { homedir as homedir19 } from "node:os";
|
|
53719
|
-
import { isAbsolute as isAbsolute5, join as
|
|
54050
|
+
import { isAbsolute as isAbsolute5, join as join26, resolve as resolve5 } from "node:path";
|
|
53720
54051
|
var TEMPLATE_SLUG = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
53721
54052
|
var TemplateStoreError = class extends Error {
|
|
53722
54053
|
constructor(code, message) {
|
|
@@ -53736,7 +54067,7 @@ function templateDir(env = process.env) {
|
|
|
53736
54067
|
);
|
|
53737
54068
|
return resolve5(override);
|
|
53738
54069
|
}
|
|
53739
|
-
return
|
|
54070
|
+
return join26(homedir19(), "Library/Application Support/apple-notes-mcp/templates");
|
|
53740
54071
|
}
|
|
53741
54072
|
function checkName(name, action) {
|
|
53742
54073
|
if (isBuiltinTemplate(name) && action !== "read")
|
|
@@ -53773,19 +54104,19 @@ var TemplateStore = class {
|
|
|
53773
54104
|
return this.dir;
|
|
53774
54105
|
}
|
|
53775
54106
|
file(name) {
|
|
53776
|
-
return
|
|
54107
|
+
return join26(this.dir, `${name}.json`);
|
|
53777
54108
|
}
|
|
53778
54109
|
/** Read a saved template's text, refusing symlinks and non-regular files. */
|
|
53779
54110
|
readText(path10) {
|
|
53780
54111
|
let fd;
|
|
53781
54112
|
try {
|
|
53782
|
-
fd =
|
|
54113
|
+
fd = openSync8(path10, constants8.O_RDONLY | constants8.O_NOFOLLOW);
|
|
53783
54114
|
} catch (error2) {
|
|
53784
54115
|
if (error2.code === "ENOENT") return void 0;
|
|
53785
54116
|
throw new TemplateStoreError("unsafe-path", `Refusing to read ${path10}: not a regular file.`);
|
|
53786
54117
|
}
|
|
53787
54118
|
try {
|
|
53788
|
-
const stat =
|
|
54119
|
+
const stat = fstatSync8(fd);
|
|
53789
54120
|
if (!stat.isFile())
|
|
53790
54121
|
throw new TemplateStoreError(
|
|
53791
54122
|
"unsafe-path",
|
|
@@ -53801,13 +54132,13 @@ var TemplateStore = class {
|
|
|
53801
54132
|
const data = Buffer.alloc(stat.size);
|
|
53802
54133
|
let read = 0;
|
|
53803
54134
|
while (read < stat.size) {
|
|
53804
|
-
const n =
|
|
54135
|
+
const n = readSync6(fd, data, read, stat.size - read, read);
|
|
53805
54136
|
if (n <= 0) break;
|
|
53806
54137
|
read += n;
|
|
53807
54138
|
}
|
|
53808
54139
|
return { text: data.subarray(0, read).toString("utf8"), bytes: read, mtime: stat.mtime };
|
|
53809
54140
|
} finally {
|
|
53810
|
-
|
|
54141
|
+
closeSync8(fd);
|
|
53811
54142
|
}
|
|
53812
54143
|
}
|
|
53813
54144
|
/** Saved templates in name order; `skipped` counts entries that are not usable. */
|
|
@@ -53887,10 +54218,10 @@ var TemplateStore = class {
|
|
|
53887
54218
|
"template-exists",
|
|
53888
54219
|
`A template named "${name}" already exists. Pass force: true to replace it.`
|
|
53889
54220
|
);
|
|
53890
|
-
const temp =
|
|
53891
|
-
const fd =
|
|
54221
|
+
const temp = join26(this.dir, `.${name}.${randomBytes(6).toString("hex")}.tmp`);
|
|
54222
|
+
const fd = openSync8(
|
|
53892
54223
|
temp,
|
|
53893
|
-
|
|
54224
|
+
constants8.O_WRONLY | constants8.O_CREAT | constants8.O_EXCL | constants8.O_NOFOLLOW,
|
|
53894
54225
|
384
|
|
53895
54226
|
);
|
|
53896
54227
|
try {
|
|
@@ -53898,7 +54229,7 @@ var TemplateStore = class {
|
|
|
53898
54229
|
let written = 0;
|
|
53899
54230
|
while (written < data.length) written += writeSync5(fd, data, written);
|
|
53900
54231
|
} finally {
|
|
53901
|
-
|
|
54232
|
+
closeSync8(fd);
|
|
53902
54233
|
}
|
|
53903
54234
|
try {
|
|
53904
54235
|
if (force) renameSync(temp, path10);
|
|
@@ -54118,7 +54449,7 @@ function registerMarkdownTemplates(server2, store = () => new TemplateStore()) {
|
|
|
54118
54449
|
|
|
54119
54450
|
// src/utils/svgAnalyzer.ts
|
|
54120
54451
|
import { createHash as createHash5 } from "node:crypto";
|
|
54121
|
-
import { closeSync as
|
|
54452
|
+
import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, openSync as openSync9, readSync as readSync7 } from "node:fs";
|
|
54122
54453
|
|
|
54123
54454
|
// src/utils/svgColor.ts
|
|
54124
54455
|
var NAMED = {
|
|
@@ -56087,14 +56418,14 @@ function analyzeSvgBuffer(source) {
|
|
|
56087
56418
|
function readSvgSource(path10) {
|
|
56088
56419
|
let fd;
|
|
56089
56420
|
try {
|
|
56090
|
-
fd =
|
|
56421
|
+
fd = openSync9(path10, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
|
|
56091
56422
|
} catch (error2) {
|
|
56092
56423
|
if (error2.code === "ELOOP")
|
|
56093
56424
|
throw new SvgError("svg_file_invalid", "The SVG path is a symbolic link");
|
|
56094
56425
|
throw new SvgError("svg_file_invalid", "The SVG file does not exist or cannot be read");
|
|
56095
56426
|
}
|
|
56096
56427
|
try {
|
|
56097
|
-
const info =
|
|
56428
|
+
const info = fstatSync9(fd);
|
|
56098
56429
|
if (!info.isFile())
|
|
56099
56430
|
throw new SvgError("svg_file_invalid", "The SVG path is not a regular file");
|
|
56100
56431
|
if (info.size > SVG_LIMITS.maxSourceBytes)
|
|
@@ -56105,7 +56436,7 @@ function readSvgSource(path10) {
|
|
|
56105
56436
|
const buffer = Buffer.alloc(SVG_LIMITS.maxSourceBytes + 1);
|
|
56106
56437
|
let total = 0;
|
|
56107
56438
|
for (; ; ) {
|
|
56108
|
-
const n =
|
|
56439
|
+
const n = readSync7(fd, buffer, total, buffer.length - total, null);
|
|
56109
56440
|
if (n === 0) break;
|
|
56110
56441
|
total += n;
|
|
56111
56442
|
if (total > SVG_LIMITS.maxSourceBytes)
|
|
@@ -56116,7 +56447,7 @@ function readSvgSource(path10) {
|
|
|
56116
56447
|
}
|
|
56117
56448
|
return buffer.subarray(0, total);
|
|
56118
56449
|
} finally {
|
|
56119
|
-
|
|
56450
|
+
closeSync9(fd);
|
|
56120
56451
|
}
|
|
56121
56452
|
}
|
|
56122
56453
|
function analyzeSvgFile(path10) {
|
|
@@ -56356,7 +56687,9 @@ function appendChecklistItems(args, deps, readRich) {
|
|
|
56356
56687
|
stoppedAt: {
|
|
56357
56688
|
index,
|
|
56358
56689
|
text: text2,
|
|
56359
|
-
|
|
56690
|
+
// A bridge refusal or failure with an unchanged readback is
|
|
56691
|
+
// reported as not written even though verification ran (#248).
|
|
56692
|
+
outcome: wrote && !(error2 instanceof CodedError && error2.envelope.committed === false) ? "uncertain" : "not-written",
|
|
56360
56693
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
56361
56694
|
},
|
|
56362
56695
|
notAttempted: args.items.slice(index + 1),
|
|
@@ -56779,14 +57112,14 @@ import {
|
|
|
56779
57112
|
chmodSync,
|
|
56780
57113
|
existsSync as existsSync14,
|
|
56781
57114
|
mkdirSync as mkdirSync8,
|
|
56782
|
-
mkdtempSync as
|
|
56783
|
-
readFileSync as
|
|
57115
|
+
mkdtempSync as mkdtempSync7,
|
|
57116
|
+
readFileSync as readFileSync5,
|
|
56784
57117
|
renameSync as renameSync2,
|
|
56785
|
-
rmSync as
|
|
56786
|
-
writeFileSync as
|
|
57118
|
+
rmSync as rmSync7,
|
|
57119
|
+
writeFileSync as writeFileSync6
|
|
56787
57120
|
} from "node:fs";
|
|
56788
57121
|
import { homedir as homedir20, release as release3 } from "node:os";
|
|
56789
|
-
import { dirname as dirname8, join as
|
|
57122
|
+
import { dirname as dirname8, join as join27, resolve as resolve7 } from "node:path";
|
|
56790
57123
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
56791
57124
|
var PUBLIC_HELPER_PROTOCOL = 1;
|
|
56792
57125
|
var PUBLIC_HELPER_DIR_ENV = "APPLE_NOTES_MCP_PUBLIC_HELPER_DIR";
|
|
@@ -56814,10 +57147,10 @@ var publicManifestSchema = external_exports.object({
|
|
|
56814
57147
|
function packageRoot(fromDir = dirname8(fileURLToPath2(import.meta.url))) {
|
|
56815
57148
|
let dir = fromDir;
|
|
56816
57149
|
for (; ; ) {
|
|
56817
|
-
const candidate =
|
|
57150
|
+
const candidate = join27(dir, "package.json");
|
|
56818
57151
|
if (existsSync14(candidate)) {
|
|
56819
57152
|
try {
|
|
56820
|
-
if (JSON.parse(
|
|
57153
|
+
if (JSON.parse(readFileSync5(candidate, "utf8")).name === "apple-notes-mcp")
|
|
56821
57154
|
return dir;
|
|
56822
57155
|
} catch {
|
|
56823
57156
|
}
|
|
@@ -56831,9 +57164,9 @@ function defaultPublicHelperDeps(overrides = {}) {
|
|
|
56831
57164
|
return {
|
|
56832
57165
|
env: process.env,
|
|
56833
57166
|
platform: process.platform,
|
|
56834
|
-
sourcePath:
|
|
57167
|
+
sourcePath: join27(packageRoot(), PUBLIC_HELPER_SOURCE),
|
|
56835
57168
|
exists: existsSync14,
|
|
56836
|
-
readFile: (path10) =>
|
|
57169
|
+
readFile: (path10) => readFileSync5(path10),
|
|
56837
57170
|
spawn: spawnSync3,
|
|
56838
57171
|
spawnAsync: spawn,
|
|
56839
57172
|
...overrides
|
|
@@ -56842,14 +57175,14 @@ function defaultPublicHelperDeps(overrides = {}) {
|
|
|
56842
57175
|
function publicHelperInstallDir(env = process.env) {
|
|
56843
57176
|
const override = env[PUBLIC_HELPER_DIR_ENV]?.trim();
|
|
56844
57177
|
if (override) return override;
|
|
56845
|
-
return
|
|
57178
|
+
return join27(homedir20(), "Library", "Application Support", "apple-notes-mcp", "public-helper");
|
|
56846
57179
|
}
|
|
56847
57180
|
function sha256Hex(data) {
|
|
56848
57181
|
return createHash6("sha256").update(data).digest("hex");
|
|
56849
57182
|
}
|
|
56850
57183
|
function inspectPublicHelper(deps = defaultPublicHelperDeps()) {
|
|
56851
57184
|
const installDir = publicHelperInstallDir(deps.env);
|
|
56852
|
-
const binaryPath =
|
|
57185
|
+
const binaryPath = join27(installDir, PUBLIC_HELPER_BINARY);
|
|
56853
57186
|
const base = { installDir, binaryPath, sourcePath: deps.sourcePath, manifest: null };
|
|
56854
57187
|
const fail = (reason, detail) => ({
|
|
56855
57188
|
...base,
|
|
@@ -56860,7 +57193,7 @@ function inspectPublicHelper(deps = defaultPublicHelperDeps()) {
|
|
|
56860
57193
|
if (deps.platform !== "darwin") return fail("unsupported_platform", "macOS only");
|
|
56861
57194
|
if (!deps.exists(deps.sourcePath))
|
|
56862
57195
|
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
56863
|
-
const manifestPath =
|
|
57196
|
+
const manifestPath = join27(installDir, PUBLIC_HELPER_MANIFEST);
|
|
56864
57197
|
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
56865
57198
|
return fail(
|
|
56866
57199
|
"helper_not_installed",
|
|
@@ -56900,14 +57233,14 @@ var UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
|
|
|
56900
57233
|
"helper_modified",
|
|
56901
57234
|
"helper_manifest_invalid"
|
|
56902
57235
|
]);
|
|
56903
|
-
var
|
|
57236
|
+
var ENVELOPE_CODES2 = {
|
|
56904
57237
|
invalid_request: "validation_error",
|
|
56905
57238
|
attachment_not_found: "not_found"
|
|
56906
57239
|
};
|
|
56907
57240
|
var PublicHelperError = class extends CodedError {
|
|
56908
57241
|
constructor(code, message) {
|
|
56909
57242
|
super(message, {
|
|
56910
|
-
code: UNAVAILABLE_CODES.has(code) ? "unsupported" :
|
|
57243
|
+
code: UNAVAILABLE_CODES.has(code) ? "unsupported" : ENVELOPE_CODES2[code] ?? "operation_failed",
|
|
56911
57244
|
helperCode: code
|
|
56912
57245
|
});
|
|
56913
57246
|
this.code = code;
|
|
@@ -57137,13 +57470,13 @@ function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
|
57137
57470
|
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
57138
57471
|
const installDir = publicHelperInstallDir(deps.env);
|
|
57139
57472
|
mkdirSync8(installDir, { recursive: true, mode: 448 });
|
|
57140
|
-
const staging =
|
|
57473
|
+
const staging = mkdtempSync7(join27(installDir, ".staging-"));
|
|
57141
57474
|
try {
|
|
57142
|
-
const stagedBinary =
|
|
57143
|
-
const digestPath =
|
|
57144
|
-
const plistPath =
|
|
57145
|
-
|
|
57146
|
-
|
|
57475
|
+
const stagedBinary = join27(staging, PUBLIC_HELPER_BINARY);
|
|
57476
|
+
const digestPath = join27(staging, "source-digest.swift");
|
|
57477
|
+
const plistPath = join27(staging, "Info.plist");
|
|
57478
|
+
writeFileSync6(digestPath, sourceDigestSwift(sourceSha), { mode: 384 });
|
|
57479
|
+
writeFileSync6(plistPath, publicHelperInfoPlist(), { mode: 384 });
|
|
57147
57480
|
const compile = deps.spawn(
|
|
57148
57481
|
"/usr/bin/xcrun",
|
|
57149
57482
|
publicHelperCompileArguments(deps.sourcePath, digestPath, plistPath, stagedBinary),
|
|
@@ -57204,15 +57537,15 @@ function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
|
57204
57537
|
compiler
|
|
57205
57538
|
};
|
|
57206
57539
|
chmodSync(stagedBinary, 448);
|
|
57207
|
-
renameSync2(stagedBinary,
|
|
57208
|
-
|
|
57209
|
-
|
|
57540
|
+
renameSync2(stagedBinary, join27(installDir, PUBLIC_HELPER_BINARY));
|
|
57541
|
+
writeFileSync6(
|
|
57542
|
+
join27(installDir, PUBLIC_HELPER_MANIFEST),
|
|
57210
57543
|
JSON.stringify(manifest, null, 2) + "\n",
|
|
57211
57544
|
{ mode: 384 }
|
|
57212
57545
|
);
|
|
57213
57546
|
steps.push({ step: "install", ok: true, detail: installDir });
|
|
57214
57547
|
} finally {
|
|
57215
|
-
|
|
57548
|
+
rmSync7(staging, { recursive: true, force: true });
|
|
57216
57549
|
}
|
|
57217
57550
|
const installation = inspectPublicHelper(deps);
|
|
57218
57551
|
steps.push({
|
|
@@ -57450,8 +57783,8 @@ function formatNoteDrawings(result) {
|
|
|
57450
57783
|
}
|
|
57451
57784
|
|
|
57452
57785
|
// src/utils/noteAudio.ts
|
|
57453
|
-
import { readdirSync as readdirSync5, statSync as
|
|
57454
|
-
import { join as
|
|
57786
|
+
import { readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
|
|
57787
|
+
import { join as join28 } from "node:path";
|
|
57455
57788
|
var EXTRA_AUDIO_UTIS = [
|
|
57456
57789
|
"public.mp3",
|
|
57457
57790
|
"public.aiff-audio",
|
|
@@ -57476,7 +57809,7 @@ function accountDirsFor(containerDir, accountIdentifier) {
|
|
|
57476
57809
|
if (own) return [own];
|
|
57477
57810
|
let names;
|
|
57478
57811
|
try {
|
|
57479
|
-
names = readdirSync5(
|
|
57812
|
+
names = readdirSync5(join28(containerDir, "Accounts")).sort();
|
|
57480
57813
|
} catch {
|
|
57481
57814
|
return [];
|
|
57482
57815
|
}
|
|
@@ -57488,8 +57821,8 @@ function resolveMediaPath(media, accountIdentifier = null, containerDir = NOTES_
|
|
|
57488
57821
|
if (!id2 || !filename) return null;
|
|
57489
57822
|
const generation = safeComponent(media?.generation);
|
|
57490
57823
|
for (const dir of accountDirsFor(containerDir, accountIdentifier)) {
|
|
57491
|
-
const base =
|
|
57492
|
-
const candidates = generation ? [
|
|
57824
|
+
const base = join28(dir, "Media", id2);
|
|
57825
|
+
const candidates = generation ? [join28(base, generation, filename), join28(base, filename)] : [join28(base, filename)];
|
|
57493
57826
|
for (const candidate of candidates) {
|
|
57494
57827
|
const real = realInside(candidate, dir);
|
|
57495
57828
|
if (real && isFile2(real)) return real;
|
|
@@ -57499,7 +57832,7 @@ function resolveMediaPath(media, accountIdentifier = null, containerDir = NOTES_
|
|
|
57499
57832
|
}
|
|
57500
57833
|
function isFile2(path10) {
|
|
57501
57834
|
try {
|
|
57502
|
-
return
|
|
57835
|
+
return statSync5(path10).isFile();
|
|
57503
57836
|
} catch {
|
|
57504
57837
|
return false;
|
|
57505
57838
|
}
|
|
@@ -57762,20 +58095,20 @@ import {
|
|
|
57762
58095
|
chmodSync as chmodSync2,
|
|
57763
58096
|
existsSync as existsSync16,
|
|
57764
58097
|
mkdirSync as mkdirSync9,
|
|
57765
|
-
mkdtempSync as
|
|
58098
|
+
mkdtempSync as mkdtempSync8,
|
|
57766
58099
|
renameSync as renameSync3,
|
|
57767
|
-
rmSync as
|
|
57768
|
-
writeFileSync as
|
|
58100
|
+
rmSync as rmSync8,
|
|
58101
|
+
writeFileSync as writeFileSync7
|
|
57769
58102
|
} from "node:fs";
|
|
57770
58103
|
import { release as release4 } from "node:os";
|
|
57771
|
-
import { join as
|
|
58104
|
+
import { join as join30 } from "node:path";
|
|
57772
58105
|
|
|
57773
58106
|
// src/services/privateHelper.ts
|
|
57774
58107
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
57775
58108
|
import { createHash as createHash7 } from "node:crypto";
|
|
57776
|
-
import { existsSync as existsSync15, readFileSync as
|
|
58109
|
+
import { existsSync as existsSync15, readFileSync as readFileSync6 } from "node:fs";
|
|
57777
58110
|
import { homedir as homedir21 } from "node:os";
|
|
57778
|
-
import { dirname as dirname9, join as
|
|
58111
|
+
import { dirname as dirname9, join as join29, resolve as resolve8 } from "node:path";
|
|
57779
58112
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
57780
58113
|
var PRIVATE_HELPER_PROTOCOL = 1;
|
|
57781
58114
|
var ENABLE_ENV = "APPLE_NOTES_MCP_ENABLE_PRIVATE";
|
|
@@ -57803,10 +58136,10 @@ var manifestSchema = external_exports.object({
|
|
|
57803
58136
|
function packageRoot2(fromDir = dirname9(fileURLToPath3(import.meta.url))) {
|
|
57804
58137
|
let dir = fromDir;
|
|
57805
58138
|
for (; ; ) {
|
|
57806
|
-
const candidate =
|
|
58139
|
+
const candidate = join29(dir, "package.json");
|
|
57807
58140
|
if (existsSync15(candidate)) {
|
|
57808
58141
|
try {
|
|
57809
|
-
const pkg = JSON.parse(
|
|
58142
|
+
const pkg = JSON.parse(readFileSync6(candidate, "utf8"));
|
|
57810
58143
|
if (pkg.name === "apple-notes-mcp") return dir;
|
|
57811
58144
|
} catch {
|
|
57812
58145
|
}
|
|
@@ -57820,9 +58153,9 @@ function defaultDeps2(overrides = {}) {
|
|
|
57820
58153
|
return {
|
|
57821
58154
|
env: process.env,
|
|
57822
58155
|
platform: process.platform,
|
|
57823
|
-
sourcePath:
|
|
58156
|
+
sourcePath: join29(packageRoot2(), HELPER_SOURCE_RELATIVE),
|
|
57824
58157
|
exists: existsSync15,
|
|
57825
|
-
readFile: (path10) =>
|
|
58158
|
+
readFile: (path10) => readFileSync6(path10),
|
|
57826
58159
|
spawn: spawnSync4,
|
|
57827
58160
|
...overrides
|
|
57828
58161
|
};
|
|
@@ -57833,14 +58166,14 @@ function privateHelperEnabled(env = process.env) {
|
|
|
57833
58166
|
function helperInstallDir(env = process.env) {
|
|
57834
58167
|
const override = env[HELPER_DIR_ENV]?.trim();
|
|
57835
58168
|
if (override) return override;
|
|
57836
|
-
return
|
|
58169
|
+
return join29(homedir21(), "Library", "Application Support", "apple-notes-mcp", "private-helper");
|
|
57837
58170
|
}
|
|
57838
58171
|
function sha256Hex2(data) {
|
|
57839
58172
|
return createHash7("sha256").update(data).digest("hex");
|
|
57840
58173
|
}
|
|
57841
58174
|
function inspectInstallation(deps = defaultDeps2()) {
|
|
57842
58175
|
const installDir = helperInstallDir(deps.env);
|
|
57843
|
-
const binaryPath =
|
|
58176
|
+
const binaryPath = join29(installDir, HELPER_BINARY_NAME);
|
|
57844
58177
|
const base = {
|
|
57845
58178
|
installDir,
|
|
57846
58179
|
binaryPath,
|
|
@@ -57858,7 +58191,7 @@ function inspectInstallation(deps = defaultDeps2()) {
|
|
|
57858
58191
|
if (!deps.exists(deps.sourcePath))
|
|
57859
58192
|
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
57860
58193
|
base.expectedSourceSha256 = sha256Hex2(deps.readFile(deps.sourcePath));
|
|
57861
|
-
const manifestPath =
|
|
58194
|
+
const manifestPath = join29(installDir, MANIFEST_NAME);
|
|
57862
58195
|
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
57863
58196
|
return fail(
|
|
57864
58197
|
"helper_not_installed",
|
|
@@ -58173,9 +58506,9 @@ function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
|
58173
58506
|
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
58174
58507
|
const installDir = helperInstallDir(deps.env);
|
|
58175
58508
|
mkdirSync9(installDir, { recursive: true, mode: 448 });
|
|
58176
|
-
const staging =
|
|
58509
|
+
const staging = mkdtempSync8(join30(installDir, ".staging-"));
|
|
58177
58510
|
try {
|
|
58178
|
-
const stagedBinary =
|
|
58511
|
+
const stagedBinary = join30(staging, HELPER_BINARY_NAME);
|
|
58179
58512
|
const compile = deps.spawn(
|
|
58180
58513
|
"/usr/bin/xcrun",
|
|
58181
58514
|
compileArguments(deps.sourcePath, stagedBinary, sourceSha),
|
|
@@ -58252,13 +58585,13 @@ function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
|
58252
58585
|
compiler
|
|
58253
58586
|
};
|
|
58254
58587
|
chmodSync2(stagedBinary, 448);
|
|
58255
|
-
renameSync3(stagedBinary,
|
|
58256
|
-
|
|
58588
|
+
renameSync3(stagedBinary, join30(installDir, HELPER_BINARY_NAME));
|
|
58589
|
+
writeFileSync7(join30(installDir, MANIFEST_NAME), JSON.stringify(manifest, null, 2) + "\n", {
|
|
58257
58590
|
mode: 384
|
|
58258
58591
|
});
|
|
58259
58592
|
steps.push({ step: "install", ok: true, detail: installDir });
|
|
58260
58593
|
} finally {
|
|
58261
|
-
if (existsSync16(staging))
|
|
58594
|
+
if (existsSync16(staging)) rmSync8(staging, { recursive: true, force: true });
|
|
58262
58595
|
}
|
|
58263
58596
|
const installation = inspectInstallation(deps);
|
|
58264
58597
|
steps.push({
|
|
@@ -60304,7 +60637,7 @@ ${noteList}`, {
|
|
|
60304
60637
|
registerTool(
|
|
60305
60638
|
"list-folders",
|
|
60306
60639
|
{
|
|
60307
|
-
description: "Use when: listing all folders, with full nested paths, for an account.\nReturns: folder names/paths.\nDo not use when: listing notes (list-notes).\nNote:
|
|
60640
|
+
description: "Use when: listing all folders, with full nested paths, for an account.\nReturns: folder names/paths.\nDo not use when: listing notes (list-notes).\nNote: smart folders are listed too (AppleScript lists them like ordinary folders) and are marked smartFolder: true when Full Disk Access lets the server read the Notes database; they cannot hold notes. Warns if iCloud sync is active.",
|
|
60308
60641
|
inputSchema: {
|
|
60309
60642
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account to list folders from")
|
|
60310
60643
|
},
|
|
@@ -60334,7 +60667,7 @@ ${syncWarnings.join(" ")}` : "";
|
|
|
60334
60667
|
return successResponse(`No folders found${acct}${syncNote}`, { folders: [], count: 0 });
|
|
60335
60668
|
}
|
|
60336
60669
|
const resolvedAcct = folders[0]?.account ? ` (${folders[0].account})` : acct;
|
|
60337
|
-
const folderList = folders.map((f) => ` - ${f.name}`).join("\n");
|
|
60670
|
+
const folderList = folders.map((f) => ` - ${f.name}${f.smartFolder ? " (smart folder)" : ""}`).join("\n");
|
|
60338
60671
|
return successResponse(
|
|
60339
60672
|
`Found ${folders.length} folders${resolvedAcct}:
|
|
60340
60673
|
${folderList}${syncNote}`,
|
|
@@ -60350,7 +60683,7 @@ registerTool(
|
|
|
60350
60683
|
{
|
|
60351
60684
|
description: `Use when: listing Smart Folders and the rules that define them.
|
|
60352
60685
|
Returns: each smart folder's name, ids, account, and parent, its rules decoded as match ("all"/"any"/"none") plus filters (each with a readable description), the stored query with the outer deleted wrapper removed, and the raw stored query JSON. With includeMatchingNotes, also the notes Notes.app currently shows in each folder.
|
|
60353
|
-
Do not use when: listing
|
|
60686
|
+
Do not use when: listing all folders with paths (list-folders, which lists smart folders alongside ordinary ones) or searching notes (search-notes).
|
|
60354
60687
|
Safety: read-only; reads the NoteStore database and requires Full Disk Access. includeMatchingNotes asks Notes.app (Automation permission) for each folder's current contents rather than re-evaluating the rules. Unrecognized rules are kept as "unknown" filters and set fullyDecoded false.`,
|
|
60355
60688
|
inputSchema: {
|
|
60356
60689
|
includeMatchingNotes: external_exports.boolean().optional().describe(
|