apple-notes-mcp 2.9.15 → 2.9.17
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 +150 -1
- package/build/index.js +862 -153
- package/docs/markdown-templates.md +10 -1
- 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) {
|
|
@@ -2991,7 +2991,7 @@ var require_compile = __commonJS({
|
|
|
2991
2991
|
const schOrFunc = root.refs[ref];
|
|
2992
2992
|
if (schOrFunc)
|
|
2993
2993
|
return schOrFunc;
|
|
2994
|
-
let _sch =
|
|
2994
|
+
let _sch = resolve9.call(this, root, ref);
|
|
2995
2995
|
if (_sch === void 0) {
|
|
2996
2996
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2997
2997
|
const { schemaId } = this.opts;
|
|
@@ -3018,7 +3018,7 @@ var require_compile = __commonJS({
|
|
|
3018
3018
|
function sameSchemaEnv(s1, s2) {
|
|
3019
3019
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3020
3020
|
}
|
|
3021
|
-
function
|
|
3021
|
+
function resolve9(root, ref) {
|
|
3022
3022
|
let sch;
|
|
3023
3023
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3024
3024
|
ref = sch;
|
|
@@ -3843,7 +3843,7 @@ var require_fast_uri = __commonJS({
|
|
|
3843
3843
|
}
|
|
3844
3844
|
return uri;
|
|
3845
3845
|
}
|
|
3846
|
-
function
|
|
3846
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
3847
3847
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3848
3848
|
const {
|
|
3849
3849
|
parsed: baseParsed,
|
|
@@ -4205,7 +4205,7 @@ var require_fast_uri = __commonJS({
|
|
|
4205
4205
|
var fastUri = {
|
|
4206
4206
|
SCHEMES,
|
|
4207
4207
|
normalize,
|
|
4208
|
-
resolve:
|
|
4208
|
+
resolve: resolve9,
|
|
4209
4209
|
resolveComponent,
|
|
4210
4210
|
equal,
|
|
4211
4211
|
serialize,
|
|
@@ -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);
|
|
@@ -36608,7 +36608,7 @@ var Protocol = class {
|
|
|
36608
36608
|
return;
|
|
36609
36609
|
}
|
|
36610
36610
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
36611
|
-
await new Promise((
|
|
36611
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
36612
36612
|
options?.signal?.throwIfAborted();
|
|
36613
36613
|
}
|
|
36614
36614
|
} catch (error2) {
|
|
@@ -36625,7 +36625,7 @@ var Protocol = class {
|
|
|
36625
36625
|
*/
|
|
36626
36626
|
request(request, resultSchema, options) {
|
|
36627
36627
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
36628
|
-
return new Promise((
|
|
36628
|
+
return new Promise((resolve9, reject) => {
|
|
36629
36629
|
const earlyReject = (error2) => {
|
|
36630
36630
|
reject(error2);
|
|
36631
36631
|
};
|
|
@@ -36703,7 +36703,7 @@ var Protocol = class {
|
|
|
36703
36703
|
if (!parseResult.success) {
|
|
36704
36704
|
reject(parseResult.error);
|
|
36705
36705
|
} else {
|
|
36706
|
-
|
|
36706
|
+
resolve9(parseResult.data);
|
|
36707
36707
|
}
|
|
36708
36708
|
} catch (error2) {
|
|
36709
36709
|
reject(error2);
|
|
@@ -36964,12 +36964,12 @@ var Protocol = class {
|
|
|
36964
36964
|
}
|
|
36965
36965
|
} catch {
|
|
36966
36966
|
}
|
|
36967
|
-
return new Promise((
|
|
36967
|
+
return new Promise((resolve9, reject) => {
|
|
36968
36968
|
if (signal.aborted) {
|
|
36969
36969
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
36970
36970
|
return;
|
|
36971
36971
|
}
|
|
36972
|
-
const timeoutId = setTimeout(
|
|
36972
|
+
const timeoutId = setTimeout(resolve9, interval);
|
|
36973
36973
|
signal.addEventListener("abort", () => {
|
|
36974
36974
|
clearTimeout(timeoutId);
|
|
36975
36975
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -38282,7 +38282,7 @@ var McpServer = class {
|
|
|
38282
38282
|
let task = createTaskResult.task;
|
|
38283
38283
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
38284
38284
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
38285
|
-
await new Promise((
|
|
38285
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
38286
38286
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
38287
38287
|
if (!updatedTask) {
|
|
38288
38288
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -38970,12 +38970,12 @@ var StdioServerTransport = class {
|
|
|
38970
38970
|
this.onclose?.();
|
|
38971
38971
|
}
|
|
38972
38972
|
send(message) {
|
|
38973
|
-
return new Promise((
|
|
38973
|
+
return new Promise((resolve9) => {
|
|
38974
38974
|
const json2 = serializeMessage(message);
|
|
38975
38975
|
if (this._stdout.write(json2)) {
|
|
38976
|
-
|
|
38976
|
+
resolve9();
|
|
38977
38977
|
} else {
|
|
38978
|
-
this._stdout.once("drain",
|
|
38978
|
+
this._stdout.once("drain", resolve9);
|
|
38979
38979
|
}
|
|
38980
38980
|
});
|
|
38981
38981
|
}
|
|
@@ -47633,13 +47633,13 @@ var escapeSegment = escapeFolderName;
|
|
|
47633
47633
|
function resolveFolders(rows) {
|
|
47634
47634
|
const byPk = new Map(rows.map((row) => [row.pk, row]));
|
|
47635
47635
|
const resolved = /* @__PURE__ */ new Map();
|
|
47636
|
-
const
|
|
47636
|
+
const resolve9 = (pk, seen) => {
|
|
47637
47637
|
const cached2 = resolved.get(pk);
|
|
47638
47638
|
if (cached2) return cached2;
|
|
47639
47639
|
const row = byPk.get(pk);
|
|
47640
47640
|
if (!row || seen.has(pk)) return void 0;
|
|
47641
47641
|
seen.add(pk);
|
|
47642
|
-
const parent = row.parent !== null ?
|
|
47642
|
+
const parent = row.parent !== null ? resolve9(row.parent, seen) : void 0;
|
|
47643
47643
|
const name = row.name ?? "";
|
|
47644
47644
|
const path10 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
|
|
47645
47645
|
const plainPath = parent ? `${parent.plainPath}/${name}` : name;
|
|
@@ -47653,7 +47653,7 @@ function resolveFolders(rows) {
|
|
|
47653
47653
|
resolved.set(pk, info);
|
|
47654
47654
|
return info;
|
|
47655
47655
|
};
|
|
47656
|
-
for (const row of rows)
|
|
47656
|
+
for (const row of rows) resolve9(row.pk, /* @__PURE__ */ new Set());
|
|
47657
47657
|
return resolved;
|
|
47658
47658
|
}
|
|
47659
47659
|
var coreDataMs = (seconds) => seconds === null || !Number.isFinite(seconds) ? void 0 : CORE_DATA_EPOCH_MS2 + seconds * 1e3;
|
|
@@ -48860,6 +48860,19 @@ var FEATURES = [
|
|
|
48860
48860
|
tools: [],
|
|
48861
48861
|
minimumMacOSVersion: null,
|
|
48862
48862
|
requirements: [{ kind: "native_write_helper" }]
|
|
48863
|
+
},
|
|
48864
|
+
{
|
|
48865
|
+
name: "markdownTemplateLibrary",
|
|
48866
|
+
description: "Validate, save, list, show and delete Markdown export templates (local JSON files; exporting with one still needs Full Disk Access)",
|
|
48867
|
+
tools: [
|
|
48868
|
+
"list-markdown-templates",
|
|
48869
|
+
"show-markdown-template",
|
|
48870
|
+
"validate-markdown-template",
|
|
48871
|
+
"save-markdown-template",
|
|
48872
|
+
"delete-markdown-template"
|
|
48873
|
+
],
|
|
48874
|
+
minimumMacOSVersion: null,
|
|
48875
|
+
requirements: []
|
|
48863
48876
|
}
|
|
48864
48877
|
];
|
|
48865
48878
|
function requirementLabel(requirement) {
|
|
@@ -51653,16 +51666,16 @@ var NOTE_PLACEHOLDERS = [
|
|
|
51653
51666
|
"exportStem"
|
|
51654
51667
|
];
|
|
51655
51668
|
var PLACEHOLDER_MODIFIERS = ["raw", "yaml"];
|
|
51656
|
-
var wrap2 = (before, after = "",
|
|
51669
|
+
var wrap2 = (before, after = "", join31) => ({
|
|
51657
51670
|
mode: "wrap",
|
|
51658
51671
|
before,
|
|
51659
51672
|
after,
|
|
51660
|
-
...
|
|
51673
|
+
...join31 ? { join: join31 } : {}
|
|
51661
51674
|
});
|
|
51662
|
-
var pattern = (value,
|
|
51675
|
+
var pattern = (value, join31) => ({
|
|
51663
51676
|
mode: "pattern",
|
|
51664
51677
|
value,
|
|
51665
|
-
...
|
|
51678
|
+
...join31 ? { join: join31 } : {}
|
|
51666
51679
|
});
|
|
51667
51680
|
var STANDARD = {
|
|
51668
51681
|
schemaVersion: 1,
|
|
@@ -53043,6 +53056,21 @@ function exportWithTemplate(request, deps, chosen, notes, skipped, { output, ass
|
|
|
53043
53056
|
|
|
53044
53057
|
// src/tools/directOperations.ts
|
|
53045
53058
|
import { createHash as createHash3 } from "node:crypto";
|
|
53059
|
+
import {
|
|
53060
|
+
closeSync as closeSync7,
|
|
53061
|
+
constants as constants7,
|
|
53062
|
+
fstatSync as fstatSync7,
|
|
53063
|
+
mkdtempSync as mkdtempSync6,
|
|
53064
|
+
openSync as openSync7,
|
|
53065
|
+
readFileSync as readFileSync4,
|
|
53066
|
+
rmSync as rmSync6,
|
|
53067
|
+
writeFileSync as writeFileSync5
|
|
53068
|
+
} from "node:fs";
|
|
53069
|
+
import { tmpdir as tmpdir6 } from "node:os";
|
|
53070
|
+
import { basename as basename6, extname as extname7, isAbsolute as isAbsolute4, join as join24 } from "node:path";
|
|
53071
|
+
|
|
53072
|
+
// src/utils/pasteboardFreeze.ts
|
|
53073
|
+
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
53046
53074
|
import {
|
|
53047
53075
|
closeSync as closeSync6,
|
|
53048
53076
|
constants as constants6,
|
|
@@ -53051,10 +53079,224 @@ import {
|
|
|
53051
53079
|
openSync as openSync6,
|
|
53052
53080
|
readFileSync as readFileSync3,
|
|
53053
53081
|
rmSync as rmSync5,
|
|
53082
|
+
statSync as statSync4,
|
|
53054
53083
|
writeFileSync as writeFileSync4
|
|
53055
53084
|
} from "node:fs";
|
|
53056
53085
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
53057
|
-
import { basename as basename5, extname as extname6,
|
|
53086
|
+
import { basename as basename5, extname as extname6, join as join23 } from "node:path";
|
|
53087
|
+
var PASTEBOARD_NAME_ENV = "APPLE_NOTES_MCP_PASTEBOARD_NAME";
|
|
53088
|
+
var MAX_PASTEBOARD_BYTES = 64 * 1024 * 1024;
|
|
53089
|
+
var PASTEBOARD_DATA_TYPES = [
|
|
53090
|
+
{ type: "public.png", ext: "png", label: "image" },
|
|
53091
|
+
{ type: "public.jpeg", ext: "jpg", label: "image" },
|
|
53092
|
+
{ type: "public.heic", ext: "heic", label: "image" },
|
|
53093
|
+
{ type: "com.compuserve.gif", ext: "gif", label: "image" },
|
|
53094
|
+
{ type: "public.tiff", ext: "tiff", label: "image" },
|
|
53095
|
+
{ type: "com.adobe.pdf", ext: "pdf", label: "document" }
|
|
53096
|
+
];
|
|
53097
|
+
var PASTEBOARD_ACCESS_BEHAVIORS = {
|
|
53098
|
+
0: "default",
|
|
53099
|
+
1: "ask",
|
|
53100
|
+
2: "alwaysAllow",
|
|
53101
|
+
3: "alwaysDeny"
|
|
53102
|
+
};
|
|
53103
|
+
var PASTEBOARD_FREEZE_JXA = `
|
|
53104
|
+
ObjC.import("AppKit");
|
|
53105
|
+
function run(argv) {
|
|
53106
|
+
var dir = argv[0], name = argv[1], limit = Number(argv[2]), prefs = JSON.parse(argv[3]);
|
|
53107
|
+
var allowAlert = argv[4] === "1";
|
|
53108
|
+
var pb = name ? $.NSPasteboard.pasteboardWithName(name) : $.NSPasteboard.generalPasteboard;
|
|
53109
|
+
if (!pb || pb.isNil()) return JSON.stringify({ status: "error", code: "pasteboard_unavailable" });
|
|
53110
|
+
if (!name && pb.respondsToSelector("accessBehavior")) {
|
|
53111
|
+
var behavior = Number(pb.accessBehavior);
|
|
53112
|
+
if (behavior === 3 || (behavior !== 2 && !allowAlert))
|
|
53113
|
+
return JSON.stringify({ status: "error", code: "pasteboard_access_denied", accessBehavior: behavior });
|
|
53114
|
+
}
|
|
53115
|
+
var before = pb.changeCount;
|
|
53116
|
+
var types = ObjC.deepUnwrap(pb.types) || [];
|
|
53117
|
+
if (types.length === 0) return JSON.stringify({ status: "error", code: "pasteboard_empty" });
|
|
53118
|
+
var result = null;
|
|
53119
|
+
var items = pb.pasteboardItems;
|
|
53120
|
+
var itemCount = items && !items.isNil() ? Number(items.count) : 0;
|
|
53121
|
+
var fileUrls = [];
|
|
53122
|
+
for (var k = 0; k < itemCount; k++) {
|
|
53123
|
+
var item = items.objectAtIndex(k);
|
|
53124
|
+
var itemTypes = ObjC.deepUnwrap(item.types) || [];
|
|
53125
|
+
if (itemTypes.indexOf("public.file-url") < 0) continue;
|
|
53126
|
+
var value = ObjC.unwrap(item.stringForType("public.file-url"));
|
|
53127
|
+
if (value) fileUrls.push(value);
|
|
53128
|
+
}
|
|
53129
|
+
if (fileUrls.length > 1)
|
|
53130
|
+
return JSON.stringify({ status: "error", code: "multiple_files", count: fileUrls.length });
|
|
53131
|
+
if (fileUrls.length === 1) {
|
|
53132
|
+
var url = $.NSURL.URLWithString(fileUrls[0]);
|
|
53133
|
+
if (url && !url.isNil() && url.isFileURL) {
|
|
53134
|
+
result = { status: "ok", kind: "file", type: "public.file-url", path: ObjC.unwrap(url.path) };
|
|
53135
|
+
}
|
|
53136
|
+
}
|
|
53137
|
+
if (!result) {
|
|
53138
|
+
for (var i = 0; i < prefs.length && !result; i++) {
|
|
53139
|
+
if (types.indexOf(prefs[i][0]) < 0) continue;
|
|
53140
|
+
var data = pb.dataForType(prefs[i][0]);
|
|
53141
|
+
if (!data || data.isNil() || data.length === 0) continue;
|
|
53142
|
+
if (data.length > limit) return JSON.stringify({ status: "error", code: "too_large", bytes: data.length });
|
|
53143
|
+
var path = dir + "/pasteboard." + prefs[i][1];
|
|
53144
|
+
if (!data.writeToFileAtomically(path, true))
|
|
53145
|
+
return JSON.stringify({ status: "error", code: "write_failed" });
|
|
53146
|
+
result = { status: "ok", kind: "data", type: prefs[i][0], path: path, bytes: data.length };
|
|
53147
|
+
}
|
|
53148
|
+
}
|
|
53149
|
+
if (pb.changeCount !== before) return JSON.stringify({ status: "error", code: "pasteboard_changed" });
|
|
53150
|
+
return JSON.stringify(result || { status: "error", code: "unsupported_content", types: types.slice(0, 20) });
|
|
53151
|
+
}
|
|
53152
|
+
`;
|
|
53153
|
+
var ENVELOPE_CODES = {
|
|
53154
|
+
pasteboard_access_denied: "permission_denied",
|
|
53155
|
+
pasteboard_unavailable: "operation_failed",
|
|
53156
|
+
pasteboard_timeout: "operation_failed",
|
|
53157
|
+
pasteboard_empty: "validation_error",
|
|
53158
|
+
pasteboard_changed: "operation_failed",
|
|
53159
|
+
unsupported_content: "validation_error",
|
|
53160
|
+
multiple_files: "validation_error",
|
|
53161
|
+
too_large: "validation_error",
|
|
53162
|
+
write_failed: "operation_failed",
|
|
53163
|
+
file_unreadable: "validation_error"
|
|
53164
|
+
};
|
|
53165
|
+
var PasteboardError = class extends CodedError {
|
|
53166
|
+
constructor(code, message, details = {}) {
|
|
53167
|
+
super(message, {
|
|
53168
|
+
...details,
|
|
53169
|
+
code: ENVELOPE_CODES[code],
|
|
53170
|
+
pasteboardCode: code,
|
|
53171
|
+
committed: false
|
|
53172
|
+
});
|
|
53173
|
+
this.code = code;
|
|
53174
|
+
this.name = "PasteboardError";
|
|
53175
|
+
}
|
|
53176
|
+
code;
|
|
53177
|
+
};
|
|
53178
|
+
var SUPPORTED = "an image (PNG, JPEG, HEIC, GIF, TIFF), a PDF, or one copied file";
|
|
53179
|
+
var MESSAGES = {
|
|
53180
|
+
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.",
|
|
53181
|
+
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).",
|
|
53182
|
+
pasteboard_timeout: "Reading the pasteboard timed out; nothing was attached. If macOS showed a paste alert, answer it and try again.",
|
|
53183
|
+
pasteboard_empty: "The pasteboard is empty. Copy an image or a file first.",
|
|
53184
|
+
pasteboard_changed: "The pasteboard changed while it was being read. Try again.",
|
|
53185
|
+
unsupported_content: `The pasteboard holds no supported content: copy ${SUPPORTED}. Text belongs in append-to-note, not in an attachment.`,
|
|
53186
|
+
multiple_files: "The pasteboard holds more than one copied file. Copy exactly one file, or attach each file with add-attachment.",
|
|
53187
|
+
too_large: "The pasteboard contents exceed the 64 MiB attachment limit.",
|
|
53188
|
+
write_failed: "Could not write the pasteboard contents to a temporary file.",
|
|
53189
|
+
file_unreadable: "The copied file could not be read as a regular file of at most 64 MiB."
|
|
53190
|
+
};
|
|
53191
|
+
function replyError(code, reply) {
|
|
53192
|
+
if (code === "pasteboard_access_denied") {
|
|
53193
|
+
const raw = typeof reply.accessBehavior === "number" ? reply.accessBehavior : void 0;
|
|
53194
|
+
const accessBehavior = raw !== void 0 && PASTEBOARD_ACCESS_BEHAVIORS[raw] || "unknown";
|
|
53195
|
+
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;
|
|
53196
|
+
return new PasteboardError(code, message, { accessBehavior });
|
|
53197
|
+
}
|
|
53198
|
+
if (code === "multiple_files") {
|
|
53199
|
+
const count = typeof reply.count === "number" ? reply.count : void 0;
|
|
53200
|
+
return new PasteboardError(
|
|
53201
|
+
code,
|
|
53202
|
+
count ? `The pasteboard holds ${count} copied files. Copy exactly one file, or attach each file with add-attachment.` : MESSAGES[code],
|
|
53203
|
+
count ? { count } : {}
|
|
53204
|
+
);
|
|
53205
|
+
}
|
|
53206
|
+
if (code === "unsupported_content" && Array.isArray(reply.types)) {
|
|
53207
|
+
const types = reply.types.filter((t) => typeof t === "string").slice(0, 20);
|
|
53208
|
+
if (types.length)
|
|
53209
|
+
return new PasteboardError(
|
|
53210
|
+
code,
|
|
53211
|
+
`${MESSAGES[code]} Pasteboard types found: ${types.join(", ")}.`,
|
|
53212
|
+
{ types }
|
|
53213
|
+
);
|
|
53214
|
+
}
|
|
53215
|
+
if (code === "too_large" && typeof reply.bytes === "number")
|
|
53216
|
+
return new PasteboardError(code, MESSAGES[code], { bytes: reply.bytes });
|
|
53217
|
+
return new PasteboardError(code, MESSAGES[code]);
|
|
53218
|
+
}
|
|
53219
|
+
function pasteboardTimeoutMs() {
|
|
53220
|
+
return callTimeoutMs() ?? envPositiveNumber("APPLE_NOTES_MCP_TIMEOUT_MS") ?? 3e4;
|
|
53221
|
+
}
|
|
53222
|
+
function defaultRunJxa(args) {
|
|
53223
|
+
return execFileSync19("osascript", ["-l", "JavaScript", "-e", PASTEBOARD_FREEZE_JXA, ...args], {
|
|
53224
|
+
encoding: "utf8",
|
|
53225
|
+
timeout: pasteboardTimeoutMs(),
|
|
53226
|
+
maxBuffer: 1024 * 1024,
|
|
53227
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
53228
|
+
});
|
|
53229
|
+
}
|
|
53230
|
+
function pasteboardFilename(requested, frozenFilename) {
|
|
53231
|
+
if (requested === void 0) return void 0;
|
|
53232
|
+
const ext = extname6(frozenFilename);
|
|
53233
|
+
return ext && !extname6(requested) ? requested + ext : requested;
|
|
53234
|
+
}
|
|
53235
|
+
function readRegularFile(path10) {
|
|
53236
|
+
let descriptor;
|
|
53237
|
+
try {
|
|
53238
|
+
descriptor = openSync6(path10, constants6.O_RDONLY | constants6.O_NOFOLLOW);
|
|
53239
|
+
} catch {
|
|
53240
|
+
throw new PasteboardError("file_unreadable", MESSAGES.file_unreadable);
|
|
53241
|
+
}
|
|
53242
|
+
try {
|
|
53243
|
+
const stat = fstatSync6(descriptor);
|
|
53244
|
+
if (!stat.isFile() || stat.size === 0 || stat.size > MAX_PASTEBOARD_BYTES)
|
|
53245
|
+
throw new PasteboardError("file_unreadable", MESSAGES.file_unreadable);
|
|
53246
|
+
return readFileSync3(descriptor);
|
|
53247
|
+
} finally {
|
|
53248
|
+
closeSync6(descriptor);
|
|
53249
|
+
}
|
|
53250
|
+
}
|
|
53251
|
+
function freezePasteboard(options = {}) {
|
|
53252
|
+
const directory = mkdtempSync5(join23(tmpdir5(), "notes-pasteboard-"));
|
|
53253
|
+
const cleanup = () => rmSync5(directory, { recursive: true, force: true });
|
|
53254
|
+
try {
|
|
53255
|
+
const prefs = JSON.stringify(PASTEBOARD_DATA_TYPES.map((t) => [t.type, t.ext]));
|
|
53256
|
+
let raw;
|
|
53257
|
+
try {
|
|
53258
|
+
raw = (options.runJxa ?? defaultRunJxa)([
|
|
53259
|
+
directory,
|
|
53260
|
+
options.pasteboardName ?? "",
|
|
53261
|
+
String(MAX_PASTEBOARD_BYTES),
|
|
53262
|
+
prefs,
|
|
53263
|
+
options.allowPasteAlert ? "1" : "0"
|
|
53264
|
+
]);
|
|
53265
|
+
} catch (error2) {
|
|
53266
|
+
throw error2?.code === "ETIMEDOUT" ? new PasteboardError("pasteboard_timeout", MESSAGES.pasteboard_timeout) : new PasteboardError("pasteboard_unavailable", MESSAGES.pasteboard_unavailable);
|
|
53267
|
+
}
|
|
53268
|
+
let reply;
|
|
53269
|
+
try {
|
|
53270
|
+
reply = JSON.parse(raw.trim());
|
|
53271
|
+
} catch {
|
|
53272
|
+
throw new PasteboardError("pasteboard_unavailable", MESSAGES.pasteboard_unavailable);
|
|
53273
|
+
}
|
|
53274
|
+
if (reply.status !== "ok" || !reply.path || !reply.type) {
|
|
53275
|
+
const code = reply.code && reply.code in MESSAGES ? reply.code : "pasteboard_unavailable";
|
|
53276
|
+
throw replyError(code, reply);
|
|
53277
|
+
}
|
|
53278
|
+
if (reply.kind === "file") {
|
|
53279
|
+
const bytes = readRegularFile(reply.path);
|
|
53280
|
+
const filename2 = basename5(reply.path);
|
|
53281
|
+
const path11 = join23(directory, filename2);
|
|
53282
|
+
writeFileSync4(path11, bytes, { mode: 384 });
|
|
53283
|
+
return { kind: "file", type: reply.type, path: path11, filename: filename2, bytes: bytes.length, cleanup };
|
|
53284
|
+
}
|
|
53285
|
+
if (join23(directory, basename5(reply.path)) !== reply.path)
|
|
53286
|
+
throw new PasteboardError("write_failed", MESSAGES.write_failed);
|
|
53287
|
+
const label = PASTEBOARD_DATA_TYPES.find((t) => t.type === reply.type)?.label ?? "item";
|
|
53288
|
+
const filename = `Pasted ${label}${extname6(reply.path)}`;
|
|
53289
|
+
const path10 = join23(directory, filename);
|
|
53290
|
+
writeFileSync4(path10, readFileSync3(reply.path), { mode: 384 });
|
|
53291
|
+
rmSync5(reply.path, { force: true });
|
|
53292
|
+
return { kind: "data", type: reply.type, path: path10, filename, bytes: statSync4(path10).size, cleanup };
|
|
53293
|
+
} catch (error2) {
|
|
53294
|
+
cleanup();
|
|
53295
|
+
throw error2;
|
|
53296
|
+
}
|
|
53297
|
+
}
|
|
53298
|
+
|
|
53299
|
+
// src/tools/directOperations.ts
|
|
53058
53300
|
var noteId = exactIdInput(
|
|
53059
53301
|
"ICNote",
|
|
53060
53302
|
/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
|
|
@@ -53099,17 +53341,17 @@ function assertExistingContentPreserved(before, after) {
|
|
|
53099
53341
|
}
|
|
53100
53342
|
function localAttachment(path10) {
|
|
53101
53343
|
if (!isAbsolute4(path10)) throw new Error("An absolute local file path is required");
|
|
53102
|
-
const descriptor =
|
|
53344
|
+
const descriptor = openSync7(path10, constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
53103
53345
|
try {
|
|
53104
|
-
const stat =
|
|
53346
|
+
const stat = fstatSync7(descriptor);
|
|
53105
53347
|
if (!stat.isFile() || stat.size === 0 || stat.size > 64 * 1024 * 1024)
|
|
53106
53348
|
throw new Error("Attachment must be a nonempty regular file of at most 64 MiB");
|
|
53107
|
-
const bytes =
|
|
53349
|
+
const bytes = readFileSync4(descriptor);
|
|
53108
53350
|
if (bytes.length !== stat.size)
|
|
53109
53351
|
throw new Error("Attachment changed while it was being read; try again");
|
|
53110
53352
|
return bytes;
|
|
53111
53353
|
} finally {
|
|
53112
|
-
|
|
53354
|
+
closeSync7(descriptor);
|
|
53113
53355
|
}
|
|
53114
53356
|
}
|
|
53115
53357
|
function registerDirectOperations(server2, manager) {
|
|
@@ -53222,19 +53464,67 @@ function registerDirectOperations(server2, manager) {
|
|
|
53222
53464
|
}
|
|
53223
53465
|
}
|
|
53224
53466
|
);
|
|
53467
|
+
tool(
|
|
53468
|
+
"add-attachment-from-pasteboard",
|
|
53469
|
+
"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.",
|
|
53470
|
+
{
|
|
53471
|
+
id: noteId,
|
|
53472
|
+
expectedContentHash: revision,
|
|
53473
|
+
filename: attachmentInput.filename.describe(
|
|
53474
|
+
`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.`
|
|
53475
|
+
),
|
|
53476
|
+
allowPasteAlert: external_exports.boolean().optional().describe(
|
|
53477
|
+
"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."
|
|
53478
|
+
)
|
|
53479
|
+
},
|
|
53480
|
+
({ id: id2, expectedContentHash, filename, allowPasteAlert }) => {
|
|
53481
|
+
if (filename !== void 0) checkAttachmentFilename(filename);
|
|
53482
|
+
const before = readSnapshot(manager, id2);
|
|
53483
|
+
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
53484
|
+
const frozen = freezePasteboard({
|
|
53485
|
+
pasteboardName: process.env[PASTEBOARD_NAME_ENV]?.trim() || void 0,
|
|
53486
|
+
allowPasteAlert: allowPasteAlert === true
|
|
53487
|
+
});
|
|
53488
|
+
try {
|
|
53489
|
+
const source = {
|
|
53490
|
+
kind: frozen.kind,
|
|
53491
|
+
type: frozen.type,
|
|
53492
|
+
filename: frozen.filename
|
|
53493
|
+
};
|
|
53494
|
+
return {
|
|
53495
|
+
...attachFile(
|
|
53496
|
+
manager,
|
|
53497
|
+
{
|
|
53498
|
+
id: id2,
|
|
53499
|
+
expectedContentHash,
|
|
53500
|
+
path: frozen.path,
|
|
53501
|
+
filename: pasteboardFilename(filename, frozen.filename)
|
|
53502
|
+
},
|
|
53503
|
+
before
|
|
53504
|
+
),
|
|
53505
|
+
source
|
|
53506
|
+
};
|
|
53507
|
+
} finally {
|
|
53508
|
+
frozen.cleanup();
|
|
53509
|
+
}
|
|
53510
|
+
}
|
|
53511
|
+
);
|
|
53225
53512
|
}
|
|
53226
|
-
function
|
|
53227
|
-
const source = basename5(path10);
|
|
53228
|
-
if (filename === void 0) return source;
|
|
53513
|
+
function checkAttachmentFilename(filename) {
|
|
53229
53514
|
if (Buffer.byteLength(filename, "utf8") > 255)
|
|
53230
53515
|
throw new Error("filename must be at most 255 bytes");
|
|
53231
53516
|
if (filename !== filename.trim() || filename === "" || filename.startsWith(".") || /[/:\\\p{Cc}]/u.test(filename))
|
|
53232
53517
|
throw new Error(
|
|
53233
53518
|
"filename must be one path component with no slash, colon, backslash, control character, leading dot, or surrounding spaces"
|
|
53234
53519
|
);
|
|
53235
|
-
|
|
53520
|
+
}
|
|
53521
|
+
function attachmentName(path10, filename) {
|
|
53522
|
+
const source = basename6(path10);
|
|
53523
|
+
if (filename === void 0) return source;
|
|
53524
|
+
checkAttachmentFilename(filename);
|
|
53525
|
+
if (extname7(filename).toLowerCase() !== extname7(source).toLowerCase())
|
|
53236
53526
|
throw new Error(
|
|
53237
|
-
`filename must keep the source file's extension (${
|
|
53527
|
+
`filename must keep the source file's extension (${extname7(source) || "none"})`
|
|
53238
53528
|
);
|
|
53239
53529
|
return filename;
|
|
53240
53530
|
}
|
|
@@ -53273,13 +53563,13 @@ function storedInsertion(manager, id2, before, bytes, returnedId) {
|
|
|
53273
53563
|
const matches = row.assetPaths.some((path10) => {
|
|
53274
53564
|
let descriptor;
|
|
53275
53565
|
try {
|
|
53276
|
-
descriptor =
|
|
53277
|
-
const stat =
|
|
53278
|
-
return stat.isFile() && stat.size === bytes.length && sha256(
|
|
53566
|
+
descriptor = openSync7(path10, constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
53567
|
+
const stat = fstatSync7(descriptor);
|
|
53568
|
+
return stat.isFile() && stat.size === bytes.length && sha256(readFileSync4(descriptor)) === expected;
|
|
53279
53569
|
} catch {
|
|
53280
53570
|
return false;
|
|
53281
53571
|
} finally {
|
|
53282
|
-
if (descriptor !== void 0)
|
|
53572
|
+
if (descriptor !== void 0) closeSync7(descriptor);
|
|
53283
53573
|
}
|
|
53284
53574
|
});
|
|
53285
53575
|
if (!matches)
|
|
@@ -53288,18 +53578,18 @@ function storedInsertion(manager, id2, before, bytes, returnedId) {
|
|
|
53288
53578
|
);
|
|
53289
53579
|
return { attachmentId, name: row.filename };
|
|
53290
53580
|
}
|
|
53291
|
-
function attachFile(manager, args) {
|
|
53581
|
+
function attachFile(manager, args, checked) {
|
|
53292
53582
|
const { id: id2, expectedContentHash, path: path10 } = args;
|
|
53293
53583
|
const name = attachmentName(path10, args.filename);
|
|
53294
|
-
const before = readSnapshot(manager, id2);
|
|
53584
|
+
const before = checked ?? readSnapshot(manager, id2);
|
|
53295
53585
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
53296
53586
|
const bytes = localAttachment(path10);
|
|
53297
53587
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
53298
53588
|
const beforeStored = storedAttachmentIds(manager, id2);
|
|
53299
|
-
const directory =
|
|
53300
|
-
const temporaryFile =
|
|
53589
|
+
const directory = mkdtempSync6(join24(tmpdir6(), "notes-attachment-add-"));
|
|
53590
|
+
const temporaryFile = join24(directory, name);
|
|
53301
53591
|
try {
|
|
53302
|
-
|
|
53592
|
+
writeFileSync5(temporaryFile, bytes, { mode: 384 });
|
|
53303
53593
|
if (readSnapshot(manager, id2).hash !== before.hash) throw new Error("Note revision changed");
|
|
53304
53594
|
let returnedId;
|
|
53305
53595
|
let transportUncertain = false;
|
|
@@ -53359,7 +53649,7 @@ function attachFile(manager, args) {
|
|
|
53359
53649
|
} : {}
|
|
53360
53650
|
};
|
|
53361
53651
|
} finally {
|
|
53362
|
-
|
|
53652
|
+
rmSync6(directory, { recursive: true, force: true });
|
|
53363
53653
|
}
|
|
53364
53654
|
}
|
|
53365
53655
|
|
|
@@ -53367,7 +53657,7 @@ function attachFile(manager, args) {
|
|
|
53367
53657
|
import { createHash as createHash4 } from "node:crypto";
|
|
53368
53658
|
|
|
53369
53659
|
// src/utils/folderStore.ts
|
|
53370
|
-
import { execFileSync as
|
|
53660
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
53371
53661
|
import * as os9 from "os";
|
|
53372
53662
|
import * as path9 from "path";
|
|
53373
53663
|
var NOTE_STORE_PATH = path9.join(
|
|
@@ -53431,7 +53721,7 @@ function runSqlite7(dbPath2, sql, pk) {
|
|
|
53431
53721
|
const args = ["-readonly"];
|
|
53432
53722
|
if (pk !== void 0) args.push("-cmd", `.parameter set @pk ${pk}`);
|
|
53433
53723
|
args.push(dbPath2, sql);
|
|
53434
|
-
return
|
|
53724
|
+
return execFileSync20("sqlite3", args, {
|
|
53435
53725
|
encoding: "utf8",
|
|
53436
53726
|
timeout: 5e3,
|
|
53437
53727
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -53686,9 +53976,426 @@ function registerFolderDelete(server2, manager, deps = defaultDeps) {
|
|
|
53686
53976
|
);
|
|
53687
53977
|
}
|
|
53688
53978
|
|
|
53979
|
+
// src/services/templateStore.ts
|
|
53980
|
+
import { randomBytes } from "node:crypto";
|
|
53981
|
+
import {
|
|
53982
|
+
closeSync as closeSync8,
|
|
53983
|
+
constants as constants8,
|
|
53984
|
+
fstatSync as fstatSync8,
|
|
53985
|
+
linkSync,
|
|
53986
|
+
lstatSync as lstatSync5,
|
|
53987
|
+
mkdirSync as mkdirSync7,
|
|
53988
|
+
openSync as openSync8,
|
|
53989
|
+
readdirSync as readdirSync4,
|
|
53990
|
+
readSync as readSync5,
|
|
53991
|
+
renameSync,
|
|
53992
|
+
unlinkSync as unlinkSync3,
|
|
53993
|
+
writeSync as writeSync5
|
|
53994
|
+
} from "node:fs";
|
|
53995
|
+
import { homedir as homedir19 } from "node:os";
|
|
53996
|
+
import { isAbsolute as isAbsolute5, join as join26, resolve as resolve5 } from "node:path";
|
|
53997
|
+
var TEMPLATE_SLUG = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
53998
|
+
var TemplateStoreError = class extends Error {
|
|
53999
|
+
constructor(code, message) {
|
|
54000
|
+
super(message);
|
|
54001
|
+
this.code = code;
|
|
54002
|
+
this.name = "TemplateStoreError";
|
|
54003
|
+
}
|
|
54004
|
+
code;
|
|
54005
|
+
};
|
|
54006
|
+
function templateDir(env = process.env) {
|
|
54007
|
+
const override = env.APPLE_NOTES_MCP_TEMPLATE_DIR?.trim();
|
|
54008
|
+
if (override) {
|
|
54009
|
+
if (!isAbsolute5(override))
|
|
54010
|
+
throw new TemplateStoreError(
|
|
54011
|
+
"unsafe-path",
|
|
54012
|
+
"APPLE_NOTES_MCP_TEMPLATE_DIR must be an absolute path."
|
|
54013
|
+
);
|
|
54014
|
+
return resolve5(override);
|
|
54015
|
+
}
|
|
54016
|
+
return join26(homedir19(), "Library/Application Support/apple-notes-mcp/templates");
|
|
54017
|
+
}
|
|
54018
|
+
function checkName(name, action) {
|
|
54019
|
+
if (isBuiltinTemplate(name) && action !== "read")
|
|
54020
|
+
throw new TemplateStoreError(
|
|
54021
|
+
"reserved-name",
|
|
54022
|
+
`"${name}" is a built-in template and cannot be ${action === "save" ? "saved over" : "deleted"}.`
|
|
54023
|
+
);
|
|
54024
|
+
if (!TEMPLATE_SLUG.test(name))
|
|
54025
|
+
throw new TemplateStoreError(
|
|
54026
|
+
"invalid-name",
|
|
54027
|
+
`Template names are 1-64 characters of a-z, 0-9, "-" and "_", starting with a letter or digit; got "${name}".`
|
|
54028
|
+
);
|
|
54029
|
+
}
|
|
54030
|
+
var TemplateStore = class {
|
|
54031
|
+
constructor(dir = templateDir()) {
|
|
54032
|
+
this.dir = dir;
|
|
54033
|
+
}
|
|
54034
|
+
dir;
|
|
54035
|
+
/** The library root, which must be a real directory (not a symlink) when it exists. */
|
|
54036
|
+
root(create) {
|
|
54037
|
+
let stat;
|
|
54038
|
+
try {
|
|
54039
|
+
stat = lstatSync5(this.dir);
|
|
54040
|
+
} catch {
|
|
54041
|
+
if (!create) return void 0;
|
|
54042
|
+
mkdirSync7(this.dir, { recursive: true, mode: 448 });
|
|
54043
|
+
stat = lstatSync5(this.dir);
|
|
54044
|
+
}
|
|
54045
|
+
if (!stat.isDirectory())
|
|
54046
|
+
throw new TemplateStoreError(
|
|
54047
|
+
"unsafe-path",
|
|
54048
|
+
`The template library ${this.dir} is not a directory (symlinks are refused).`
|
|
54049
|
+
);
|
|
54050
|
+
return this.dir;
|
|
54051
|
+
}
|
|
54052
|
+
file(name) {
|
|
54053
|
+
return join26(this.dir, `${name}.json`);
|
|
54054
|
+
}
|
|
54055
|
+
/** Read a saved template's text, refusing symlinks and non-regular files. */
|
|
54056
|
+
readText(path10) {
|
|
54057
|
+
let fd;
|
|
54058
|
+
try {
|
|
54059
|
+
fd = openSync8(path10, constants8.O_RDONLY | constants8.O_NOFOLLOW);
|
|
54060
|
+
} catch (error2) {
|
|
54061
|
+
if (error2.code === "ENOENT") return void 0;
|
|
54062
|
+
throw new TemplateStoreError("unsafe-path", `Refusing to read ${path10}: not a regular file.`);
|
|
54063
|
+
}
|
|
54064
|
+
try {
|
|
54065
|
+
const stat = fstatSync8(fd);
|
|
54066
|
+
if (!stat.isFile())
|
|
54067
|
+
throw new TemplateStoreError(
|
|
54068
|
+
"unsafe-path",
|
|
54069
|
+
`Refusing to read ${path10}: not a regular file.`
|
|
54070
|
+
);
|
|
54071
|
+
if (stat.size > MAX_TEMPLATE_BYTES)
|
|
54072
|
+
throw new TemplateValidationError([
|
|
54073
|
+
{
|
|
54074
|
+
path: "$",
|
|
54075
|
+
message: `template is ${stat.size} bytes; the limit is ${MAX_TEMPLATE_BYTES}`
|
|
54076
|
+
}
|
|
54077
|
+
]);
|
|
54078
|
+
const data = Buffer.alloc(stat.size);
|
|
54079
|
+
let read = 0;
|
|
54080
|
+
while (read < stat.size) {
|
|
54081
|
+
const n = readSync5(fd, data, read, stat.size - read, read);
|
|
54082
|
+
if (n <= 0) break;
|
|
54083
|
+
read += n;
|
|
54084
|
+
}
|
|
54085
|
+
return { text: data.subarray(0, read).toString("utf8"), bytes: read, mtime: stat.mtime };
|
|
54086
|
+
} finally {
|
|
54087
|
+
closeSync8(fd);
|
|
54088
|
+
}
|
|
54089
|
+
}
|
|
54090
|
+
/** Saved templates in name order; `skipped` counts entries that are not usable. */
|
|
54091
|
+
list() {
|
|
54092
|
+
const templates = [];
|
|
54093
|
+
let skipped = 0;
|
|
54094
|
+
if (!this.root(false)) return { templates, skipped, dir: this.dir };
|
|
54095
|
+
for (const entry of readdirSync4(this.dir).sort()) {
|
|
54096
|
+
if (!entry.endsWith(".json") || entry.startsWith(".")) continue;
|
|
54097
|
+
const name = entry.slice(0, -5);
|
|
54098
|
+
if (!TEMPLATE_SLUG.test(name) || isBuiltinTemplate(name)) {
|
|
54099
|
+
skipped++;
|
|
54100
|
+
continue;
|
|
54101
|
+
}
|
|
54102
|
+
try {
|
|
54103
|
+
const read = this.readText(this.file(name));
|
|
54104
|
+
if (!read) continue;
|
|
54105
|
+
const template = parseTemplate(read.text);
|
|
54106
|
+
templates.push({
|
|
54107
|
+
name,
|
|
54108
|
+
...template.name && template.name !== name ? { displayName: template.name } : {},
|
|
54109
|
+
...template.description ? { description: template.description } : {},
|
|
54110
|
+
...template.extends ? { extends: template.extends } : {},
|
|
54111
|
+
bytes: read.bytes,
|
|
54112
|
+
modified: read.mtime.toISOString()
|
|
54113
|
+
});
|
|
54114
|
+
} catch {
|
|
54115
|
+
skipped++;
|
|
54116
|
+
}
|
|
54117
|
+
}
|
|
54118
|
+
return { templates, skipped, dir: this.dir };
|
|
54119
|
+
}
|
|
54120
|
+
/**
|
|
54121
|
+
* A saved template in its portable (as stored) form, or undefined when
|
|
54122
|
+
* there is none. Throws {@link TemplateValidationError} for a corrupt file.
|
|
54123
|
+
*/
|
|
54124
|
+
find(name) {
|
|
54125
|
+
checkName(name, "read");
|
|
54126
|
+
if (!this.root(false)) return void 0;
|
|
54127
|
+
const read = this.readText(this.file(name));
|
|
54128
|
+
return read ? parseTemplate(read.text) : void 0;
|
|
54129
|
+
}
|
|
54130
|
+
/** Like {@link find}, but a missing template is an error. */
|
|
54131
|
+
get(name) {
|
|
54132
|
+
const template = this.find(name);
|
|
54133
|
+
if (!template)
|
|
54134
|
+
throw new TemplateStoreError(
|
|
54135
|
+
"template-not-found",
|
|
54136
|
+
`No saved template named "${name}". Built-in templates: ${BUILTIN_TEMPLATE_NAMES.join(", ")}.`
|
|
54137
|
+
);
|
|
54138
|
+
return template;
|
|
54139
|
+
}
|
|
54140
|
+
/**
|
|
54141
|
+
* Validate and save a template under `name`. Create-only unless `force`.
|
|
54142
|
+
* Returns the file path and whether an existing template was replaced.
|
|
54143
|
+
*/
|
|
54144
|
+
save(name, text2, { force = false } = {}) {
|
|
54145
|
+
checkName(name, "save");
|
|
54146
|
+
const template = parseTemplate(text2);
|
|
54147
|
+
const body = JSON.stringify(template, null, 2) + "\n";
|
|
54148
|
+
this.root(true);
|
|
54149
|
+
const path10 = this.file(name);
|
|
54150
|
+
let exists = false;
|
|
54151
|
+
try {
|
|
54152
|
+
const stat = lstatSync5(path10);
|
|
54153
|
+
if (!stat.isFile())
|
|
54154
|
+
throw new TemplateStoreError(
|
|
54155
|
+
"unsafe-path",
|
|
54156
|
+
`Refusing to replace ${path10}: it is not a regular file.`
|
|
54157
|
+
);
|
|
54158
|
+
exists = true;
|
|
54159
|
+
} catch (error2) {
|
|
54160
|
+
if (error2 instanceof TemplateStoreError) throw error2;
|
|
54161
|
+
}
|
|
54162
|
+
if (exists && !force)
|
|
54163
|
+
throw new TemplateStoreError(
|
|
54164
|
+
"template-exists",
|
|
54165
|
+
`A template named "${name}" already exists. Pass force: true to replace it.`
|
|
54166
|
+
);
|
|
54167
|
+
const temp = join26(this.dir, `.${name}.${randomBytes(6).toString("hex")}.tmp`);
|
|
54168
|
+
const fd = openSync8(
|
|
54169
|
+
temp,
|
|
54170
|
+
constants8.O_WRONLY | constants8.O_CREAT | constants8.O_EXCL | constants8.O_NOFOLLOW,
|
|
54171
|
+
384
|
|
54172
|
+
);
|
|
54173
|
+
try {
|
|
54174
|
+
const data = Buffer.from(body, "utf8");
|
|
54175
|
+
let written = 0;
|
|
54176
|
+
while (written < data.length) written += writeSync5(fd, data, written);
|
|
54177
|
+
} finally {
|
|
54178
|
+
closeSync8(fd);
|
|
54179
|
+
}
|
|
54180
|
+
try {
|
|
54181
|
+
if (force) renameSync(temp, path10);
|
|
54182
|
+
else {
|
|
54183
|
+
try {
|
|
54184
|
+
linkSync(temp, path10);
|
|
54185
|
+
} catch (error2) {
|
|
54186
|
+
if (error2.code === "EEXIST")
|
|
54187
|
+
throw new TemplateStoreError(
|
|
54188
|
+
"template-exists",
|
|
54189
|
+
`A template named "${name}" already exists. Pass force: true to replace it.`
|
|
54190
|
+
);
|
|
54191
|
+
throw error2;
|
|
54192
|
+
}
|
|
54193
|
+
}
|
|
54194
|
+
} finally {
|
|
54195
|
+
try {
|
|
54196
|
+
unlinkSync3(temp);
|
|
54197
|
+
} catch {
|
|
54198
|
+
}
|
|
54199
|
+
}
|
|
54200
|
+
return { path: path10, replaced: exists, bytes: Buffer.byteLength(body), template };
|
|
54201
|
+
}
|
|
54202
|
+
/** Delete a saved template. Built-ins cannot be deleted. */
|
|
54203
|
+
delete(name) {
|
|
54204
|
+
checkName(name, "delete");
|
|
54205
|
+
const path10 = this.file(name);
|
|
54206
|
+
let stat;
|
|
54207
|
+
try {
|
|
54208
|
+
if (!this.root(false)) throw new Error("absent");
|
|
54209
|
+
stat = lstatSync5(path10);
|
|
54210
|
+
} catch (error2) {
|
|
54211
|
+
if (error2 instanceof TemplateStoreError) throw error2;
|
|
54212
|
+
throw new TemplateStoreError("template-not-found", `No saved template named "${name}".`);
|
|
54213
|
+
}
|
|
54214
|
+
if (!stat.isFile())
|
|
54215
|
+
throw new TemplateStoreError(
|
|
54216
|
+
"unsafe-path",
|
|
54217
|
+
`Refusing to delete ${path10}: not a regular file.`
|
|
54218
|
+
);
|
|
54219
|
+
unlinkSync3(path10);
|
|
54220
|
+
return { path: path10 };
|
|
54221
|
+
}
|
|
54222
|
+
};
|
|
54223
|
+
|
|
54224
|
+
// src/tools/markdownTemplates.ts
|
|
54225
|
+
var ok = (text2, structured) => ({
|
|
54226
|
+
content: [{ type: "text", text: text2 }],
|
|
54227
|
+
structuredContent: structured
|
|
54228
|
+
});
|
|
54229
|
+
function failure(action, error2) {
|
|
54230
|
+
if (error2 instanceof TemplateValidationError)
|
|
54231
|
+
return errorResult(
|
|
54232
|
+
`Error ${action} [invalid-template]: the template is invalid:
|
|
54233
|
+
` + error2.errors.map((e) => `${e.path}: ${e.message}`).join("\n"),
|
|
54234
|
+
error2
|
|
54235
|
+
);
|
|
54236
|
+
if (error2 instanceof TemplateStoreError)
|
|
54237
|
+
return errorResult(`Error ${action} [${error2.code}]: ${error2.message}`, error2);
|
|
54238
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
54239
|
+
return errorResult(`Error ${action}: ${message}`, error2);
|
|
54240
|
+
}
|
|
54241
|
+
var nameInput = external_exports.string().min(1).max(64).describe("Template name: a built-in (standard-markdown, obsidian) or a saved template's name");
|
|
54242
|
+
var templateInput = external_exports.union([external_exports.record(external_exports.unknown()), external_exports.string().max(MAX_TEMPLATE_BYTES)]).optional().describe("The template itself, as a JSON object or JSON text (schemaVersion 1)");
|
|
54243
|
+
var templateFileInput = external_exports.string().min(1).max(4096).optional().describe(
|
|
54244
|
+
"Absolute path of a JSON template file ending in .json (home, a temp dir, or /Volumes; symlinks refused; at most 256 KiB)"
|
|
54245
|
+
);
|
|
54246
|
+
function sourceText(args) {
|
|
54247
|
+
if (args.template === void 0 === (args.templateFile === void 0))
|
|
54248
|
+
throw new Error("Provide exactly one of 'template' or 'templateFile'.");
|
|
54249
|
+
if (args.templateFile !== void 0) return readTemplateFile(args.templateFile);
|
|
54250
|
+
return typeof args.template === "string" ? args.template : JSON.stringify(args.template);
|
|
54251
|
+
}
|
|
54252
|
+
function registerMarkdownTemplates(server2, store = () => new TemplateStore()) {
|
|
54253
|
+
const loose = external_exports.object({}).passthrough();
|
|
54254
|
+
server2.registerTool(
|
|
54255
|
+
"list-markdown-templates",
|
|
54256
|
+
{
|
|
54257
|
+
description: "Use when: choosing a Markdown export template, or checking which saved templates exist.\nReturns: the built-in templates, the saved templates (name, display name, description, size, modified date), the library directory, and how many unusable files were skipped.\nDo not use when: you need a template's rules (show-markdown-template).\nSafety: read-only; never opens Notes.",
|
|
54258
|
+
inputSchema: {},
|
|
54259
|
+
outputSchema: loose,
|
|
54260
|
+
annotations: { readOnlyHint: true }
|
|
54261
|
+
},
|
|
54262
|
+
(async () => {
|
|
54263
|
+
try {
|
|
54264
|
+
const listing = store().list();
|
|
54265
|
+
const builtins = BUILTIN_TEMPLATE_NAMES.map((name) => ({
|
|
54266
|
+
name,
|
|
54267
|
+
description: builtinTemplate(name).description ?? ""
|
|
54268
|
+
}));
|
|
54269
|
+
return ok(
|
|
54270
|
+
`${builtins.length} built-in and ${listing.templates.length} saved template(s)` + (listing.skipped ? `; skipped ${listing.skipped} unusable file(s)` : "") + ".",
|
|
54271
|
+
{ builtins, ...listing }
|
|
54272
|
+
);
|
|
54273
|
+
} catch (error2) {
|
|
54274
|
+
return failure("listing templates", error2);
|
|
54275
|
+
}
|
|
54276
|
+
})
|
|
54277
|
+
);
|
|
54278
|
+
const showInput = {
|
|
54279
|
+
name: nameInput,
|
|
54280
|
+
expanded: external_exports.boolean().optional().describe("Also return the fully expanded template (every rule filled from its base)")
|
|
54281
|
+
};
|
|
54282
|
+
server2.registerTool(
|
|
54283
|
+
"show-markdown-template",
|
|
54284
|
+
{
|
|
54285
|
+
description: "Use when: reading a built-in or saved template, to copy it as the start of a new one or to see what an export will do.\nReturns: the template in its portable form (as saved, overrides only), its source, and with expanded: true every rule filled in.\nDo not use when: listing names (list-markdown-templates).\nSafety: read-only.",
|
|
54286
|
+
inputSchema: showInput,
|
|
54287
|
+
outputSchema: loose,
|
|
54288
|
+
annotations: { readOnlyHint: true }
|
|
54289
|
+
},
|
|
54290
|
+
(async ({ name, expanded }) => {
|
|
54291
|
+
try {
|
|
54292
|
+
const builtin = isBuiltinTemplate(name);
|
|
54293
|
+
const template = builtin ? builtinTemplate(name) : store().get(name);
|
|
54294
|
+
const structured = {
|
|
54295
|
+
name,
|
|
54296
|
+
source: builtin ? "builtin" : "saved",
|
|
54297
|
+
template,
|
|
54298
|
+
...expanded ? { expanded: resolveTemplate(template, name) } : {}
|
|
54299
|
+
};
|
|
54300
|
+
return ok(JSON.stringify(template, null, 2), structured);
|
|
54301
|
+
} catch (error2) {
|
|
54302
|
+
return failure("showing template", error2);
|
|
54303
|
+
}
|
|
54304
|
+
})
|
|
54305
|
+
);
|
|
54306
|
+
const validateInput = {
|
|
54307
|
+
name: nameInput.optional(),
|
|
54308
|
+
template: templateInput,
|
|
54309
|
+
templateFile: templateFileInput
|
|
54310
|
+
};
|
|
54311
|
+
server2.registerTool(
|
|
54312
|
+
"validate-markdown-template",
|
|
54313
|
+
{
|
|
54314
|
+
description: 'Use when: checking a template before saving it or exporting with it.\nReturns: valid true, or valid false with every problem as {path, message}, where path is a JSON path such as $.rules["inline.bold"].after.\nDo not use when: you want to store it (save-markdown-template validates too).\nSafety: read-only. Pass exactly one of name (a saved or built-in template), template (JSON object or text) or templateFile.',
|
|
54315
|
+
inputSchema: validateInput,
|
|
54316
|
+
outputSchema: loose,
|
|
54317
|
+
annotations: { readOnlyHint: true }
|
|
54318
|
+
},
|
|
54319
|
+
(async (args) => {
|
|
54320
|
+
try {
|
|
54321
|
+
const given = [args.name, args.template, args.templateFile].filter(
|
|
54322
|
+
(value) => value !== void 0
|
|
54323
|
+
).length;
|
|
54324
|
+
if (given !== 1)
|
|
54325
|
+
throw new Error("Provide exactly one of 'name', 'template' or 'templateFile'.");
|
|
54326
|
+
try {
|
|
54327
|
+
if (args.name !== void 0) {
|
|
54328
|
+
if (!isBuiltinTemplate(args.name)) store().get(args.name);
|
|
54329
|
+
} else parseTemplate(sourceText(args));
|
|
54330
|
+
} catch (error2) {
|
|
54331
|
+
if (!(error2 instanceof TemplateValidationError)) throw error2;
|
|
54332
|
+
return ok(
|
|
54333
|
+
`Invalid template (${error2.errors.length} problem(s)):
|
|
54334
|
+
` + error2.errors.map((e) => `${e.path}: ${e.message}`).join("\n"),
|
|
54335
|
+
{ valid: false, errors: error2.errors }
|
|
54336
|
+
);
|
|
54337
|
+
}
|
|
54338
|
+
return ok("The template is valid.", { valid: true, errors: [] });
|
|
54339
|
+
} catch (error2) {
|
|
54340
|
+
return failure("validating template", error2);
|
|
54341
|
+
}
|
|
54342
|
+
})
|
|
54343
|
+
);
|
|
54344
|
+
const saveInput = {
|
|
54345
|
+
name: external_exports.string().min(1).max(64).describe(
|
|
54346
|
+
'Name to save under: lowercase a-z, 0-9, "-" and "_", starting with a letter or digit'
|
|
54347
|
+
),
|
|
54348
|
+
template: templateInput,
|
|
54349
|
+
templateFile: templateFileInput,
|
|
54350
|
+
force: external_exports.boolean().optional().describe("Replace an existing saved template of this name (default false: create-only)")
|
|
54351
|
+
};
|
|
54352
|
+
server2.registerTool(
|
|
54353
|
+
"save-markdown-template",
|
|
54354
|
+
{
|
|
54355
|
+
description: "Use when: storing a template so export-notes-markdown can use it by name.\nReturns: name, path, bytes and whether an existing template was replaced.\nDo not use when: exporting once (pass templateFile to export-notes-markdown instead).\nSafety: writes one file in the template library only (APPLE_NOTES_MCP_TEMPLATE_DIR, default ~/Library/Application Support/apple-notes-mcp/templates, mode 0600). Validates first; an invalid template is refused with every JSON path. Create-only: an existing name is refused with [template-exists] unless force is true. Built-in names are reserved.",
|
|
54356
|
+
inputSchema: saveInput,
|
|
54357
|
+
outputSchema: loose,
|
|
54358
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
54359
|
+
},
|
|
54360
|
+
(async (args) => {
|
|
54361
|
+
try {
|
|
54362
|
+
const saved = store().save(args.name, sourceText(args), { force: args.force });
|
|
54363
|
+
return ok(
|
|
54364
|
+
`${saved.replaced ? "Replaced" : "Saved"} template "${args.name}" (${saved.bytes} bytes) at ${saved.path}.`,
|
|
54365
|
+
{ name: args.name, path: saved.path, bytes: saved.bytes, replaced: saved.replaced }
|
|
54366
|
+
);
|
|
54367
|
+
} catch (error2) {
|
|
54368
|
+
return failure("saving template", error2);
|
|
54369
|
+
}
|
|
54370
|
+
})
|
|
54371
|
+
);
|
|
54372
|
+
const deleteInput = { name: external_exports.string().min(1).max(64).describe("Saved template to delete") };
|
|
54373
|
+
server2.registerTool(
|
|
54374
|
+
"delete-markdown-template",
|
|
54375
|
+
{
|
|
54376
|
+
description: "Use when: removing a saved Markdown template the user no longer wants.\nReturns: the name and the path of the removed file.\nDo not use when: the template is built in (standard-markdown, obsidian cannot be deleted).\nSafety: permanently removes one file from the template library; never touches Notes.",
|
|
54377
|
+
inputSchema: deleteInput,
|
|
54378
|
+
outputSchema: loose,
|
|
54379
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false }
|
|
54380
|
+
},
|
|
54381
|
+
(async ({ name }) => {
|
|
54382
|
+
try {
|
|
54383
|
+
const removed = store().delete(name);
|
|
54384
|
+
return ok(`Deleted template "${name}" (${removed.path}).`, {
|
|
54385
|
+
name,
|
|
54386
|
+
path: removed.path,
|
|
54387
|
+
deleted: true
|
|
54388
|
+
});
|
|
54389
|
+
} catch (error2) {
|
|
54390
|
+
return failure("deleting template", error2);
|
|
54391
|
+
}
|
|
54392
|
+
})
|
|
54393
|
+
);
|
|
54394
|
+
}
|
|
54395
|
+
|
|
53689
54396
|
// src/utils/svgAnalyzer.ts
|
|
53690
54397
|
import { createHash as createHash5 } from "node:crypto";
|
|
53691
|
-
import { closeSync as
|
|
54398
|
+
import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, openSync as openSync9, readSync as readSync6 } from "node:fs";
|
|
53692
54399
|
|
|
53693
54400
|
// src/utils/svgColor.ts
|
|
53694
54401
|
var NAMED = {
|
|
@@ -54629,7 +55336,7 @@ function parseXml(source, limits) {
|
|
|
54629
55336
|
if (a.name === "xmlns") scope2.set("", a.value);
|
|
54630
55337
|
else if (a.name.startsWith("xmlns:")) scope2.set(a.name.slice(6), a.value);
|
|
54631
55338
|
}
|
|
54632
|
-
const
|
|
55339
|
+
const resolve9 = (qualified, isAttribute) => {
|
|
54633
55340
|
const colon = qualified.indexOf(":");
|
|
54634
55341
|
if (colon < 0) return { local: qualified, ns: isAttribute ? null : scope2.get("") ?? null };
|
|
54635
55342
|
const prefix = qualified.slice(0, colon);
|
|
@@ -54639,12 +55346,12 @@ function parseXml(source, limits) {
|
|
|
54639
55346
|
if (ns === void 0) fail(`Undeclared namespace prefix "${prefix.slice(0, 40)}"`);
|
|
54640
55347
|
return { local, ns };
|
|
54641
55348
|
};
|
|
54642
|
-
const resolved =
|
|
55349
|
+
const resolved = resolve9(name, false);
|
|
54643
55350
|
const element = {
|
|
54644
55351
|
name,
|
|
54645
55352
|
local: resolved.local,
|
|
54646
55353
|
ns: resolved.ns,
|
|
54647
|
-
attributes: rawAttributes.filter((a) => a.name !== "xmlns" && !a.name.startsWith("xmlns:")).map((a) => ({ name: a.name, value: a.value, ...
|
|
55354
|
+
attributes: rawAttributes.filter((a) => a.name !== "xmlns" && !a.name.startsWith("xmlns:")).map((a) => ({ name: a.name, value: a.value, ...resolve9(a.name, true) })),
|
|
54648
55355
|
children: [],
|
|
54649
55356
|
text: "",
|
|
54650
55357
|
line: tagLine
|
|
@@ -55657,14 +56364,14 @@ function analyzeSvgBuffer(source) {
|
|
|
55657
56364
|
function readSvgSource(path10) {
|
|
55658
56365
|
let fd;
|
|
55659
56366
|
try {
|
|
55660
|
-
fd =
|
|
56367
|
+
fd = openSync9(path10, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
|
|
55661
56368
|
} catch (error2) {
|
|
55662
56369
|
if (error2.code === "ELOOP")
|
|
55663
56370
|
throw new SvgError("svg_file_invalid", "The SVG path is a symbolic link");
|
|
55664
56371
|
throw new SvgError("svg_file_invalid", "The SVG file does not exist or cannot be read");
|
|
55665
56372
|
}
|
|
55666
56373
|
try {
|
|
55667
|
-
const info =
|
|
56374
|
+
const info = fstatSync9(fd);
|
|
55668
56375
|
if (!info.isFile())
|
|
55669
56376
|
throw new SvgError("svg_file_invalid", "The SVG path is not a regular file");
|
|
55670
56377
|
if (info.size > SVG_LIMITS.maxSourceBytes)
|
|
@@ -55675,7 +56382,7 @@ function readSvgSource(path10) {
|
|
|
55675
56382
|
const buffer = Buffer.alloc(SVG_LIMITS.maxSourceBytes + 1);
|
|
55676
56383
|
let total = 0;
|
|
55677
56384
|
for (; ; ) {
|
|
55678
|
-
const n =
|
|
56385
|
+
const n = readSync6(fd, buffer, total, buffer.length - total, null);
|
|
55679
56386
|
if (n === 0) break;
|
|
55680
56387
|
total += n;
|
|
55681
56388
|
if (total > SVG_LIMITS.maxSourceBytes)
|
|
@@ -55686,7 +56393,7 @@ function readSvgSource(path10) {
|
|
|
55686
56393
|
}
|
|
55687
56394
|
return buffer.subarray(0, total);
|
|
55688
56395
|
} finally {
|
|
55689
|
-
|
|
56396
|
+
closeSync9(fd);
|
|
55690
56397
|
}
|
|
55691
56398
|
}
|
|
55692
56399
|
function analyzeSvgFile(path10) {
|
|
@@ -56257,7 +56964,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
56257
56964
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
56258
56965
|
import { existsSync as existsSync13 } from "node:fs";
|
|
56259
56966
|
import { release as release2 } from "node:os";
|
|
56260
|
-
import { dirname as dirname7, resolve as
|
|
56967
|
+
import { dirname as dirname7, resolve as resolve6 } from "node:path";
|
|
56261
56968
|
import { fileURLToPath } from "node:url";
|
|
56262
56969
|
var OPTIONAL_BRIDGE_NOTE = "(optional \u2014 needed only for create-note format: markdown, macOS 26+)";
|
|
56263
56970
|
var MARKDOWN_MIN_DARWIN_MAJOR = 25;
|
|
@@ -56280,11 +56987,11 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
56280
56987
|
const result = spawnSync2("/usr/bin/open", [path10], { encoding: "utf8" });
|
|
56281
56988
|
return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
|
|
56282
56989
|
});
|
|
56283
|
-
const baseDirectory = dependencies.baseDirectory ||
|
|
56990
|
+
const baseDirectory = dependencies.baseDirectory || resolve6(dirname7(fileURLToPath(import.meta.url)), "../shortcuts");
|
|
56284
56991
|
const osRelease = (dependencies.osRelease || release2)();
|
|
56285
56992
|
const darwinMajor = Number.parseInt(osRelease.split(".")[0], 10);
|
|
56286
56993
|
const items = shortcutFiles.map(({ name, file, optional: optional2 }) => {
|
|
56287
|
-
const path10 =
|
|
56994
|
+
const path10 = resolve6(baseDirectory, file);
|
|
56288
56995
|
let installed = false;
|
|
56289
56996
|
let identifier;
|
|
56290
56997
|
let error2;
|
|
@@ -56348,15 +57055,15 @@ import { createHash as createHash6 } from "node:crypto";
|
|
|
56348
57055
|
import {
|
|
56349
57056
|
chmodSync,
|
|
56350
57057
|
existsSync as existsSync14,
|
|
56351
|
-
mkdirSync as
|
|
56352
|
-
mkdtempSync as
|
|
56353
|
-
readFileSync as
|
|
56354
|
-
renameSync,
|
|
56355
|
-
rmSync as
|
|
56356
|
-
writeFileSync as
|
|
57058
|
+
mkdirSync as mkdirSync8,
|
|
57059
|
+
mkdtempSync as mkdtempSync7,
|
|
57060
|
+
readFileSync as readFileSync5,
|
|
57061
|
+
renameSync as renameSync2,
|
|
57062
|
+
rmSync as rmSync7,
|
|
57063
|
+
writeFileSync as writeFileSync6
|
|
56357
57064
|
} from "node:fs";
|
|
56358
|
-
import { homedir as
|
|
56359
|
-
import { dirname as dirname8, join as
|
|
57065
|
+
import { homedir as homedir20, release as release3 } from "node:os";
|
|
57066
|
+
import { dirname as dirname8, join as join27, resolve as resolve7 } from "node:path";
|
|
56360
57067
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
56361
57068
|
var PUBLIC_HELPER_PROTOCOL = 1;
|
|
56362
57069
|
var PUBLIC_HELPER_DIR_ENV = "APPLE_NOTES_MCP_PUBLIC_HELPER_DIR";
|
|
@@ -56384,16 +57091,16 @@ var publicManifestSchema = external_exports.object({
|
|
|
56384
57091
|
function packageRoot(fromDir = dirname8(fileURLToPath2(import.meta.url))) {
|
|
56385
57092
|
let dir = fromDir;
|
|
56386
57093
|
for (; ; ) {
|
|
56387
|
-
const candidate =
|
|
57094
|
+
const candidate = join27(dir, "package.json");
|
|
56388
57095
|
if (existsSync14(candidate)) {
|
|
56389
57096
|
try {
|
|
56390
|
-
if (JSON.parse(
|
|
57097
|
+
if (JSON.parse(readFileSync5(candidate, "utf8")).name === "apple-notes-mcp")
|
|
56391
57098
|
return dir;
|
|
56392
57099
|
} catch {
|
|
56393
57100
|
}
|
|
56394
57101
|
}
|
|
56395
57102
|
const parent = dirname8(dir);
|
|
56396
|
-
if (parent === dir) return
|
|
57103
|
+
if (parent === dir) return resolve7(fromDir, "..");
|
|
56397
57104
|
dir = parent;
|
|
56398
57105
|
}
|
|
56399
57106
|
}
|
|
@@ -56401,9 +57108,9 @@ function defaultPublicHelperDeps(overrides = {}) {
|
|
|
56401
57108
|
return {
|
|
56402
57109
|
env: process.env,
|
|
56403
57110
|
platform: process.platform,
|
|
56404
|
-
sourcePath:
|
|
57111
|
+
sourcePath: join27(packageRoot(), PUBLIC_HELPER_SOURCE),
|
|
56405
57112
|
exists: existsSync14,
|
|
56406
|
-
readFile: (path10) =>
|
|
57113
|
+
readFile: (path10) => readFileSync5(path10),
|
|
56407
57114
|
spawn: spawnSync3,
|
|
56408
57115
|
spawnAsync: spawn,
|
|
56409
57116
|
...overrides
|
|
@@ -56412,14 +57119,14 @@ function defaultPublicHelperDeps(overrides = {}) {
|
|
|
56412
57119
|
function publicHelperInstallDir(env = process.env) {
|
|
56413
57120
|
const override = env[PUBLIC_HELPER_DIR_ENV]?.trim();
|
|
56414
57121
|
if (override) return override;
|
|
56415
|
-
return
|
|
57122
|
+
return join27(homedir20(), "Library", "Application Support", "apple-notes-mcp", "public-helper");
|
|
56416
57123
|
}
|
|
56417
57124
|
function sha256Hex(data) {
|
|
56418
57125
|
return createHash6("sha256").update(data).digest("hex");
|
|
56419
57126
|
}
|
|
56420
57127
|
function inspectPublicHelper(deps = defaultPublicHelperDeps()) {
|
|
56421
57128
|
const installDir = publicHelperInstallDir(deps.env);
|
|
56422
|
-
const binaryPath =
|
|
57129
|
+
const binaryPath = join27(installDir, PUBLIC_HELPER_BINARY);
|
|
56423
57130
|
const base = { installDir, binaryPath, sourcePath: deps.sourcePath, manifest: null };
|
|
56424
57131
|
const fail = (reason, detail) => ({
|
|
56425
57132
|
...base,
|
|
@@ -56430,7 +57137,7 @@ function inspectPublicHelper(deps = defaultPublicHelperDeps()) {
|
|
|
56430
57137
|
if (deps.platform !== "darwin") return fail("unsupported_platform", "macOS only");
|
|
56431
57138
|
if (!deps.exists(deps.sourcePath))
|
|
56432
57139
|
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
56433
|
-
const manifestPath =
|
|
57140
|
+
const manifestPath = join27(installDir, PUBLIC_HELPER_MANIFEST);
|
|
56434
57141
|
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
56435
57142
|
return fail(
|
|
56436
57143
|
"helper_not_installed",
|
|
@@ -56470,14 +57177,14 @@ var UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
|
|
|
56470
57177
|
"helper_modified",
|
|
56471
57178
|
"helper_manifest_invalid"
|
|
56472
57179
|
]);
|
|
56473
|
-
var
|
|
57180
|
+
var ENVELOPE_CODES2 = {
|
|
56474
57181
|
invalid_request: "validation_error",
|
|
56475
57182
|
attachment_not_found: "not_found"
|
|
56476
57183
|
};
|
|
56477
57184
|
var PublicHelperError = class extends CodedError {
|
|
56478
57185
|
constructor(code, message) {
|
|
56479
57186
|
super(message, {
|
|
56480
|
-
code: UNAVAILABLE_CODES.has(code) ? "unsupported" :
|
|
57187
|
+
code: UNAVAILABLE_CODES.has(code) ? "unsupported" : ENVELOPE_CODES2[code] ?? "operation_failed",
|
|
56481
57188
|
helperCode: code
|
|
56482
57189
|
});
|
|
56483
57190
|
this.code = code;
|
|
@@ -56522,9 +57229,9 @@ async function callPublicHelperAsync(action, fields = {}, deps = defaultPublicHe
|
|
|
56522
57229
|
});
|
|
56523
57230
|
const chunks = [];
|
|
56524
57231
|
let size = 0;
|
|
56525
|
-
let
|
|
57232
|
+
let failure2 = null;
|
|
56526
57233
|
const stop = (error2) => {
|
|
56527
|
-
|
|
57234
|
+
failure2 ??= error2;
|
|
56528
57235
|
child2.kill("SIGKILL");
|
|
56529
57236
|
};
|
|
56530
57237
|
const timer = setTimeout(
|
|
@@ -56546,12 +57253,12 @@ async function callPublicHelperAsync(action, fields = {}, deps = defaultPublicHe
|
|
|
56546
57253
|
child2.on("error", (error2) => {
|
|
56547
57254
|
settle();
|
|
56548
57255
|
reject(
|
|
56549
|
-
|
|
57256
|
+
failure2 ?? new PublicHelperError("helper_unreachable", `Could not run the helper: ${error2.message}`)
|
|
56550
57257
|
);
|
|
56551
57258
|
});
|
|
56552
57259
|
child2.on("close", (status, exitSignal) => {
|
|
56553
57260
|
settle();
|
|
56554
|
-
if (
|
|
57261
|
+
if (failure2) return reject(failure2);
|
|
56555
57262
|
if (status === null && exitSignal)
|
|
56556
57263
|
return reject(
|
|
56557
57264
|
new PublicHelperError("helper_crashed", `The helper stopped on signal ${exitSignal}.`)
|
|
@@ -56669,8 +57376,8 @@ function publicHelperCompileArguments(sourcePath, digestPath, plistPath, outputP
|
|
|
56669
57376
|
}
|
|
56670
57377
|
function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
56671
57378
|
const steps = [];
|
|
56672
|
-
const done = (
|
|
56673
|
-
ok,
|
|
57379
|
+
const done = (ok2) => ({
|
|
57380
|
+
ok: ok2,
|
|
56674
57381
|
checkOnly,
|
|
56675
57382
|
steps,
|
|
56676
57383
|
installation: inspectPublicHelper(deps)
|
|
@@ -56706,14 +57413,14 @@ function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
|
56706
57413
|
const compiler = String(version3.stdout || version3.stderr || "").split("\n").find((line) => line.includes("Swift version"))?.trim() || "swiftc";
|
|
56707
57414
|
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
56708
57415
|
const installDir = publicHelperInstallDir(deps.env);
|
|
56709
|
-
|
|
56710
|
-
const staging =
|
|
57416
|
+
mkdirSync8(installDir, { recursive: true, mode: 448 });
|
|
57417
|
+
const staging = mkdtempSync7(join27(installDir, ".staging-"));
|
|
56711
57418
|
try {
|
|
56712
|
-
const stagedBinary =
|
|
56713
|
-
const digestPath =
|
|
56714
|
-
const plistPath =
|
|
56715
|
-
|
|
56716
|
-
|
|
57419
|
+
const stagedBinary = join27(staging, PUBLIC_HELPER_BINARY);
|
|
57420
|
+
const digestPath = join27(staging, "source-digest.swift");
|
|
57421
|
+
const plistPath = join27(staging, "Info.plist");
|
|
57422
|
+
writeFileSync6(digestPath, sourceDigestSwift(sourceSha), { mode: 384 });
|
|
57423
|
+
writeFileSync6(plistPath, publicHelperInfoPlist(), { mode: 384 });
|
|
56717
57424
|
const compile = deps.spawn(
|
|
56718
57425
|
"/usr/bin/xcrun",
|
|
56719
57426
|
publicHelperCompileArguments(deps.sourcePath, digestPath, plistPath, stagedBinary),
|
|
@@ -56774,15 +57481,15 @@ function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
|
56774
57481
|
compiler
|
|
56775
57482
|
};
|
|
56776
57483
|
chmodSync(stagedBinary, 448);
|
|
56777
|
-
|
|
56778
|
-
|
|
56779
|
-
|
|
57484
|
+
renameSync2(stagedBinary, join27(installDir, PUBLIC_HELPER_BINARY));
|
|
57485
|
+
writeFileSync6(
|
|
57486
|
+
join27(installDir, PUBLIC_HELPER_MANIFEST),
|
|
56780
57487
|
JSON.stringify(manifest, null, 2) + "\n",
|
|
56781
57488
|
{ mode: 384 }
|
|
56782
57489
|
);
|
|
56783
57490
|
steps.push({ step: "install", ok: true, detail: installDir });
|
|
56784
57491
|
} finally {
|
|
56785
|
-
|
|
57492
|
+
rmSync7(staging, { recursive: true, force: true });
|
|
56786
57493
|
}
|
|
56787
57494
|
const installation = inspectPublicHelper(deps);
|
|
56788
57495
|
steps.push({
|
|
@@ -56999,11 +57706,11 @@ function getNoteDrawings(noteId3, options = {}) {
|
|
|
56999
57706
|
if (!install.ready)
|
|
57000
57707
|
throw new PublicHelperError(install.reason ?? "helper_not_installed", install.detail ?? "");
|
|
57001
57708
|
const drawings = rows.map((row) => decodeRow(row, format, includePoints, deps));
|
|
57002
|
-
const
|
|
57709
|
+
const ok2 = drawings.filter((d) => d.status === "ok").length;
|
|
57003
57710
|
return {
|
|
57004
57711
|
id: noteId3,
|
|
57005
57712
|
drawingCount: drawings.length,
|
|
57006
|
-
status:
|
|
57713
|
+
status: ok2 === drawings.length ? "ok" : ok2 ? "partial" : "error",
|
|
57007
57714
|
drawings
|
|
57008
57715
|
};
|
|
57009
57716
|
}
|
|
@@ -57020,8 +57727,8 @@ function formatNoteDrawings(result) {
|
|
|
57020
57727
|
}
|
|
57021
57728
|
|
|
57022
57729
|
// src/utils/noteAudio.ts
|
|
57023
|
-
import { readdirSync as
|
|
57024
|
-
import { join as
|
|
57730
|
+
import { readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
|
|
57731
|
+
import { join as join28 } from "node:path";
|
|
57025
57732
|
var EXTRA_AUDIO_UTIS = [
|
|
57026
57733
|
"public.mp3",
|
|
57027
57734
|
"public.aiff-audio",
|
|
@@ -57046,7 +57753,7 @@ function accountDirsFor(containerDir, accountIdentifier) {
|
|
|
57046
57753
|
if (own) return [own];
|
|
57047
57754
|
let names;
|
|
57048
57755
|
try {
|
|
57049
|
-
names =
|
|
57756
|
+
names = readdirSync5(join28(containerDir, "Accounts")).sort();
|
|
57050
57757
|
} catch {
|
|
57051
57758
|
return [];
|
|
57052
57759
|
}
|
|
@@ -57058,8 +57765,8 @@ function resolveMediaPath(media, accountIdentifier = null, containerDir = NOTES_
|
|
|
57058
57765
|
if (!id2 || !filename) return null;
|
|
57059
57766
|
const generation = safeComponent(media?.generation);
|
|
57060
57767
|
for (const dir of accountDirsFor(containerDir, accountIdentifier)) {
|
|
57061
|
-
const base =
|
|
57062
|
-
const candidates = generation ? [
|
|
57768
|
+
const base = join28(dir, "Media", id2);
|
|
57769
|
+
const candidates = generation ? [join28(base, generation, filename), join28(base, filename)] : [join28(base, filename)];
|
|
57063
57770
|
for (const candidate of candidates) {
|
|
57064
57771
|
const real = realInside(candidate, dir);
|
|
57065
57772
|
if (real && isFile2(real)) return real;
|
|
@@ -57069,7 +57776,7 @@ function resolveMediaPath(media, accountIdentifier = null, containerDir = NOTES_
|
|
|
57069
57776
|
}
|
|
57070
57777
|
function isFile2(path10) {
|
|
57071
57778
|
try {
|
|
57072
|
-
return
|
|
57779
|
+
return statSync5(path10).isFile();
|
|
57073
57780
|
} catch {
|
|
57074
57781
|
return false;
|
|
57075
57782
|
}
|
|
@@ -57331,21 +58038,21 @@ import { spawnSync as spawnSync5 } from "node:child_process";
|
|
|
57331
58038
|
import {
|
|
57332
58039
|
chmodSync as chmodSync2,
|
|
57333
58040
|
existsSync as existsSync16,
|
|
57334
|
-
mkdirSync as
|
|
57335
|
-
mkdtempSync as
|
|
57336
|
-
renameSync as
|
|
57337
|
-
rmSync as
|
|
57338
|
-
writeFileSync as
|
|
58041
|
+
mkdirSync as mkdirSync9,
|
|
58042
|
+
mkdtempSync as mkdtempSync8,
|
|
58043
|
+
renameSync as renameSync3,
|
|
58044
|
+
rmSync as rmSync8,
|
|
58045
|
+
writeFileSync as writeFileSync7
|
|
57339
58046
|
} from "node:fs";
|
|
57340
58047
|
import { release as release4 } from "node:os";
|
|
57341
|
-
import { join as
|
|
58048
|
+
import { join as join30 } from "node:path";
|
|
57342
58049
|
|
|
57343
58050
|
// src/services/privateHelper.ts
|
|
57344
58051
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
57345
58052
|
import { createHash as createHash7 } from "node:crypto";
|
|
57346
|
-
import { existsSync as existsSync15, readFileSync as
|
|
57347
|
-
import { homedir as
|
|
57348
|
-
import { dirname as dirname9, join as
|
|
58053
|
+
import { existsSync as existsSync15, readFileSync as readFileSync6 } from "node:fs";
|
|
58054
|
+
import { homedir as homedir21 } from "node:os";
|
|
58055
|
+
import { dirname as dirname9, join as join29, resolve as resolve8 } from "node:path";
|
|
57349
58056
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
57350
58057
|
var PRIVATE_HELPER_PROTOCOL = 1;
|
|
57351
58058
|
var ENABLE_ENV = "APPLE_NOTES_MCP_ENABLE_PRIVATE";
|
|
@@ -57373,16 +58080,16 @@ var manifestSchema = external_exports.object({
|
|
|
57373
58080
|
function packageRoot2(fromDir = dirname9(fileURLToPath3(import.meta.url))) {
|
|
57374
58081
|
let dir = fromDir;
|
|
57375
58082
|
for (; ; ) {
|
|
57376
|
-
const candidate =
|
|
58083
|
+
const candidate = join29(dir, "package.json");
|
|
57377
58084
|
if (existsSync15(candidate)) {
|
|
57378
58085
|
try {
|
|
57379
|
-
const pkg = JSON.parse(
|
|
58086
|
+
const pkg = JSON.parse(readFileSync6(candidate, "utf8"));
|
|
57380
58087
|
if (pkg.name === "apple-notes-mcp") return dir;
|
|
57381
58088
|
} catch {
|
|
57382
58089
|
}
|
|
57383
58090
|
}
|
|
57384
58091
|
const parent = dirname9(dir);
|
|
57385
|
-
if (parent === dir) return
|
|
58092
|
+
if (parent === dir) return resolve8(fromDir, "..");
|
|
57386
58093
|
dir = parent;
|
|
57387
58094
|
}
|
|
57388
58095
|
}
|
|
@@ -57390,9 +58097,9 @@ function defaultDeps2(overrides = {}) {
|
|
|
57390
58097
|
return {
|
|
57391
58098
|
env: process.env,
|
|
57392
58099
|
platform: process.platform,
|
|
57393
|
-
sourcePath:
|
|
58100
|
+
sourcePath: join29(packageRoot2(), HELPER_SOURCE_RELATIVE),
|
|
57394
58101
|
exists: existsSync15,
|
|
57395
|
-
readFile: (path10) =>
|
|
58102
|
+
readFile: (path10) => readFileSync6(path10),
|
|
57396
58103
|
spawn: spawnSync4,
|
|
57397
58104
|
...overrides
|
|
57398
58105
|
};
|
|
@@ -57403,14 +58110,14 @@ function privateHelperEnabled(env = process.env) {
|
|
|
57403
58110
|
function helperInstallDir(env = process.env) {
|
|
57404
58111
|
const override = env[HELPER_DIR_ENV]?.trim();
|
|
57405
58112
|
if (override) return override;
|
|
57406
|
-
return
|
|
58113
|
+
return join29(homedir21(), "Library", "Application Support", "apple-notes-mcp", "private-helper");
|
|
57407
58114
|
}
|
|
57408
58115
|
function sha256Hex2(data) {
|
|
57409
58116
|
return createHash7("sha256").update(data).digest("hex");
|
|
57410
58117
|
}
|
|
57411
58118
|
function inspectInstallation(deps = defaultDeps2()) {
|
|
57412
58119
|
const installDir = helperInstallDir(deps.env);
|
|
57413
|
-
const binaryPath =
|
|
58120
|
+
const binaryPath = join29(installDir, HELPER_BINARY_NAME);
|
|
57414
58121
|
const base = {
|
|
57415
58122
|
installDir,
|
|
57416
58123
|
binaryPath,
|
|
@@ -57428,7 +58135,7 @@ function inspectInstallation(deps = defaultDeps2()) {
|
|
|
57428
58135
|
if (!deps.exists(deps.sourcePath))
|
|
57429
58136
|
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
57430
58137
|
base.expectedSourceSha256 = sha256Hex2(deps.readFile(deps.sourcePath));
|
|
57431
|
-
const manifestPath =
|
|
58138
|
+
const manifestPath = join29(installDir, MANIFEST_NAME);
|
|
57432
58139
|
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
57433
58140
|
return fail(
|
|
57434
58141
|
"helper_not_installed",
|
|
@@ -57699,8 +58406,8 @@ function compileArguments(sourcePath, outputPath, sourceSha) {
|
|
|
57699
58406
|
}
|
|
57700
58407
|
function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
57701
58408
|
const steps = [];
|
|
57702
|
-
const done = (
|
|
57703
|
-
ok,
|
|
58409
|
+
const done = (ok2) => ({
|
|
58410
|
+
ok: ok2,
|
|
57704
58411
|
checkOnly,
|
|
57705
58412
|
steps,
|
|
57706
58413
|
installation: inspectInstallation(deps)
|
|
@@ -57742,10 +58449,10 @@ function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
|
57742
58449
|
const compiler = String(clangVersion.stdout || "").split("\n")[0] || "clang";
|
|
57743
58450
|
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
57744
58451
|
const installDir = helperInstallDir(deps.env);
|
|
57745
|
-
|
|
57746
|
-
const staging =
|
|
58452
|
+
mkdirSync9(installDir, { recursive: true, mode: 448 });
|
|
58453
|
+
const staging = mkdtempSync8(join30(installDir, ".staging-"));
|
|
57747
58454
|
try {
|
|
57748
|
-
const stagedBinary =
|
|
58455
|
+
const stagedBinary = join30(staging, HELPER_BINARY_NAME);
|
|
57749
58456
|
const compile = deps.spawn(
|
|
57750
58457
|
"/usr/bin/xcrun",
|
|
57751
58458
|
compileArguments(deps.sourcePath, stagedBinary, sourceSha),
|
|
@@ -57822,13 +58529,13 @@ function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
|
57822
58529
|
compiler
|
|
57823
58530
|
};
|
|
57824
58531
|
chmodSync2(stagedBinary, 448);
|
|
57825
|
-
|
|
57826
|
-
|
|
58532
|
+
renameSync3(stagedBinary, join30(installDir, HELPER_BINARY_NAME));
|
|
58533
|
+
writeFileSync7(join30(installDir, MANIFEST_NAME), JSON.stringify(manifest, null, 2) + "\n", {
|
|
57827
58534
|
mode: 384
|
|
57828
58535
|
});
|
|
57829
58536
|
steps.push({ step: "install", ok: true, detail: installDir });
|
|
57830
58537
|
} finally {
|
|
57831
|
-
if (existsSync16(staging))
|
|
58538
|
+
if (existsSync16(staging)) rmSync8(staging, { recursive: true, force: true });
|
|
57832
58539
|
}
|
|
57833
58540
|
const installation = inspectInstallation(deps);
|
|
57834
58541
|
steps.push({
|
|
@@ -57991,6 +58698,7 @@ var server = new McpServer({
|
|
|
57991
58698
|
var notesManager = new AppleNotesManager();
|
|
57992
58699
|
registerDirectOperations(server, notesManager);
|
|
57993
58700
|
registerFolderDelete(server, notesManager);
|
|
58701
|
+
registerMarkdownTemplates(server);
|
|
57994
58702
|
registerSvgAnalysis(server);
|
|
57995
58703
|
registerNativeTagsBridge(server, notesManager);
|
|
57996
58704
|
registerNativeOperations(server, notesManager);
|
|
@@ -60808,7 +61516,7 @@ registerTool(
|
|
|
60808
61516
|
assetsDir: exportPathInput("Directory that receives copies of attachment files"),
|
|
60809
61517
|
wrap: external_exports.number().int().min(0).max(1e3).optional().describe("Hard-wrap prose at this many columns (0 or omitted: no wrapping)"),
|
|
60810
61518
|
template: external_exports.string().min(1).max(64).optional().describe(
|
|
60811
|
-
`Render through this template: built-in ${BUILTIN_TEMPLATE_NAMES.map((n) => `'${n}'`).join(" or ")}. Exclusive with templateFile`
|
|
61519
|
+
`Render through this template: built-in ${BUILTIN_TEMPLATE_NAMES.map((n) => `'${n}'`).join(" or ")}, or a saved template's name (list-markdown-templates). Exclusive with templateFile`
|
|
60812
61520
|
),
|
|
60813
61521
|
templateFile: exportPathInput(
|
|
60814
61522
|
"JSON template file (.json) to render through (exclusive with template; at most 256 KiB)"
|
|
@@ -60837,6 +61545,7 @@ registerTool(
|
|
|
60837
61545
|
try {
|
|
60838
61546
|
receipt = exportNotesMarkdown(request, {
|
|
60839
61547
|
listNoteRefs: (account, folder, since, limit) => notesManager.listNoteRefs(account, folder, since, limit),
|
|
61548
|
+
findTemplate: (name) => TEMPLATE_SLUG.test(name) ? new TemplateStore().find(name) : void 0,
|
|
60840
61549
|
// The document travels twice (text and structuredContent).
|
|
60841
61550
|
maxInlineBytes: Math.floor(exportMaxResponseBytes() / 2) - 64 * 1024
|
|
60842
61551
|
});
|