apple-notes-mcp 2.8.33 → 2.8.34
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 +31 -3
- package/build/index.js +873 -144
- 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, constants4) {
|
|
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, constants4);
|
|
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, constants4) {
|
|
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, constants4);
|
|
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, constants4) {
|
|
506
|
+
this.code = optimizeExpr(this.code, names, constants4);
|
|
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, constants4) {
|
|
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, constants4))
|
|
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, constants4) {
|
|
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, constants4);
|
|
596
|
+
if (!(super.optimizeNames(names, constants4) || this.else))
|
|
597
597
|
return;
|
|
598
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
598
|
+
this.condition = optimizeExpr(this.condition, names, constants4);
|
|
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, constants4) {
|
|
622
|
+
if (!super.optimizeNames(names, constants4))
|
|
623
623
|
return;
|
|
624
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
624
|
+
this.iteration = optimizeExpr(this.iteration, names, constants4);
|
|
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, constants4) {
|
|
661
|
+
if (!super.optimizeNames(names, constants4))
|
|
662
662
|
return;
|
|
663
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
663
|
+
this.iterable = optimizeExpr(this.iterable, names, constants4);
|
|
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, constants4) {
|
|
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, constants4);
|
|
708
|
+
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants4);
|
|
709
|
+
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants4);
|
|
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, constants4) {
|
|
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 = constants4[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 && constants4[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 = resolve4.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 resolve4(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 resolve4(baseURI, relativeURI, options) {
|
|
3847
3847
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3848
3848
|
const {
|
|
3849
3849
|
parsed: baseParsed,
|
|
@@ -3876,49 +3876,49 @@ var require_fast_uri = __commonJS({
|
|
|
3876
3876
|
schemelessOptions.skipEscape = true;
|
|
3877
3877
|
return serialize(resolved, schemelessOptions);
|
|
3878
3878
|
}
|
|
3879
|
-
function resolveComponent(base,
|
|
3879
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
3880
3880
|
const target = {};
|
|
3881
3881
|
if (!skipNormalization) {
|
|
3882
3882
|
base = parse3(serialize(base, options), options);
|
|
3883
|
-
|
|
3883
|
+
relative3 = parse3(serialize(relative3, options), options);
|
|
3884
3884
|
}
|
|
3885
3885
|
options = options || {};
|
|
3886
|
-
if (!options.tolerant &&
|
|
3887
|
-
target.scheme =
|
|
3888
|
-
target.userinfo =
|
|
3889
|
-
target.host =
|
|
3890
|
-
target.port =
|
|
3891
|
-
target.path = removeDotSegments(
|
|
3892
|
-
target.query =
|
|
3886
|
+
if (!options.tolerant && relative3.scheme) {
|
|
3887
|
+
target.scheme = relative3.scheme;
|
|
3888
|
+
target.userinfo = relative3.userinfo;
|
|
3889
|
+
target.host = relative3.host;
|
|
3890
|
+
target.port = relative3.port;
|
|
3891
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
3892
|
+
target.query = relative3.query;
|
|
3893
3893
|
} else {
|
|
3894
|
-
if (
|
|
3895
|
-
target.userinfo =
|
|
3896
|
-
target.host =
|
|
3897
|
-
target.port =
|
|
3898
|
-
target.path = removeDotSegments(
|
|
3899
|
-
target.query =
|
|
3894
|
+
if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
|
|
3895
|
+
target.userinfo = relative3.userinfo;
|
|
3896
|
+
target.host = relative3.host;
|
|
3897
|
+
target.port = relative3.port;
|
|
3898
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
3899
|
+
target.query = relative3.query;
|
|
3900
3900
|
} else {
|
|
3901
|
-
if (!
|
|
3901
|
+
if (!relative3.path) {
|
|
3902
3902
|
target.path = base.path;
|
|
3903
|
-
if (
|
|
3904
|
-
target.query =
|
|
3903
|
+
if (relative3.query !== void 0) {
|
|
3904
|
+
target.query = relative3.query;
|
|
3905
3905
|
} else {
|
|
3906
3906
|
target.query = base.query;
|
|
3907
3907
|
}
|
|
3908
3908
|
} else {
|
|
3909
|
-
if (
|
|
3910
|
-
target.path = removeDotSegments(
|
|
3909
|
+
if (relative3.path[0] === "/") {
|
|
3910
|
+
target.path = removeDotSegments(relative3.path);
|
|
3911
3911
|
} else {
|
|
3912
3912
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
3913
|
-
target.path = "/" +
|
|
3913
|
+
target.path = "/" + relative3.path;
|
|
3914
3914
|
} else if (!base.path) {
|
|
3915
|
-
target.path =
|
|
3915
|
+
target.path = relative3.path;
|
|
3916
3916
|
} else {
|
|
3917
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
3917
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
3918
3918
|
}
|
|
3919
3919
|
target.path = removeDotSegments(target.path);
|
|
3920
3920
|
}
|
|
3921
|
-
target.query =
|
|
3921
|
+
target.query = relative3.query;
|
|
3922
3922
|
}
|
|
3923
3923
|
target.userinfo = base.userinfo;
|
|
3924
3924
|
target.host = base.host;
|
|
@@ -3926,7 +3926,7 @@ var require_fast_uri = __commonJS({
|
|
|
3926
3926
|
}
|
|
3927
3927
|
target.scheme = base.scheme;
|
|
3928
3928
|
}
|
|
3929
|
-
target.fragment =
|
|
3929
|
+
target.fragment = relative3.fragment;
|
|
3930
3930
|
return target;
|
|
3931
3931
|
}
|
|
3932
3932
|
function equal(uriA, uriB, options) {
|
|
@@ -4205,7 +4205,7 @@ var require_fast_uri = __commonJS({
|
|
|
4205
4205
|
var fastUri = {
|
|
4206
4206
|
SCHEMES,
|
|
4207
4207
|
normalize,
|
|
4208
|
-
resolve:
|
|
4208
|
+
resolve: resolve4,
|
|
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 constants4 = {
|
|
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 constants4) {
|
|
7465
|
+
v = { value: constants4[c] };
|
|
7466
7466
|
Object.defineProperty(DOMException, c, v);
|
|
7467
7467
|
Object.defineProperty(DOMException.prototype, c, v);
|
|
7468
7468
|
}
|
|
@@ -12184,9 +12184,9 @@ var require_URL = __commonJS({
|
|
|
12184
12184
|
},
|
|
12185
12185
|
// See: http://tools.ietf.org/html/rfc3986#section-5.2
|
|
12186
12186
|
// and https://url.spec.whatwg.org/#constructors
|
|
12187
|
-
resolve: function(
|
|
12187
|
+
resolve: function(relative3) {
|
|
12188
12188
|
var base = this;
|
|
12189
|
-
var r = new URL2(
|
|
12189
|
+
var r = new URL2(relative3);
|
|
12190
12190
|
var t = new URL2();
|
|
12191
12191
|
if (r.scheme !== void 0) {
|
|
12192
12192
|
t.scheme = r.scheme;
|
|
@@ -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 join17(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 = join17(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 join17(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((resolve4) => setTimeout(resolve4, 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((resolve4, 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
|
+
resolve4(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((resolve4, 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(resolve4, 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((resolve4) => setTimeout(resolve4, 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((resolve4) => {
|
|
38974
38974
|
const json2 = serializeMessage(message);
|
|
38975
38975
|
if (this._stdout.write(json2)) {
|
|
38976
|
-
|
|
38976
|
+
resolve4();
|
|
38977
38977
|
} else {
|
|
38978
|
-
this._stdout.once("drain",
|
|
38978
|
+
this._stdout.once("drain", resolve4);
|
|
38979
38979
|
}
|
|
38980
38980
|
});
|
|
38981
38981
|
}
|
|
@@ -41036,11 +41036,586 @@ function cleanupTempDir(dir) {
|
|
|
41036
41036
|
}
|
|
41037
41037
|
}
|
|
41038
41038
|
|
|
41039
|
+
// src/utils/attachmentAssets.ts
|
|
41040
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
41041
|
+
import {
|
|
41042
|
+
closeSync as closeSync2,
|
|
41043
|
+
constants as constants2,
|
|
41044
|
+
fstatSync as fstatSync2,
|
|
41045
|
+
lstatSync as lstatSync2,
|
|
41046
|
+
mkdirSync as mkdirSync2,
|
|
41047
|
+
openSync as openSync2,
|
|
41048
|
+
readdirSync,
|
|
41049
|
+
readSync,
|
|
41050
|
+
realpathSync as realpathSync2,
|
|
41051
|
+
unlinkSync,
|
|
41052
|
+
writeSync
|
|
41053
|
+
} from "node:fs";
|
|
41054
|
+
import { homedir as homedir6 } from "node:os";
|
|
41055
|
+
import { basename, dirname as dirname2, extname, isAbsolute as isAbsolute2, join as join6, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
41056
|
+
import { gunzipSync as gunzipSync5 } from "node:zlib";
|
|
41057
|
+
var NOTES_CONTAINER_DIR = join6(
|
|
41058
|
+
homedir6(),
|
|
41059
|
+
"Library/Group Containers/group.com.apple.notes"
|
|
41060
|
+
);
|
|
41061
|
+
var AttachmentStoreError = class extends Error {
|
|
41062
|
+
constructor(message, code) {
|
|
41063
|
+
super(message);
|
|
41064
|
+
this.code = code;
|
|
41065
|
+
this.name = "AttachmentStoreError";
|
|
41066
|
+
}
|
|
41067
|
+
code;
|
|
41068
|
+
};
|
|
41069
|
+
var MAX_PREVIEW_DIR_ENTRIES = 1e5;
|
|
41070
|
+
var MAX_BUNDLE_ENTRIES = 64;
|
|
41071
|
+
var MAX_GENERATION_DIRS = 64;
|
|
41072
|
+
var MAX_COLLISION_SUFFIX = 1e4;
|
|
41073
|
+
var MAX_BODY_BYTES = 32 * 1024 * 1024;
|
|
41074
|
+
var PREVIEW_IMAGE_SUFFIXES = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".heic", ".tiff", ".gif"]);
|
|
41075
|
+
var GENERIC_FILE_NAMES = /* @__PURE__ */ new Set([
|
|
41076
|
+
"fallbackimage.png",
|
|
41077
|
+
"fallbackimage.jpg",
|
|
41078
|
+
"fallbackpdf.pdf",
|
|
41079
|
+
"preview.png",
|
|
41080
|
+
"orientedpreview.png"
|
|
41081
|
+
]);
|
|
41082
|
+
var IMAGE_UTIS = /* @__PURE__ */ new Set([
|
|
41083
|
+
"public.jpeg",
|
|
41084
|
+
"public.png",
|
|
41085
|
+
"public.heic",
|
|
41086
|
+
"public.heif",
|
|
41087
|
+
"public.tiff",
|
|
41088
|
+
"public.image",
|
|
41089
|
+
"com.compuserve.gif",
|
|
41090
|
+
"org.webmproject.webp",
|
|
41091
|
+
"com.microsoft.bmp",
|
|
41092
|
+
"com.adobe.raw-image"
|
|
41093
|
+
]);
|
|
41094
|
+
function classifyAttachmentKind(uti) {
|
|
41095
|
+
if (!uti) return "other";
|
|
41096
|
+
const u = uti.toLowerCase();
|
|
41097
|
+
if (u === "com.apple.notes.table") return "table";
|
|
41098
|
+
if (u === "public.url" || u.startsWith("public.url")) return "url";
|
|
41099
|
+
if (u === "com.apple.paper.doc.scan" || u === "com.apple.notes.gallery" || u.includes("scan"))
|
|
41100
|
+
return "scan";
|
|
41101
|
+
if (u === "com.apple.paper" || u.startsWith("com.apple.drawing")) return "drawing";
|
|
41102
|
+
if (u === "com.adobe.pdf" || u.includes("pdf")) return "pdf";
|
|
41103
|
+
if (u.includes("audio")) return "audio";
|
|
41104
|
+
if (u === "public.mpeg-4" || u.includes("movie") || u.includes("video")) return "video";
|
|
41105
|
+
if (IMAGE_UTIS.has(u) || u.includes("image")) return "image";
|
|
41106
|
+
return "other";
|
|
41107
|
+
}
|
|
41108
|
+
function parseNoteId2(noteId3) {
|
|
41109
|
+
const m = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/ICNote\/p(\d+)$/.exec(noteId3);
|
|
41110
|
+
if (!m) {
|
|
41111
|
+
throw new AttachmentStoreError(
|
|
41112
|
+
`Invalid note ID format: "${noteId3}". Expected x-coredata://UUID/ICNote/pNNN`,
|
|
41113
|
+
"invalid_id"
|
|
41114
|
+
);
|
|
41115
|
+
}
|
|
41116
|
+
return { store: m[1], pk: Number(m[2]) };
|
|
41117
|
+
}
|
|
41118
|
+
function attachmentCoreDataId(noteId3, pk) {
|
|
41119
|
+
return noteId3.replace(/ICNote\/p\d+$/, `ICAttachment/p${pk}`);
|
|
41120
|
+
}
|
|
41121
|
+
function buildAttachmentRowsSql(notePk, columns) {
|
|
41122
|
+
if (!Number.isSafeInteger(notePk) || notePk < 0) throw new Error("Invalid note primary key");
|
|
41123
|
+
const col2 = (alias, name) => columns.has(name) ? `${alias}.${name}` : "NULL";
|
|
41124
|
+
const firstOf = (alias, names) => {
|
|
41125
|
+
const present = names.filter((n) => columns.has(n)).map((n) => `${alias}.${n}`);
|
|
41126
|
+
if (present.length === 0) return "NULL";
|
|
41127
|
+
return present.length === 1 ? present[0] : `COALESCE(${present.join(", ")})`;
|
|
41128
|
+
};
|
|
41129
|
+
const accountCols = [...columns].filter((c) => /^ZACCOUNT\d*$/.test(c)).sort();
|
|
41130
|
+
const accountRefs = [
|
|
41131
|
+
...accountCols.map((c) => `a.${c}`),
|
|
41132
|
+
...accountCols.map((c) => `n.${c}`)
|
|
41133
|
+
].join(", ");
|
|
41134
|
+
const account = accountCols.length ? `(SELECT acc.ZIDENTIFIER FROM ZICCLOUDSYNCINGOBJECT acc WHERE acc.Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICAccount') AND acc.Z_PK IN (${accountRefs}) LIMIT 1)` : "NULL";
|
|
41135
|
+
const noteLink = columns.has("ZNOTE") ? "a.ZNOTE" : "NULL";
|
|
41136
|
+
const parent = col2("a", "ZPARENTATTACHMENT");
|
|
41137
|
+
const deleted = columns.has("ZMARKEDFORDELETION") ? " AND COALESCE(a.ZMARKEDFORDELETION, 0) = 0" : "";
|
|
41138
|
+
const fields = [
|
|
41139
|
+
`'pk', a.Z_PK`,
|
|
41140
|
+
`'identifier', a.ZIDENTIFIER`,
|
|
41141
|
+
`'uti', ${col2("a", "ZTYPEUTI")}`,
|
|
41142
|
+
`'parentPk', ${parent}`,
|
|
41143
|
+
`'filename', ${col2("a", "ZFILENAME")}`,
|
|
41144
|
+
`'mediaIdentifier', m.ZIDENTIFIER`,
|
|
41145
|
+
`'mediaFilename', ${col2("m", "ZFILENAME")}`,
|
|
41146
|
+
`'mediaGeneration', ${firstOf("m", ["ZGENERATION1", "ZGENERATION"])}`,
|
|
41147
|
+
`'fallbackImageGeneration', ${col2("a", "ZFALLBACKIMAGEGENERATION")}`,
|
|
41148
|
+
`'fallbackPdfGeneration', ${col2("a", "ZFALLBACKPDFGENERATION")}`,
|
|
41149
|
+
`'accountIdentifier', ${account}`
|
|
41150
|
+
].join(", ");
|
|
41151
|
+
const mediaJoin = columns.has("ZMEDIA") ? "LEFT JOIN ZICCLOUDSYNCINGOBJECT m ON m.Z_PK = a.ZMEDIA" : "LEFT JOIN ZICCLOUDSYNCINGOBJECT m ON 0";
|
|
41152
|
+
const childClause = parent === "NULL" ? "" : ` OR a.ZPARENTATTACHMENT IN (SELECT p.Z_PK FROM ZICCLOUDSYNCINGOBJECT p WHERE p.ZNOTE = ${notePk})`;
|
|
41153
|
+
return [
|
|
41154
|
+
"BEGIN;",
|
|
41155
|
+
`SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = ${notePk} AND Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICNote');`,
|
|
41156
|
+
`SELECT json_group_array(json_object(${fields})) FROM ZICCLOUDSYNCINGOBJECT a ${mediaJoin} LEFT JOIN ZICCLOUDSYNCINGOBJECT n ON n.Z_PK = ${noteLink} WHERE a.Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICAttachment') AND (${noteLink} = ${notePk}${childClause})${deleted};`,
|
|
41157
|
+
`SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = ${notePk};`,
|
|
41158
|
+
"COMMIT;"
|
|
41159
|
+
].join(" ");
|
|
41160
|
+
}
|
|
41161
|
+
function runSqlite3(dbPath2, sql) {
|
|
41162
|
+
try {
|
|
41163
|
+
return execFileSync6("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
|
|
41164
|
+
encoding: "utf8",
|
|
41165
|
+
timeout: 1e4,
|
|
41166
|
+
maxBuffer: 128 * 1024 * 1024,
|
|
41167
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
41168
|
+
});
|
|
41169
|
+
} catch (error2) {
|
|
41170
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
41171
|
+
if (/authorization denied|unable to open database/i.test(message)) {
|
|
41172
|
+
throw new AttachmentStoreError(
|
|
41173
|
+
"Full Disk Access is required to read attachment paths. Grant it to the app that launches this server, then relaunch it (run the doctor tool to verify).",
|
|
41174
|
+
"no_fda"
|
|
41175
|
+
);
|
|
41176
|
+
}
|
|
41177
|
+
throw new AttachmentStoreError(`Failed to read attachment rows: ${message}`, "query_error");
|
|
41178
|
+
}
|
|
41179
|
+
}
|
|
41180
|
+
function toStringOrNull(value) {
|
|
41181
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
41182
|
+
}
|
|
41183
|
+
function toIntOrNull(value) {
|
|
41184
|
+
return typeof value === "number" && Number.isSafeInteger(value) ? value : null;
|
|
41185
|
+
}
|
|
41186
|
+
function parseAttachmentRows(json2) {
|
|
41187
|
+
const raw = JSON.parse(json2 || "[]");
|
|
41188
|
+
if (!Array.isArray(raw)) throw new Error("Invalid attachment rows");
|
|
41189
|
+
const rows = [];
|
|
41190
|
+
for (const item of raw) {
|
|
41191
|
+
if (!item || typeof item !== "object") continue;
|
|
41192
|
+
const r = item;
|
|
41193
|
+
const pk = toIntOrNull(r.pk);
|
|
41194
|
+
const identifier = toStringOrNull(r.identifier);
|
|
41195
|
+
if (pk === null || identifier === null) continue;
|
|
41196
|
+
rows.push({
|
|
41197
|
+
pk,
|
|
41198
|
+
identifier,
|
|
41199
|
+
uti: toStringOrNull(r.uti),
|
|
41200
|
+
parentPk: toIntOrNull(r.parentPk),
|
|
41201
|
+
filename: toStringOrNull(r.filename),
|
|
41202
|
+
mediaIdentifier: toStringOrNull(r.mediaIdentifier),
|
|
41203
|
+
mediaFilename: toStringOrNull(r.mediaFilename),
|
|
41204
|
+
mediaGeneration: toStringOrNull(r.mediaGeneration),
|
|
41205
|
+
fallbackImageGeneration: toStringOrNull(r.fallbackImageGeneration),
|
|
41206
|
+
fallbackPdfGeneration: toStringOrNull(r.fallbackPdfGeneration),
|
|
41207
|
+
accountIdentifier: toStringOrNull(r.accountIdentifier)
|
|
41208
|
+
});
|
|
41209
|
+
}
|
|
41210
|
+
return rows.sort((a, b) => a.pk - b.pk);
|
|
41211
|
+
}
|
|
41212
|
+
function attachmentOrderFromNoteData(hex3) {
|
|
41213
|
+
if (!hex3 || !/^[0-9a-f]+$/i.test(hex3)) return null;
|
|
41214
|
+
try {
|
|
41215
|
+
const data = gunzipSync5(Buffer.from(hex3, "hex"), { maxOutputLength: MAX_BODY_BYTES });
|
|
41216
|
+
const wrapper = embeddedMessage(getField(decodeMessage(data), 2));
|
|
41217
|
+
const body = wrapper && embeddedMessage(getField(wrapper, 3));
|
|
41218
|
+
if (!body) return null;
|
|
41219
|
+
const ids = [];
|
|
41220
|
+
for (const run of getFields(body, 5)) {
|
|
41221
|
+
const fields = embeddedMessage(run);
|
|
41222
|
+
const info = fields && embeddedMessage(getField(fields, 12));
|
|
41223
|
+
const id2 = info && stringValue(getField(info, 1));
|
|
41224
|
+
if (id2 && !ids.some((seen) => seen.toLowerCase() === id2.toLowerCase())) ids.push(id2);
|
|
41225
|
+
}
|
|
41226
|
+
return ids;
|
|
41227
|
+
} catch {
|
|
41228
|
+
return null;
|
|
41229
|
+
}
|
|
41230
|
+
}
|
|
41231
|
+
function readNoteAttachmentRows(noteId3, dbPath2 = join6(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
|
|
41232
|
+
const { pk } = parseNoteId2(noteId3);
|
|
41233
|
+
const columnList = runSqlite3(
|
|
41234
|
+
dbPath2,
|
|
41235
|
+
"SELECT group_concat(name, ',') FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');"
|
|
41236
|
+
).trim();
|
|
41237
|
+
const columns = new Set(columnList.split(",").filter(Boolean));
|
|
41238
|
+
if (!columns.has("ZIDENTIFIER")) {
|
|
41239
|
+
throw new AttachmentStoreError("Unsupported Notes database schema", "query_error");
|
|
41240
|
+
}
|
|
41241
|
+
const lines = runSqlite3(dbPath2, buildAttachmentRowsSql(pk, columns)).split("\n");
|
|
41242
|
+
if (lines[0]?.trim() !== "1") {
|
|
41243
|
+
throw new AttachmentStoreError(
|
|
41244
|
+
`No note found in the database for ID "${noteId3}".`,
|
|
41245
|
+
"not_found"
|
|
41246
|
+
);
|
|
41247
|
+
}
|
|
41248
|
+
let rows;
|
|
41249
|
+
try {
|
|
41250
|
+
rows = parseAttachmentRows(lines[1] ?? "[]");
|
|
41251
|
+
} catch {
|
|
41252
|
+
throw new AttachmentStoreError("Attachment rows could not be parsed", "query_error");
|
|
41253
|
+
}
|
|
41254
|
+
return { rows, bodyOrder: attachmentOrderFromNoteData(lines[2]?.trim()) };
|
|
41255
|
+
}
|
|
41256
|
+
function safeComponent(value) {
|
|
41257
|
+
if (typeof value !== "string" || !value || value.length > 255) return null;
|
|
41258
|
+
if (value === "." || value === ".." || value.includes("/") || value.includes("\0")) return null;
|
|
41259
|
+
return value;
|
|
41260
|
+
}
|
|
41261
|
+
function realInside(path7, rootReal) {
|
|
41262
|
+
try {
|
|
41263
|
+
const real = realpathSync2.native(path7);
|
|
41264
|
+
return real === rootReal || real.startsWith(rootReal + sep2) ? real : null;
|
|
41265
|
+
} catch {
|
|
41266
|
+
return null;
|
|
41267
|
+
}
|
|
41268
|
+
}
|
|
41269
|
+
function isRegularFile(path7) {
|
|
41270
|
+
try {
|
|
41271
|
+
return lstatSync2(path7).isFile();
|
|
41272
|
+
} catch {
|
|
41273
|
+
return false;
|
|
41274
|
+
}
|
|
41275
|
+
}
|
|
41276
|
+
function isDirectory(path7) {
|
|
41277
|
+
try {
|
|
41278
|
+
return lstatSync2(path7).isDirectory();
|
|
41279
|
+
} catch {
|
|
41280
|
+
return false;
|
|
41281
|
+
}
|
|
41282
|
+
}
|
|
41283
|
+
function boundedEntries(dir, limit) {
|
|
41284
|
+
try {
|
|
41285
|
+
const entries = readdirSync(dir);
|
|
41286
|
+
return entries.length > limit ? null : entries.sort();
|
|
41287
|
+
} catch {
|
|
41288
|
+
return null;
|
|
41289
|
+
}
|
|
41290
|
+
}
|
|
41291
|
+
function resolveAccountDir(containerDir, accountIdentifier) {
|
|
41292
|
+
const accountsDir = join6(containerDir, "Accounts");
|
|
41293
|
+
let accountsReal;
|
|
41294
|
+
try {
|
|
41295
|
+
accountsReal = realpathSync2.native(accountsDir);
|
|
41296
|
+
} catch {
|
|
41297
|
+
return null;
|
|
41298
|
+
}
|
|
41299
|
+
const account = safeComponent(accountIdentifier);
|
|
41300
|
+
if (account) {
|
|
41301
|
+
const dir = realInside(join6(accountsReal, account), accountsReal);
|
|
41302
|
+
if (dir && isDirectory(dir)) return dir;
|
|
41303
|
+
}
|
|
41304
|
+
const entries = (boundedEntries(accountsReal, 64) ?? []).filter(
|
|
41305
|
+
(e) => isDirectory(join6(accountsReal, e))
|
|
41306
|
+
);
|
|
41307
|
+
if (entries.length !== 1) return null;
|
|
41308
|
+
return realInside(join6(accountsReal, entries[0]), accountsReal);
|
|
41309
|
+
}
|
|
41310
|
+
function generationRank(name) {
|
|
41311
|
+
const m = /^(\d+)_/.exec(name);
|
|
41312
|
+
return m ? Number(m[1]) : 0;
|
|
41313
|
+
}
|
|
41314
|
+
function generationDirs(base, accountDir) {
|
|
41315
|
+
const entries = boundedEntries(base, MAX_GENERATION_DIRS) ?? [];
|
|
41316
|
+
return entries.map((e) => realInside(join6(base, e), accountDir)).filter((p) => p !== null && isDirectory(p)).sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
|
|
41317
|
+
}
|
|
41318
|
+
function fallbackFiles(accountDir, rootName, identifier, generation, names) {
|
|
41319
|
+
const base = join6(accountDir, rootName, identifier);
|
|
41320
|
+
const found = [];
|
|
41321
|
+
const add = (candidate) => {
|
|
41322
|
+
const real = realInside(candidate, accountDir);
|
|
41323
|
+
if (real && isRegularFile(real) && !found.includes(real)) found.push(real);
|
|
41324
|
+
};
|
|
41325
|
+
const gen = safeComponent(generation);
|
|
41326
|
+
if (gen) for (const name of names) add(join6(base, gen, name));
|
|
41327
|
+
if (isDirectory(base)) {
|
|
41328
|
+
for (const dir of generationDirs(base, accountDir))
|
|
41329
|
+
for (const name of names) add(join6(dir, name));
|
|
41330
|
+
for (const name of names) add(join6(base, name));
|
|
41331
|
+
}
|
|
41332
|
+
for (const name of names) add(join6(accountDir, rootName, `${identifier}${extname(name)}`));
|
|
41333
|
+
return found;
|
|
41334
|
+
}
|
|
41335
|
+
function previewPixelArea(name) {
|
|
41336
|
+
const matches = [...name.matchAll(/-(\d{1,6})x(\d{1,6})(?=-|\.|$)/g)];
|
|
41337
|
+
const last = matches.at(-1);
|
|
41338
|
+
return last ? Number(last[1]) * Number(last[2]) : 0;
|
|
41339
|
+
}
|
|
41340
|
+
function previewFileInBundle(bundle, accountDir) {
|
|
41341
|
+
const direct = [];
|
|
41342
|
+
const nested = [];
|
|
41343
|
+
for (const entry of boundedEntries(bundle, MAX_BUNDLE_ENTRIES) ?? []) {
|
|
41344
|
+
const child = realInside(join6(bundle, entry), accountDir);
|
|
41345
|
+
if (!child) continue;
|
|
41346
|
+
if (isRegularFile(child)) direct.push(child);
|
|
41347
|
+
else if (isDirectory(child)) nested.push(child);
|
|
41348
|
+
}
|
|
41349
|
+
nested.sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
|
|
41350
|
+
const pick2 = (files) => files.find((f) => basename(f) === "Preview.png") ?? files.find((f) => PREVIEW_IMAGE_SUFFIXES.has(extname(f).toLowerCase())) ?? null;
|
|
41351
|
+
for (const dir of nested) {
|
|
41352
|
+
const files = (boundedEntries(dir, MAX_BUNDLE_ENTRIES) ?? []).map((e) => realInside(join6(dir, e), accountDir)).filter((p) => p !== null && isRegularFile(p));
|
|
41353
|
+
const chosen = pick2(files);
|
|
41354
|
+
if (chosen) return chosen;
|
|
41355
|
+
}
|
|
41356
|
+
return pick2(direct);
|
|
41357
|
+
}
|
|
41358
|
+
function listPreviewEntries(accountDir) {
|
|
41359
|
+
return boundedEntries(join6(accountDir, "Previews"), MAX_PREVIEW_DIR_ENTRIES) ?? [];
|
|
41360
|
+
}
|
|
41361
|
+
function previewPaths(accountDir, identifier, entries) {
|
|
41362
|
+
const id2 = safeComponent(identifier);
|
|
41363
|
+
if (!id2) return [];
|
|
41364
|
+
const prefix = `${id2}-`.toLowerCase();
|
|
41365
|
+
const candidates = entries.filter((name) => name.toLowerCase().startsWith(prefix)).filter((name) => {
|
|
41366
|
+
const ext = extname(name).toLowerCase();
|
|
41367
|
+
return !ext || PREVIEW_IMAGE_SUFFIXES.has(ext) || /^\.\d+$/.test(ext);
|
|
41368
|
+
}).sort((a, b) => previewPixelArea(b) - previewPixelArea(a) || a.localeCompare(b));
|
|
41369
|
+
const files = [];
|
|
41370
|
+
for (const name of candidates) {
|
|
41371
|
+
const real = realInside(join6(accountDir, "Previews", name), accountDir);
|
|
41372
|
+
if (!real) continue;
|
|
41373
|
+
const file = isRegularFile(real) ? real : isDirectory(real) ? previewFileInBundle(real, accountDir) : null;
|
|
41374
|
+
if (file && !files.includes(file)) files.push(file);
|
|
41375
|
+
}
|
|
41376
|
+
return files;
|
|
41377
|
+
}
|
|
41378
|
+
function assetPathsFor(accountDir, row) {
|
|
41379
|
+
const found = [];
|
|
41380
|
+
const add = (candidate) => {
|
|
41381
|
+
const real = realInside(candidate, accountDir);
|
|
41382
|
+
if (real && isRegularFile(real) && !found.includes(real)) found.push(real);
|
|
41383
|
+
};
|
|
41384
|
+
const mediaId = safeComponent(row.mediaIdentifier);
|
|
41385
|
+
const mediaName = safeComponent(row.mediaFilename);
|
|
41386
|
+
const mediaGen = safeComponent(row.mediaGeneration);
|
|
41387
|
+
if (mediaId && mediaName) {
|
|
41388
|
+
if (mediaGen) add(join6(accountDir, "Media", mediaId, mediaGen, mediaName));
|
|
41389
|
+
add(join6(accountDir, "Media", mediaId, mediaName));
|
|
41390
|
+
}
|
|
41391
|
+
const id2 = safeComponent(row.identifier);
|
|
41392
|
+
if (!id2) return found;
|
|
41393
|
+
const ownName = safeComponent(row.filename);
|
|
41394
|
+
if (ownName) add(join6(accountDir, "Media", id2, ownName));
|
|
41395
|
+
for (const file of fallbackFiles(accountDir, "FallbackImages", id2, row.fallbackImageGeneration, [
|
|
41396
|
+
"FallbackImage.png",
|
|
41397
|
+
"FallbackImage.jpg"
|
|
41398
|
+
]))
|
|
41399
|
+
add(file);
|
|
41400
|
+
for (const file of fallbackFiles(accountDir, "FallbackPDFs", id2, row.fallbackPdfGeneration, [
|
|
41401
|
+
"FallbackPDF.pdf"
|
|
41402
|
+
]))
|
|
41403
|
+
add(file);
|
|
41404
|
+
return found;
|
|
41405
|
+
}
|
|
41406
|
+
function assembleAttachmentAssets(rows, bodyOrder, containerDir = NOTES_CONTAINER_DIR) {
|
|
41407
|
+
const byPk = new Map(rows.map((r) => [r.pk, r]));
|
|
41408
|
+
const roots = rows.filter((r) => r.parentPk === null || !byPk.has(r.parentPk));
|
|
41409
|
+
const bodyIndex = /* @__PURE__ */ new Map();
|
|
41410
|
+
(bodyOrder ?? []).forEach((id2, index) => bodyIndex.set(id2.toLowerCase(), index));
|
|
41411
|
+
const indexOf = (r) => bodyIndex.get(r.identifier.toLowerCase()) ?? null;
|
|
41412
|
+
const inBody = roots.filter((r) => indexOf(r) !== null);
|
|
41413
|
+
const orderSource = inBody.length > 0 ? "body" : "creation";
|
|
41414
|
+
const orderedRoots = [
|
|
41415
|
+
...inBody.sort((a, b) => indexOf(a) - indexOf(b)),
|
|
41416
|
+
...roots.filter((r) => indexOf(r) === null)
|
|
41417
|
+
];
|
|
41418
|
+
const accountDirs = /* @__PURE__ */ new Map();
|
|
41419
|
+
const previewEntries = /* @__PURE__ */ new Map();
|
|
41420
|
+
const accountDirFor = (account) => {
|
|
41421
|
+
if (!accountDirs.has(account))
|
|
41422
|
+
accountDirs.set(account, resolveAccountDir(containerDir, account));
|
|
41423
|
+
return accountDirs.get(account) ?? null;
|
|
41424
|
+
};
|
|
41425
|
+
const record2 = (row, parent) => {
|
|
41426
|
+
const accountDir = accountDirFor(row.accountIdentifier ?? parent?.accountIdentifier ?? null);
|
|
41427
|
+
let assetPaths = [];
|
|
41428
|
+
let previews = [];
|
|
41429
|
+
if (accountDir) {
|
|
41430
|
+
if (!previewEntries.has(accountDir))
|
|
41431
|
+
previewEntries.set(accountDir, listPreviewEntries(accountDir));
|
|
41432
|
+
assetPaths = assetPathsFor(accountDir, row);
|
|
41433
|
+
previews = previewPaths(accountDir, row.identifier, previewEntries.get(accountDir));
|
|
41434
|
+
}
|
|
41435
|
+
const previewPath = previews[0] ?? null;
|
|
41436
|
+
return {
|
|
41437
|
+
pk: row.pk,
|
|
41438
|
+
identifier: row.identifier,
|
|
41439
|
+
uti: row.uti,
|
|
41440
|
+
kind: classifyAttachmentKind(row.uti),
|
|
41441
|
+
parentIdentifier: parent?.identifier ?? null,
|
|
41442
|
+
filename: row.filename ?? row.mediaFilename,
|
|
41443
|
+
bodyIndex: parent ? null : indexOf(row),
|
|
41444
|
+
assetPaths,
|
|
41445
|
+
previewPath,
|
|
41446
|
+
paths: previewPath ? [...assetPaths, previewPath] : [...assetPaths]
|
|
41447
|
+
};
|
|
41448
|
+
};
|
|
41449
|
+
const attachments = [];
|
|
41450
|
+
for (const root of orderedRoots) {
|
|
41451
|
+
attachments.push(record2(root, null));
|
|
41452
|
+
for (const child of rows.filter((r) => r.parentPk === root.pk && r !== root)) {
|
|
41453
|
+
attachments.push(record2(child, root));
|
|
41454
|
+
}
|
|
41455
|
+
}
|
|
41456
|
+
return { orderSource, attachments };
|
|
41457
|
+
}
|
|
41458
|
+
function selectFirstImage(assets) {
|
|
41459
|
+
const candidates = assets.attachments.filter((a) => {
|
|
41460
|
+
if (a.parentIdentifier !== null) return true;
|
|
41461
|
+
return assets.orderSource === "creation" || a.bodyIndex !== null;
|
|
41462
|
+
});
|
|
41463
|
+
const eligibleChild = (a) => a.parentIdentifier === null || candidates.some((p) => p.parentIdentifier === null && p.identifier === a.parentIdentifier);
|
|
41464
|
+
const ordered = candidates.filter(eligibleChild);
|
|
41465
|
+
const chosen = ordered.find((a) => a.kind === "image") ?? ordered.find((a) => a.kind === "scan" || a.kind === "drawing");
|
|
41466
|
+
if (!chosen) return null;
|
|
41467
|
+
const siblings = chosen.parentIdentifier ? ordered.filter((a) => a.parentIdentifier === chosen.parentIdentifier) : [];
|
|
41468
|
+
return {
|
|
41469
|
+
pk: chosen.pk,
|
|
41470
|
+
identifier: chosen.identifier,
|
|
41471
|
+
uti: chosen.uti,
|
|
41472
|
+
kind: chosen.kind,
|
|
41473
|
+
path: chosen.assetPaths[0] ?? null,
|
|
41474
|
+
previewPath: chosen.previewPath,
|
|
41475
|
+
parentIdentifier: chosen.parentIdentifier,
|
|
41476
|
+
galleryIndex: chosen.parentIdentifier ? siblings.indexOf(chosen) : null,
|
|
41477
|
+
orderSource: assets.orderSource
|
|
41478
|
+
};
|
|
41479
|
+
}
|
|
41480
|
+
function canonicalTail(p) {
|
|
41481
|
+
let current = resolve2(p);
|
|
41482
|
+
const tail = [];
|
|
41483
|
+
for (; ; ) {
|
|
41484
|
+
try {
|
|
41485
|
+
return join6(realpathSync2.native(current), ...tail);
|
|
41486
|
+
} catch {
|
|
41487
|
+
const parent = dirname2(current);
|
|
41488
|
+
if (parent === current) return join6(current, ...tail);
|
|
41489
|
+
tail.unshift(basename(current));
|
|
41490
|
+
current = parent;
|
|
41491
|
+
}
|
|
41492
|
+
}
|
|
41493
|
+
}
|
|
41494
|
+
function isInsideNotesContainer(p, containerDir = NOTES_CONTAINER_DIR) {
|
|
41495
|
+
const target = canonicalTail(p).toLowerCase();
|
|
41496
|
+
const container = canonicalTail(containerDir).toLowerCase();
|
|
41497
|
+
const rel = relative2(container, target);
|
|
41498
|
+
return !(rel === ".." || rel.startsWith(`..${sep2}`) || isAbsolute2(rel));
|
|
41499
|
+
}
|
|
41500
|
+
function copyFileExclusive(src, dest) {
|
|
41501
|
+
const input = openSync2(src, constants2.O_RDONLY | constants2.O_NOFOLLOW);
|
|
41502
|
+
try {
|
|
41503
|
+
if (!fstatSync2(input).isFile()) throw new Error("Source is not a regular file");
|
|
41504
|
+
const output = openSync2(
|
|
41505
|
+
dest,
|
|
41506
|
+
constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW,
|
|
41507
|
+
384
|
|
41508
|
+
);
|
|
41509
|
+
try {
|
|
41510
|
+
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
41511
|
+
for (; ; ) {
|
|
41512
|
+
const read = readSync(input, buffer, 0, buffer.length, null);
|
|
41513
|
+
if (read === 0) break;
|
|
41514
|
+
let written = 0;
|
|
41515
|
+
while (written < read) written += writeSync(output, buffer, written, read - written);
|
|
41516
|
+
}
|
|
41517
|
+
} catch (error2) {
|
|
41518
|
+
closeSync2(output);
|
|
41519
|
+
unlinkSync(dest);
|
|
41520
|
+
throw error2;
|
|
41521
|
+
}
|
|
41522
|
+
closeSync2(output);
|
|
41523
|
+
} finally {
|
|
41524
|
+
closeSync2(input);
|
|
41525
|
+
}
|
|
41526
|
+
}
|
|
41527
|
+
function exportFileName(record2, source, kind) {
|
|
41528
|
+
const ext = extname(source);
|
|
41529
|
+
if (kind === "preview") {
|
|
41530
|
+
const stored2 = safeComponent(record2.filename ? basename(record2.filename) : null);
|
|
41531
|
+
const stem = stored2 ? basename(stored2, extname(stored2)) : record2.identifier;
|
|
41532
|
+
return `${safeComponent(stem) ?? record2.identifier}-preview${ext}`;
|
|
41533
|
+
}
|
|
41534
|
+
const stored = safeComponent(record2.filename ? basename(record2.filename) : null);
|
|
41535
|
+
if (stored && !GENERIC_FILE_NAMES.has(stored.toLowerCase())) return stored;
|
|
41536
|
+
const own = basename(source);
|
|
41537
|
+
if (!GENERIC_FILE_NAMES.has(own.toLowerCase())) return own;
|
|
41538
|
+
return `${record2.identifier}${ext}`;
|
|
41539
|
+
}
|
|
41540
|
+
function collisionName(name, attempt) {
|
|
41541
|
+
if (attempt <= 1) return name;
|
|
41542
|
+
const ext = extname(name);
|
|
41543
|
+
const stem = ext && ext !== name ? name.slice(0, -ext.length) : name;
|
|
41544
|
+
return `${stem}-${attempt}${ext}`;
|
|
41545
|
+
}
|
|
41546
|
+
function prepareExportDir(exportDir, containerDir = NOTES_CONTAINER_DIR) {
|
|
41547
|
+
const abs = assertSafeSavePath(exportDir);
|
|
41548
|
+
if (isInsideNotesContainer(abs, containerDir)) {
|
|
41549
|
+
throw new Error(`Refusing to write inside the Notes data container: "${abs}"`);
|
|
41550
|
+
}
|
|
41551
|
+
mkdirSync2(abs, { recursive: true });
|
|
41552
|
+
if (isInsideNotesContainer(abs, containerDir)) {
|
|
41553
|
+
throw new Error(`Refusing to write inside the Notes data container: "${abs}"`);
|
|
41554
|
+
}
|
|
41555
|
+
return abs;
|
|
41556
|
+
}
|
|
41557
|
+
function exportOneAttachment(record2, dir, source) {
|
|
41558
|
+
const base = {
|
|
41559
|
+
pk: record2.pk,
|
|
41560
|
+
identifier: record2.identifier,
|
|
41561
|
+
uti: record2.uti,
|
|
41562
|
+
kind: record2.kind,
|
|
41563
|
+
parentIdentifier: record2.parentIdentifier,
|
|
41564
|
+
exportedTo: null,
|
|
41565
|
+
exportedKind: null
|
|
41566
|
+
};
|
|
41567
|
+
if (!source) return base;
|
|
41568
|
+
const name = exportFileName(record2, source.path, source.kind);
|
|
41569
|
+
for (let attempt = 1; attempt <= MAX_COLLISION_SUFFIX; attempt++) {
|
|
41570
|
+
const dest = join6(dir, collisionName(name, attempt));
|
|
41571
|
+
try {
|
|
41572
|
+
assertSafeSavePath(dest);
|
|
41573
|
+
copyFileExclusive(source.path, dest);
|
|
41574
|
+
return { ...base, exportedTo: dest, exportedKind: source.kind };
|
|
41575
|
+
} catch (error2) {
|
|
41576
|
+
if (error2.code === "EEXIST") continue;
|
|
41577
|
+
return { ...base, error: error2 instanceof Error ? error2.message : String(error2) };
|
|
41578
|
+
}
|
|
41579
|
+
}
|
|
41580
|
+
return { ...base, error: "Too many name collisions in the export directory" };
|
|
41581
|
+
}
|
|
41582
|
+
function exportSource(record2) {
|
|
41583
|
+
if (record2.assetPaths[0]) return { path: record2.assetPaths[0], kind: "asset" };
|
|
41584
|
+
if (record2.previewPath) return { path: record2.previewPath, kind: "preview" };
|
|
41585
|
+
return null;
|
|
41586
|
+
}
|
|
41587
|
+
function exportAttachmentAssets(assets, exportDir, options = {}) {
|
|
41588
|
+
const dir = prepareExportDir(exportDir, options.containerDir);
|
|
41589
|
+
if (options.firstImageOnly) {
|
|
41590
|
+
const first2 = selectFirstImage(assets);
|
|
41591
|
+
if (!first2) return { exportDir: dir, results: [], firstImage: null };
|
|
41592
|
+
const record2 = assets.attachments.find((a) => a.pk === first2.pk);
|
|
41593
|
+
return {
|
|
41594
|
+
exportDir: dir,
|
|
41595
|
+
results: [exportOneAttachment(record2, dir, exportSource(record2))],
|
|
41596
|
+
firstImage: first2
|
|
41597
|
+
};
|
|
41598
|
+
}
|
|
41599
|
+
const results = [];
|
|
41600
|
+
for (const record2 of assets.attachments) {
|
|
41601
|
+
if (record2.parentIdentifier !== null) {
|
|
41602
|
+
const parent = assets.attachments.find(
|
|
41603
|
+
(a) => a.parentIdentifier === null && a.identifier === record2.parentIdentifier
|
|
41604
|
+
);
|
|
41605
|
+
if (parent && exportSource(parent)) continue;
|
|
41606
|
+
} else if (!exportSource(record2) && assets.attachments.some((c) => c.parentIdentifier === record2.identifier)) {
|
|
41607
|
+
continue;
|
|
41608
|
+
}
|
|
41609
|
+
results.push(exportOneAttachment(record2, dir, exportSource(record2)));
|
|
41610
|
+
}
|
|
41611
|
+
return { exportDir: dir, results };
|
|
41612
|
+
}
|
|
41613
|
+
|
|
41039
41614
|
// src/services/appleNotesManager.ts
|
|
41040
41615
|
var import_turndown = __toESM(require_turndown_cjs(), 1);
|
|
41041
41616
|
import { existsSync as existsSync4 } from "fs";
|
|
41042
|
-
import { homedir as
|
|
41043
|
-
import { join as
|
|
41617
|
+
import { homedir as homedir7 } from "os";
|
|
41618
|
+
import { join as join7 } from "path";
|
|
41044
41619
|
var FIELD_SEP = "";
|
|
41045
41620
|
var RECORD_SEP = "";
|
|
41046
41621
|
var AS_FIELD_SEP = "(character id 31)";
|
|
@@ -41268,7 +41843,7 @@ function getNoteLinkFromDB(coreDataId2) {
|
|
|
41268
41843
|
const match = coreDataId2.match(/\/p(\d+)$/);
|
|
41269
41844
|
if (!match) return null;
|
|
41270
41845
|
const pk = parseInt(match[1], 10);
|
|
41271
|
-
const dbPath2 =
|
|
41846
|
+
const dbPath2 = join7(homedir7(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
|
|
41272
41847
|
if (!existsSync4(dbPath2)) return null;
|
|
41273
41848
|
try {
|
|
41274
41849
|
const { DatabaseSync } = __require("node:sqlite");
|
|
@@ -43190,6 +43765,37 @@ var AppleNotesManager = class {
|
|
|
43190
43765
|
cleanupTempDir(dir);
|
|
43191
43766
|
}
|
|
43192
43767
|
}
|
|
43768
|
+
/**
|
|
43769
|
+
* Reads one note's attachments with their on-disk asset and preview paths,
|
|
43770
|
+
* in body order, from the NoteStore database and the Notes group container
|
|
43771
|
+
* (both read-only). Requires Full Disk Access.
|
|
43772
|
+
*
|
|
43773
|
+
* @param noteId - canonical CoreData note id
|
|
43774
|
+
* @throws AttachmentStoreError (`no_fda`, `invalid_id`, `not_found`, `query_error`)
|
|
43775
|
+
*/
|
|
43776
|
+
getAttachmentAssetsById(noteId3) {
|
|
43777
|
+
const { rows, bodyOrder } = readNoteAttachmentRows(noteId3);
|
|
43778
|
+
return assembleAttachmentAssets(rows, bodyOrder);
|
|
43779
|
+
}
|
|
43780
|
+
/**
|
|
43781
|
+
* The note's lead visual: the first image in body order (even when its asset
|
|
43782
|
+
* has not downloaded), else the first scan or drawing, else null.
|
|
43783
|
+
*/
|
|
43784
|
+
getFirstImageById(noteId3) {
|
|
43785
|
+
return selectFirstImage(this.getAttachmentAssetsById(noteId3));
|
|
43786
|
+
}
|
|
43787
|
+
/**
|
|
43788
|
+
* Copies a note's attachment files into a directory. Each attachment exports
|
|
43789
|
+
* its real asset; its rendered preview only when no asset exists. Existing
|
|
43790
|
+
* files are never replaced: name collisions get `-2`, `-3`, ... suffixes.
|
|
43791
|
+
* The directory must satisfy the same allowlist as save-attachment and may
|
|
43792
|
+
* not be inside the Notes group container.
|
|
43793
|
+
*/
|
|
43794
|
+
exportAttachmentsById(noteId3, exportDir, firstImageOnly = false) {
|
|
43795
|
+
return exportAttachmentAssets(this.getAttachmentAssetsById(noteId3), exportDir, {
|
|
43796
|
+
firstImageOnly
|
|
43797
|
+
});
|
|
43798
|
+
}
|
|
43193
43799
|
// ===========================================================================
|
|
43194
43800
|
// Batch Operations
|
|
43195
43801
|
// ===========================================================================
|
|
@@ -43603,7 +44209,7 @@ var AppleNotesManager = class {
|
|
|
43603
44209
|
};
|
|
43604
44210
|
|
|
43605
44211
|
// src/utils/syncDetection.ts
|
|
43606
|
-
import { execFileSync as
|
|
44212
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
43607
44213
|
import * as fs3 from "fs";
|
|
43608
44214
|
import * as path3 from "path";
|
|
43609
44215
|
import * as os3 from "os";
|
|
@@ -43648,7 +44254,7 @@ function getSyncStatus(useCache = true) {
|
|
|
43648
44254
|
WHERE object.ZCLOUDSTATE = state.Z_PK
|
|
43649
44255
|
);
|
|
43650
44256
|
`;
|
|
43651
|
-
const result =
|
|
44257
|
+
const result = execFileSync7(
|
|
43652
44258
|
"sqlite3",
|
|
43653
44259
|
["-readonly", NOTES_DB_PATH4, query.replace(/\n/g, " ")],
|
|
43654
44260
|
{
|
|
@@ -43707,7 +44313,7 @@ function withSyncAwarenessSync(operation, fn) {
|
|
|
43707
44313
|
}
|
|
43708
44314
|
|
|
43709
44315
|
// src/utils/noteMetadata.ts
|
|
43710
|
-
import { execFileSync as
|
|
44316
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
43711
44317
|
import * as fs4 from "fs";
|
|
43712
44318
|
import * as path4 from "path";
|
|
43713
44319
|
import * as os4 from "os";
|
|
@@ -43727,15 +44333,15 @@ var COLUMN_MAP = [
|
|
|
43727
44333
|
{ key: "widgetSnippet", column: "ZWIDGETSNIPPET", type: "text" },
|
|
43728
44334
|
{ key: "smartFolderQuery", column: "ZSMARTFOLDERQUERYJSON", type: "text" }
|
|
43729
44335
|
];
|
|
43730
|
-
function
|
|
43731
|
-
return
|
|
44336
|
+
function runSqlite4(query) {
|
|
44337
|
+
return execFileSync8("sqlite3", ["-readonly", NOTES_DB_PATH5, query], {
|
|
43732
44338
|
encoding: "utf8",
|
|
43733
44339
|
timeout: 5e3,
|
|
43734
44340
|
stdio: ["pipe", "pipe", "pipe"]
|
|
43735
44341
|
}).trim();
|
|
43736
44342
|
}
|
|
43737
44343
|
function presentColumns() {
|
|
43738
|
-
const out =
|
|
44344
|
+
const out = runSqlite4("PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);");
|
|
43739
44345
|
const cols = /* @__PURE__ */ new Set();
|
|
43740
44346
|
for (const line of out.split("\n")) {
|
|
43741
44347
|
const name = line.split("|")[1];
|
|
@@ -43763,7 +44369,7 @@ function getNoteMetadata(noteId3) {
|
|
|
43763
44369
|
return { metadata: {} };
|
|
43764
44370
|
}
|
|
43765
44371
|
const pairs = selected.map((c) => `'${c.key}', ${c.column}`).join(", ");
|
|
43766
|
-
const row =
|
|
44372
|
+
const row = runSqlite4(
|
|
43767
44373
|
`SELECT json_object(${pairs}) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = ${pk};`
|
|
43768
44374
|
);
|
|
43769
44375
|
if (!row) {
|
|
@@ -43796,7 +44402,7 @@ function getNoteMetadata(noteId3) {
|
|
|
43796
44402
|
}
|
|
43797
44403
|
|
|
43798
44404
|
// src/utils/noteIdentifiers.ts
|
|
43799
|
-
import { execFileSync as
|
|
44405
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
43800
44406
|
import * as fs5 from "fs";
|
|
43801
44407
|
import * as os5 from "os";
|
|
43802
44408
|
import * as path5 from "path";
|
|
@@ -43878,7 +44484,7 @@ function runJsonQuery(sql, dbPath2) {
|
|
|
43878
44484
|
if (!fs5.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
|
|
43879
44485
|
let out;
|
|
43880
44486
|
try {
|
|
43881
|
-
out =
|
|
44487
|
+
out = execFileSync9("sqlite3", ["-readonly", dbPath2, sql], {
|
|
43882
44488
|
encoding: "utf8",
|
|
43883
44489
|
timeout: 5e3,
|
|
43884
44490
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -44568,11 +45174,11 @@ function positiveTextTerms(node, negated = false) {
|
|
|
44568
45174
|
}
|
|
44569
45175
|
|
|
44570
45176
|
// src/utils/noteQueryStore.ts
|
|
44571
|
-
import { execFileSync as
|
|
45177
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
44572
45178
|
import * as fs6 from "fs";
|
|
44573
45179
|
import * as os6 from "os";
|
|
44574
45180
|
import * as path6 from "path";
|
|
44575
|
-
import { gunzipSync as
|
|
45181
|
+
import { gunzipSync as gunzipSync6 } from "zlib";
|
|
44576
45182
|
var NOTES_DB_PATH7 = path6.join(
|
|
44577
45183
|
os6.homedir(),
|
|
44578
45184
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
@@ -44708,8 +45314,8 @@ function buildScanSql(available, options) {
|
|
|
44708
45314
|
"COMMIT;"
|
|
44709
45315
|
].join(" ");
|
|
44710
45316
|
}
|
|
44711
|
-
function
|
|
44712
|
-
return
|
|
45317
|
+
function runSqlite5(dbPath2, query) {
|
|
45318
|
+
return execFileSync10("sqlite3", ["-readonly", dbPath2, query], {
|
|
44713
45319
|
encoding: "utf8",
|
|
44714
45320
|
timeout: 3e4,
|
|
44715
45321
|
maxBuffer: 512 * 1024 * 1024,
|
|
@@ -44718,7 +45324,7 @@ function runSqlite4(dbPath2, query) {
|
|
|
44718
45324
|
}
|
|
44719
45325
|
function presentColumns2(dbPath2) {
|
|
44720
45326
|
const cols = /* @__PURE__ */ new Set();
|
|
44721
|
-
for (const line of
|
|
45327
|
+
for (const line of runSqlite5(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
|
|
44722
45328
|
const name = line.split("|")[1];
|
|
44723
45329
|
if (name) cols.add(name);
|
|
44724
45330
|
}
|
|
@@ -44728,13 +45334,13 @@ var escapeSegment = escapeFolderName;
|
|
|
44728
45334
|
function resolveFolders(rows) {
|
|
44729
45335
|
const byPk = new Map(rows.map((row) => [row.pk, row]));
|
|
44730
45336
|
const resolved = /* @__PURE__ */ new Map();
|
|
44731
|
-
const
|
|
45337
|
+
const resolve4 = (pk, seen) => {
|
|
44732
45338
|
const cached2 = resolved.get(pk);
|
|
44733
45339
|
if (cached2) return cached2;
|
|
44734
45340
|
const row = byPk.get(pk);
|
|
44735
45341
|
if (!row || seen.has(pk)) return void 0;
|
|
44736
45342
|
seen.add(pk);
|
|
44737
|
-
const parent = row.parent !== null ?
|
|
45343
|
+
const parent = row.parent !== null ? resolve4(row.parent, seen) : void 0;
|
|
44738
45344
|
const name = row.name ?? "";
|
|
44739
45345
|
const path7 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
|
|
44740
45346
|
const plainPath = parent ? `${parent.plainPath}/${name}` : name;
|
|
@@ -44748,7 +45354,7 @@ function resolveFolders(rows) {
|
|
|
44748
45354
|
resolved.set(pk, info);
|
|
44749
45355
|
return info;
|
|
44750
45356
|
};
|
|
44751
|
-
for (const row of rows)
|
|
45357
|
+
for (const row of rows) resolve4(row.pk, /* @__PURE__ */ new Set());
|
|
44752
45358
|
return resolved;
|
|
44753
45359
|
}
|
|
44754
45360
|
var coreDataMs = (seconds) => seconds === null || !Number.isFinite(seconds) ? void 0 : CORE_DATA_EPOCH_MS + seconds * 1e3;
|
|
@@ -44778,7 +45384,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44778
45384
|
let output;
|
|
44779
45385
|
try {
|
|
44780
45386
|
const available = presentColumns2(dbPath2);
|
|
44781
|
-
output =
|
|
45387
|
+
output = runSqlite5(
|
|
44782
45388
|
dbPath2,
|
|
44783
45389
|
buildScanSql(available, { scanLimit, includeDeleted, withBodies, withTags })
|
|
44784
45390
|
);
|
|
@@ -44834,7 +45440,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44834
45440
|
try {
|
|
44835
45441
|
decoded = decodeNoteBody(
|
|
44836
45442
|
new Uint8Array(
|
|
44837
|
-
|
|
45443
|
+
gunzipSync6(Buffer.from(row.data, "hex"), { maxOutputLength: 32 * 1024 * 1024 })
|
|
44838
45444
|
)
|
|
44839
45445
|
);
|
|
44840
45446
|
} catch {
|
|
@@ -44909,10 +45515,10 @@ function queryNotes(expression, options = {}) {
|
|
|
44909
45515
|
import { spawnSync } from "child_process";
|
|
44910
45516
|
|
|
44911
45517
|
// src/services/nativeTags.ts
|
|
44912
|
-
import { execFileSync as
|
|
45518
|
+
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
44913
45519
|
import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
|
|
44914
45520
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
44915
|
-
import { join as
|
|
45521
|
+
import { join as join12 } from "node:path";
|
|
44916
45522
|
|
|
44917
45523
|
// src/services/shortcutConsent.ts
|
|
44918
45524
|
function shortcutConsentHint(shortcut) {
|
|
@@ -44988,7 +45594,7 @@ function addNativeTags(request, deps) {
|
|
|
44988
45594
|
};
|
|
44989
45595
|
}
|
|
44990
45596
|
function listInstalledShortcuts() {
|
|
44991
|
-
return
|
|
45597
|
+
return execFileSync11("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
|
|
44992
45598
|
encoding: "utf8",
|
|
44993
45599
|
timeout: 15e3,
|
|
44994
45600
|
maxBuffer: 1024 * 1024,
|
|
@@ -45014,11 +45620,11 @@ function runNativeTagsShortcut(input) {
|
|
|
45014
45620
|
const status = nativeTagsStatus();
|
|
45015
45621
|
if (!status.installed)
|
|
45016
45622
|
throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
|
|
45017
|
-
const directory = mkdtempSync2(
|
|
45623
|
+
const directory = mkdtempSync2(join12(tmpdir2(), "apple-notes-native-tags-"));
|
|
45018
45624
|
try {
|
|
45019
|
-
const path7 =
|
|
45625
|
+
const path7 = join12(directory, "request.json");
|
|
45020
45626
|
writeFileSync(path7, JSON.stringify(input), { mode: 384 });
|
|
45021
|
-
|
|
45627
|
+
execFileSync11("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path7], {
|
|
45022
45628
|
encoding: "utf8",
|
|
45023
45629
|
timeout: 6e4,
|
|
45024
45630
|
maxBuffer: 1024 * 1024,
|
|
@@ -45037,10 +45643,10 @@ function runNativeTagsShortcut(input) {
|
|
|
45037
45643
|
}
|
|
45038
45644
|
|
|
45039
45645
|
// src/services/backgroundNotes.ts
|
|
45040
|
-
import { execFileSync as
|
|
45646
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
45041
45647
|
import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
|
|
45042
45648
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
45043
|
-
import { join as
|
|
45649
|
+
import { join as join13 } from "node:path";
|
|
45044
45650
|
|
|
45045
45651
|
// src/utils/appendMarkdown.ts
|
|
45046
45652
|
var escape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -45365,9 +45971,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
45365
45971
|
throw new Error(
|
|
45366
45972
|
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
45367
45973
|
);
|
|
45368
|
-
const directory = mkdtempSync3(
|
|
45974
|
+
const directory = mkdtempSync3(join13(tmpdir3(), "apple-notes-background-"));
|
|
45369
45975
|
try {
|
|
45370
|
-
const file =
|
|
45976
|
+
const file = join13(directory, "request.json");
|
|
45371
45977
|
writeFileSync2(
|
|
45372
45978
|
file,
|
|
45373
45979
|
JSON.stringify({
|
|
@@ -45387,7 +45993,7 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
45387
45993
|
{ mode: 384 }
|
|
45388
45994
|
);
|
|
45389
45995
|
try {
|
|
45390
|
-
|
|
45996
|
+
execFileSync12("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
|
|
45391
45997
|
encoding: "utf8",
|
|
45392
45998
|
timeout: callTimeoutMs() ?? 6e4,
|
|
45393
45999
|
maxBuffer: 1024 * 1024,
|
|
@@ -45712,7 +46318,7 @@ ${request.content}`
|
|
|
45712
46318
|
}
|
|
45713
46319
|
|
|
45714
46320
|
// src/services/capabilityMatrix.ts
|
|
45715
|
-
import { execFileSync as
|
|
46321
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
45716
46322
|
import { release } from "node:os";
|
|
45717
46323
|
var MACOS_SHORTCUTS_CLI = "12.0";
|
|
45718
46324
|
var MACOS_MARKDOWN_IMPORT = "26.0";
|
|
@@ -45919,7 +46525,7 @@ function evaluateFeatures(env, features = FEATURES) {
|
|
|
45919
46525
|
function readMacOSVersion() {
|
|
45920
46526
|
if (process.platform !== "darwin") return null;
|
|
45921
46527
|
try {
|
|
45922
|
-
const out =
|
|
46528
|
+
const out = execFileSync13("/usr/bin/sw_vers", ["-productVersion"], {
|
|
45923
46529
|
encoding: "utf8",
|
|
45924
46530
|
timeout: 3e3,
|
|
45925
46531
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -46073,12 +46679,12 @@ function formatDoctorReport(r) {
|
|
|
46073
46679
|
|
|
46074
46680
|
// src/services/fileConfig.ts
|
|
46075
46681
|
import { existsSync as existsSync9, readFileSync as readFileSync2 } from "fs";
|
|
46076
|
-
import { join as
|
|
46077
|
-
import { homedir as
|
|
46682
|
+
import { join as join14 } from "path";
|
|
46683
|
+
import { homedir as homedir12 } from "os";
|
|
46078
46684
|
function fileConfigPath(env = process.env) {
|
|
46079
46685
|
const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
|
|
46080
46686
|
if (override && override.trim()) return override.trim();
|
|
46081
|
-
return
|
|
46687
|
+
return join14(homedir12(), "Library", "Application Support", "apple-notes-mcp", "config.json");
|
|
46082
46688
|
}
|
|
46083
46689
|
function loadFileConfig(env = process.env, path7 = fileConfigPath(env)) {
|
|
46084
46690
|
const applied = [];
|
|
@@ -46381,11 +46987,11 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
|
|
|
46381
46987
|
}
|
|
46382
46988
|
|
|
46383
46989
|
// src/utils/noteBlocks.ts
|
|
46384
|
-
import { execFileSync as
|
|
46990
|
+
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
46385
46991
|
import { existsSync as existsSync10 } from "node:fs";
|
|
46386
|
-
import { homedir as
|
|
46387
|
-
import { join as
|
|
46388
|
-
import { gunzipSync as
|
|
46992
|
+
import { homedir as homedir13 } from "node:os";
|
|
46993
|
+
import { join as join15 } from "node:path";
|
|
46994
|
+
import { gunzipSync as gunzipSync7 } from "node:zlib";
|
|
46389
46995
|
var NoteBlocksError = class extends Error {
|
|
46390
46996
|
code;
|
|
46391
46997
|
constructor(code, message) {
|
|
@@ -46673,7 +47279,7 @@ function summarize(blocks, attachments) {
|
|
|
46673
47279
|
function decodeCompressedNoteBlocks(compressed) {
|
|
46674
47280
|
let data;
|
|
46675
47281
|
try {
|
|
46676
|
-
data =
|
|
47282
|
+
data = gunzipSync7(compressed, { maxOutputLength: 32 * 1024 * 1024 });
|
|
46677
47283
|
} catch (error2) {
|
|
46678
47284
|
throw new NoteBlocksError(
|
|
46679
47285
|
"decompress-failed",
|
|
@@ -46727,8 +47333,8 @@ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResp
|
|
|
46727
47333
|
undecodedFields: doc.undecodedFields
|
|
46728
47334
|
};
|
|
46729
47335
|
}
|
|
46730
|
-
var NOTES_DB_PATH8 =
|
|
46731
|
-
|
|
47336
|
+
var NOTES_DB_PATH8 = join15(
|
|
47337
|
+
homedir13(),
|
|
46732
47338
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
46733
47339
|
);
|
|
46734
47340
|
function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH8 } = {}) {
|
|
@@ -46743,7 +47349,7 @@ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH8 } = {}) {
|
|
|
46743
47349
|
const sql = "SELECT json_object('exists', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = @pk AND Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICNote')), 'data', (SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = @pk), 'encrypted', (SELECT ZCRYPTOINITIALIZATIONVECTOR IS NOT NULL FROM ZICNOTEDATA WHERE ZNOTE = @pk));";
|
|
46744
47350
|
let output;
|
|
46745
47351
|
try {
|
|
46746
|
-
output =
|
|
47352
|
+
output = execFileSync14(
|
|
46747
47353
|
"/usr/bin/sqlite3",
|
|
46748
47354
|
["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
|
|
46749
47355
|
{
|
|
@@ -46894,17 +47500,17 @@ function insertLink(request, deps) {
|
|
|
46894
47500
|
// src/tools/directOperations.ts
|
|
46895
47501
|
import { createHash as createHash2 } from "node:crypto";
|
|
46896
47502
|
import {
|
|
46897
|
-
closeSync as
|
|
46898
|
-
constants as
|
|
46899
|
-
fstatSync as
|
|
47503
|
+
closeSync as closeSync3,
|
|
47504
|
+
constants as constants3,
|
|
47505
|
+
fstatSync as fstatSync3,
|
|
46900
47506
|
mkdtempSync as mkdtempSync4,
|
|
46901
|
-
openSync as
|
|
47507
|
+
openSync as openSync3,
|
|
46902
47508
|
readFileSync as readFileSync3,
|
|
46903
47509
|
rmSync as rmSync4,
|
|
46904
47510
|
writeFileSync as writeFileSync3
|
|
46905
47511
|
} from "node:fs";
|
|
46906
47512
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
46907
|
-
import { basename, extname, isAbsolute as
|
|
47513
|
+
import { basename as basename2, extname as extname2, isAbsolute as isAbsolute3, join as join16 } from "node:path";
|
|
46908
47514
|
var noteId = exactIdInput(
|
|
46909
47515
|
"ICNote",
|
|
46910
47516
|
/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
|
|
@@ -46948,10 +47554,10 @@ function assertExistingContentPreserved(before, after) {
|
|
|
46948
47554
|
}
|
|
46949
47555
|
}
|
|
46950
47556
|
function localAttachment(path7) {
|
|
46951
|
-
if (!
|
|
46952
|
-
const descriptor =
|
|
47557
|
+
if (!isAbsolute3(path7)) throw new Error("An absolute local file path is required");
|
|
47558
|
+
const descriptor = openSync3(path7, constants3.O_RDONLY | constants3.O_NOFOLLOW);
|
|
46953
47559
|
try {
|
|
46954
|
-
const stat =
|
|
47560
|
+
const stat = fstatSync3(descriptor);
|
|
46955
47561
|
if (!stat.isFile() || stat.size === 0 || stat.size > 64 * 1024 * 1024)
|
|
46956
47562
|
throw new Error("Attachment must be a nonempty regular file of at most 64 MiB");
|
|
46957
47563
|
const bytes = readFileSync3(descriptor);
|
|
@@ -46959,7 +47565,7 @@ function localAttachment(path7) {
|
|
|
46959
47565
|
throw new Error("Attachment changed while it was being read; try again");
|
|
46960
47566
|
return bytes;
|
|
46961
47567
|
} finally {
|
|
46962
|
-
|
|
47568
|
+
closeSync3(descriptor);
|
|
46963
47569
|
}
|
|
46964
47570
|
}
|
|
46965
47571
|
function registerDirectOperations(server2, manager) {
|
|
@@ -47072,7 +47678,7 @@ function registerDirectOperations(server2, manager) {
|
|
|
47072
47678
|
);
|
|
47073
47679
|
}
|
|
47074
47680
|
function attachmentName(path7, filename) {
|
|
47075
|
-
const source =
|
|
47681
|
+
const source = basename2(path7);
|
|
47076
47682
|
if (filename === void 0) return source;
|
|
47077
47683
|
if (Buffer.byteLength(filename, "utf8") > 255)
|
|
47078
47684
|
throw new Error("filename must be at most 255 bytes");
|
|
@@ -47080,9 +47686,9 @@ function attachmentName(path7, filename) {
|
|
|
47080
47686
|
throw new Error(
|
|
47081
47687
|
"filename must be one path component with no slash, colon, backslash, control character, leading dot, or surrounding spaces"
|
|
47082
47688
|
);
|
|
47083
|
-
if (
|
|
47689
|
+
if (extname2(filename).toLowerCase() !== extname2(source).toLowerCase())
|
|
47084
47690
|
throw new Error(
|
|
47085
|
-
`filename must keep the source file's extension (${
|
|
47691
|
+
`filename must keep the source file's extension (${extname2(source) || "none"})`
|
|
47086
47692
|
);
|
|
47087
47693
|
return filename;
|
|
47088
47694
|
}
|
|
@@ -47093,8 +47699,8 @@ function attachFile(manager, args) {
|
|
|
47093
47699
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
47094
47700
|
const bytes = localAttachment(path7);
|
|
47095
47701
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
47096
|
-
const directory = mkdtempSync4(
|
|
47097
|
-
const temporaryFile =
|
|
47702
|
+
const directory = mkdtempSync4(join16(tmpdir4(), "notes-attachment-add-"));
|
|
47703
|
+
const temporaryFile = join16(directory, name);
|
|
47098
47704
|
try {
|
|
47099
47705
|
writeFileSync3(temporaryFile, bytes, { mode: 384 });
|
|
47100
47706
|
if (readSnapshot(manager, id2).hash !== before.hash) throw new Error("Note revision changed");
|
|
@@ -47654,7 +48260,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
47654
48260
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
47655
48261
|
import { existsSync as existsSync11 } from "node:fs";
|
|
47656
48262
|
import { release as release2 } from "node:os";
|
|
47657
|
-
import { dirname as
|
|
48263
|
+
import { dirname as dirname3, resolve as resolve3 } from "node:path";
|
|
47658
48264
|
import { fileURLToPath } from "node:url";
|
|
47659
48265
|
var OPTIONAL_BRIDGE_NOTE = "(optional \u2014 needed only for create-note format: markdown, macOS 26+)";
|
|
47660
48266
|
var MARKDOWN_MIN_DARWIN_MAJOR = 25;
|
|
@@ -47677,11 +48283,11 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
47677
48283
|
const result = spawnSync2("/usr/bin/open", [path7], { encoding: "utf8" });
|
|
47678
48284
|
return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
|
|
47679
48285
|
});
|
|
47680
|
-
const baseDirectory = dependencies.baseDirectory ||
|
|
48286
|
+
const baseDirectory = dependencies.baseDirectory || resolve3(dirname3(fileURLToPath(import.meta.url)), "../shortcuts");
|
|
47681
48287
|
const osRelease = (dependencies.osRelease || release2)();
|
|
47682
48288
|
const darwinMajor = Number.parseInt(osRelease.split(".")[0], 10);
|
|
47683
48289
|
const items = shortcutFiles.map(({ name, file, optional: optional2 }) => {
|
|
47684
|
-
const path7 =
|
|
48290
|
+
const path7 = resolve3(baseDirectory, file);
|
|
47685
48291
|
let installed = false;
|
|
47686
48292
|
let identifier;
|
|
47687
48293
|
let error2;
|
|
@@ -48794,10 +49400,10 @@ function guardedAppend({
|
|
|
48794
49400
|
return `<div>${escaped || "<br>"}</div>`;
|
|
48795
49401
|
}).join("");
|
|
48796
49402
|
};
|
|
48797
|
-
const separatorToHtml = (
|
|
48798
|
-
if (format === "html") return
|
|
48799
|
-
if (
|
|
48800
|
-
const escaped =
|
|
49403
|
+
const separatorToHtml = (sep3) => {
|
|
49404
|
+
if (format === "html") return sep3;
|
|
49405
|
+
if (sep3 === "\n\n") return "<div><br></div>";
|
|
49406
|
+
const escaped = sep3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
48801
49407
|
return `<div>${escaped}</div>`;
|
|
48802
49408
|
};
|
|
48803
49409
|
const snapshot = readExactNoteSnapshot(id2);
|
|
@@ -49513,21 +50119,57 @@ registerTool(
|
|
|
49513
50119
|
return successResponse(lines.join("\n"), { ...stats });
|
|
49514
50120
|
}, "Error getting notes statistics")
|
|
49515
50121
|
);
|
|
50122
|
+
function attachmentAssetView(noteId3, record2) {
|
|
50123
|
+
return {
|
|
50124
|
+
attachmentId: attachmentCoreDataId(noteId3, record2.pk),
|
|
50125
|
+
identifier: record2.identifier,
|
|
50126
|
+
uti: record2.uti,
|
|
50127
|
+
kind: record2.kind,
|
|
50128
|
+
parentIdentifier: record2.parentIdentifier,
|
|
50129
|
+
bodyIndex: record2.bodyIndex,
|
|
50130
|
+
assetPaths: record2.assetPaths,
|
|
50131
|
+
previewPath: record2.previewPath,
|
|
50132
|
+
paths: record2.paths
|
|
50133
|
+
};
|
|
50134
|
+
}
|
|
50135
|
+
function firstImageView(noteId3, first2) {
|
|
50136
|
+
if (!first2) return null;
|
|
50137
|
+
const { pk, ...rest } = first2;
|
|
50138
|
+
return { attachmentId: attachmentCoreDataId(noteId3, pk), ...rest };
|
|
50139
|
+
}
|
|
49516
50140
|
registerTool(
|
|
49517
50141
|
"list-attachments",
|
|
49518
50142
|
{
|
|
49519
|
-
description: "Use when: listing the attachments of one note, by id (preferred) or title.\nReturns: each attachment's name, content type, and id (use with save-attachment/fetch-attachment).\nDo not use when: you want the attachment bytes (fetch-attachment) or
|
|
50143
|
+
description: "Use when: listing the attachments of one note, by id (preferred) or title. With includePaths, also where each attachment's files are on disk; with firstImage, only the note's lead visual.\nReturns: each attachment's name, content type, and id (use with save-attachment/fetch-attachment). includePaths adds identifier, uti, kind, bodyIndex, assetPaths (the attachment's own files), previewPath (Notes' largest rendered thumbnail, always an image file), and paths. firstImage returns {firstImage, orderSource}: the first image in body order even when its asset has not downloaded (path null), else the first scan or drawing, else null.\nDo not use when: you want the attachment bytes (fetch-attachment) or files on disk (save-attachment, export-attachments).\nNote: includePaths and firstImage need the note id and Full Disk Access; they read NoteStore and the Notes data folder read-only. Treat the returned paths as local data: copy files out with export-attachments rather than handing raw paths on.",
|
|
49520
50144
|
inputSchema: {
|
|
49521
50145
|
id: looseNoteId(external_exports.string()).optional().describe(`Note ID (preferred - more reliable than title): ${NOTE_ID_FORMS}`),
|
|
49522
50146
|
title: external_exports.string().max(MAX.TITLE).optional().describe("Note title (use id instead when available)"),
|
|
49523
|
-
account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account containing the note (ignored if id is provided)")
|
|
50147
|
+
account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account containing the note (ignored if id is provided)"),
|
|
50148
|
+
includePaths: external_exports.boolean().optional().describe(
|
|
50149
|
+
"Add on-disk assetPaths, previewPath, and paths to each attachment (requires id and Full Disk Access)"
|
|
50150
|
+
),
|
|
50151
|
+
firstImage: external_exports.boolean().optional().describe(
|
|
50152
|
+
"Return only the note's lead visual in body order instead of the list (requires id and Full Disk Access)"
|
|
50153
|
+
)
|
|
49524
50154
|
},
|
|
49525
50155
|
outputSchema: {
|
|
49526
50156
|
attachments: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
49527
|
-
count: external_exports.number().optional()
|
|
50157
|
+
count: external_exports.number().optional(),
|
|
50158
|
+
firstImage: external_exports.object({}).passthrough().nullable().optional(),
|
|
50159
|
+
orderSource: external_exports.enum(["body", "creation"]).optional(),
|
|
50160
|
+
pathsError: external_exports.string().optional()
|
|
49528
50161
|
}
|
|
49529
50162
|
},
|
|
49530
|
-
withErrorHandling(({ id: id2, title, account }) => {
|
|
50163
|
+
withErrorHandling(({ id: id2, title, account, includePaths = false, firstImage = false }) => {
|
|
50164
|
+
if ((includePaths || firstImage) && !id2) {
|
|
50165
|
+
return errorResponse("includePaths and firstImage require the note 'id'");
|
|
50166
|
+
}
|
|
50167
|
+
if (firstImage && id2) {
|
|
50168
|
+
const assets = notesManager.getAttachmentAssetsById(id2);
|
|
50169
|
+
const first2 = firstImageView(id2, selectFirstImage(assets));
|
|
50170
|
+
const text = first2 ? `Lead visual: ${first2.kind} attachment ${first2.attachmentId} (${first2.path ? "asset on disk" : first2.previewPath ? "preview only" : "not downloaded"}; order from ${assets.orderSource}).` : "This note has no image, scan, or drawing attachment.";
|
|
50171
|
+
return successResponse(text, { firstImage: first2, orderSource: assets.orderSource });
|
|
50172
|
+
}
|
|
49531
50173
|
if (id2) {
|
|
49532
50174
|
const note2 = notesManager.getNoteById(id2);
|
|
49533
50175
|
if (!note2) {
|
|
@@ -49541,10 +50183,42 @@ registerTool(
|
|
|
49541
50183
|
});
|
|
49542
50184
|
}
|
|
49543
50185
|
const attachmentList2 = attachments2.map((a) => ` - ${a.name} (${a.contentType})`).join("\n");
|
|
50186
|
+
if (!includePaths) {
|
|
50187
|
+
return successResponse(
|
|
50188
|
+
`Found ${attachments2.length} attachment(s) in "${note2.title}":
|
|
50189
|
+
${attachmentList2}`,
|
|
50190
|
+
{ attachments: attachments2, count: attachments2.length }
|
|
50191
|
+
);
|
|
50192
|
+
}
|
|
50193
|
+
let assets;
|
|
50194
|
+
try {
|
|
50195
|
+
assets = notesManager.getAttachmentAssetsById(id2);
|
|
50196
|
+
} catch (error2) {
|
|
50197
|
+
const pathsError = error2 instanceof Error ? error2.message : String(error2);
|
|
50198
|
+
return successResponse(
|
|
50199
|
+
`Found ${attachments2.length} attachment(s) in "${note2.title}" (paths unavailable: ${pathsError}):
|
|
50200
|
+
${attachmentList2}`,
|
|
50201
|
+
{ attachments: attachments2, count: attachments2.length, pathsError }
|
|
50202
|
+
);
|
|
50203
|
+
}
|
|
50204
|
+
const byPk = new Map(assets.attachments.map((r) => [r.pk, r]));
|
|
50205
|
+
let onDisk = 0;
|
|
50206
|
+
const enriched = attachments2.map((a) => {
|
|
50207
|
+
const pk = Number(/\/ICAttachment\/p(\d+)$/.exec(a.id)?.[1]);
|
|
50208
|
+
const record2 = byPk.get(pk);
|
|
50209
|
+
if (!record2) return a;
|
|
50210
|
+
if (record2.paths.length > 0) onDisk++;
|
|
50211
|
+
const children = assets.attachments.filter((c) => c.parentIdentifier === record2.identifier).map((c) => attachmentAssetView(id2, c));
|
|
50212
|
+
return {
|
|
50213
|
+
...a,
|
|
50214
|
+
...attachmentAssetView(id2, record2),
|
|
50215
|
+
...children.length ? { children } : {}
|
|
50216
|
+
};
|
|
50217
|
+
});
|
|
49544
50218
|
return successResponse(
|
|
49545
|
-
`Found ${attachments2.length} attachment(s) in "${note2.title}":
|
|
50219
|
+
`Found ${attachments2.length} attachment(s) in "${note2.title}" (${onDisk} with files on disk; order from ${assets.orderSource}):
|
|
49546
50220
|
${attachmentList2}`,
|
|
49547
|
-
{ attachments:
|
|
50221
|
+
{ attachments: enriched, count: attachments2.length, orderSource: assets.orderSource }
|
|
49548
50222
|
);
|
|
49549
50223
|
}
|
|
49550
50224
|
if (!title) {
|
|
@@ -49698,6 +50372,61 @@ registerTool(
|
|
|
49698
50372
|
});
|
|
49699
50373
|
}, "Error saving attachment")
|
|
49700
50374
|
);
|
|
50375
|
+
registerTool(
|
|
50376
|
+
"export-attachments",
|
|
50377
|
+
{
|
|
50378
|
+
description: `Use when: copying every file attachment of one note (or only its lead visual) into a directory on disk.
|
|
50379
|
+
Returns: per attachment its id, kind, exportedTo, and exportedKind: "asset" for the real file, "preview" when the asset never downloaded and only Notes' rendered thumbnail was available, or null when nothing was on disk.
|
|
50380
|
+
Do not use when: exporting one attachment to an exact path (save-attachment) or reading bytes inline (fetch-attachment).
|
|
50381
|
+
Safety: writes files; exportDir must be absolute and under the home directory, a temp dir, or /Volumes, and not inside the Notes data folder. Existing files are never replaced: name collisions get -2, -3, ... suffixes. Reads NoteStore and the Notes data folder read-only; requires Full Disk Access. Notes.app is not opened.`,
|
|
50382
|
+
inputSchema: {
|
|
50383
|
+
noteId: noteIdInput,
|
|
50384
|
+
exportDir: external_exports.string().min(1, "exportDir is required").max(MAX.SAVE_PATH).describe("Absolute destination directory (created if missing; home, temp, or /Volumes)"),
|
|
50385
|
+
firstImageOnly: external_exports.boolean().optional().describe("Export only the note's lead visual (see list-attachments firstImage)")
|
|
50386
|
+
},
|
|
50387
|
+
outputSchema: {
|
|
50388
|
+
exportDir: external_exports.string().optional(),
|
|
50389
|
+
exported: external_exports.number().optional(),
|
|
50390
|
+
previews: external_exports.number().optional(),
|
|
50391
|
+
skipped: external_exports.number().optional(),
|
|
50392
|
+
failed: external_exports.number().optional(),
|
|
50393
|
+
results: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
50394
|
+
firstImage: external_exports.object({}).passthrough().nullable().optional()
|
|
50395
|
+
}
|
|
50396
|
+
},
|
|
50397
|
+
withErrorHandling(({ noteId: noteId3, exportDir, firstImageOnly = false }) => {
|
|
50398
|
+
const r = notesManager.exportAttachmentsById(noteId3, exportDir, firstImageOnly);
|
|
50399
|
+
const results = r.results.map(({ pk, ...rest }) => ({
|
|
50400
|
+
attachmentId: attachmentCoreDataId(noteId3, pk),
|
|
50401
|
+
...rest
|
|
50402
|
+
}));
|
|
50403
|
+
const exported = results.filter((x) => x.exportedKind !== null).length;
|
|
50404
|
+
const previews = results.filter((x) => x.exportedKind === "preview").length;
|
|
50405
|
+
const failed = results.filter((x) => x.error).length;
|
|
50406
|
+
const skipped = results.length - exported - failed;
|
|
50407
|
+
const structured = {
|
|
50408
|
+
exportDir: r.exportDir,
|
|
50409
|
+
exported,
|
|
50410
|
+
previews,
|
|
50411
|
+
skipped,
|
|
50412
|
+
failed,
|
|
50413
|
+
results
|
|
50414
|
+
};
|
|
50415
|
+
if (firstImageOnly) {
|
|
50416
|
+
structured.firstImage = firstImageView(noteId3, r.firstImage ?? null);
|
|
50417
|
+
if (!r.firstImage) {
|
|
50418
|
+
return successResponse(
|
|
50419
|
+
"This note has no image, scan, or drawing attachment; nothing was exported.",
|
|
50420
|
+
structured
|
|
50421
|
+
);
|
|
50422
|
+
}
|
|
50423
|
+
}
|
|
50424
|
+
return successResponse(
|
|
50425
|
+
`Exported ${exported} file(s) to ${r.exportDir} (${previews} preview-only, ${skipped} with nothing on disk, ${failed} failed).`,
|
|
50426
|
+
structured
|
|
50427
|
+
);
|
|
50428
|
+
}, "Error exporting attachments")
|
|
50429
|
+
);
|
|
49701
50430
|
registerTool(
|
|
49702
50431
|
"fetch-attachment",
|
|
49703
50432
|
{
|