apple-notes-mcp 2.8.5 → 2.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/build/index.js +543 -109
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -303,6 +303,26 @@ attachment-bearing notes; edit them in Notes.app.
|
|
|
303
303
|
|
|
304
304
|
---
|
|
305
305
|
|
|
306
|
+
#### `get-native-objects`
|
|
307
|
+
|
|
308
|
+
Reads native object identities and ranges, checklist item IDs and state, actual
|
|
309
|
+
native tags, and native table data from one exact note ID. Table output includes
|
|
310
|
+
stable row and column identifiers. `tableCellsComplete` is false when Notes
|
|
311
|
+
metadata cannot be decoded completely. This tool is read-only and requires Full
|
|
312
|
+
Disk Access.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
#### `list-native-tags`
|
|
317
|
+
|
|
318
|
+
Lists actual native Notes tags used within one explicit `account` and `folder`,
|
|
319
|
+
mapping each tag to its matching note IDs. This differs from textual hashtag
|
|
320
|
+
search. The response reports `complete: false` and per-note errors when some
|
|
321
|
+
native metadata is unavailable. This tool is read-only and requires Full Disk
|
|
322
|
+
Access.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
306
326
|
#### `get-note-plaintext`
|
|
307
327
|
|
|
308
328
|
Retrieves a note's body as plain text, with no HTML markup.
|
|
@@ -639,6 +659,21 @@ Creates a new folder, including a whole nested hierarchy in one call.
|
|
|
639
659
|
|
|
640
660
|
---
|
|
641
661
|
|
|
662
|
+
#### `get-folder-by-id`
|
|
663
|
+
|
|
664
|
+
Reads one exact folder's current name and parent ID. Use these values with
|
|
665
|
+
`rename-folder`; this avoids relying on ambiguous folder names or paths.
|
|
666
|
+
|
|
667
|
+
---
|
|
668
|
+
|
|
669
|
+
#### `rename-folder`
|
|
670
|
+
|
|
671
|
+
Renames an existing folder in place using its exact `id`, `expectedName`,
|
|
672
|
+
`expectedParentId`, and `newName`. The operation preserves the folder ID, notes,
|
|
673
|
+
and descendants. It refuses stale metadata and a conflicting sibling name.
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
642
677
|
#### `delete-folder`
|
|
643
678
|
|
|
644
679
|
Deletes a folder.
|
|
@@ -815,6 +850,15 @@ Reads note metadata that AppleScript cannot expose, by querying the NoteStore SQ
|
|
|
815
850
|
|
|
816
851
|
---
|
|
817
852
|
|
|
853
|
+
#### `add-attachment`
|
|
854
|
+
|
|
855
|
+
Adds one nonempty local file of at most 64 MiB to an exact note using `id`, the
|
|
856
|
+
latest `expectedContentHash`, and an absolute `path`. The server never retries
|
|
857
|
+
the insertion. It verifies that existing rich content survived and compares the
|
|
858
|
+
fetched attachment bytes with the source before reporting success.
|
|
859
|
+
|
|
860
|
+
---
|
|
861
|
+
|
|
818
862
|
#### `list-attachments`
|
|
819
863
|
|
|
820
864
|
Lists attachments in a note.
|
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, constants2) {
|
|
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, constants2);
|
|
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, constants2) {
|
|
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, constants2);
|
|
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, constants2) {
|
|
506
|
+
this.code = optimizeExpr(this.code, names, constants2);
|
|
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, constants2) {
|
|
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, constants2))
|
|
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, constants2) {
|
|
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, constants2);
|
|
596
|
+
if (!(super.optimizeNames(names, constants2) || this.else))
|
|
597
597
|
return;
|
|
598
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
598
|
+
this.condition = optimizeExpr(this.condition, names, constants2);
|
|
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, constants2) {
|
|
622
|
+
if (!super.optimizeNames(names, constants2))
|
|
623
623
|
return;
|
|
624
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
624
|
+
this.iteration = optimizeExpr(this.iteration, names, constants2);
|
|
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, constants2) {
|
|
661
|
+
if (!super.optimizeNames(names, constants2))
|
|
662
662
|
return;
|
|
663
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
663
|
+
this.iterable = optimizeExpr(this.iterable, names, constants2);
|
|
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, constants2) {
|
|
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, constants2);
|
|
708
|
+
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants2);
|
|
709
|
+
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants2);
|
|
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, constants2) {
|
|
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 = constants2[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 && constants2[c.str] !== void 0);
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
1035
|
function subtractNames(names, from) {
|
|
@@ -3358,9 +3358,9 @@ var require_utils = __commonJS({
|
|
|
3358
3358
|
let output = "";
|
|
3359
3359
|
for (let i = 0; i < input.length; i++) {
|
|
3360
3360
|
if (input[i] === "%" && i + 2 < input.length) {
|
|
3361
|
-
const
|
|
3362
|
-
if (isHexPair(
|
|
3363
|
-
const normalizedHex =
|
|
3361
|
+
const hex2 = input.slice(i + 1, i + 3);
|
|
3362
|
+
if (isHexPair(hex2)) {
|
|
3363
|
+
const normalizedHex = hex2.toUpperCase();
|
|
3364
3364
|
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3365
3365
|
if (decodeUnreserved && isUnreserved(decoded)) {
|
|
3366
3366
|
output += decoded;
|
|
@@ -3380,9 +3380,9 @@ var require_utils = __commonJS({
|
|
|
3380
3380
|
for (let i = 0; i < input.length; i++) {
|
|
3381
3381
|
const ch = input[i];
|
|
3382
3382
|
if (ch === "%" && i + 2 < input.length) {
|
|
3383
|
-
const
|
|
3384
|
-
if (isHexPair(
|
|
3385
|
-
const normalizedHex =
|
|
3383
|
+
const hex2 = input.slice(i + 1, i + 3);
|
|
3384
|
+
if (isHexPair(hex2)) {
|
|
3385
|
+
const normalizedHex = hex2.toUpperCase();
|
|
3386
3386
|
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3387
3387
|
if (decoded !== "." && isUnreserved(decoded)) {
|
|
3388
3388
|
output += decoded;
|
|
@@ -3422,9 +3422,9 @@ var require_utils = __commonJS({
|
|
|
3422
3422
|
for (let i = 0; i < input.length; i++) {
|
|
3423
3423
|
const ch = input[i];
|
|
3424
3424
|
if (ch === "%" && i + 2 < input.length) {
|
|
3425
|
-
const
|
|
3426
|
-
if (isHexPair(
|
|
3427
|
-
output += "%" +
|
|
3425
|
+
const hex2 = input.slice(i + 1, i + 3);
|
|
3426
|
+
if (isHexPair(hex2)) {
|
|
3427
|
+
output += "%" + hex2.toUpperCase();
|
|
3428
3428
|
i += 2;
|
|
3429
3429
|
continue;
|
|
3430
3430
|
}
|
|
@@ -3460,9 +3460,9 @@ var require_utils = __commonJS({
|
|
|
3460
3460
|
for (let i = 0; i < input.length; i++) {
|
|
3461
3461
|
const ch = input[i];
|
|
3462
3462
|
if (ch === "%" && i + 2 < input.length) {
|
|
3463
|
-
const
|
|
3464
|
-
if (isHexPair(
|
|
3465
|
-
output += "%" +
|
|
3463
|
+
const hex2 = input.slice(i + 1, i + 3);
|
|
3464
|
+
if (isHexPair(hex2)) {
|
|
3465
|
+
output += "%" + hex2.toUpperCase();
|
|
3466
3466
|
i += 2;
|
|
3467
3467
|
continue;
|
|
3468
3468
|
}
|
|
@@ -3507,9 +3507,9 @@ var require_utils = __commonJS({
|
|
|
3507
3507
|
for (let i = 0; i < input.length; i++) {
|
|
3508
3508
|
const ch = input[i];
|
|
3509
3509
|
if (ch === "%" && i + 2 < input.length) {
|
|
3510
|
-
const
|
|
3511
|
-
if (isHexPair(
|
|
3512
|
-
const normalizedHex =
|
|
3510
|
+
const hex2 = input.slice(i + 1, i + 3);
|
|
3511
|
+
if (isHexPair(hex2)) {
|
|
3512
|
+
const normalizedHex = hex2.toUpperCase();
|
|
3513
3513
|
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3514
3514
|
if (isUnreserved(decoded)) {
|
|
3515
3515
|
output += decoded;
|
|
@@ -3547,9 +3547,9 @@ var require_utils = __commonJS({
|
|
|
3547
3547
|
let output = "";
|
|
3548
3548
|
for (let i = 0; i < input.length; i++) {
|
|
3549
3549
|
if (input[i] === "%" && i + 2 < input.length) {
|
|
3550
|
-
const
|
|
3551
|
-
if (isHexPair(
|
|
3552
|
-
output += "%" +
|
|
3550
|
+
const hex2 = input.slice(i + 1, i + 3);
|
|
3551
|
+
if (isHexPair(hex2)) {
|
|
3552
|
+
output += "%" + hex2.toUpperCase();
|
|
3553
3553
|
i += 2;
|
|
3554
3554
|
continue;
|
|
3555
3555
|
}
|
|
@@ -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 constants2 = {
|
|
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 constants2) {
|
|
7465
|
+
v = { value: constants2[c] };
|
|
7466
7466
|
Object.defineProperty(DOMException, c, v);
|
|
7467
7467
|
Object.defineProperty(DOMException.prototype, c, v);
|
|
7468
7468
|
}
|
|
@@ -9329,8 +9329,8 @@ var require_select = __commonJS({
|
|
|
9329
9329
|
regex = regex.replace(name, val.source || val);
|
|
9330
9330
|
return new RegExp(regex);
|
|
9331
9331
|
};
|
|
9332
|
-
var truncateUrl = function(url,
|
|
9333
|
-
return url.replace(/^(?:\w+:\/\/|\/+)/, "").replace(/(?:\/+|\/*#.*?)$/, "").split("/",
|
|
9332
|
+
var truncateUrl = function(url, num2) {
|
|
9333
|
+
return url.replace(/^(?:\w+:\/\/|\/+)/, "").replace(/(?:\/+|\/*#.*?)$/, "").split("/", num2).join("/");
|
|
9334
9334
|
};
|
|
9335
9335
|
var parseNth = function(param_, test) {
|
|
9336
9336
|
var param = param_.replace(/\s+/g, ""), cap;
|
|
@@ -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 join9(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 = join9(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 join9(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);
|
|
@@ -24777,8 +24777,8 @@ var ZodError = class _ZodError extends Error {
|
|
|
24777
24777
|
constructor(issues) {
|
|
24778
24778
|
super();
|
|
24779
24779
|
this.issues = [];
|
|
24780
|
-
this.addIssue = (
|
|
24781
|
-
this.issues = [...this.issues,
|
|
24780
|
+
this.addIssue = (sub2) => {
|
|
24781
|
+
this.issues = [...this.issues, sub2];
|
|
24782
24782
|
};
|
|
24783
24783
|
this.addIssues = (subs = []) => {
|
|
24784
24784
|
this.issues = [...this.issues, ...subs];
|
|
@@ -24845,13 +24845,13 @@ var ZodError = class _ZodError extends Error {
|
|
|
24845
24845
|
flatten(mapper = (issue2) => issue2.message) {
|
|
24846
24846
|
const fieldErrors = {};
|
|
24847
24847
|
const formErrors = [];
|
|
24848
|
-
for (const
|
|
24849
|
-
if (
|
|
24850
|
-
const firstEl =
|
|
24848
|
+
for (const sub2 of this.issues) {
|
|
24849
|
+
if (sub2.path.length > 0) {
|
|
24850
|
+
const firstEl = sub2.path[0];
|
|
24851
24851
|
fieldErrors[firstEl] = fieldErrors[firstEl] || [];
|
|
24852
|
-
fieldErrors[firstEl].push(mapper(
|
|
24852
|
+
fieldErrors[firstEl].push(mapper(sub2));
|
|
24853
24853
|
} else {
|
|
24854
|
-
formErrors.push(mapper(
|
|
24854
|
+
formErrors.push(mapper(sub2));
|
|
24855
24855
|
}
|
|
24856
24856
|
}
|
|
24857
24857
|
return { formErrors, fieldErrors };
|
|
@@ -29150,12 +29150,12 @@ var $ZodRealError = $constructor("$ZodError", initializer, { Parent: Error });
|
|
|
29150
29150
|
function flattenError(error2, mapper = (issue2) => issue2.message) {
|
|
29151
29151
|
const fieldErrors = {};
|
|
29152
29152
|
const formErrors = [];
|
|
29153
|
-
for (const
|
|
29154
|
-
if (
|
|
29155
|
-
fieldErrors[
|
|
29156
|
-
fieldErrors[
|
|
29153
|
+
for (const sub2 of error2.issues) {
|
|
29154
|
+
if (sub2.path.length > 0) {
|
|
29155
|
+
fieldErrors[sub2.path[0]] = fieldErrors[sub2.path[0]] || [];
|
|
29156
|
+
fieldErrors[sub2.path[0]].push(mapper(sub2));
|
|
29157
29157
|
} else {
|
|
29158
|
-
formErrors.push(mapper(
|
|
29158
|
+
formErrors.push(mapper(sub2));
|
|
29159
29159
|
}
|
|
29160
29160
|
}
|
|
29161
29161
|
return { formErrors, fieldErrors };
|
|
@@ -39350,10 +39350,10 @@ function hasFullDiskAccess() {
|
|
|
39350
39350
|
return false;
|
|
39351
39351
|
}
|
|
39352
39352
|
}
|
|
39353
|
-
function queryNoteData(
|
|
39354
|
-
const pkMatch =
|
|
39353
|
+
function queryNoteData(noteId2) {
|
|
39354
|
+
const pkMatch = noteId2.match(/\/p(\d+)$/);
|
|
39355
39355
|
if (!pkMatch) {
|
|
39356
|
-
console.error(`Invalid note ID format: ${
|
|
39356
|
+
console.error(`Invalid note ID format: ${noteId2}`);
|
|
39357
39357
|
return { hex: null, error: "invalid_id" };
|
|
39358
39358
|
}
|
|
39359
39359
|
const pk = pkMatch[1];
|
|
@@ -39364,9 +39364,9 @@ function queryNoteData(noteId) {
|
|
|
39364
39364
|
timeout: 5e3,
|
|
39365
39365
|
stdio: ["pipe", "pipe", "pipe"]
|
|
39366
39366
|
});
|
|
39367
|
-
const
|
|
39368
|
-
if (!
|
|
39369
|
-
return { hex };
|
|
39367
|
+
const hex2 = result.trim();
|
|
39368
|
+
if (!hex2) return { hex: null };
|
|
39369
|
+
return { hex: hex2 };
|
|
39370
39370
|
} catch (error2) {
|
|
39371
39371
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
39372
39372
|
console.error(`Failed to query NoteStore database: ${message}`);
|
|
@@ -39376,10 +39376,10 @@ function queryNoteData(noteId) {
|
|
|
39376
39376
|
return { hex: null };
|
|
39377
39377
|
}
|
|
39378
39378
|
}
|
|
39379
|
-
function hexToBytes(
|
|
39380
|
-
const bytes = new Uint8Array(
|
|
39381
|
-
for (let i = 0; i <
|
|
39382
|
-
bytes[i / 2] = parseInt(
|
|
39379
|
+
function hexToBytes(hex2) {
|
|
39380
|
+
const bytes = new Uint8Array(hex2.length / 2);
|
|
39381
|
+
for (let i = 0; i < hex2.length; i += 2) {
|
|
39382
|
+
bytes[i / 2] = parseInt(hex2.substring(i, i + 2), 16);
|
|
39383
39383
|
}
|
|
39384
39384
|
return bytes;
|
|
39385
39385
|
}
|
|
@@ -39438,13 +39438,13 @@ function parseChecklistFromProtobuf(data) {
|
|
|
39438
39438
|
return null;
|
|
39439
39439
|
}
|
|
39440
39440
|
}
|
|
39441
|
-
function getChecklistItems(
|
|
39442
|
-
const { hex: hexData, error: queryError } = queryNoteData(
|
|
39441
|
+
function getChecklistItems(noteId2) {
|
|
39442
|
+
const { hex: hexData, error: queryError } = queryNoteData(noteId2);
|
|
39443
39443
|
if (queryError === "invalid_id") {
|
|
39444
39444
|
return {
|
|
39445
39445
|
items: null,
|
|
39446
39446
|
error: "invalid_id",
|
|
39447
|
-
message: `Invalid note ID format: "${
|
|
39447
|
+
message: `Invalid note ID format: "${noteId2}". Expected format: x-coredata://UUID/ICNote/pNNN`
|
|
39448
39448
|
};
|
|
39449
39449
|
}
|
|
39450
39450
|
if (queryError === "no_fda") {
|
|
@@ -40281,20 +40281,20 @@ var AppleNotesManager = class {
|
|
|
40281
40281
|
return null;
|
|
40282
40282
|
}
|
|
40283
40283
|
const rawOutput = result.output.trim();
|
|
40284
|
-
const
|
|
40285
|
-
if (!
|
|
40284
|
+
const noteId2 = extractCoreDataId(rawOutput, "note") || rawOutput;
|
|
40285
|
+
if (!noteId2) {
|
|
40286
40286
|
console.error(`Created note "${title}" but Notes.app returned no canonical note ID`);
|
|
40287
40287
|
return null;
|
|
40288
40288
|
}
|
|
40289
40289
|
try {
|
|
40290
|
-
sanitizeNoteId(
|
|
40290
|
+
sanitizeNoteId(noteId2);
|
|
40291
40291
|
} catch {
|
|
40292
40292
|
console.error(`Created note "${title}" but Notes.app returned an invalid note ID`);
|
|
40293
40293
|
return null;
|
|
40294
40294
|
}
|
|
40295
40295
|
const now = /* @__PURE__ */ new Date();
|
|
40296
40296
|
return {
|
|
40297
|
-
id:
|
|
40297
|
+
id: noteId2,
|
|
40298
40298
|
title,
|
|
40299
40299
|
content,
|
|
40300
40300
|
tags,
|
|
@@ -40408,11 +40408,11 @@ var AppleNotesManager = class {
|
|
|
40408
40408
|
for (const item of items) {
|
|
40409
40409
|
const [title, id, folder2, created, modified] = item.split(FIELD_SEP);
|
|
40410
40410
|
if (!title?.trim()) continue;
|
|
40411
|
-
const
|
|
40412
|
-
if (seenIds.has(
|
|
40413
|
-
seenIds.add(
|
|
40411
|
+
const noteId2 = id?.trim() || generateFallbackId();
|
|
40412
|
+
if (seenIds.has(noteId2)) continue;
|
|
40413
|
+
seenIds.add(noteId2);
|
|
40414
40414
|
notes.push({
|
|
40415
|
-
id:
|
|
40415
|
+
id: noteId2,
|
|
40416
40416
|
title: title.trim(),
|
|
40417
40417
|
content: "",
|
|
40418
40418
|
// Not fetched in search
|
|
@@ -40780,10 +40780,10 @@ var AppleNotesManager = class {
|
|
|
40780
40780
|
for (const item of output.split(RECORD_SEP)) {
|
|
40781
40781
|
const [title, id] = item.split(FIELD_SEP);
|
|
40782
40782
|
if (!title?.trim()) continue;
|
|
40783
|
-
const
|
|
40784
|
-
if (seenIds.has(
|
|
40785
|
-
seenIds.add(
|
|
40786
|
-
refs.push({ title: title.trim(), id:
|
|
40783
|
+
const noteId2 = id?.trim() || generateFallbackId();
|
|
40784
|
+
if (seenIds.has(noteId2)) continue;
|
|
40785
|
+
seenIds.add(noteId2);
|
|
40786
|
+
refs.push({ title: title.trim(), id: noteId2 });
|
|
40787
40787
|
if (safeLimit !== void 0 && refs.length >= safeLimit) break;
|
|
40788
40788
|
}
|
|
40789
40789
|
return refs;
|
|
@@ -40930,6 +40930,69 @@ var AppleNotesManager = class {
|
|
|
40930
40930
|
// ===========================================================================
|
|
40931
40931
|
// Folder Operations
|
|
40932
40932
|
// ===========================================================================
|
|
40933
|
+
/** Rename a folder in place while preserving its identity and contents. */
|
|
40934
|
+
renameFolderById(id, expectedName, expectedParentId, newName) {
|
|
40935
|
+
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICFolder\/p\d+$/i.test(id))
|
|
40936
|
+
throw new Error("An exact folder ID is required");
|
|
40937
|
+
if (!newName.trim() || newName.length > 1e3 || Array.from(newName).some((char) => char.charCodeAt(0) < 32))
|
|
40938
|
+
throw new Error("Invalid folder name");
|
|
40939
|
+
const literal2 = (value) => `"${escapePlainStringForAppleScript(value)}"`;
|
|
40940
|
+
const script = `tell application "Notes"
|
|
40941
|
+
set targetFolder to folder id ${literal2(id)}
|
|
40942
|
+
set parentRef to container of targetFolder
|
|
40943
|
+
set parentId to id of parentRef
|
|
40944
|
+
considering case
|
|
40945
|
+
if name of targetFolder is not ${literal2(expectedName)} or parentId is not ${literal2(expectedParentId)} then error "Folder changed; read it again"
|
|
40946
|
+
end considering
|
|
40947
|
+
repeat with sibling in folders of parentRef
|
|
40948
|
+
if name of sibling is ${literal2(newName)} and id of sibling is not ${literal2(id)} then error "A sibling folder already has this name"
|
|
40949
|
+
end repeat
|
|
40950
|
+
set name of targetFolder to ${literal2(newName)}
|
|
40951
|
+
considering case
|
|
40952
|
+
if id of targetFolder is not ${literal2(id)} or name of targetFolder is not ${literal2(newName)} or id of container of targetFolder is not parentId then error "Rename readback failed"
|
|
40953
|
+
end considering
|
|
40954
|
+
return id of targetFolder
|
|
40955
|
+
end tell`;
|
|
40956
|
+
const result = executeMutationAppleScript(script);
|
|
40957
|
+
if (!result.success)
|
|
40958
|
+
throw new Error(
|
|
40959
|
+
result.error || "Rename outcome uncertain; read folder by ID before retrying"
|
|
40960
|
+
);
|
|
40961
|
+
return { id, name: newName, parentId: expectedParentId };
|
|
40962
|
+
}
|
|
40963
|
+
/** Read the exact name and parent identity used by guarded folder rename. */
|
|
40964
|
+
getFolderById(id) {
|
|
40965
|
+
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICFolder\/p\d+$/i.test(id))
|
|
40966
|
+
throw new Error("An exact folder ID is required");
|
|
40967
|
+
const result = executeAppleScript(`tell application "Notes"
|
|
40968
|
+
set f to folder id "${id}"
|
|
40969
|
+
return (name of f) & ${AS_FIELD_SEP} & (id of container of f)
|
|
40970
|
+
end tell`);
|
|
40971
|
+
if (!result.success) throw new Error(result.error || "Folder not found");
|
|
40972
|
+
const [name, parentId] = result.output.replace(/\n$/, "").split(FIELD_SEP);
|
|
40973
|
+
if (!name || !parentId) throw new Error("Incomplete folder metadata");
|
|
40974
|
+
return { id, name, parentId };
|
|
40975
|
+
}
|
|
40976
|
+
/** Insert one file into an unchanged exact note and return Notes' attachment ID. */
|
|
40977
|
+
addAttachmentById(id, expectedBody, filePath) {
|
|
40978
|
+
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(id))
|
|
40979
|
+
throw new Error("Exact note ID required");
|
|
40980
|
+
const quote = (value) => `"${escapePlainStringForAppleScript(value)}"`;
|
|
40981
|
+
const result = executeMutationAppleScript(`tell application "Notes"
|
|
40982
|
+
set n to note id ${quote(id)}
|
|
40983
|
+
if password protected of n then error "Locked note"
|
|
40984
|
+
considering case
|
|
40985
|
+
if body of n is not ${quote(expectedBody)} and body of n is not (${quote(expectedBody)} & linefeed) then error "Note changed before attachment insertion"
|
|
40986
|
+
end considering
|
|
40987
|
+
set a to make new attachment at n with data (POSIX file ${quote(filePath)})
|
|
40988
|
+
return id of a
|
|
40989
|
+
end tell`);
|
|
40990
|
+
if (!result.success)
|
|
40991
|
+
throw new Error(
|
|
40992
|
+
result.error || "Attachment insertion outcome uncertain; read note before retrying"
|
|
40993
|
+
);
|
|
40994
|
+
return result.output.trim();
|
|
40995
|
+
}
|
|
40933
40996
|
/**
|
|
40934
40997
|
* Lists all folders in an account with full hierarchical paths.
|
|
40935
40998
|
*
|
|
@@ -41372,8 +41435,8 @@ var AppleNotesManager = class {
|
|
|
41372
41435
|
* @param separately - Open in a separate window when supported by Notes.app
|
|
41373
41436
|
* @returns true if Notes.app revealed the attachment, false otherwise
|
|
41374
41437
|
*/
|
|
41375
|
-
showAttachmentById(
|
|
41376
|
-
const safeNoteId = sanitizeId(
|
|
41438
|
+
showAttachmentById(noteId2, attachmentId, separately = false) {
|
|
41439
|
+
const safeNoteId = sanitizeId(noteId2);
|
|
41377
41440
|
const safeAttId = escapePlainStringForAppleScript(attachmentId);
|
|
41378
41441
|
const separatelyClause = separately ? " separately true" : "";
|
|
41379
41442
|
const script = `
|
|
@@ -41393,13 +41456,13 @@ var AppleNotesManager = class {
|
|
|
41393
41456
|
const result = executeMutationAppleScript(script);
|
|
41394
41457
|
if (!result.success) {
|
|
41395
41458
|
console.error(
|
|
41396
|
-
`Failed to show attachment "${attachmentId}" on note "${
|
|
41459
|
+
`Failed to show attachment "${attachmentId}" on note "${noteId2}":`,
|
|
41397
41460
|
result.error
|
|
41398
41461
|
);
|
|
41399
41462
|
return false;
|
|
41400
41463
|
}
|
|
41401
41464
|
if ((result.output ?? "").trim().startsWith("ERR")) {
|
|
41402
|
-
console.error(`Attachment "${attachmentId}" not found on note "${
|
|
41465
|
+
console.error(`Attachment "${attachmentId}" not found on note "${noteId2}"`);
|
|
41403
41466
|
return false;
|
|
41404
41467
|
}
|
|
41405
41468
|
return true;
|
|
@@ -41799,7 +41862,7 @@ var AppleNotesManager = class {
|
|
|
41799
41862
|
* @param savePath - absolute destination file path (within home / temp / /Volumes)
|
|
41800
41863
|
* @returns { success, savedPath?, name?, contentType?, error? }
|
|
41801
41864
|
*/
|
|
41802
|
-
saveAttachmentById(
|
|
41865
|
+
saveAttachmentById(noteId2, attachmentId, savePath) {
|
|
41803
41866
|
let abs;
|
|
41804
41867
|
try {
|
|
41805
41868
|
abs = assertSafeSavePath(savePath);
|
|
@@ -41807,7 +41870,7 @@ var AppleNotesManager = class {
|
|
|
41807
41870
|
} catch (e) {
|
|
41808
41871
|
return { success: false, error: e instanceof Error ? e.message : String(e) };
|
|
41809
41872
|
}
|
|
41810
|
-
const safeNoteId = sanitizeId(
|
|
41873
|
+
const safeNoteId = sanitizeId(noteId2);
|
|
41811
41874
|
const safeAttId = escapePlainStringForAppleScript(attachmentId);
|
|
41812
41875
|
const safePath = escapePlainStringForAppleScript(abs);
|
|
41813
41876
|
const script = `
|
|
@@ -41871,11 +41934,11 @@ var AppleNotesManager = class {
|
|
|
41871
41934
|
* @param attachmentId - id of the attachment
|
|
41872
41935
|
* @returns { success, name?, contentType?, base64?, bytes?, error? }
|
|
41873
41936
|
*/
|
|
41874
|
-
getAttachmentBase64ById(
|
|
41937
|
+
getAttachmentBase64ById(noteId2, attachmentId) {
|
|
41875
41938
|
const dir = makeTempDir();
|
|
41876
41939
|
try {
|
|
41877
41940
|
const dest = `${dir}/attachment.bin`;
|
|
41878
|
-
const saved = this.saveAttachmentById(
|
|
41941
|
+
const saved = this.saveAttachmentById(noteId2, attachmentId, dest);
|
|
41879
41942
|
if (!saved.success || !saved.savedPath) {
|
|
41880
41943
|
return { success: false, error: saved.error };
|
|
41881
41944
|
}
|
|
@@ -42375,13 +42438,13 @@ function presentColumns() {
|
|
|
42375
42438
|
}
|
|
42376
42439
|
return cols;
|
|
42377
42440
|
}
|
|
42378
|
-
function getNoteMetadata(
|
|
42379
|
-
const pkMatch =
|
|
42441
|
+
function getNoteMetadata(noteId2) {
|
|
42442
|
+
const pkMatch = noteId2.match(/\/p(\d+)$/);
|
|
42380
42443
|
if (!pkMatch) {
|
|
42381
42444
|
return {
|
|
42382
42445
|
metadata: null,
|
|
42383
42446
|
error: "invalid_id",
|
|
42384
|
-
message: `Invalid note ID format: "${
|
|
42447
|
+
message: `Invalid note ID format: "${noteId2}". Expected format: x-coredata://UUID/ICNote/pNNN`
|
|
42385
42448
|
};
|
|
42386
42449
|
}
|
|
42387
42450
|
const pk = pkMatch[1];
|
|
@@ -42402,7 +42465,7 @@ function getNoteMetadata(noteId) {
|
|
|
42402
42465
|
return {
|
|
42403
42466
|
metadata: null,
|
|
42404
42467
|
error: "not_found",
|
|
42405
|
-
message: `No note found in the database for ID "${
|
|
42468
|
+
message: `No note found in the database for ID "${noteId2}".`
|
|
42406
42469
|
};
|
|
42407
42470
|
}
|
|
42408
42471
|
const raw = JSON.parse(row);
|
|
@@ -42511,7 +42574,7 @@ function decodeHtmlEntities(text) {
|
|
|
42511
42574
|
}
|
|
42512
42575
|
return String.fromCodePoint(codePoint);
|
|
42513
42576
|
};
|
|
42514
|
-
return text.replace(/&#x([0-9a-f]+);?/gi, (match,
|
|
42577
|
+
return text.replace(/&#x([0-9a-f]+);?/gi, (match, hex2) => decodeCodePoint(match, hex2, 16)).replace(/&#([0-9]+);?/g, (match, decimal) => decodeCodePoint(match, decimal, 10)).replace(/ (?:;|(?![0-9a-z]))/gi, " ").replace(/"(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/<(?:;|(?![0-9a-z]))/gi, "<").replace(/>(?:;|(?![0-9a-z]))/gi, ">").replace(/&(?:;|(?![0-9a-z]))/gi, "&");
|
|
42515
42578
|
}
|
|
42516
42579
|
function firstVisibleHtmlLine(html) {
|
|
42517
42580
|
let text = html;
|
|
@@ -42852,6 +42915,282 @@ function comparableVisibleText(html) {
|
|
|
42852
42915
|
).replace(/\s+/g, " ").trim();
|
|
42853
42916
|
}
|
|
42854
42917
|
|
|
42918
|
+
// src/utils/noteTables.ts
|
|
42919
|
+
import { gunzipSync as gunzipSync3 } from "node:zlib";
|
|
42920
|
+
var sub = (f, n) => {
|
|
42921
|
+
const value = embeddedMessage(getField(f, n));
|
|
42922
|
+
if (!value) throw new Error(`Missing table field ${n}`);
|
|
42923
|
+
return value;
|
|
42924
|
+
};
|
|
42925
|
+
var num = (f, n) => {
|
|
42926
|
+
const v = varintValue(getField(f, n));
|
|
42927
|
+
if (v === void 0) throw new Error(`Missing table index ${n}`);
|
|
42928
|
+
return v;
|
|
42929
|
+
};
|
|
42930
|
+
var many = (f, n) => getFields(f, n).map((v) => {
|
|
42931
|
+
const m = embeddedMessage(v);
|
|
42932
|
+
if (!m) throw new Error("Invalid table entry");
|
|
42933
|
+
return m;
|
|
42934
|
+
});
|
|
42935
|
+
var hex = (f) => {
|
|
42936
|
+
if (!(f?.value instanceof Uint8Array)) throw new Error("Missing table UUID");
|
|
42937
|
+
return Buffer.from(f.value).toString("hex");
|
|
42938
|
+
};
|
|
42939
|
+
function parseNoteTable(compressed) {
|
|
42940
|
+
const root = decodeMessage(gunzipSync3(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
|
|
42941
|
+
const data = sub(sub(root, 2), 3), entries = many(data, 3);
|
|
42942
|
+
if (entries.length > 1e5) throw new Error("Table too large");
|
|
42943
|
+
const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
|
|
42944
|
+
const entry = (index) => {
|
|
42945
|
+
if (!entries[index]) throw new Error("Invalid table reference");
|
|
42946
|
+
return entries[index];
|
|
42947
|
+
};
|
|
42948
|
+
const uuidIndex = (index) => num(sub(many(sub(entry(index), 13), 3)[0], 2), 2);
|
|
42949
|
+
const roots = entries.filter((e) => {
|
|
42950
|
+
const map = embeddedMessage(getField(e, 13));
|
|
42951
|
+
return map && types[num(map, 1)] === "com.apple.notes.ICTable";
|
|
42952
|
+
});
|
|
42953
|
+
if (roots.length !== 1) throw new Error("Ambiguous native table root");
|
|
42954
|
+
const refs = new Map(
|
|
42955
|
+
many(sub(roots[0], 13), 3).filter((m) => ["crRows", "crColumns", "cellColumns"].includes(keys[num(m, 1)] || "")).map((m) => [keys[num(m, 1)], num(sub(m, 2), 6)])
|
|
42956
|
+
);
|
|
42957
|
+
const ordered = (key) => {
|
|
42958
|
+
const ref = refs.get(key);
|
|
42959
|
+
if (ref === void 0) throw new Error("Missing table dimension");
|
|
42960
|
+
const ordering = sub(sub(entry(ref), 16), 1), array2 = sub(ordering, 1);
|
|
42961
|
+
const ids = many(array2, 2).map((a) => hex(getField(a, 2)));
|
|
42962
|
+
const map = /* @__PURE__ */ new Map();
|
|
42963
|
+
ids.forEach((id, i) => {
|
|
42964
|
+
const index = uuids.indexOf(id);
|
|
42965
|
+
if (index < 0) throw new Error("Missing dimension UUID");
|
|
42966
|
+
map.set(index, i);
|
|
42967
|
+
});
|
|
42968
|
+
const aliases = many(sub(ordering, 2), 1).map((pair) => [
|
|
42969
|
+
uuidIndex(num(sub(pair, 1), 6)),
|
|
42970
|
+
uuidIndex(num(sub(pair, 2), 6))
|
|
42971
|
+
]);
|
|
42972
|
+
for (let pass = 0; pass < aliases.length + 1; pass++) {
|
|
42973
|
+
let changed = false;
|
|
42974
|
+
for (const [key2, value] of aliases)
|
|
42975
|
+
if (map.has(key2) && !map.has(value)) {
|
|
42976
|
+
map.set(value, map.get(key2));
|
|
42977
|
+
changed = true;
|
|
42978
|
+
}
|
|
42979
|
+
if (!changed) break;
|
|
42980
|
+
}
|
|
42981
|
+
return { ids, map };
|
|
42982
|
+
};
|
|
42983
|
+
const rows = ordered("crRows"), columns = ordered("crColumns");
|
|
42984
|
+
if (!rows.ids.length || !columns.ids.length || rows.ids.length * columns.ids.length > 1e5)
|
|
42985
|
+
throw new Error("Unsupported table size");
|
|
42986
|
+
const values = rows.ids.map(() => columns.ids.map(() => ""));
|
|
42987
|
+
const cellRef = refs.get("cellColumns");
|
|
42988
|
+
if (cellRef === void 0) throw new Error("Missing table cells");
|
|
42989
|
+
for (const column of many(sub(entry(cellRef), 6), 1)) {
|
|
42990
|
+
const ci = columns.map.get(uuidIndex(num(sub(column, 1), 6)));
|
|
42991
|
+
const cells = entry(num(sub(column, 2), 6));
|
|
42992
|
+
for (const row of many(sub(cells, 6), 1)) {
|
|
42993
|
+
const ri = rows.map.get(uuidIndex(num(sub(row, 1), 6)));
|
|
42994
|
+
if (ri === void 0 || ci === void 0) continue;
|
|
42995
|
+
const note = sub(entry(num(sub(row, 2), 6)), 10);
|
|
42996
|
+
const text = stringValue(getField(note, 2));
|
|
42997
|
+
if (text === void 0 || text.includes("\uFFFC"))
|
|
42998
|
+
throw new Error("Embedded or unsupported table cell");
|
|
42999
|
+
values[ri][ci] = text.replace(/\n$/u, "");
|
|
43000
|
+
}
|
|
43001
|
+
}
|
|
43002
|
+
const rtl = entries.some((e) => {
|
|
43003
|
+
const map = embeddedMessage(getField(e, 13));
|
|
43004
|
+
return map && many(map, 3).some(
|
|
43005
|
+
(m) => stringValue(getField(sub(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
|
|
43006
|
+
);
|
|
43007
|
+
});
|
|
43008
|
+
if (rtl) {
|
|
43009
|
+
for (const row of values) row.reverse();
|
|
43010
|
+
columns.ids.reverse();
|
|
43011
|
+
}
|
|
43012
|
+
return { rows: values, rowIds: rows.ids, columnIds: columns.ids };
|
|
43013
|
+
}
|
|
43014
|
+
|
|
43015
|
+
// src/tools/directOperations.ts
|
|
43016
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
43017
|
+
import {
|
|
43018
|
+
closeSync,
|
|
43019
|
+
constants,
|
|
43020
|
+
fstatSync,
|
|
43021
|
+
mkdtempSync as mkdtempSync2,
|
|
43022
|
+
openSync,
|
|
43023
|
+
readFileSync as readFileSync3,
|
|
43024
|
+
rmSync as rmSync2,
|
|
43025
|
+
writeFileSync
|
|
43026
|
+
} from "node:fs";
|
|
43027
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
43028
|
+
import { basename, isAbsolute as isAbsolute2, join as join8 } from "node:path";
|
|
43029
|
+
var noteId = external_exports.string().regex(/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i);
|
|
43030
|
+
var revision = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
43031
|
+
function readSnapshot(manager, id) {
|
|
43032
|
+
const note = manager.getNoteById(id);
|
|
43033
|
+
if (!note) throw new Error("Note not found");
|
|
43034
|
+
if (note.passwordProtected) throw new Error("Locked notes cannot receive attachments");
|
|
43035
|
+
const body = manager.getNoteContentById(id);
|
|
43036
|
+
if (!body) throw new Error("Note content is unavailable");
|
|
43037
|
+
const enriched = enrichNoteRead(id, body);
|
|
43038
|
+
const rich = readRichNote(id);
|
|
43039
|
+
if (!enriched.complete || enriched.revision !== rich.revision)
|
|
43040
|
+
throw new Error("Native metadata changed during read; read the note again");
|
|
43041
|
+
return { id, title: note.title, body, rich, hash: richContentHash(body, enriched) };
|
|
43042
|
+
}
|
|
43043
|
+
function assertExistingContentPreserved(before, after) {
|
|
43044
|
+
if (before.id !== after.id || before.title !== after.title)
|
|
43045
|
+
throw new Error("Note identity changed");
|
|
43046
|
+
const tidy = (text) => text.replace(/\r\n/g, "\n").replace(/[\s\ufffc]+$/gu, "");
|
|
43047
|
+
if (!tidy(after.rich.text).startsWith(tidy(before.rich.text)))
|
|
43048
|
+
throw new Error("Existing note text was not preserved");
|
|
43049
|
+
if (linkSignature(before.rich.links) !== linkSignature(after.rich.links.slice(0, before.rich.links.length)))
|
|
43050
|
+
throw new Error("Existing links were not preserved");
|
|
43051
|
+
if (before.rich.nativeObjectIds.some((id) => !after.rich.nativeObjectIds.includes(id)))
|
|
43052
|
+
throw new Error("Existing native object was lost");
|
|
43053
|
+
if (before.rich.nativeTags.some((tag) => !after.rich.nativeTags.includes(tag)))
|
|
43054
|
+
throw new Error("Existing native tag was lost");
|
|
43055
|
+
for (const item of before.rich.checklistItems || []) {
|
|
43056
|
+
const actual = after.rich.checklistItems?.find((current) => current.id === item.id);
|
|
43057
|
+
if (!actual || actual.text !== item.text || actual.done !== item.done)
|
|
43058
|
+
throw new Error("Existing checklist item identity or state changed");
|
|
43059
|
+
}
|
|
43060
|
+
for (const object3 of before.rich.objectData || []) {
|
|
43061
|
+
const actual = after.rich.objectData?.find((current) => current.id === object3.id);
|
|
43062
|
+
if (!actual || actual.mergeable !== object3.mergeable || actual.view !== object3.view)
|
|
43063
|
+
throw new Error("Existing native object content or presentation changed");
|
|
43064
|
+
}
|
|
43065
|
+
}
|
|
43066
|
+
function localAttachment(path4) {
|
|
43067
|
+
if (!isAbsolute2(path4)) throw new Error("An absolute local file path is required");
|
|
43068
|
+
const descriptor = openSync(path4, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
43069
|
+
try {
|
|
43070
|
+
const stat = fstatSync(descriptor);
|
|
43071
|
+
if (!stat.isFile() || stat.size === 0 || stat.size > 64 * 1024 * 1024)
|
|
43072
|
+
throw new Error("Attachment must be a nonempty regular file of at most 64 MiB");
|
|
43073
|
+
const bytes = readFileSync3(descriptor);
|
|
43074
|
+
if (bytes.length !== stat.size)
|
|
43075
|
+
throw new Error("Attachment changed while it was being read; try again");
|
|
43076
|
+
return bytes;
|
|
43077
|
+
} finally {
|
|
43078
|
+
closeSync(descriptor);
|
|
43079
|
+
}
|
|
43080
|
+
}
|
|
43081
|
+
function registerDirectOperations(server2, manager) {
|
|
43082
|
+
function tool(name, description, inputSchema, handler, readOnly = false) {
|
|
43083
|
+
server2.registerTool(
|
|
43084
|
+
name,
|
|
43085
|
+
{
|
|
43086
|
+
description,
|
|
43087
|
+
inputSchema,
|
|
43088
|
+
outputSchema: external_exports.object({ ok: external_exports.boolean().optional() }).passthrough(),
|
|
43089
|
+
annotations: { readOnlyHint: readOnly }
|
|
43090
|
+
},
|
|
43091
|
+
(async (args) => {
|
|
43092
|
+
try {
|
|
43093
|
+
const result = handler(args);
|
|
43094
|
+
return {
|
|
43095
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
43096
|
+
structuredContent: result
|
|
43097
|
+
};
|
|
43098
|
+
} catch (error2) {
|
|
43099
|
+
return {
|
|
43100
|
+
content: [
|
|
43101
|
+
{
|
|
43102
|
+
type: "text",
|
|
43103
|
+
text: error2 instanceof Error ? error2.message : String(error2)
|
|
43104
|
+
}
|
|
43105
|
+
],
|
|
43106
|
+
isError: true
|
|
43107
|
+
};
|
|
43108
|
+
}
|
|
43109
|
+
})
|
|
43110
|
+
);
|
|
43111
|
+
}
|
|
43112
|
+
tool(
|
|
43113
|
+
"get-folder-by-id",
|
|
43114
|
+
"Use when: reading the exact folder name and parent before a guarded rename.\nReturns: folder id, current name, and parent id.\nDo not use when: listing folders by path (list-folders).\nSafety: read-only.",
|
|
43115
|
+
{ id: external_exports.string().max(2e3) },
|
|
43116
|
+
({ id }) => manager.getFolderById(id),
|
|
43117
|
+
true
|
|
43118
|
+
);
|
|
43119
|
+
tool(
|
|
43120
|
+
"rename-folder",
|
|
43121
|
+
"Use when: renaming one previously read folder in place.\nReturns: the unchanged folder id, new name, and parent id after readback.\nDo not use when: creating, moving, or deleting a folder.\nSafety: requires the expected current name and parent; refuses stale metadata and sibling conflicts.",
|
|
43122
|
+
{
|
|
43123
|
+
id: external_exports.string().max(2e3),
|
|
43124
|
+
expectedName: external_exports.string().max(1e3),
|
|
43125
|
+
expectedParentId: external_exports.string().max(2e3),
|
|
43126
|
+
newName: external_exports.string().min(1).max(1e3)
|
|
43127
|
+
},
|
|
43128
|
+
(args) => ({
|
|
43129
|
+
ok: true,
|
|
43130
|
+
...manager.renameFolderById(args.id, args.expectedName, args.expectedParentId, args.newName)
|
|
43131
|
+
})
|
|
43132
|
+
);
|
|
43133
|
+
tool(
|
|
43134
|
+
"add-attachment",
|
|
43135
|
+
"Use when: adding one local file to an exact note without replacing its body.\nReturns: the new attachment id, byte count, and post-write content hash after exact byte verification.\nDo not use when: reading or exporting an existing attachment.\nSafety: requires a fresh rich revision, copies at most 64 MiB through a private temporary file, never retries insertion, and verifies existing content plus fetched bytes.",
|
|
43136
|
+
{ id: noteId, expectedContentHash: revision, path: external_exports.string().min(1).max(4096) },
|
|
43137
|
+
({ id, expectedContentHash, path: path4 }) => {
|
|
43138
|
+
const before = readSnapshot(manager, id);
|
|
43139
|
+
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
43140
|
+
const bytes = localAttachment(path4);
|
|
43141
|
+
const beforeAttachments = manager.listAttachmentsById(id);
|
|
43142
|
+
const directory = mkdtempSync2(join8(tmpdir2(), "notes-attachment-add-"));
|
|
43143
|
+
const temporaryFile = join8(directory, basename(path4));
|
|
43144
|
+
try {
|
|
43145
|
+
writeFileSync(temporaryFile, bytes, { mode: 384 });
|
|
43146
|
+
if (readSnapshot(manager, id).hash !== before.hash)
|
|
43147
|
+
throw new Error("Note revision changed");
|
|
43148
|
+
let returnedId;
|
|
43149
|
+
let transportUncertain = false;
|
|
43150
|
+
try {
|
|
43151
|
+
returnedId = manager.addAttachmentById(id, before.body, temporaryFile);
|
|
43152
|
+
} catch {
|
|
43153
|
+
transportUncertain = true;
|
|
43154
|
+
}
|
|
43155
|
+
const after = readSnapshot(manager, id);
|
|
43156
|
+
assertExistingContentPreserved(before, after);
|
|
43157
|
+
const readInserted = () => [
|
|
43158
|
+
...new Map(manager.listAttachmentsById(id).map((item) => [item.id, item])).values()
|
|
43159
|
+
].filter((item) => !beforeAttachments.some((existing) => existing.id === item.id));
|
|
43160
|
+
let inserted = readInserted();
|
|
43161
|
+
for (let attempt = 0; attempt < 4 && (inserted.length !== 1 || returnedId && returnedId !== inserted[0].id); attempt++) {
|
|
43162
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 250);
|
|
43163
|
+
inserted = readInserted();
|
|
43164
|
+
}
|
|
43165
|
+
const persistentReturnedId = returnedId && /\/ICAttachment\/p\d+$/.test(returnedId);
|
|
43166
|
+
if (inserted.length !== 1 || persistentReturnedId && returnedId !== inserted[0].id)
|
|
43167
|
+
throw new Error(
|
|
43168
|
+
"Attachment insertion outcome uncertain; read the exact note before retrying"
|
|
43169
|
+
);
|
|
43170
|
+
const attachmentId = inserted[0].id;
|
|
43171
|
+
const fetched = manager.getAttachmentBase64ById(id, attachmentId);
|
|
43172
|
+
const actual = typeof fetched.base64 === "string" ? Buffer.from(fetched.base64, "base64") : null;
|
|
43173
|
+
if (!actual || createHash2("sha256").update(actual).digest("hex") !== createHash2("sha256").update(bytes).digest("hex"))
|
|
43174
|
+
throw new Error(
|
|
43175
|
+
"Attachment bytes were not verified; read the exact note before retrying"
|
|
43176
|
+
);
|
|
43177
|
+
return {
|
|
43178
|
+
ok: true,
|
|
43179
|
+
id,
|
|
43180
|
+
attachmentId,
|
|
43181
|
+
contentHash: after.hash,
|
|
43182
|
+
bytes: bytes.length,
|
|
43183
|
+
...transportUncertain ? {
|
|
43184
|
+
transportWarning: "Transport was uncertain; exact bytes and prior content were verified"
|
|
43185
|
+
} : {}
|
|
43186
|
+
};
|
|
43187
|
+
} finally {
|
|
43188
|
+
rmSync2(directory, { recursive: true, force: true });
|
|
43189
|
+
}
|
|
43190
|
+
}
|
|
43191
|
+
);
|
|
43192
|
+
}
|
|
43193
|
+
|
|
42855
43194
|
// src/index.ts
|
|
42856
43195
|
loadFileConfig();
|
|
42857
43196
|
var require2 = createRequire(import.meta.url);
|
|
@@ -42862,6 +43201,7 @@ var server = new McpServer({
|
|
|
42862
43201
|
description: "MCP server for managing Apple Notes - create, search, update, and organize notes"
|
|
42863
43202
|
});
|
|
42864
43203
|
var notesManager = new AppleNotesManager();
|
|
43204
|
+
registerDirectOperations(server, notesManager);
|
|
42865
43205
|
function successResponse(message, structured) {
|
|
42866
43206
|
const res = { content: [{ type: "text", text: message }] };
|
|
42867
43207
|
if (structured) res.structuredContent = structured;
|
|
@@ -43406,6 +43746,100 @@ registerTool(
|
|
|
43406
43746
|
return successResponse(`Shown account with ID "${id}" in Notes.app`, { id, separately });
|
|
43407
43747
|
}, "Error showing account")
|
|
43408
43748
|
);
|
|
43749
|
+
registerTool(
|
|
43750
|
+
"get-native-objects",
|
|
43751
|
+
{
|
|
43752
|
+
description: "Use when: inspecting native objects, checklist identities, or tables in one exact note.\nReturns: native object IDs and ranges, checklist IDs and state, actual native tags, decoded tables, and the current rich content hash.\nDo not use when: you only need the note body (get-note-content) or AppleScript attachment metadata (list-attachments).\nSafety: read-only; requires Full Disk Access and reports incomplete table metadata instead of guessing.",
|
|
43753
|
+
inputSchema: { id: noteIdInput },
|
|
43754
|
+
outputSchema: {
|
|
43755
|
+
id: external_exports.string().optional(),
|
|
43756
|
+
contentHash: external_exports.string().optional(),
|
|
43757
|
+
objects: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
|
|
43758
|
+
checklistItems: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
|
|
43759
|
+
nativeTags: external_exports.array(external_exports.string()).optional(),
|
|
43760
|
+
tables: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
|
|
43761
|
+
tableCellsComplete: external_exports.boolean().optional()
|
|
43762
|
+
},
|
|
43763
|
+
annotations: { readOnlyHint: true }
|
|
43764
|
+
},
|
|
43765
|
+
withErrorHandling(({ id }) => {
|
|
43766
|
+
const note = notesManager.getNoteById(id);
|
|
43767
|
+
if (!note) return errorResponse(`Note with ID "${id}" not found`);
|
|
43768
|
+
const body = notesManager.getNoteContentById(id);
|
|
43769
|
+
if (!body) return errorResponse(`Failed to read content of note "${note.title}"`);
|
|
43770
|
+
const rich = readRichNote(id);
|
|
43771
|
+
const tables = (rich.objectData || []).filter((object3) => object3.type?.includes("table")).map((object3) => {
|
|
43772
|
+
try {
|
|
43773
|
+
return {
|
|
43774
|
+
id: object3.id,
|
|
43775
|
+
attachmentId: id.replace(/ICNote\/p\d+$/, `ICAttachment/p${object3.pk}`),
|
|
43776
|
+
complete: true,
|
|
43777
|
+
...parseNoteTable(Buffer.from(object3.mergeable, "hex"))
|
|
43778
|
+
};
|
|
43779
|
+
} catch (error2) {
|
|
43780
|
+
return { id: object3.id, complete: false, reason: String(error2) };
|
|
43781
|
+
}
|
|
43782
|
+
});
|
|
43783
|
+
for (const object3 of rich.objects || []) {
|
|
43784
|
+
if (object3.type.includes("table") && !tables.some((table) => table.id === object3.id)) {
|
|
43785
|
+
tables.push({
|
|
43786
|
+
id: object3.id,
|
|
43787
|
+
complete: false,
|
|
43788
|
+
reason: "Native table metadata is unavailable"
|
|
43789
|
+
});
|
|
43790
|
+
}
|
|
43791
|
+
}
|
|
43792
|
+
const richRead = {
|
|
43793
|
+
content: body,
|
|
43794
|
+
links: rich.links,
|
|
43795
|
+
nativeTags: rich.nativeTags,
|
|
43796
|
+
complete: true,
|
|
43797
|
+
writable: !rich.hasNativeObjects && !rich.hasChecklist,
|
|
43798
|
+
revision: rich.revision
|
|
43799
|
+
};
|
|
43800
|
+
return successResponse("Native objects read from the exact note", {
|
|
43801
|
+
id,
|
|
43802
|
+
contentHash: richContentHash(body, richRead),
|
|
43803
|
+
objects: rich.objects,
|
|
43804
|
+
checklistItems: rich.checklistItems,
|
|
43805
|
+
nativeTags: rich.nativeTags,
|
|
43806
|
+
tables,
|
|
43807
|
+
tableCellsComplete: tables.every((table) => table.complete)
|
|
43808
|
+
});
|
|
43809
|
+
}, "Error reading native objects")
|
|
43810
|
+
);
|
|
43811
|
+
registerTool(
|
|
43812
|
+
"list-native-tags",
|
|
43813
|
+
{
|
|
43814
|
+
description: "Use when: listing actual native Notes tags used in one explicit account and folder.\nReturns: each native tag mapped to exact matching note IDs, plus completeness and per-note errors.\nDo not use when: searching textual #hashtags in note bodies (search-notes).\nSafety: read-only; requires Full Disk Access and discloses partial reads.",
|
|
43815
|
+
inputSchema: {
|
|
43816
|
+
account: external_exports.string().min(1).max(MAX.ACCOUNT),
|
|
43817
|
+
folder: external_exports.string().min(1).max(MAX.FOLDER)
|
|
43818
|
+
},
|
|
43819
|
+
outputSchema: {
|
|
43820
|
+
tags: external_exports.record(external_exports.array(external_exports.string())).optional(),
|
|
43821
|
+
complete: external_exports.boolean().optional(),
|
|
43822
|
+
errors: external_exports.record(external_exports.string()).optional()
|
|
43823
|
+
},
|
|
43824
|
+
annotations: { readOnlyHint: true }
|
|
43825
|
+
},
|
|
43826
|
+
withErrorHandling(({ account, folder }) => {
|
|
43827
|
+
const tags = {};
|
|
43828
|
+
const errors = {};
|
|
43829
|
+
for (const note of notesManager.listNoteRefs(account, folder)) {
|
|
43830
|
+
try {
|
|
43831
|
+
for (const tag of readRichNote(note.id).nativeTags) (tags[tag] ||= []).push(note.id);
|
|
43832
|
+
} catch {
|
|
43833
|
+
errors[note.id] = "Native metadata unavailable";
|
|
43834
|
+
}
|
|
43835
|
+
}
|
|
43836
|
+
return successResponse("Native tags read from the requested folder", {
|
|
43837
|
+
tags,
|
|
43838
|
+
complete: Object.keys(errors).length === 0,
|
|
43839
|
+
errors
|
|
43840
|
+
});
|
|
43841
|
+
}, "Error listing native tags")
|
|
43842
|
+
);
|
|
43409
43843
|
registerTool(
|
|
43410
43844
|
"update-note",
|
|
43411
43845
|
{
|
|
@@ -44263,8 +44697,8 @@ registerTool(
|
|
|
44263
44697
|
contentType: external_exports.string().optional()
|
|
44264
44698
|
}
|
|
44265
44699
|
},
|
|
44266
|
-
withErrorHandling(({ noteId, attachmentId, savePath }) => {
|
|
44267
|
-
const r = notesManager.saveAttachmentById(
|
|
44700
|
+
withErrorHandling(({ noteId: noteId2, attachmentId, savePath }) => {
|
|
44701
|
+
const r = notesManager.saveAttachmentById(noteId2, attachmentId, savePath);
|
|
44268
44702
|
if (!r.success) {
|
|
44269
44703
|
return errorResponse(`Failed to save attachment: ${r.error ?? "unknown error"}`);
|
|
44270
44704
|
}
|
|
@@ -44290,8 +44724,8 @@ registerTool(
|
|
|
44290
44724
|
base64: external_exports.string().optional()
|
|
44291
44725
|
}
|
|
44292
44726
|
},
|
|
44293
|
-
withErrorHandling(({ noteId, attachmentId }) => {
|
|
44294
|
-
const r = notesManager.getAttachmentBase64ById(
|
|
44727
|
+
withErrorHandling(({ noteId: noteId2, attachmentId }) => {
|
|
44728
|
+
const r = notesManager.getAttachmentBase64ById(noteId2, attachmentId);
|
|
44295
44729
|
if (!r.success || !r.base64) {
|
|
44296
44730
|
return errorResponse(`Failed to fetch attachment: ${r.error ?? "unknown error"}`);
|
|
44297
44731
|
}
|
|
@@ -44316,13 +44750,13 @@ registerTool(
|
|
|
44316
44750
|
separately: external_exports.boolean().optional()
|
|
44317
44751
|
}
|
|
44318
44752
|
},
|
|
44319
|
-
withErrorHandling(({ noteId, attachmentId, separately = false }) => {
|
|
44320
|
-
const success = notesManager.showAttachmentById(
|
|
44753
|
+
withErrorHandling(({ noteId: noteId2, attachmentId, separately = false }) => {
|
|
44754
|
+
const success = notesManager.showAttachmentById(noteId2, attachmentId, separately);
|
|
44321
44755
|
if (!success) {
|
|
44322
|
-
return errorResponse(`Failed to show attachment "${attachmentId}" on note "${
|
|
44756
|
+
return errorResponse(`Failed to show attachment "${attachmentId}" on note "${noteId2}"`);
|
|
44323
44757
|
}
|
|
44324
44758
|
return successResponse(`Shown attachment "${attachmentId}" in Notes.app`, {
|
|
44325
|
-
noteId,
|
|
44759
|
+
noteId: noteId2,
|
|
44326
44760
|
attachmentId,
|
|
44327
44761
|
separately
|
|
44328
44762
|
});
|
package/package.json
CHANGED