apple-notes-mcp 2.9.26 → 2.9.28
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 +21 -15
- package/build/index.js +511 -270
- package/docs/markdown-templates.md +1 -1
- package/native/public-helper/apple-notes-public-helper.swift +130 -52
- 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, constants9) {
|
|
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, constants9);
|
|
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, constants9) {
|
|
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, constants9);
|
|
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, constants9) {
|
|
506
|
+
this.code = optimizeExpr(this.code, names, constants9);
|
|
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, constants9) {
|
|
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, constants9))
|
|
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, constants9) {
|
|
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, constants9);
|
|
596
|
+
if (!(super.optimizeNames(names, constants9) || this.else))
|
|
597
597
|
return;
|
|
598
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
598
|
+
this.condition = optimizeExpr(this.condition, names, constants9);
|
|
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, constants9) {
|
|
622
|
+
if (!super.optimizeNames(names, constants9))
|
|
623
623
|
return;
|
|
624
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
624
|
+
this.iteration = optimizeExpr(this.iteration, names, constants9);
|
|
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, constants9) {
|
|
661
|
+
if (!super.optimizeNames(names, constants9))
|
|
662
662
|
return;
|
|
663
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
663
|
+
this.iterable = optimizeExpr(this.iterable, names, constants9);
|
|
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, constants9) {
|
|
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, constants9);
|
|
708
|
+
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants9);
|
|
709
|
+
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants9);
|
|
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, constants9) {
|
|
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 = constants9[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 && constants9[c.str] !== void 0);
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
1035
|
function subtractNames(names, from) {
|
|
@@ -7422,7 +7422,7 @@ var require_DOMException = __commonJS({
|
|
|
7422
7422
|
"INVALID_NODE_TYPE_ERR (24): the supplied node is invalid or has an invalid ancestor for this operation",
|
|
7423
7423
|
"DATA_CLONE_ERR (25): the object can not be cloned."
|
|
7424
7424
|
];
|
|
7425
|
-
var
|
|
7425
|
+
var constants9 = {
|
|
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 constants9) {
|
|
7465
|
+
v = { value: constants9[c] };
|
|
7466
7466
|
Object.defineProperty(DOMException, c, v);
|
|
7467
7467
|
Object.defineProperty(DOMException.prototype, c, v);
|
|
7468
7468
|
}
|
|
@@ -30351,7 +30351,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
30351
30351
|
return (payload, ctx) => fn(shape, payload, ctx);
|
|
30352
30352
|
};
|
|
30353
30353
|
let fastpass;
|
|
30354
|
-
const
|
|
30354
|
+
const isObject4 = isObject;
|
|
30355
30355
|
const jit = !globalConfig.jitless;
|
|
30356
30356
|
const allowsEval2 = allowsEval;
|
|
30357
30357
|
const fastEnabled = jit && allowsEval2.value;
|
|
@@ -30360,7 +30360,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
30360
30360
|
inst._zod.parse = (payload, ctx) => {
|
|
30361
30361
|
value ?? (value = _normalized.value);
|
|
30362
30362
|
const input = payload.value;
|
|
30363
|
-
if (!
|
|
30363
|
+
if (!isObject4(input)) {
|
|
30364
30364
|
payload.issues.push({
|
|
30365
30365
|
expected: "object",
|
|
30366
30366
|
code: "invalid_type",
|
|
@@ -39361,7 +39361,7 @@ function bytesValue(field) {
|
|
|
39361
39361
|
function stringValue(field) {
|
|
39362
39362
|
const bytes = bytesValue(field);
|
|
39363
39363
|
if (!bytes) return void 0;
|
|
39364
|
-
return new TextDecoder().decode(bytes);
|
|
39364
|
+
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes);
|
|
39365
39365
|
}
|
|
39366
39366
|
function embeddedMessage(field) {
|
|
39367
39367
|
const bytes = bytesValue(field);
|
|
@@ -39641,6 +39641,10 @@ function identifierFailureIn(message) {
|
|
|
39641
39641
|
function canonicalKey(value) {
|
|
39642
39642
|
return BigInt(value).toString();
|
|
39643
39643
|
}
|
|
39644
|
+
function canonicalCoreDataId(id2) {
|
|
39645
|
+
const match = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(IC[A-Za-z]+)\/p(\d+)$/.exec(id2);
|
|
39646
|
+
return match ? `x-coredata://${match[1].toUpperCase()}/${match[2]}/p${canonicalKey(match[3])}` : id2;
|
|
39647
|
+
}
|
|
39644
39648
|
function assertAll(values, pattern2, label) {
|
|
39645
39649
|
for (const value of values) {
|
|
39646
39650
|
if (!pattern2.test(value)) throw new Error(`Refusing to query with an invalid ${label}`);
|
|
@@ -39853,6 +39857,10 @@ var CodedError = class extends Error {
|
|
|
39853
39857
|
};
|
|
39854
39858
|
var RULES = [
|
|
39855
39859
|
{ code: "timeout_indeterminate", pattern: /timed out|\bETIMEDOUT\b/i },
|
|
39860
|
+
// A scope guard is checked before the write runs, so nothing was written:
|
|
39861
|
+
// the note is not where it was reviewed. Its advice to read the note before
|
|
39862
|
+
// retrying must not make it look like an unverified write.
|
|
39863
|
+
{ code: "revision_conflict", pattern: /\bScope guard failed:/ },
|
|
39856
39864
|
{
|
|
39857
39865
|
code: "verification_failed",
|
|
39858
39866
|
pattern: /\buncertain\b|may have (?:been|succeeded)|\baccepted (?:the|an) \w+, but|\bnot verified\b|Do not retry automatically|before any retry|read (?:the )?(?:exact )?(?:note|ID)\b[^.]*before retr/i
|
|
@@ -39881,7 +39889,7 @@ var RULES = [
|
|
|
39881
39889
|
},
|
|
39882
39890
|
{
|
|
39883
39891
|
code: "validation_error",
|
|
39884
|
-
pattern: /\bis required\b|required\b|\bInvalid\b|\bmust\b|\bProvide\b|No (?:note IDs|reviewed notes) provided|response limit|Refusing to write|cannot be resolved|at most \d|between \d+ and \d+|supports the (?:end|default)|Use a distinctive|Use create-table|too large|equal cell counts|Folder path is empty/i
|
|
39892
|
+
pattern: /\bis required\b|required\b|\bInvalid\b|\bmust\b|\bProvide\b|No (?:note IDs|reviewed notes) provided|response limit|Refusing to (?:read|write)|cannot be resolved|at most \d|between \d+ and \d+|supports the (?:end|default)|Use a distinctive|Use create-table|too large|equal cell counts|Folder path is empty/i
|
|
39885
39893
|
}
|
|
39886
39894
|
];
|
|
39887
39895
|
var NOTHING_WRITTEN = /nothing (?:was )?(?:changed|created|written)|no replacement started|No content was (?:replaced|appended)|Nothing was created/i;
|
|
@@ -40034,7 +40042,7 @@ function styleValue(field) {
|
|
|
40034
40042
|
}
|
|
40035
40043
|
return Buffer.from(field.value).toString("hex");
|
|
40036
40044
|
}
|
|
40037
|
-
function parseRichNote(data, nativeTags = []) {
|
|
40045
|
+
function parseRichNote(data, nativeTags = [], options = {}) {
|
|
40038
40046
|
const doc = decodeMessage(data);
|
|
40039
40047
|
const wrapper = embeddedMessage(getField(doc, 2));
|
|
40040
40048
|
const body = wrapper && embeddedMessage(getField(wrapper, 3));
|
|
@@ -40074,7 +40082,7 @@ function parseRichNote(data, nativeTags = []) {
|
|
|
40074
40082
|
if (varintValue(getField(fields, 14))) lossy.add("highlight");
|
|
40075
40083
|
}
|
|
40076
40084
|
const url = stringValue(getField(fields, 9));
|
|
40077
|
-
if (url) {
|
|
40085
|
+
if (url && !(options.skipUnsafeLinks && !safeUrl(url))) {
|
|
40078
40086
|
if (!safeUrl(url)) throw new Error("Unsupported link scheme in note");
|
|
40079
40087
|
const previous = links.at(-1);
|
|
40080
40088
|
if (previous?.url === url && previous.start + previous.length === position) {
|
|
@@ -40129,7 +40137,7 @@ function parseRichNote(data, nativeTags = []) {
|
|
|
40129
40137
|
...lossy.size ? { htmlLossyFormatting: HTML_LOSSY_ORDER.filter((f) => lossy.has(f)) } : {}
|
|
40130
40138
|
};
|
|
40131
40139
|
}
|
|
40132
|
-
function readRichNote(id2) {
|
|
40140
|
+
function readRichNote(id2, options = {}) {
|
|
40133
40141
|
const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]+)$/i.exec(id2)?.[1];
|
|
40134
40142
|
if (!pk) throw new Error("Invalid exact note ID");
|
|
40135
40143
|
const sql = `BEGIN; SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE=${pk}; SELECT json_group_object(ZIDENTIFIER,ZALTTEXT) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} AND ZTYPEUTI1='com.apple.notes.inlinetextattachment.hashtag'; SELECT json_group_array(json_object('id',ZIDENTIFIER,'pk',Z_PK,'type',COALESCE(ZTYPEUTI1,ZTYPEUTI),'mergeable',hex(COALESCE(ZMERGEABLEDATA1,ZMERGEABLEDATA)),'view',ZATTACHMENTVIEWTYPE)) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} OR ZNOTE=${pk}; COMMIT;`;
|
|
@@ -40144,7 +40152,9 @@ function readRichNote(id2) {
|
|
|
40144
40152
|
if (!tags || Array.isArray(tags) || typeof tags !== "object" || Object.values(tags).some((tag) => typeof tag !== "string"))
|
|
40145
40153
|
throw new Error("Invalid native tags");
|
|
40146
40154
|
const rich = parseRichNote(
|
|
40147
|
-
gunzipSync2(Buffer.from(rows[0], "hex"), { maxOutputLength: 32 * 1024 * 1024 })
|
|
40155
|
+
gunzipSync2(Buffer.from(rows[0], "hex"), { maxOutputLength: 32 * 1024 * 1024 }),
|
|
40156
|
+
[],
|
|
40157
|
+
options
|
|
40148
40158
|
);
|
|
40149
40159
|
const tagMap = tags;
|
|
40150
40160
|
const objectData = JSON.parse(rows[2] || "[]");
|
|
@@ -41660,6 +41670,7 @@ function buildAttachmentRowsSql(notePk, columns) {
|
|
|
41660
41670
|
`'identifier', a.ZIDENTIFIER`,
|
|
41661
41671
|
`'uti', ${col3("a", "ZTYPEUTI")}`,
|
|
41662
41672
|
`'parentPk', ${parent}`,
|
|
41673
|
+
`'parentDeleted', ${parent !== "NULL" && columns.has("ZMARKEDFORDELETION") ? `(SELECT COALESCE(p.ZMARKEDFORDELETION, 0) FROM ZICCLOUDSYNCINGOBJECT p WHERE p.Z_PK = ${parent})` : "0"}`,
|
|
41663
41674
|
`'filename', ${col3("a", "ZFILENAME")}`,
|
|
41664
41675
|
`'mediaIdentifier', m.ZIDENTIFIER`,
|
|
41665
41676
|
`'mediaFilename', ${col3("m", "ZFILENAME")}`,
|
|
@@ -41718,6 +41729,7 @@ function parseAttachmentRows(json2) {
|
|
|
41718
41729
|
identifier,
|
|
41719
41730
|
uti: toStringOrNull(r.uti),
|
|
41720
41731
|
parentPk: toIntOrNull(r.parentPk),
|
|
41732
|
+
...toIntOrNull(r.parentDeleted) ? { parentDeleted: true } : {},
|
|
41721
41733
|
filename: toStringOrNull(r.filename),
|
|
41722
41734
|
mediaIdentifier: toStringOrNull(r.mediaIdentifier),
|
|
41723
41735
|
mediaFilename: toStringOrNull(r.mediaFilename),
|
|
@@ -41835,7 +41847,7 @@ function generationDirs(base, accountDir) {
|
|
|
41835
41847
|
const entries2 = boundedEntries(base, MAX_GENERATION_DIRS) ?? [];
|
|
41836
41848
|
return entries2.map((e) => realInside(join8(base, e), accountDir)).filter((p) => p !== null && isDirectory(p)).sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
|
|
41837
41849
|
}
|
|
41838
|
-
function fallbackFiles(accountDir, rootName, identifier, generation, names) {
|
|
41850
|
+
function fallbackFiles(accountDir, rootName, identifier, generation, names, onStale) {
|
|
41839
41851
|
const base = join8(accountDir, rootName, identifier);
|
|
41840
41852
|
const found = [];
|
|
41841
41853
|
const add = (candidate) => {
|
|
@@ -41844,12 +41856,14 @@ function fallbackFiles(accountDir, rootName, identifier, generation, names) {
|
|
|
41844
41856
|
};
|
|
41845
41857
|
const gen = safeComponent(generation);
|
|
41846
41858
|
if (gen) for (const name of names) add(join8(base, gen, name));
|
|
41859
|
+
const recorded = found.length;
|
|
41847
41860
|
if (isDirectory(base)) {
|
|
41848
41861
|
for (const dir of generationDirs(base, accountDir))
|
|
41849
41862
|
for (const name of names) add(join8(dir, name));
|
|
41850
41863
|
for (const name of names) add(join8(base, name));
|
|
41851
41864
|
}
|
|
41852
41865
|
for (const name of names) add(join8(accountDir, rootName, `${identifier}${extname(name)}`));
|
|
41866
|
+
if (gen && recorded === 0 && found.length > 0) onStale?.();
|
|
41853
41867
|
return found;
|
|
41854
41868
|
}
|
|
41855
41869
|
function previewPixelArea(name) {
|
|
@@ -41895,7 +41909,7 @@ function previewPaths(accountDir, identifier, entries2) {
|
|
|
41895
41909
|
}
|
|
41896
41910
|
return files;
|
|
41897
41911
|
}
|
|
41898
|
-
function assetPathsFor(accountDir, row) {
|
|
41912
|
+
function assetPathsFor(accountDir, row, onStale) {
|
|
41899
41913
|
const found = [];
|
|
41900
41914
|
const add = (candidate) => {
|
|
41901
41915
|
const real = realInside(candidate, accountDir);
|
|
@@ -41912,18 +41926,28 @@ function assetPathsFor(accountDir, row) {
|
|
|
41912
41926
|
if (!id2) return found;
|
|
41913
41927
|
const ownName = safeComponent(row.filename);
|
|
41914
41928
|
if (ownName) add(join8(accountDir, "Media", id2, ownName));
|
|
41915
|
-
for (const file of fallbackFiles(
|
|
41916
|
-
|
|
41917
|
-
"
|
|
41918
|
-
|
|
41929
|
+
for (const file of fallbackFiles(
|
|
41930
|
+
accountDir,
|
|
41931
|
+
"FallbackImages",
|
|
41932
|
+
id2,
|
|
41933
|
+
row.fallbackImageGeneration,
|
|
41934
|
+
["FallbackImage.png", "FallbackImage.jpg"],
|
|
41935
|
+
onStale
|
|
41936
|
+
))
|
|
41919
41937
|
add(file);
|
|
41920
|
-
for (const file of fallbackFiles(
|
|
41921
|
-
|
|
41922
|
-
|
|
41938
|
+
for (const file of fallbackFiles(
|
|
41939
|
+
accountDir,
|
|
41940
|
+
"FallbackPDFs",
|
|
41941
|
+
id2,
|
|
41942
|
+
row.fallbackPdfGeneration,
|
|
41943
|
+
["FallbackPDF.pdf"],
|
|
41944
|
+
onStale
|
|
41945
|
+
))
|
|
41923
41946
|
add(file);
|
|
41924
41947
|
return found;
|
|
41925
41948
|
}
|
|
41926
|
-
function assembleAttachmentAssets(
|
|
41949
|
+
function assembleAttachmentAssets(allRows, bodyOrder, containerDir = NOTES_CONTAINER_DIR) {
|
|
41950
|
+
const rows = allRows.filter((r) => !r.parentDeleted);
|
|
41927
41951
|
const byPk = new Map(rows.map((r) => [r.pk, r]));
|
|
41928
41952
|
const roots = rows.filter((r) => r.parentPk === null || !byPk.has(r.parentPk));
|
|
41929
41953
|
const bodyIndex = /* @__PURE__ */ new Map();
|
|
@@ -41946,10 +41970,11 @@ function assembleAttachmentAssets(rows, bodyOrder, containerDir = NOTES_CONTAINE
|
|
|
41946
41970
|
const accountDir = accountDirFor(row.accountIdentifier ?? parent?.accountIdentifier ?? null);
|
|
41947
41971
|
let assetPaths = [];
|
|
41948
41972
|
let previews = [];
|
|
41973
|
+
let fallbackStale = false;
|
|
41949
41974
|
if (accountDir) {
|
|
41950
41975
|
if (!previewEntries.has(accountDir))
|
|
41951
41976
|
previewEntries.set(accountDir, listPreviewEntries(accountDir));
|
|
41952
|
-
assetPaths = assetPathsFor(accountDir, row);
|
|
41977
|
+
assetPaths = assetPathsFor(accountDir, row, () => fallbackStale = true);
|
|
41953
41978
|
previews = previewPaths(accountDir, row.identifier, previewEntries.get(accountDir));
|
|
41954
41979
|
}
|
|
41955
41980
|
const previewPath = previews[0] ?? null;
|
|
@@ -41962,6 +41987,7 @@ function assembleAttachmentAssets(rows, bodyOrder, containerDir = NOTES_CONTAINE
|
|
|
41962
41987
|
filename: row.filename ?? row.mediaFilename,
|
|
41963
41988
|
bodyIndex: parent ? null : indexOf(row),
|
|
41964
41989
|
assetPaths,
|
|
41990
|
+
...fallbackStale ? { fallbackStale: true } : {},
|
|
41965
41991
|
previewPath,
|
|
41966
41992
|
paths: previewPath ? [...assetPaths, previewPath] : [...assetPaths]
|
|
41967
41993
|
};
|
|
@@ -42047,6 +42073,11 @@ function copyFileExclusive(src, dest) {
|
|
|
42047
42073
|
function exportFileName(record2, source, kind) {
|
|
42048
42074
|
const ext = extname(source);
|
|
42049
42075
|
const id2 = safeComponent(record2.identifier) ?? "attachment";
|
|
42076
|
+
if (kind === "fallback") {
|
|
42077
|
+
const stored2 = safeComponent(record2.filename ? basename(record2.filename) : null);
|
|
42078
|
+
const stem = stored2 && !GENERIC_FILE_NAMES.has(stored2.toLowerCase()) ? basename(stored2, extname(stored2)) : id2;
|
|
42079
|
+
return `${safeComponent(stem) ?? id2}${ext}`;
|
|
42080
|
+
}
|
|
42050
42081
|
if (kind === "preview") {
|
|
42051
42082
|
const stored2 = safeComponent(record2.filename ? basename(record2.filename) : null);
|
|
42052
42083
|
const stem = stored2 ? basename(stored2, extname(stored2)) : id2;
|
|
@@ -42075,7 +42106,7 @@ function prepareExportDir(exportDir, containerDir = NOTES_CONTAINER_DIR) {
|
|
|
42075
42106
|
}
|
|
42076
42107
|
return abs;
|
|
42077
42108
|
}
|
|
42078
|
-
function exportOneAttachment(record2, dir, source) {
|
|
42109
|
+
function exportOneAttachment(record2, dir, source, inBody) {
|
|
42079
42110
|
const base = {
|
|
42080
42111
|
pk: record2.pk,
|
|
42081
42112
|
identifier: record2.identifier,
|
|
@@ -42083,7 +42114,8 @@ function exportOneAttachment(record2, dir, source) {
|
|
|
42083
42114
|
kind: record2.kind,
|
|
42084
42115
|
parentIdentifier: record2.parentIdentifier,
|
|
42085
42116
|
exportedTo: null,
|
|
42086
|
-
exportedKind: null
|
|
42117
|
+
exportedKind: null,
|
|
42118
|
+
...inBody === false ? { inBody: false } : {}
|
|
42087
42119
|
};
|
|
42088
42120
|
if (!source) return base;
|
|
42089
42121
|
const name = exportFileName(record2, source.path, source.kind);
|
|
@@ -42094,7 +42126,12 @@ function exportOneAttachment(record2, dir, source) {
|
|
|
42094
42126
|
throw new Error(`Refusing to write outside the export directory: "${dest}"`);
|
|
42095
42127
|
assertSafeSavePath(dest);
|
|
42096
42128
|
copyFileExclusive(source.path, dest);
|
|
42097
|
-
return {
|
|
42129
|
+
return {
|
|
42130
|
+
...base,
|
|
42131
|
+
exportedTo: dest,
|
|
42132
|
+
exportedKind: source.kind,
|
|
42133
|
+
...source.kind === "fallback" && record2.fallbackStale ? { stale: true } : {}
|
|
42134
|
+
};
|
|
42098
42135
|
} catch (error2) {
|
|
42099
42136
|
if (error2.code === "EEXIST") continue;
|
|
42100
42137
|
return { ...base, error: error2 instanceof Error ? error2.message : String(error2) };
|
|
@@ -42102,20 +42139,31 @@ function exportOneAttachment(record2, dir, source) {
|
|
|
42102
42139
|
}
|
|
42103
42140
|
return { ...base, error: "Too many name collisions in the export directory" };
|
|
42104
42141
|
}
|
|
42142
|
+
function isFallbackRendering(path10) {
|
|
42143
|
+
return /[\\/]Fallback(?:Images|PDFs)[\\/]/.test(path10);
|
|
42144
|
+
}
|
|
42105
42145
|
function exportSource(record2) {
|
|
42106
|
-
|
|
42146
|
+
const asset = record2.assetPaths[0];
|
|
42147
|
+
if (asset) return { path: asset, kind: isFallbackRendering(asset) ? "fallback" : "asset" };
|
|
42107
42148
|
if (record2.previewPath) return { path: record2.previewPath, kind: "preview" };
|
|
42108
42149
|
return null;
|
|
42109
42150
|
}
|
|
42110
42151
|
function exportAttachmentAssets(assets, exportDir, options = {}) {
|
|
42111
42152
|
const dir = prepareExportDir(exportDir, options.containerDir);
|
|
42153
|
+
const inBody = (record2) => {
|
|
42154
|
+
if (assets.orderSource !== "body") return void 0;
|
|
42155
|
+
const root = record2.parentIdentifier === null ? record2 : assets.attachments.find(
|
|
42156
|
+
(a) => a.parentIdentifier === null && a.identifier === record2.parentIdentifier
|
|
42157
|
+
);
|
|
42158
|
+
return root ? root.bodyIndex !== null : void 0;
|
|
42159
|
+
};
|
|
42112
42160
|
if (options.firstImageOnly) {
|
|
42113
42161
|
const first2 = selectFirstImage(assets);
|
|
42114
42162
|
if (!first2) return { exportDir: dir, results: [], firstImage: null };
|
|
42115
42163
|
const record2 = assets.attachments.find((a) => a.pk === first2.pk);
|
|
42116
42164
|
return {
|
|
42117
42165
|
exportDir: dir,
|
|
42118
|
-
results: [exportOneAttachment(record2, dir, exportSource(record2))],
|
|
42166
|
+
results: [exportOneAttachment(record2, dir, exportSource(record2), inBody(record2))],
|
|
42119
42167
|
firstImage: first2
|
|
42120
42168
|
};
|
|
42121
42169
|
}
|
|
@@ -42129,7 +42177,7 @@ function exportAttachmentAssets(assets, exportDir, options = {}) {
|
|
|
42129
42177
|
} else if (!exportSource(record2) && assets.attachments.some((c) => c.parentIdentifier === record2.identifier)) {
|
|
42130
42178
|
continue;
|
|
42131
42179
|
}
|
|
42132
|
-
results.push(exportOneAttachment(record2, dir, exportSource(record2)));
|
|
42180
|
+
results.push(exportOneAttachment(record2, dir, exportSource(record2), inBody(record2)));
|
|
42133
42181
|
}
|
|
42134
42182
|
return { exportDir: dir, results };
|
|
42135
42183
|
}
|
|
@@ -42298,7 +42346,17 @@ function entries(dir, limit) {
|
|
|
42298
42346
|
}
|
|
42299
42347
|
}
|
|
42300
42348
|
var byGenerationDesc = (a, b) => generationRank(basename2(b)) - generationRank(basename2(a));
|
|
42301
|
-
function
|
|
42349
|
+
function locateFallbackImage(accountDir, identifier, generation) {
|
|
42350
|
+
const gen = safeComponent(generation);
|
|
42351
|
+
const path10 = searchFallbackImage(accountDir, identifier, generation);
|
|
42352
|
+
if (!path10 || !gen) return { path: path10, stale: false };
|
|
42353
|
+
const id2 = safeComponent(identifier);
|
|
42354
|
+
const recorded = ["FallbackImage.png", "FallbackImage.jpg"].map(
|
|
42355
|
+
(n) => id2 ? realInside(join9(accountDir, "FallbackImages", id2, gen, n), accountDir) : null
|
|
42356
|
+
).filter((p) => p !== null);
|
|
42357
|
+
return { path: path10, stale: !recorded.includes(path10) };
|
|
42358
|
+
}
|
|
42359
|
+
function searchFallbackImage(accountDir, identifier, generation) {
|
|
42302
42360
|
const id2 = safeComponent(identifier);
|
|
42303
42361
|
if (!id2) return null;
|
|
42304
42362
|
const base = join9(accountDir, "FallbackImages", id2);
|
|
@@ -42345,7 +42403,8 @@ function describeDrawings(rows, containerDir = NOTES_CONTAINER_DIR) {
|
|
|
42345
42403
|
const accountDir = resolveAccountDir(containerDir, row.accountIdentifier);
|
|
42346
42404
|
const kind = row.uti === "com.apple.paper" ? "paper" : "drawing";
|
|
42347
42405
|
const id2 = safeComponent(row.identifier);
|
|
42348
|
-
const
|
|
42406
|
+
const fallback = accountDir ? locateFallbackImage(accountDir, row.identifier, row.fallbackImageGeneration) : { path: null, stale: false };
|
|
42407
|
+
const fallbackImagePath = fallback.path;
|
|
42349
42408
|
const previewPath = accountDir ? findLargestPreview(accountDir, row.identifier) : null;
|
|
42350
42409
|
const bundle = accountDir && id2 && kind === "paper" ? realInside(join9(accountDir, "Paper", "Bundles", `${id2}.bundle`), accountDir) : null;
|
|
42351
42410
|
let raster = null;
|
|
@@ -42367,6 +42426,7 @@ function describeDrawings(rows, containerDir = NOTES_CONTAINER_DIR) {
|
|
|
42367
42426
|
handwritingSummary: row.handwritingSummary,
|
|
42368
42427
|
bundlePresent: bundle !== null && kindOf(bundle) === "dir",
|
|
42369
42428
|
fallbackImagePath,
|
|
42429
|
+
fallbackImageStale: fallback.stale,
|
|
42370
42430
|
previewPath,
|
|
42371
42431
|
raster
|
|
42372
42432
|
};
|
|
@@ -42507,18 +42567,25 @@ function idList(ids) {
|
|
|
42507
42567
|
function buildScopeGuardScript(noteVar, guard, destinationVar) {
|
|
42508
42568
|
if (!hasScopeGuard(guard)) return "";
|
|
42509
42569
|
validateScopeGuard(guard);
|
|
42510
|
-
const
|
|
42570
|
+
const ifFolderId = guard.ifFolderId && canonicalCoreDataId(guard.ifFolderId);
|
|
42571
|
+
const ifAncestorFolderId = guard.ifAncestorFolderId && canonicalCoreDataId(guard.ifAncestorFolderId);
|
|
42572
|
+
const forbidden = [...new Set((guard.forbiddenAncestorFolderIds ?? []).map(canonicalCoreDataId))];
|
|
42511
42573
|
let script = `
|
|
42512
42574
|
set scopeFolder to container of ${noteVar}
|
|
42513
42575
|
if class of scopeFolder is not folder then return "${SCOPE_MARKER}:the note is not in a folder"`;
|
|
42514
|
-
if (
|
|
42576
|
+
if (forbidden.length > 0)
|
|
42515
42577
|
script += `
|
|
42516
|
-
|
|
42517
|
-
|
|
42578
|
+
repeat with forbiddenId in ${idList(forbidden)}
|
|
42579
|
+
if not (exists folder id (contents of forbiddenId)) then return "${SCOPE_MARKER}:a forbidden folder id does not match any folder"
|
|
42580
|
+
end repeat`;
|
|
42581
|
+
if (ifFolderId)
|
|
42582
|
+
script += `
|
|
42583
|
+
if (id of scopeFolder) is not "${ifFolderId}" then return "${SCOPE_MARKER}:the note is not in the expected folder"`;
|
|
42584
|
+
if (ifAncestorFolderId || forbidden.length > 0)
|
|
42518
42585
|
script += chainScript("scopeChain", "scopeFolder");
|
|
42519
|
-
if (
|
|
42586
|
+
if (ifAncestorFolderId)
|
|
42520
42587
|
script += `
|
|
42521
|
-
if scopeChain does not contain "${
|
|
42588
|
+
if scopeChain does not contain "${ifAncestorFolderId}" then return "${SCOPE_MARKER}:the note is not inside the expected ancestor folder"`;
|
|
42522
42589
|
if (forbidden.length > 0) {
|
|
42523
42590
|
script += `
|
|
42524
42591
|
repeat with forbiddenId in ${idList(forbidden)}
|
|
@@ -43435,7 +43502,7 @@ var AppleNotesManager = class {
|
|
|
43435
43502
|
* @throws Error when the note's rich data cannot be read (e.g. no Full Disk Access)
|
|
43436
43503
|
*/
|
|
43437
43504
|
getNoteTablesById(id2) {
|
|
43438
|
-
return collectNoteTables(readRichNote(id2), id2);
|
|
43505
|
+
return collectNoteTables(readRichNote(id2, { skipUnsafeLinks: true }), id2);
|
|
43439
43506
|
}
|
|
43440
43507
|
/**
|
|
43441
43508
|
* Retrieves detailed metadata for a note by title.
|
|
@@ -43568,6 +43635,7 @@ var AppleNotesManager = class {
|
|
|
43568
43635
|
return `
|
|
43569
43636
|
if not (exists note id "${safeGuardId}") then ${inactive("missing")}
|
|
43570
43637
|
set ${ref} to note id "${safeGuardId}"
|
|
43638
|
+
if (id of ${ref}) is (id of noteRef) then ${inactive("the note being deleted")}
|
|
43571
43639
|
if password protected of ${ref} then ${inactive("locked")}
|
|
43572
43640
|
set ${folderVar} to missing value
|
|
43573
43641
|
try
|
|
@@ -46328,7 +46396,7 @@ var HIGHLIGHTS = {
|
|
|
46328
46396
|
var KNOWN_RUN_FIELDS = /* @__PURE__ */ new Set([1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14]);
|
|
46329
46397
|
var KNOWN_PARAGRAPH_FIELDS = /* @__PURE__ */ new Set([1, 2, 4, 5, 8, 9]);
|
|
46330
46398
|
var isSafeLink = (url) => /^(?:https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url) && !Array.from(url).some((char) => char.charCodeAt(0) < 32);
|
|
46331
|
-
var utf8 = new TextDecoder();
|
|
46399
|
+
var utf8 = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
46332
46400
|
var first = (fields, n) => fields.find((f) => f.fieldNumber === n);
|
|
46333
46401
|
var varintOf = (fields, n) => {
|
|
46334
46402
|
const f = first(fields, n);
|
|
@@ -46477,7 +46545,8 @@ function decodeNoteBlocks(data) {
|
|
|
46477
46545
|
const end = newline === -1 ? text2.length : newline;
|
|
46478
46546
|
while (runIndex < runs.length - 1 && runs[runIndex].start + runs[runIndex].length <= paragraphStart)
|
|
46479
46547
|
runIndex++;
|
|
46480
|
-
const
|
|
46548
|
+
const covering = runs[runIndex];
|
|
46549
|
+
const attrs = !covering ? DEFAULT_PARAGRAPH : covering.paragraph.styleType === 103 && checklistRunLineStart(text2, covering.start, covering.length) > paragraphStart ? DEFAULT_PARAGRAPH : covering.paragraph;
|
|
46481
46550
|
const style = attrs.styleType === null ? "body" : STYLE_NAMES[attrs.styleType] ?? "unknown";
|
|
46482
46551
|
const alignment = ALIGNMENTS[attrs.alignmentValue] ?? "unknown";
|
|
46483
46552
|
const block = {
|
|
@@ -46684,6 +46753,25 @@ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH9 } = {}) {
|
|
|
46684
46753
|
return decodeCompressedNoteBlocks(Buffer.from(row.data, "hex"));
|
|
46685
46754
|
}
|
|
46686
46755
|
|
|
46756
|
+
// src/utils/wordCount.ts
|
|
46757
|
+
var UNSPACED_SCRIPT = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Thai}\p{Script=Lao}\p{Script=Khmer}\p{Script=Myanmar}]/u;
|
|
46758
|
+
var segmenter;
|
|
46759
|
+
function countWords(text2) {
|
|
46760
|
+
let count = 0;
|
|
46761
|
+
for (const run of text2.replace(//gu, " ").split(/\s+/u)) {
|
|
46762
|
+
if (!/[\p{L}\p{N}]/u.test(run)) continue;
|
|
46763
|
+
if (!UNSPACED_SCRIPT.test(run)) {
|
|
46764
|
+
count++;
|
|
46765
|
+
continue;
|
|
46766
|
+
}
|
|
46767
|
+
segmenter ??= new Intl.Segmenter(void 0, { granularity: "word" });
|
|
46768
|
+
let words = 0;
|
|
46769
|
+
for (const segment of segmenter.segment(run)) if (segment.isWordLike) words++;
|
|
46770
|
+
count += Math.max(1, words);
|
|
46771
|
+
}
|
|
46772
|
+
return count;
|
|
46773
|
+
}
|
|
46774
|
+
|
|
46687
46775
|
// src/utils/noteRecentList.ts
|
|
46688
46776
|
var RECENT_LIMIT = { DEFAULT: 50, MAX: 1e3 };
|
|
46689
46777
|
var CHECKPOINT_PREFIX = "cdts1:";
|
|
@@ -46738,9 +46826,7 @@ function parseSince(input) {
|
|
|
46738
46826
|
}
|
|
46739
46827
|
function textStats(text2) {
|
|
46740
46828
|
const visible2 = text2.replace(OBJECT_REPLACEMENT, "");
|
|
46741
|
-
|
|
46742
|
-
for (const token of visible2.split(/\s+/u)) if (/[\p{L}\p{N}]/u.test(token)) wordCount2++;
|
|
46743
|
-
return { wordCount: wordCount2, charCount: [...visible2].length };
|
|
46829
|
+
return { wordCount: countWords(visible2), charCount: [...visible2].length };
|
|
46744
46830
|
}
|
|
46745
46831
|
function previewText(text2) {
|
|
46746
46832
|
const flat = text2.replace(OBJECT_REPLACEMENT, " ").replace(/\s+/gu, " ").trim();
|
|
@@ -47362,6 +47448,10 @@ var Parser = class {
|
|
|
47362
47448
|
function parseNoteQuery(input) {
|
|
47363
47449
|
return new Parser(tokenize(input), input.length).parse();
|
|
47364
47450
|
}
|
|
47451
|
+
function noteBodyText(text2) {
|
|
47452
|
+
const firstBreak = text2.indexOf("\n");
|
|
47453
|
+
return firstBreak === -1 ? "" : text2.slice(firstBreak + 1);
|
|
47454
|
+
}
|
|
47365
47455
|
function normalizeForMatch(text2) {
|
|
47366
47456
|
return text2.normalize("NFC").replace(/[\ufffc\u00a0]/gu, " ").toLowerCase();
|
|
47367
47457
|
}
|
|
@@ -47436,20 +47526,36 @@ function cheapFirst(children) {
|
|
|
47436
47526
|
}
|
|
47437
47527
|
return ordered;
|
|
47438
47528
|
}
|
|
47439
|
-
function
|
|
47529
|
+
function evaluateNoteQueryTruth(node, note) {
|
|
47440
47530
|
switch (node.type) {
|
|
47441
|
-
case "and":
|
|
47442
|
-
|
|
47443
|
-
|
|
47444
|
-
|
|
47445
|
-
|
|
47446
|
-
|
|
47531
|
+
case "and": {
|
|
47532
|
+
let unknown2 = false;
|
|
47533
|
+
for (const child2 of cheapFirst(node.children)) {
|
|
47534
|
+
const value = evaluateNoteQueryTruth(child2, note);
|
|
47535
|
+
if (value === false) return false;
|
|
47536
|
+
if (value === null) unknown2 = true;
|
|
47537
|
+
}
|
|
47538
|
+
return unknown2 ? null : true;
|
|
47539
|
+
}
|
|
47540
|
+
case "or": {
|
|
47541
|
+
let unknown2 = false;
|
|
47542
|
+
for (const child2 of cheapFirst(node.children)) {
|
|
47543
|
+
const value = evaluateNoteQueryTruth(child2, note);
|
|
47544
|
+
if (value === true) return true;
|
|
47545
|
+
if (value === null) unknown2 = true;
|
|
47546
|
+
}
|
|
47547
|
+
return unknown2 ? null : false;
|
|
47548
|
+
}
|
|
47549
|
+
case "not": {
|
|
47550
|
+
const value = evaluateNoteQueryTruth(node.child, note);
|
|
47551
|
+
return value === null ? null : !value;
|
|
47552
|
+
}
|
|
47447
47553
|
case "text": {
|
|
47448
47554
|
const needle = normalizeForMatch(node.value);
|
|
47449
47555
|
if (node.field === "title") return note.titleLower.includes(needle);
|
|
47450
47556
|
if (node.field === "any" && note.titleLower.includes(needle)) return true;
|
|
47451
47557
|
const content = note.content();
|
|
47452
|
-
if (!content) return
|
|
47558
|
+
if (!content) return null;
|
|
47453
47559
|
return (node.field === "body" ? content.bodyLower : content.textLower).includes(needle);
|
|
47454
47560
|
}
|
|
47455
47561
|
case "folder":
|
|
@@ -47462,21 +47568,27 @@ function evaluateNoteQuery(node, note) {
|
|
|
47462
47568
|
return compareDate(node.field === "created" ? note.created : note.modified, node);
|
|
47463
47569
|
case "tag": {
|
|
47464
47570
|
const content = note.content();
|
|
47465
|
-
return
|
|
47571
|
+
return content ? content.tags.includes(normalizeForMatch(node.value)) : null;
|
|
47572
|
+
}
|
|
47573
|
+
case "has": {
|
|
47574
|
+
const content = note.content();
|
|
47575
|
+
return content ? content.facets.has(node.facet) : null;
|
|
47466
47576
|
}
|
|
47467
|
-
case "has":
|
|
47468
|
-
return Boolean(note.content()?.facets.has(node.facet));
|
|
47469
47577
|
case "checklist": {
|
|
47470
47578
|
const content = note.content();
|
|
47471
|
-
if (!content
|
|
47579
|
+
if (!content) return null;
|
|
47580
|
+
if (content.checklist.total === 0) return false;
|
|
47472
47581
|
return node.state === "open" ? content.checklist.open > 0 : content.checklist.open === 0;
|
|
47473
47582
|
}
|
|
47474
47583
|
case "words": {
|
|
47475
47584
|
const content = note.content();
|
|
47476
|
-
return content ? compare(content.words, node.op, node.value) :
|
|
47585
|
+
return content ? compare(content.words, node.op, node.value) : null;
|
|
47477
47586
|
}
|
|
47478
47587
|
}
|
|
47479
47588
|
}
|
|
47589
|
+
function evaluateNoteQuery(node, note) {
|
|
47590
|
+
return evaluateNoteQueryTruth(node, note) === true;
|
|
47591
|
+
}
|
|
47480
47592
|
function positiveTextPredicates(node, negated = false) {
|
|
47481
47593
|
switch (node.type) {
|
|
47482
47594
|
case "and":
|
|
@@ -47499,7 +47611,7 @@ function matchLocations(predicates, title, text2) {
|
|
|
47499
47611
|
const firstBreak = text2 === null ? -1 : text2.indexOf("\n");
|
|
47500
47612
|
const titleLower = normalizeForMatch(title);
|
|
47501
47613
|
const firstLineLower = text2 === null ? "" : normalizeForMatch(firstBreak === -1 ? text2 : text2.slice(0, firstBreak));
|
|
47502
|
-
const bodyLower = text2 === null
|
|
47614
|
+
const bodyLower = text2 === null ? "" : normalizeForMatch(noteBodyText(text2));
|
|
47503
47615
|
let inTitle = false;
|
|
47504
47616
|
let inBody = false;
|
|
47505
47617
|
for (const predicate of predicates) {
|
|
@@ -47611,13 +47723,6 @@ function decodeBodyHex(hex3) {
|
|
|
47611
47723
|
return null;
|
|
47612
47724
|
}
|
|
47613
47725
|
}
|
|
47614
|
-
function countWords(text2) {
|
|
47615
|
-
let count = 0;
|
|
47616
|
-
for (const word of text2.replace(/\ufffc/gu, " ").split(/\s+/u)) {
|
|
47617
|
-
if (/[\p{L}\p{N}]/u.test(word)) count++;
|
|
47618
|
-
}
|
|
47619
|
-
return count;
|
|
47620
|
-
}
|
|
47621
47726
|
var REQUIRED_COLUMNS3 = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
|
|
47622
47727
|
function col2(available, alias, name) {
|
|
47623
47728
|
return available.has(name) ? `${alias}.${name}` : "NULL";
|
|
@@ -47862,7 +47967,6 @@ function runNoteQuery(ast, options = {}) {
|
|
|
47862
47967
|
const body2 = decode2();
|
|
47863
47968
|
if (!body2) return content = null;
|
|
47864
47969
|
const text2 = body2.text;
|
|
47865
|
-
const firstBreak = text2.indexOf("\n");
|
|
47866
47970
|
const tags = /* @__PURE__ */ new Set();
|
|
47867
47971
|
for (const [id2, alt] of row.tags ?? []) {
|
|
47868
47972
|
if (id2 && alt && body2.objectIds.has(id2))
|
|
@@ -47872,7 +47976,7 @@ function runNoteQuery(ast, options = {}) {
|
|
|
47872
47976
|
if (tags.size) facets.add("tag");
|
|
47873
47977
|
content = {
|
|
47874
47978
|
textLower: normalizeForMatch(text2),
|
|
47875
|
-
bodyLower: normalizeForMatch(
|
|
47979
|
+
bodyLower: normalizeForMatch(noteBodyText(text2)),
|
|
47876
47980
|
words: countWords(text2),
|
|
47877
47981
|
facets,
|
|
47878
47982
|
checklist: body2.checklist,
|
|
@@ -48365,13 +48469,16 @@ var LEGACY_ENTITIES = {
|
|
|
48365
48469
|
gt: ">",
|
|
48366
48470
|
amp: "&"
|
|
48367
48471
|
};
|
|
48472
|
+
function codePointText(value) {
|
|
48473
|
+
return Number.isSafeInteger(value) && value <= 1114111 ? String.fromCodePoint(value) : "\uFFFD";
|
|
48474
|
+
}
|
|
48368
48475
|
function comparableVisibleText(html) {
|
|
48369
48476
|
return html.replace(/<br\s*\/?\s*>/gi, " ").replace(/<[^>]*>/g, (tag) => INLINE_TAG.test(tag) ? "" : " ").replace(
|
|
48370
48477
|
/&(?:(nbsp|quot|lt|gt|amp);?|apos;|#(\d+);|#x([0-9a-f]+);)/gi,
|
|
48371
48478
|
(_match, legacy, dec, hex3) => {
|
|
48372
48479
|
if (legacy) return LEGACY_ENTITIES[legacy.toLowerCase()];
|
|
48373
|
-
if (dec) return
|
|
48374
|
-
if (hex3) return
|
|
48480
|
+
if (dec) return codePointText(Number(dec));
|
|
48481
|
+
if (hex3) return codePointText(Number.parseInt(hex3, 16));
|
|
48375
48482
|
return "'";
|
|
48376
48483
|
}
|
|
48377
48484
|
).replace(/\s+/g, " ").trim();
|
|
@@ -48796,15 +48903,26 @@ function createMarkdownNote(manager, request, run = runBackgroundShortcut) {
|
|
|
48796
48903
|
const segments = (path10) => JSON.stringify(splitFolderPath(path10).map((part) => part.toLocaleLowerCase()));
|
|
48797
48904
|
if (request.folder) {
|
|
48798
48905
|
const wanted = segments(request.folder);
|
|
48799
|
-
const accounts = manager.listAccounts().filter(
|
|
48800
|
-
|
|
48801
|
-
|
|
48802
|
-
)
|
|
48906
|
+
const accounts = manager.listAccounts().filter(
|
|
48907
|
+
(account) => account.defaultFolder && manager.listFolders(account.name).some((folder) => segments(folder.name) === wanted)
|
|
48908
|
+
);
|
|
48909
|
+
if (!accounts.length)
|
|
48803
48910
|
throw new Error(
|
|
48804
48911
|
`Folder "${request.folder}" does not exist; create it with create-folder first. Nothing was created`
|
|
48805
48912
|
);
|
|
48806
|
-
|
|
48807
|
-
|
|
48913
|
+
let smartRefusal;
|
|
48914
|
+
const ordinary = accounts.filter((account) => {
|
|
48915
|
+
try {
|
|
48916
|
+
manager.assertNotSmartFolderDestination(request.folder, account.name);
|
|
48917
|
+
return true;
|
|
48918
|
+
} catch (error2) {
|
|
48919
|
+
if (!(error2 instanceof CodedError && error2.envelope.reason === "smart_folder_destination"))
|
|
48920
|
+
throw error2;
|
|
48921
|
+
smartRefusal ??= error2;
|
|
48922
|
+
return false;
|
|
48923
|
+
}
|
|
48924
|
+
});
|
|
48925
|
+
if (!ordinary.length) throw smartRefusal;
|
|
48808
48926
|
}
|
|
48809
48927
|
const defaultFolderNotes = () => new Map(
|
|
48810
48928
|
manager.listAccounts().flatMap(
|
|
@@ -49501,7 +49619,63 @@ function withJsonSchema2020_12(transport2) {
|
|
|
49501
49619
|
|
|
49502
49620
|
// src/utils/shutdown.ts
|
|
49503
49621
|
var SHUTDOWN_DRAIN_TIMEOUT_MS = 2e3;
|
|
49504
|
-
|
|
49622
|
+
var SHUTDOWN_IN_FLIGHT_TIMEOUT_MS = 3e4;
|
|
49623
|
+
var InFlightRequests = class {
|
|
49624
|
+
open = /* @__PURE__ */ new Set();
|
|
49625
|
+
waiters = [];
|
|
49626
|
+
get count() {
|
|
49627
|
+
return this.open.size;
|
|
49628
|
+
}
|
|
49629
|
+
received(id2) {
|
|
49630
|
+
this.open.add(id2);
|
|
49631
|
+
}
|
|
49632
|
+
/** A response was sent, or the client cancelled the request (no response follows). */
|
|
49633
|
+
settled(id2) {
|
|
49634
|
+
if (!this.open.delete(id2) || this.open.size > 0) return;
|
|
49635
|
+
const waiters = this.waiters;
|
|
49636
|
+
this.waiters = [];
|
|
49637
|
+
for (const waiter of waiters) waiter();
|
|
49638
|
+
}
|
|
49639
|
+
onIdle(listener) {
|
|
49640
|
+
if (this.open.size === 0) listener();
|
|
49641
|
+
else this.waiters.push(listener);
|
|
49642
|
+
}
|
|
49643
|
+
};
|
|
49644
|
+
var isObject2 = (value) => typeof value === "object" && value !== null;
|
|
49645
|
+
var isId = (value) => typeof value === "string" || typeof value === "number";
|
|
49646
|
+
function trackRequests(transport2, requests) {
|
|
49647
|
+
if (typeof transport2.send !== "function") return transport2;
|
|
49648
|
+
let handler;
|
|
49649
|
+
Object.defineProperty(transport2, "onmessage", {
|
|
49650
|
+
configurable: true,
|
|
49651
|
+
enumerable: true,
|
|
49652
|
+
get: () => handler,
|
|
49653
|
+
set: (next) => {
|
|
49654
|
+
handler = next ? (message, extra) => {
|
|
49655
|
+
const m = message;
|
|
49656
|
+
if (typeof m.method === "string") {
|
|
49657
|
+
if (isId(m.id)) requests.received(m.id);
|
|
49658
|
+
else if (m.method === "notifications/cancelled" && isObject2(m.params)) {
|
|
49659
|
+
const id2 = m.params.requestId;
|
|
49660
|
+
if (isId(id2)) requests.settled(id2);
|
|
49661
|
+
}
|
|
49662
|
+
}
|
|
49663
|
+
next(message, extra);
|
|
49664
|
+
} : next;
|
|
49665
|
+
}
|
|
49666
|
+
});
|
|
49667
|
+
const send = transport2.send.bind(transport2);
|
|
49668
|
+
transport2.send = async (message, options) => {
|
|
49669
|
+
try {
|
|
49670
|
+
return await send(message, options);
|
|
49671
|
+
} finally {
|
|
49672
|
+
const m = message;
|
|
49673
|
+
if (typeof m.method !== "string" && isId(m.id)) requests.settled(m.id);
|
|
49674
|
+
}
|
|
49675
|
+
};
|
|
49676
|
+
return transport2;
|
|
49677
|
+
}
|
|
49678
|
+
function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS, pending, inFlightTimeoutMs = SHUTDOWN_IN_FLIGHT_TIMEOUT_MS) {
|
|
49505
49679
|
let shuttingDown = false;
|
|
49506
49680
|
let exited = false;
|
|
49507
49681
|
const exitOnce = () => {
|
|
@@ -49518,8 +49692,11 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
|
|
|
49518
49692
|
return () => {
|
|
49519
49693
|
if (shuttingDown) return;
|
|
49520
49694
|
shuttingDown = true;
|
|
49521
|
-
|
|
49522
|
-
|
|
49695
|
+
const busy = (pending?.count ?? 0) > 0;
|
|
49696
|
+
timer = setTimeout(exitOnce, busy ? Math.max(timeoutMs, inFlightTimeoutMs) : timeoutMs);
|
|
49697
|
+
setImmediate(
|
|
49698
|
+
() => pending ? pending.onIdle(() => setImmediate(exitWhenDrained)) : exitWhenDrained()
|
|
49699
|
+
);
|
|
49523
49700
|
};
|
|
49524
49701
|
}
|
|
49525
49702
|
|
|
@@ -49867,7 +50044,7 @@ function lastViewedOf(raw, columnPresent = true, now = Date.now()) {
|
|
|
49867
50044
|
return { lastViewed: new Date(ms).toISOString(), lastViewedStatus: "viewed" };
|
|
49868
50045
|
}
|
|
49869
50046
|
var visible = (text2) => text2.replace(/\ufffc/g, "");
|
|
49870
|
-
var wordCount = (text2) =>
|
|
50047
|
+
var wordCount = (text2) => countWords(text2);
|
|
49871
50048
|
var charCount = (text2) => Array.from(visible(text2)).length;
|
|
49872
50049
|
function noteStructureSql(columns) {
|
|
49873
50050
|
const c = (alias, name) => col(columns, alias, name);
|
|
@@ -50155,7 +50332,7 @@ function inventorySql(columns) {
|
|
|
50155
50332
|
ORDER BY d.ZNOTE LIMIT ${BODY_BATCH};`
|
|
50156
50333
|
};
|
|
50157
50334
|
}
|
|
50158
|
-
function decodeBodies(dbPath2, sql, params) {
|
|
50335
|
+
function decodeBodies(dbPath2, sql, params, notes) {
|
|
50159
50336
|
const docs = /* @__PURE__ */ new Map();
|
|
50160
50337
|
let after = 0;
|
|
50161
50338
|
for (; ; ) {
|
|
@@ -50164,7 +50341,7 @@ function decodeBodies(dbPath2, sql, params) {
|
|
|
50164
50341
|
);
|
|
50165
50342
|
for (const row of rows) {
|
|
50166
50343
|
after = Math.max(after, row.note);
|
|
50167
|
-
if (row.encrypted || !row.data) continue;
|
|
50344
|
+
if (row.encrypted || !row.data || !notes.has(row.note)) continue;
|
|
50168
50345
|
try {
|
|
50169
50346
|
docs.set(row.note, decodeCompressedNoteBlocks(Buffer.from(row.data, "hex")));
|
|
50170
50347
|
} catch {
|
|
@@ -50212,7 +50389,7 @@ function listNoteLinks(options = {}) {
|
|
|
50212
50389
|
if (options.id && !notes.length)
|
|
50213
50390
|
throw new NoteStoreError(`No note found for ID "${options.id}".`, "invalid_input");
|
|
50214
50391
|
const includeInline = options.includeInline ?? Boolean(options.id);
|
|
50215
|
-
const docs = includeInline ? decodeBodies(dbPath2, sql.bodies, params) : /* @__PURE__ */ new Map();
|
|
50392
|
+
const docs = includeInline ? decodeBodies(dbPath2, sql.bodies, params, new Set(notes.map((note) => note.pk))) : /* @__PURE__ */ new Map();
|
|
50216
50393
|
const noteById = new Map(notes.map((note) => [note.pk, note]));
|
|
50217
50394
|
const accountById = new Map(context.accounts.map((account) => [account.pk, account]));
|
|
50218
50395
|
const folderById = new Map(context.folders.map((folder) => [folder.pk, folder]));
|
|
@@ -50527,7 +50704,14 @@ var AssetLocator = class {
|
|
|
50527
50704
|
["FallbackImage.png", "FallbackImage.jpg"]
|
|
50528
50705
|
);
|
|
50529
50706
|
if (fallback)
|
|
50530
|
-
return {
|
|
50707
|
+
return {
|
|
50708
|
+
primary: {
|
|
50709
|
+
path: fallback.path,
|
|
50710
|
+
name: `${source.kind}.png`,
|
|
50711
|
+
role: "fallback",
|
|
50712
|
+
...fallback.stale ? { stale: true } : {}
|
|
50713
|
+
}
|
|
50714
|
+
};
|
|
50531
50715
|
return preview ? { primary: preview } : {};
|
|
50532
50716
|
}
|
|
50533
50717
|
case "scan": {
|
|
@@ -50535,7 +50719,14 @@ var AssetLocator = class {
|
|
|
50535
50719
|
"FallbackPDF.pdf"
|
|
50536
50720
|
]);
|
|
50537
50721
|
return {
|
|
50538
|
-
...pdf ? {
|
|
50722
|
+
...pdf ? {
|
|
50723
|
+
primary: {
|
|
50724
|
+
path: pdf.path,
|
|
50725
|
+
name: "scan.pdf",
|
|
50726
|
+
role: "fallback",
|
|
50727
|
+
...pdf.stale ? { stale: true } : {}
|
|
50728
|
+
}
|
|
50729
|
+
} : {},
|
|
50539
50730
|
...preview ? { preview } : {}
|
|
50540
50731
|
};
|
|
50541
50732
|
}
|
|
@@ -50564,7 +50755,11 @@ var AssetLocator = class {
|
|
|
50564
50755
|
}
|
|
50565
50756
|
return void 0;
|
|
50566
50757
|
}
|
|
50567
|
-
/**
|
|
50758
|
+
/**
|
|
50759
|
+
* <dir>/<id>/<generation>/<name>, <dir>/<id>/<name>, then any generation.
|
|
50760
|
+
* `stale` is set when a generation was recorded but the file came from
|
|
50761
|
+
* somewhere else.
|
|
50762
|
+
*/
|
|
50568
50763
|
fallback(account, dir, id2, generation, names) {
|
|
50569
50764
|
const base = join19(account, dir, id2);
|
|
50570
50765
|
const gen = safeComponent2(generation);
|
|
@@ -50576,7 +50771,7 @@ var AssetLocator = class {
|
|
|
50576
50771
|
for (const g of generations)
|
|
50577
50772
|
for (const name of names) {
|
|
50578
50773
|
const path10 = confine(g ? join19(base, g, name) : join19(base, name), account);
|
|
50579
|
-
if (path10 && isFile(path10)) return path10;
|
|
50774
|
+
if (path10 && isFile(path10)) return { path: path10, stale: Boolean(gen) && g !== gen };
|
|
50580
50775
|
}
|
|
50581
50776
|
return void 0;
|
|
50582
50777
|
}
|
|
@@ -50733,6 +50928,9 @@ function readHead(fd) {
|
|
|
50733
50928
|
const n = readSync3(fd, head, 0, 16, 0);
|
|
50734
50929
|
return head.subarray(0, n);
|
|
50735
50930
|
}
|
|
50931
|
+
function directoryFailure(dir, error2) {
|
|
50932
|
+
return `Could not create the assets directory ${dir}: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
50933
|
+
}
|
|
50736
50934
|
var SidecarWriter = class {
|
|
50737
50935
|
constructor(dir, linkBase) {
|
|
50738
50936
|
this.dir = dir;
|
|
@@ -50743,9 +50941,16 @@ var SidecarWriter = class {
|
|
|
50743
50941
|
placed = /* @__PURE__ */ new Map();
|
|
50744
50942
|
created = false;
|
|
50745
50943
|
count = 0;
|
|
50944
|
+
/**
|
|
50945
|
+
* Set when the directory could not be created. Every later asset fails the
|
|
50946
|
+
* same way without another attempt; the export checks this and fails
|
|
50947
|
+
* instead of reporting each asset as unavailable.
|
|
50948
|
+
*/
|
|
50949
|
+
directoryError;
|
|
50746
50950
|
place(asset) {
|
|
50747
50951
|
const done = this.placed.get(asset.path);
|
|
50748
50952
|
if (done) return done;
|
|
50953
|
+
if (this.directoryError) return { error: this.directoryError };
|
|
50749
50954
|
let source;
|
|
50750
50955
|
try {
|
|
50751
50956
|
source = openSource(asset.path);
|
|
@@ -50755,8 +50960,13 @@ var SidecarWriter = class {
|
|
|
50755
50960
|
try {
|
|
50756
50961
|
const mime = sniffMime(readHead(source.fd), asset.name);
|
|
50757
50962
|
if (!this.created) {
|
|
50758
|
-
|
|
50759
|
-
|
|
50963
|
+
try {
|
|
50964
|
+
assertExportPath(this.dir);
|
|
50965
|
+
mkdirSync4(this.dir, { recursive: true });
|
|
50966
|
+
} catch (error2) {
|
|
50967
|
+
this.directoryError = directoryFailure(this.dir, error2);
|
|
50968
|
+
return { error: this.directoryError };
|
|
50969
|
+
}
|
|
50760
50970
|
this.created = true;
|
|
50761
50971
|
}
|
|
50762
50972
|
const name = safeAssetName(asset.name, mime);
|
|
@@ -50909,7 +51119,10 @@ function titleBlockIndex(note) {
|
|
|
50909
51119
|
return first2.text.trim() === note.title.trim() && first2.style !== "monospaced" ? first2.index : -1;
|
|
50910
51120
|
}
|
|
50911
51121
|
function place(ctx, asset) {
|
|
50912
|
-
|
|
51122
|
+
const placed = asset && ctx.writer ? ctx.writer.place(asset) : void 0;
|
|
51123
|
+
if (asset?.stale && placed && "url" in placed)
|
|
51124
|
+
ctx.stats.staleRenderings = (ctx.stats.staleRenderings ?? 0) + 1;
|
|
51125
|
+
return placed;
|
|
50913
51126
|
}
|
|
50914
51127
|
function planAttachment(attachment, ctx) {
|
|
50915
51128
|
ctx.stats.attachments++;
|
|
@@ -51075,6 +51288,7 @@ function inlinePlanHtml(plan) {
|
|
|
51075
51288
|
}
|
|
51076
51289
|
}
|
|
51077
51290
|
function blockPlanHtml(plan) {
|
|
51291
|
+
if (!isHtmlBlockPlan(plan)) return `<p>${inlinePlanHtml(plan)}</p>`;
|
|
51078
51292
|
switch (plan.type) {
|
|
51079
51293
|
case "table":
|
|
51080
51294
|
return tableHtml(plan.rows);
|
|
@@ -51942,7 +52156,7 @@ function child(path10, key) {
|
|
|
51942
52156
|
if (key.length > MAX_QUOTED_TOKEN) key = `${key.slice(0, MAX_QUOTED_TOKEN - 3)}...`;
|
|
51943
52157
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? `${path10}.${key}` : `${path10}[${JSON.stringify(key)}]`;
|
|
51944
52158
|
}
|
|
51945
|
-
var
|
|
52159
|
+
var isObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
51946
52160
|
var describe = (value) => value === null ? "null" : Array.isArray(value) ? "an array" : `a ${typeof value}`;
|
|
51947
52161
|
var describeVersion = (value) => typeof value === "number" && Number.isFinite(value) ? String(value) : describe(value);
|
|
51948
52162
|
var listed = (values) => values.map((v) => JSON.stringify(v)).join(", ");
|
|
@@ -52023,7 +52237,7 @@ var RULE_FIELDS = {
|
|
|
52023
52237
|
omit: []
|
|
52024
52238
|
};
|
|
52025
52239
|
function validateRule(c, rule, path10) {
|
|
52026
|
-
if (!
|
|
52240
|
+
if (!isObject3(rule)) {
|
|
52027
52241
|
c.add(path10, `must be an object, not ${describe(rule)}`);
|
|
52028
52242
|
return;
|
|
52029
52243
|
}
|
|
@@ -52070,7 +52284,7 @@ function validateInlineOrder(c, value, path10) {
|
|
|
52070
52284
|
if (missing.length) c.add(path10, `must list every inline format; missing ${listed(missing)}`);
|
|
52071
52285
|
}
|
|
52072
52286
|
function validateOptions(c, value, path10) {
|
|
52073
|
-
if (!
|
|
52287
|
+
if (!isObject3(value)) {
|
|
52074
52288
|
c.add(path10, `must be an object, not ${describe(value)}`);
|
|
52075
52289
|
return;
|
|
52076
52290
|
}
|
|
@@ -52091,7 +52305,7 @@ function validateOptions(c, value, path10) {
|
|
|
52091
52305
|
}
|
|
52092
52306
|
function templateErrors(input) {
|
|
52093
52307
|
const c = new Collector();
|
|
52094
|
-
if (!
|
|
52308
|
+
if (!isObject3(input)) {
|
|
52095
52309
|
c.add("$", `a template must be a JSON object, not ${describe(input)}`);
|
|
52096
52310
|
return c.errors;
|
|
52097
52311
|
}
|
|
@@ -52126,7 +52340,7 @@ function templateErrors(input) {
|
|
|
52126
52340
|
c.oneOf(value, path10, BUILTIN_TEMPLATE_NAMES);
|
|
52127
52341
|
break;
|
|
52128
52342
|
case "assets":
|
|
52129
|
-
if (!
|
|
52343
|
+
if (!isObject3(value)) {
|
|
52130
52344
|
c.add(path10, `must be an object, not ${describe(value)}`);
|
|
52131
52345
|
break;
|
|
52132
52346
|
}
|
|
@@ -52142,7 +52356,7 @@ function templateErrors(input) {
|
|
|
52142
52356
|
validateInlineOrder(c, value, path10);
|
|
52143
52357
|
break;
|
|
52144
52358
|
case "rules":
|
|
52145
|
-
if (!
|
|
52359
|
+
if (!isObject3(value)) {
|
|
52146
52360
|
c.add(path10, `must be an object keyed by rule id, not ${describe(value)}`);
|
|
52147
52361
|
break;
|
|
52148
52362
|
}
|
|
@@ -52772,16 +52986,26 @@ var HashedSidecarWriter = class {
|
|
|
52772
52986
|
/** Absolute paths of files written or reused. */
|
|
52773
52987
|
files = [];
|
|
52774
52988
|
count = 0;
|
|
52989
|
+
/** Set when the directory could not be created; see SidecarWriter.directoryError. */
|
|
52990
|
+
directoryError;
|
|
52991
|
+
/** Creates the directory once; returns an error message when it cannot. */
|
|
52775
52992
|
prepare() {
|
|
52776
|
-
if (this.ready) return;
|
|
52777
|
-
|
|
52778
|
-
|
|
52779
|
-
|
|
52993
|
+
if (this.ready) return void 0;
|
|
52994
|
+
try {
|
|
52995
|
+
assertExportPath(this.dir);
|
|
52996
|
+
mkdirSync5(this.dir, { recursive: true });
|
|
52997
|
+
if (!lstatSync4(this.dir).isDirectory())
|
|
52998
|
+
throw new Error("assets directory is not a directory");
|
|
52999
|
+
} catch (error2) {
|
|
53000
|
+
return this.directoryError = directoryFailure(this.dir, error2);
|
|
53001
|
+
}
|
|
52780
53002
|
this.ready = true;
|
|
53003
|
+
return void 0;
|
|
52781
53004
|
}
|
|
52782
53005
|
place(asset) {
|
|
52783
53006
|
const done = this.placed.get(asset.path);
|
|
52784
53007
|
if (done) return done;
|
|
53008
|
+
if (this.directoryError) return { error: this.directoryError };
|
|
52785
53009
|
let source;
|
|
52786
53010
|
try {
|
|
52787
53011
|
source = openRegular(asset.path);
|
|
@@ -52791,7 +53015,8 @@ var HashedSidecarWriter = class {
|
|
|
52791
53015
|
try {
|
|
52792
53016
|
const { hash, head } = digest(source);
|
|
52793
53017
|
const mime = sniffMime(head, asset.name);
|
|
52794
|
-
this.prepare();
|
|
53018
|
+
const directoryError = this.prepare();
|
|
53019
|
+
if (directoryError) return { error: directoryError };
|
|
52795
53020
|
const name = safeAssetName(asset.name, mime);
|
|
52796
53021
|
const ext = extname4(name);
|
|
52797
53022
|
const target = join21(
|
|
@@ -52920,33 +53145,9 @@ function templateAssetsDir(directory, outputDir, values) {
|
|
|
52920
53145
|
return dir;
|
|
52921
53146
|
}
|
|
52922
53147
|
function readTemplateFile(path10) {
|
|
52923
|
-
|
|
52924
|
-
|
|
52925
|
-
|
|
52926
|
-
let fd;
|
|
52927
|
-
try {
|
|
52928
|
-
fd = openRegular(abs);
|
|
52929
|
-
} catch (error2) {
|
|
52930
|
-
const code = error2.code;
|
|
52931
|
-
throw new Error(
|
|
52932
|
-
code === "ENOENT" ? `Template file not found: ${abs}` : code === "ELOOP" ? `Refusing to read the symbolic link ${abs}` : `Template file is not a readable regular file: ${abs}`
|
|
52933
|
-
);
|
|
52934
|
-
}
|
|
52935
|
-
try {
|
|
52936
|
-
const size = fstatSync5(fd).size;
|
|
52937
|
-
if (size > MAX_TEMPLATE_BYTES)
|
|
52938
|
-
throw new Error(`Template file is ${size} bytes; the limit is ${MAX_TEMPLATE_BYTES}`);
|
|
52939
|
-
const data = Buffer.alloc(size);
|
|
52940
|
-
let read = 0;
|
|
52941
|
-
while (read < size) {
|
|
52942
|
-
const n = readSync4(fd, data, read, size - read, read);
|
|
52943
|
-
if (n <= 0) break;
|
|
52944
|
-
read += n;
|
|
52945
|
-
}
|
|
52946
|
-
return data.subarray(0, read).toString("utf8");
|
|
52947
|
-
} finally {
|
|
52948
|
-
closeSync5(fd);
|
|
52949
|
-
}
|
|
53148
|
+
if (extname4(path10).toLowerCase() !== ".json")
|
|
53149
|
+
throw new Error(`Template file must have a .json extension: ${path10}`);
|
|
53150
|
+
return readAllowedFile(path10, MAX_TEMPLATE_BYTES, { label: "Template file" }).toString("utf8");
|
|
52950
53151
|
}
|
|
52951
53152
|
|
|
52952
53153
|
// src/services/notesExport.ts
|
|
@@ -53021,10 +53222,11 @@ function validPath(path10, what) {
|
|
|
53021
53222
|
function selectNotes(request, deps) {
|
|
53022
53223
|
if (!!request.id === !!request.folder)
|
|
53023
53224
|
throw new NotesExportError("invalid-request", "Provide exactly one of 'id' or 'folder'.");
|
|
53024
|
-
if (request.id) return [request.id];
|
|
53225
|
+
if (request.id) return { ids: [request.id], truncated: false };
|
|
53025
53226
|
const limit = Math.min(request.limit ?? DEFAULT_FOLDER_EXPORT_LIMIT, MAX_FOLDER_EXPORT_LIMIT);
|
|
53026
53227
|
try {
|
|
53027
|
-
|
|
53228
|
+
const ids = deps.listNoteRefs(request.account, request.folder, void 0, limit + 1).map((ref) => ref.id);
|
|
53229
|
+
return { ids: ids.slice(0, limit), truncated: ids.length > limit };
|
|
53028
53230
|
} catch (error2) {
|
|
53029
53231
|
throw new NotesExportError(
|
|
53030
53232
|
"folder-unavailable",
|
|
@@ -53056,9 +53258,13 @@ function openOutput(output) {
|
|
|
53056
53258
|
throw error2;
|
|
53057
53259
|
}
|
|
53058
53260
|
}
|
|
53059
|
-
function renderInto(fd, render) {
|
|
53261
|
+
function renderInto(fd, render, writers = []) {
|
|
53060
53262
|
try {
|
|
53061
|
-
|
|
53263
|
+
const document = render();
|
|
53264
|
+
const directoryError = writers.find((writer) => writer.directoryError)?.directoryError;
|
|
53265
|
+
if (directoryError)
|
|
53266
|
+
throw new NotesExportError("invalid-path", `${directoryError}. Nothing was exported.`);
|
|
53267
|
+
return document;
|
|
53062
53268
|
} catch (error2) {
|
|
53063
53269
|
if (fd !== void 0) writeAllAndClose(fd, "");
|
|
53064
53270
|
throw error2;
|
|
@@ -53075,10 +53281,15 @@ function exportNotesMarkdown(request, deps) {
|
|
|
53075
53281
|
"invalid-request",
|
|
53076
53282
|
`assetsDir has no effect: template "${chosen.info.name}" sets assets.mode to "${chosen.template.assets.mode}".`
|
|
53077
53283
|
);
|
|
53078
|
-
const ids = selectNotes(request, deps);
|
|
53284
|
+
const { ids, truncated } = selectNotes(request, deps);
|
|
53079
53285
|
const { notes, skipped } = loadNotes(ids, !!request.id, deps.readNote);
|
|
53080
|
-
if (chosen)
|
|
53081
|
-
|
|
53286
|
+
if (chosen) {
|
|
53287
|
+
const receipt2 = exportWithTemplate(request, deps, chosen, notes, skipped, {
|
|
53288
|
+
output,
|
|
53289
|
+
assetsDir
|
|
53290
|
+
});
|
|
53291
|
+
return truncated ? { ...receipt2, truncated } : receipt2;
|
|
53292
|
+
}
|
|
53082
53293
|
const fd = output ? openOutput(output) : void 0;
|
|
53083
53294
|
const writer = assetsDir ? new SidecarWriter(assetsDir, output ? dirname6(output) : void 0) : void 0;
|
|
53084
53295
|
const ctx = {
|
|
@@ -53087,12 +53298,14 @@ function exportNotesMarkdown(request, deps) {
|
|
|
53087
53298
|
};
|
|
53088
53299
|
const markdown = renderInto(
|
|
53089
53300
|
fd,
|
|
53090
|
-
() => renderNotesMarkdown(notes, ctx, { wrap: request.wrap ?? 0 })
|
|
53301
|
+
() => renderNotesMarkdown(notes, ctx, { wrap: request.wrap ?? 0 }),
|
|
53302
|
+
writer ? [writer] : []
|
|
53091
53303
|
);
|
|
53092
53304
|
const bytes = Buffer.byteLength(markdown);
|
|
53093
53305
|
const receipt = {
|
|
53094
53306
|
format: "markdown",
|
|
53095
53307
|
count: notes.length,
|
|
53308
|
+
...truncated ? { truncated } : {},
|
|
53096
53309
|
bytes,
|
|
53097
53310
|
stats: ctx.stats,
|
|
53098
53311
|
skipped,
|
|
@@ -53128,7 +53341,7 @@ function exportNotesHtml(request, deps) {
|
|
|
53128
53341
|
const assetsDir = embed ? void 0 : validPath(request.assetsDir ?? defaultSidecarDir(output), "assetsDir");
|
|
53129
53342
|
if (assetsDir === output)
|
|
53130
53343
|
throw new NotesExportError("invalid-path", "outputPath and assetsDir must differ.");
|
|
53131
|
-
const ids = selectNotes(request, deps);
|
|
53344
|
+
const { ids, truncated } = selectNotes(request, deps);
|
|
53132
53345
|
const { notes, skipped } = loadNotes(ids, !!request.id, deps.readNote);
|
|
53133
53346
|
const fd = openOutput(output);
|
|
53134
53347
|
const writer = assetsDir ? new SidecarWriter(assetsDir, dirname6(output)) : new DataUrlWriter();
|
|
@@ -53138,11 +53351,16 @@ function exportNotesHtml(request, deps) {
|
|
|
53138
53351
|
locator: deps.locator ?? new AssetLocator()
|
|
53139
53352
|
};
|
|
53140
53353
|
const title = request.id ? notes[0]?.title || "Note" : request.folder;
|
|
53141
|
-
const html = renderInto(
|
|
53354
|
+
const html = renderInto(
|
|
53355
|
+
fd,
|
|
53356
|
+
() => renderNotesHtml(notes, ctx, { title }),
|
|
53357
|
+
writer instanceof SidecarWriter ? [writer] : []
|
|
53358
|
+
);
|
|
53142
53359
|
const bytes = writeAllAndClose(fd, html);
|
|
53143
53360
|
return {
|
|
53144
53361
|
format: "html",
|
|
53145
53362
|
count: notes.length,
|
|
53363
|
+
...truncated ? { truncated } : {},
|
|
53146
53364
|
bytes,
|
|
53147
53365
|
output,
|
|
53148
53366
|
stats: ctx.stats,
|
|
@@ -53224,7 +53442,7 @@ function exportWithTemplate(request, deps, chosen, notes, skipped, { output, ass
|
|
|
53224
53442
|
});
|
|
53225
53443
|
warnings = result.warnings;
|
|
53226
53444
|
return result.markdown;
|
|
53227
|
-
});
|
|
53445
|
+
}, [...writers.values()]);
|
|
53228
53446
|
const bytes = Buffer.byteLength(markdown);
|
|
53229
53447
|
const used = [...writers.values()].filter((writer) => writer.count > 0);
|
|
53230
53448
|
const files = used.flatMap((writer) => writer.files);
|
|
@@ -53290,12 +53508,12 @@ import { basename as basename5, extname as extname6, join as join23 } from "node
|
|
|
53290
53508
|
var PASTEBOARD_NAME_ENV = "APPLE_NOTES_MCP_PASTEBOARD_NAME";
|
|
53291
53509
|
var MAX_PASTEBOARD_BYTES = 64 * 1024 * 1024;
|
|
53292
53510
|
var PASTEBOARD_DATA_TYPES = [
|
|
53511
|
+
{ type: "com.adobe.pdf", ext: "pdf", label: "document" },
|
|
53293
53512
|
{ type: "public.png", ext: "png", label: "image" },
|
|
53294
53513
|
{ type: "public.jpeg", ext: "jpg", label: "image" },
|
|
53295
53514
|
{ type: "public.heic", ext: "heic", label: "image" },
|
|
53296
53515
|
{ type: "com.compuserve.gif", ext: "gif", label: "image" },
|
|
53297
|
-
{ type: "public.tiff", ext: "tiff", label: "image" }
|
|
53298
|
-
{ type: "com.adobe.pdf", ext: "pdf", label: "document" }
|
|
53516
|
+
{ type: "public.tiff", ext: "tiff", label: "image" }
|
|
53299
53517
|
];
|
|
53300
53518
|
var PASTEBOARD_ACCESS_BEHAVIORS = {
|
|
53301
53519
|
0: "default",
|
|
@@ -53322,15 +53540,26 @@ function run(argv) {
|
|
|
53322
53540
|
var items = pb.pasteboardItems;
|
|
53323
53541
|
var itemCount = items && !items.isNil() ? Number(items.count) : 0;
|
|
53324
53542
|
var fileUrls = [];
|
|
53543
|
+
var dataItems = 0;
|
|
53325
53544
|
for (var k = 0; k < itemCount; k++) {
|
|
53326
53545
|
var item = items.objectAtIndex(k);
|
|
53327
53546
|
var itemTypes = ObjC.deepUnwrap(item.types) || [];
|
|
53328
|
-
if (itemTypes.indexOf("public.file-url") < 0)
|
|
53547
|
+
if (itemTypes.indexOf("public.file-url") < 0) {
|
|
53548
|
+
for (var j = 0; j < prefs.length; j++) {
|
|
53549
|
+
if (itemTypes.indexOf(prefs[j][0]) >= 0) {
|
|
53550
|
+
dataItems++;
|
|
53551
|
+
break;
|
|
53552
|
+
}
|
|
53553
|
+
}
|
|
53554
|
+
continue;
|
|
53555
|
+
}
|
|
53329
53556
|
var value = ObjC.unwrap(item.stringForType("public.file-url"));
|
|
53330
53557
|
if (value) fileUrls.push(value);
|
|
53331
53558
|
}
|
|
53332
53559
|
if (fileUrls.length > 1)
|
|
53333
53560
|
return JSON.stringify({ status: "error", code: "multiple_files", count: fileUrls.length });
|
|
53561
|
+
if (fileUrls.length === 0 && dataItems > 1)
|
|
53562
|
+
return JSON.stringify({ status: "error", code: "multiple_items", count: dataItems });
|
|
53334
53563
|
if (fileUrls.length === 1) {
|
|
53335
53564
|
var url = $.NSURL.URLWithString(fileUrls[0]);
|
|
53336
53565
|
if (url && !url.isNil() && url.isFileURL) {
|
|
@@ -53361,6 +53590,7 @@ var ENVELOPE_CODES = {
|
|
|
53361
53590
|
pasteboard_changed: "operation_failed",
|
|
53362
53591
|
unsupported_content: "validation_error",
|
|
53363
53592
|
multiple_files: "validation_error",
|
|
53593
|
+
multiple_items: "validation_error",
|
|
53364
53594
|
too_large: "validation_error",
|
|
53365
53595
|
write_failed: "operation_failed",
|
|
53366
53596
|
file_unreadable: "validation_error"
|
|
@@ -53387,6 +53617,7 @@ var MESSAGES = {
|
|
|
53387
53617
|
pasteboard_changed: "The pasteboard changed while it was being read. Try again.",
|
|
53388
53618
|
unsupported_content: `The pasteboard holds no supported content: copy ${SUPPORTED}. Text belongs in append-to-note, not in an attachment.`,
|
|
53389
53619
|
multiple_files: "The pasteboard holds more than one copied file. Copy exactly one file, or attach each file with add-attachment.",
|
|
53620
|
+
multiple_items: "The pasteboard holds more than one image or PDF. Copy exactly one, or save each and attach it with add-attachment.",
|
|
53390
53621
|
too_large: "The pasteboard contents exceed the 64 MiB attachment limit.",
|
|
53391
53622
|
write_failed: "Could not write the pasteboard contents to a temporary file.",
|
|
53392
53623
|
file_unreadable: "The copied file could not be read as a regular file of at most 64 MiB."
|
|
@@ -53398,13 +53629,10 @@ function replyError(code, reply) {
|
|
|
53398
53629
|
const message = accessBehavior === "alwaysDeny" ? "macOS is set to deny pasteboard access to the app that runs this server, so nothing was read. Change it to allow in System Settings, then try again." : MESSAGES.pasteboard_access_denied;
|
|
53399
53630
|
return new PasteboardError(code, message, { accessBehavior });
|
|
53400
53631
|
}
|
|
53401
|
-
if (code === "multiple_files") {
|
|
53632
|
+
if (code === "multiple_files" || code === "multiple_items") {
|
|
53402
53633
|
const count = typeof reply.count === "number" ? reply.count : void 0;
|
|
53403
|
-
|
|
53404
|
-
|
|
53405
|
-
count ? `The pasteboard holds ${count} copied files. Copy exactly one file, or attach each file with add-attachment.` : MESSAGES[code],
|
|
53406
|
-
count ? { count } : {}
|
|
53407
|
-
);
|
|
53634
|
+
const counted = code === "multiple_files" ? `The pasteboard holds ${count} copied files. Copy exactly one file, or attach each file with add-attachment.` : `The pasteboard holds ${count} images or PDFs. Copy exactly one, or save each and attach it with add-attachment.`;
|
|
53635
|
+
return new PasteboardError(code, count ? counted : MESSAGES[code], count ? { count } : {});
|
|
53408
53636
|
}
|
|
53409
53637
|
if (code === "unsupported_content" && Array.isArray(reply.types)) {
|
|
53410
53638
|
const types = reply.types.filter((t) => typeof t === "string").slice(0, 20);
|
|
@@ -53617,7 +53845,10 @@ function registerDirectOperations(server2, manager) {
|
|
|
53617
53845
|
{
|
|
53618
53846
|
id: folderId,
|
|
53619
53847
|
expectedName: external_exports.string().max(1e3),
|
|
53620
|
-
|
|
53848
|
+
// Same forms as id, so list-folders' parentIdentifier (a folder UUID)
|
|
53849
|
+
// works; a top-level folder's parent is its account, passed as the
|
|
53850
|
+
// account's x-coredata id.
|
|
53851
|
+
expectedParentId: folderId,
|
|
53621
53852
|
newName: external_exports.string().min(1).max(1e3)
|
|
53622
53853
|
},
|
|
53623
53854
|
(args) => ({
|
|
@@ -54020,7 +54251,9 @@ function readFolderStoreFacts(pk, dbPath2 = NOTE_STORE_PATH) {
|
|
|
54020
54251
|
|
|
54021
54252
|
// src/tools/folderDelete.ts
|
|
54022
54253
|
var folderIdSchema = external_exports.string().max(2e3).transform(looseIdTransform("ICFolder"));
|
|
54023
|
-
var accountIdSchema = external_exports.string().max(2e3).
|
|
54254
|
+
var accountIdSchema = external_exports.string().max(2e3).transform(looseIdTransform("ICAccount")).pipe(
|
|
54255
|
+
external_exports.string().regex(/^x-coredata:\/\/[0-9a-f-]+\/ICAccount\/p\d+$/i, "An exact account ID is required")
|
|
54256
|
+
);
|
|
54024
54257
|
var revisionSchema = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
54025
54258
|
var defaultDeps = {
|
|
54026
54259
|
readStore: (pk) => readFolderStoreFacts(pk),
|
|
@@ -54506,7 +54739,7 @@ function failure(action, error2) {
|
|
|
54506
54739
|
var nameInput = external_exports.string().min(1).max(64).describe("Template name: a built-in (standard-markdown, obsidian) or a saved template's name");
|
|
54507
54740
|
var templateInput = external_exports.union([external_exports.record(external_exports.unknown()), external_exports.string().max(MAX_TEMPLATE_BYTES)]).optional().describe("The template itself, as a JSON object or JSON text (schemaVersion 1)");
|
|
54508
54741
|
var templateFileInput = external_exports.string().min(1).max(4096).optional().describe(
|
|
54509
|
-
"Absolute path of a JSON template file ending in .json (home, a temp dir, or /Volumes; symlinks refused; at most 256 KiB)"
|
|
54742
|
+
"Absolute path of a JSON template file ending in .json (home, a temp dir, or /Volumes; hidden paths and ~/Library outside iCloud Drive and CloudStorage refused unless the server sets APPLE_NOTES_MCP_ALLOW_PRIVATE_CONTENT_PATHS=1; symlinks refused; at most 256 KiB)"
|
|
54510
54743
|
);
|
|
54511
54744
|
function sourceText(args) {
|
|
54512
54745
|
if (args.template === void 0 === (args.templateFile === void 0))
|
|
@@ -54576,7 +54809,7 @@ function registerMarkdownTemplates(server2, store = () => new TemplateStore()) {
|
|
|
54576
54809
|
server2.registerTool(
|
|
54577
54810
|
"validate-markdown-template",
|
|
54578
54811
|
{
|
|
54579
|
-
description: 'Use when: checking a template before saving it or exporting with it.\nReturns: valid true, or valid false with every problem as {path, message}, where path is a JSON path such as $.rules["inline.bold"].after.\nDo not use when: you want to store it (save-markdown-template validates too).\nSafety: read-only. Pass exactly one of name (a saved or built-in template), template (JSON object or text) or templateFile.',
|
|
54812
|
+
description: 'Use when: checking a template before saving it or exporting with it.\nReturns: valid true, or valid false with every problem as {path, message}, where path is a JSON path such as $.rules["inline.bold"].after.\nDo not use when: you want to store it (save-markdown-template validates too).\nSafety: read-only. Pass exactly one of name (a saved or built-in template), template (JSON object or text) or templateFile. templateFile reads only a regular .json file in home, temp or /Volumes, refusing hidden paths (~/.docker, .env) and ~/Library outside iCloud Drive and CloudStorage unless the server sets APPLE_NOTES_MCP_ALLOW_PRIVATE_CONTENT_PATHS=1; errors never quote the file.',
|
|
54580
54813
|
inputSchema: validateInput,
|
|
54581
54814
|
outputSchema: loose,
|
|
54582
54815
|
annotations: { readOnlyHint: true }
|
|
@@ -54617,7 +54850,7 @@ function registerMarkdownTemplates(server2, store = () => new TemplateStore()) {
|
|
|
54617
54850
|
server2.registerTool(
|
|
54618
54851
|
"save-markdown-template",
|
|
54619
54852
|
{
|
|
54620
|
-
description: "Use when: storing a template so export-notes-markdown can use it by name.\nReturns: name, path, bytes and whether an existing template was replaced.\nDo not use when: exporting once (pass templateFile to export-notes-markdown instead).\nSafety: writes one file in the template library only (APPLE_NOTES_MCP_TEMPLATE_DIR, default ~/Library/Application Support/apple-notes-mcp/templates, mode 0600). Validates first; an invalid template is refused with every JSON path. Create-only: an existing name is refused with [template-exists] unless force is true. Built-in names are reserved.",
|
|
54853
|
+
description: "Use when: storing a template so export-notes-markdown can use it by name.\nReturns: name, path, bytes and whether an existing template was replaced.\nDo not use when: exporting once (pass templateFile to export-notes-markdown instead).\nSafety: writes one file in the template library only (APPLE_NOTES_MCP_TEMPLATE_DIR, default ~/Library/Application Support/apple-notes-mcp/templates, mode 0600). Validates first; an invalid template is refused with every JSON path. Create-only: an existing name is refused with [template-exists] unless force is true. Built-in names are reserved. templateFile follows the same read scope as validate-markdown-template.",
|
|
54621
54854
|
inputSchema: saveInput,
|
|
54622
54855
|
outputSchema: loose,
|
|
54623
54856
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
@@ -54660,7 +54893,6 @@ function registerMarkdownTemplates(server2, store = () => new TemplateStore()) {
|
|
|
54660
54893
|
|
|
54661
54894
|
// src/utils/svgAnalyzer.ts
|
|
54662
54895
|
import { createHash as createHash5 } from "node:crypto";
|
|
54663
|
-
import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, openSync as openSync9, readSync as readSync7 } from "node:fs";
|
|
54664
54896
|
|
|
54665
54897
|
// src/utils/svgColor.ts
|
|
54666
54898
|
var NAMED = {
|
|
@@ -55503,7 +55735,7 @@ function decodeEntities(raw, line) {
|
|
|
55503
55735
|
if (body in PREDEFINED) return PREDEFINED[body];
|
|
55504
55736
|
throw new SvgError(
|
|
55505
55737
|
"svg_unsafe",
|
|
55506
|
-
`
|
|
55738
|
+
`A named entity on line ${line} is not allowed: only the five XML entities are`
|
|
55507
55739
|
);
|
|
55508
55740
|
});
|
|
55509
55741
|
}
|
|
@@ -55574,6 +55806,8 @@ function parseXml(source, limits) {
|
|
|
55574
55806
|
if (!NAME_START.test(source[i] ?? "")) fail("Invalid tag");
|
|
55575
55807
|
while (i < source.length && NAME_CHAR.test(source[i])) i++;
|
|
55576
55808
|
const name = source.slice(pos + 1, i);
|
|
55809
|
+
if (!root && !stack.length && name.slice(name.indexOf(":") + 1) !== "svg")
|
|
55810
|
+
fail("The root element is not <svg>");
|
|
55577
55811
|
const rawAttributes = [];
|
|
55578
55812
|
const attributeNames = /* @__PURE__ */ new Set();
|
|
55579
55813
|
let selfClosing = false;
|
|
@@ -56691,50 +56925,12 @@ function analyzeSvgBuffer(source) {
|
|
|
56691
56925
|
const viewport = analyzer.run();
|
|
56692
56926
|
return analysisFor(source, analyzer, viewport);
|
|
56693
56927
|
}
|
|
56694
|
-
function readSvgSource(path10) {
|
|
56695
|
-
let fd;
|
|
56696
|
-
try {
|
|
56697
|
-
fd = openSync9(path10, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
|
|
56698
|
-
} catch (error2) {
|
|
56699
|
-
if (error2.code === "ELOOP")
|
|
56700
|
-
throw new SvgError("svg_file_invalid", "The SVG path is a symbolic link");
|
|
56701
|
-
throw new SvgError("svg_file_invalid", "The SVG file does not exist or cannot be read");
|
|
56702
|
-
}
|
|
56703
|
-
try {
|
|
56704
|
-
const info = fstatSync9(fd);
|
|
56705
|
-
if (!info.isFile())
|
|
56706
|
-
throw new SvgError("svg_file_invalid", "The SVG path is not a regular file");
|
|
56707
|
-
if (info.size > SVG_LIMITS.maxSourceBytes)
|
|
56708
|
-
throw new SvgError(
|
|
56709
|
-
"svg_file_invalid",
|
|
56710
|
-
`The SVG is larger than ${SVG_LIMITS.maxSourceBytes} bytes`
|
|
56711
|
-
);
|
|
56712
|
-
const buffer = Buffer.alloc(SVG_LIMITS.maxSourceBytes + 1);
|
|
56713
|
-
let total = 0;
|
|
56714
|
-
for (; ; ) {
|
|
56715
|
-
const n = readSync7(fd, buffer, total, buffer.length - total, null);
|
|
56716
|
-
if (n === 0) break;
|
|
56717
|
-
total += n;
|
|
56718
|
-
if (total > SVG_LIMITS.maxSourceBytes)
|
|
56719
|
-
throw new SvgError(
|
|
56720
|
-
"svg_file_invalid",
|
|
56721
|
-
`The SVG is larger than ${SVG_LIMITS.maxSourceBytes} bytes`
|
|
56722
|
-
);
|
|
56723
|
-
}
|
|
56724
|
-
return buffer.subarray(0, total);
|
|
56725
|
-
} finally {
|
|
56726
|
-
closeSync9(fd);
|
|
56727
|
-
}
|
|
56728
|
-
}
|
|
56729
|
-
function analyzeSvgFile(path10) {
|
|
56730
|
-
return analyzeSvgBuffer(readSvgSource(path10));
|
|
56731
|
-
}
|
|
56732
56928
|
|
|
56733
56929
|
// src/tools/svgAnalysis.ts
|
|
56734
56930
|
function runSvgAnalysis(args, roots = allowedSaveRoots()) {
|
|
56735
|
-
let
|
|
56931
|
+
let source;
|
|
56736
56932
|
try {
|
|
56737
|
-
|
|
56933
|
+
source = readAllowedFile(args.path, SVG_LIMITS.maxSourceBytes, { roots, label: "SVG file" });
|
|
56738
56934
|
} catch (error2) {
|
|
56739
56935
|
throw new CodedError(error2.message, {
|
|
56740
56936
|
code: "validation_error",
|
|
@@ -56742,7 +56938,7 @@ function runSvgAnalysis(args, roots = allowedSaveRoots()) {
|
|
|
56742
56938
|
});
|
|
56743
56939
|
}
|
|
56744
56940
|
try {
|
|
56745
|
-
const result =
|
|
56941
|
+
const result = analyzeSvgBuffer(source);
|
|
56746
56942
|
return {
|
|
56747
56943
|
...result.analysis,
|
|
56748
56944
|
...args.includeDrawing ? { drawing: result.drawing } : {}
|
|
@@ -56760,7 +56956,7 @@ function runSvgAnalysis(args, roots = allowedSaveRoots()) {
|
|
|
56760
56956
|
function registerSvgAnalysis(server2) {
|
|
56761
56957
|
const inputSchema = {
|
|
56762
56958
|
path: external_exports.string().min(1).max(4096).describe(
|
|
56763
|
-
`Absolute path of one .svg file (home, temp, or /Volumes; not a symbolic link; at most ${SVG_LIMITS.maxSourceBytes} bytes of UTF-8)`
|
|
56959
|
+
`Absolute path of one .svg file (home, temp, or /Volumes; not a hidden path or ~/Library outside iCloud Drive and CloudStorage; not a symbolic link; at most ${SVG_LIMITS.maxSourceBytes} bytes of UTF-8)`
|
|
56764
56960
|
),
|
|
56765
56961
|
includeDrawing: external_exports.boolean().optional().describe(
|
|
56766
56962
|
"Also return the normalized drawing (strokes with sRGB color, width and points). Off by default: it can be large."
|
|
@@ -56769,7 +56965,7 @@ function registerSvgAnalysis(server2) {
|
|
|
56769
56965
|
server2.registerTool(
|
|
56770
56966
|
"analyze-svg",
|
|
56771
56967
|
{
|
|
56772
|
-
description: "Use when: checking whether an SVG file can be represented as editable monoline strokes, and what that conversion would approximate or drop.\nReturns: classification (safe, lossy, unsupported), importable, defaultWriteAllowed, requiredLosses (geometry-approximation, paint-approximation, drop-content), issues with element locations, counts and work budgets, the source SHA-256, and analysisDigest, a SHA-256 over the canonical analysis and normalized drawing. includeDrawing adds the drawing.\nDo not use when: you want to attach the SVG file itself to a note (add-attachment).\nSafety: read-only and local. Opens no Notes data and makes no network request. Scripts, event handlers, style elements, animation, DOCTYPE/entities and external resources are refused with svgCode svg_unsafe; malformed files with svg_invalid; oversized work with svg_complexity_limit.",
|
|
56968
|
+
description: "Use when: checking whether an SVG file can be represented as editable monoline strokes, and what that conversion would approximate or drop.\nReturns: classification (safe, lossy, unsupported), importable, defaultWriteAllowed, requiredLosses (geometry-approximation, paint-approximation, drop-content), issues with element locations, counts and work budgets, the source SHA-256, and analysisDigest, a SHA-256 over the canonical analysis and normalized drawing. includeDrawing adds the drawing.\nDo not use when: you want to attach the SVG file itself to a note (add-attachment).\nSafety: read-only and local. Opens no Notes data and makes no network request. Reads only a regular file in home, temp or /Volumes, refusing hidden paths (~/.ssh, .env) and ~/Library outside iCloud Drive and CloudStorage unless the server sets APPLE_NOTES_MCP_ALLOW_PRIVATE_CONTENT_PATHS=1; a file whose root element is not <svg> is refused without quoting it. Scripts, event handlers, style elements, animation, DOCTYPE/entities and external resources are refused with svgCode svg_unsafe; malformed files with svg_invalid; oversized work with svg_complexity_limit.",
|
|
56773
56969
|
inputSchema,
|
|
56774
56970
|
outputSchema: external_exports.object({ classification: external_exports.string().optional() }).passthrough(),
|
|
56775
56971
|
annotations: { readOnlyHint: true, openWorldHint: false }
|
|
@@ -58005,14 +58201,17 @@ var strokeSchema = external_exports.object({
|
|
|
58005
58201
|
force: external_exports.number()
|
|
58006
58202
|
})
|
|
58007
58203
|
).optional(),
|
|
58008
|
-
transformApplied: external_exports.boolean().optional()
|
|
58204
|
+
transformApplied: external_exports.boolean().optional(),
|
|
58205
|
+
masked: external_exports.boolean().optional(),
|
|
58206
|
+
pointsTruncated: external_exports.boolean().optional()
|
|
58009
58207
|
}).strip();
|
|
58010
58208
|
var decodedDrawingSchema = external_exports.object({
|
|
58011
58209
|
status: external_exports.literal("ok"),
|
|
58012
58210
|
strokeCount: external_exports.number().int(),
|
|
58013
58211
|
strokes: external_exports.array(strokeSchema),
|
|
58014
58212
|
truncated: external_exports.boolean(),
|
|
58015
|
-
bounds
|
|
58213
|
+
bounds,
|
|
58214
|
+
hiddenStrokeCount: external_exports.number().int().optional()
|
|
58016
58215
|
});
|
|
58017
58216
|
function decodeRow(row, format, includePoints, deps) {
|
|
58018
58217
|
const base = {
|
|
@@ -58045,7 +58244,8 @@ function decodeRow(row, format, includePoints, deps) {
|
|
|
58045
58244
|
status: "ok",
|
|
58046
58245
|
strokeCount: decoded.strokeCount,
|
|
58047
58246
|
bounds: decoded.bounds,
|
|
58048
|
-
truncated: decoded.truncated
|
|
58247
|
+
truncated: decoded.truncated,
|
|
58248
|
+
...decoded.hiddenStrokeCount ? { hiddenStrokeCount: decoded.hiddenStrokeCount } : {}
|
|
58049
58249
|
};
|
|
58050
58250
|
if (format !== "json") result.svg = drawingToSvg(strokes, decoded.bounds);
|
|
58051
58251
|
if (format !== "svg")
|
|
@@ -58070,6 +58270,28 @@ function getNoteDrawings(noteId3, options = {}) {
|
|
|
58070
58270
|
drawings
|
|
58071
58271
|
};
|
|
58072
58272
|
}
|
|
58273
|
+
function fitNoteDrawings(result, maxBytes, measure = (r) => Buffer.byteLength(JSON.stringify(r))) {
|
|
58274
|
+
let next = result;
|
|
58275
|
+
let pointsOmitted = false;
|
|
58276
|
+
let svgOmitted = false;
|
|
58277
|
+
if (measure(next) > maxBytes && next.drawings.some((d) => d.strokes?.some((s) => s.points))) {
|
|
58278
|
+
next = {
|
|
58279
|
+
...next,
|
|
58280
|
+
drawings: next.drawings.map(
|
|
58281
|
+
(d) => d.strokes ? { ...d, strokes: d.strokes.map(({ points: _points, ...rest }) => rest) } : d
|
|
58282
|
+
)
|
|
58283
|
+
};
|
|
58284
|
+
pointsOmitted = true;
|
|
58285
|
+
}
|
|
58286
|
+
if (measure(next) > maxBytes && next.drawings.some((d) => d.svg !== void 0)) {
|
|
58287
|
+
next = {
|
|
58288
|
+
...next,
|
|
58289
|
+
drawings: next.drawings.map(({ svg: _svg, ...rest }) => rest)
|
|
58290
|
+
};
|
|
58291
|
+
svgOmitted = true;
|
|
58292
|
+
}
|
|
58293
|
+
return { result: next, pointsOmitted, svgOmitted, oversized: measure(next) > maxBytes };
|
|
58294
|
+
}
|
|
58073
58295
|
function formatNoteDrawings(result) {
|
|
58074
58296
|
if (result.drawingCount === 0) return `No classic PencilKit drawings in note ${result.id}.`;
|
|
58075
58297
|
const lines = [
|
|
@@ -58077,7 +58299,7 @@ function formatNoteDrawings(result) {
|
|
|
58077
58299
|
];
|
|
58078
58300
|
for (const d of result.drawings)
|
|
58079
58301
|
lines.push(
|
|
58080
|
-
d.status === "ok" ? `- ${d.attachmentId}: ${d.strokeCount} stroke${d.strokeCount === 1 ? "" : "s"}${d.truncated ? " (truncated)" : ""}` : `- ${d.attachmentId}: error ${d.code}: ${d.message}`
|
|
58302
|
+
d.status === "ok" ? `- ${d.attachmentId}: ${d.strokeCount} stroke${d.strokeCount === 1 ? "" : "s"}${d.hiddenStrokeCount ? ` (${d.hiddenStrokeCount} fully erased)` : ""}${d.truncated ? " (truncated)" : ""}` : `- ${d.attachmentId}: error ${d.code}: ${d.message}`
|
|
58081
58303
|
);
|
|
58082
58304
|
return lines.join("\n");
|
|
58083
58305
|
}
|
|
@@ -58177,9 +58399,6 @@ function readAudioAssets(noteId3, options = {}) {
|
|
|
58177
58399
|
};
|
|
58178
58400
|
});
|
|
58179
58401
|
}
|
|
58180
|
-
function countWords2(text2) {
|
|
58181
|
-
return text2.split(/\s+/u).filter((w) => /[\p{L}\p{N}]/u.test(w)).length;
|
|
58182
|
-
}
|
|
58183
58402
|
|
|
58184
58403
|
// src/services/noteTranscription.ts
|
|
58185
58404
|
var DEFAULT_TRANSCRIPTION_LOCALE = "en-US";
|
|
@@ -58194,6 +58413,7 @@ var BUDGET_MARGIN_MS = 1e4;
|
|
|
58194
58413
|
var MIN_TAKE_SECONDS = 5;
|
|
58195
58414
|
var CALL_WIDE_CODES = /* @__PURE__ */ new Set([
|
|
58196
58415
|
"permission_required",
|
|
58416
|
+
"permission_not_requested",
|
|
58197
58417
|
"asset_unavailable",
|
|
58198
58418
|
"unsupported_locale",
|
|
58199
58419
|
"speech_unavailable"
|
|
@@ -58272,7 +58492,7 @@ async function transcribeTake(take, ctx) {
|
|
|
58272
58492
|
status: complete ? "ok" : text2 ? "partial" : "indeterminate",
|
|
58273
58493
|
...complete ? {} : { code: "incomplete", message: stopReason ?? "stopped early" },
|
|
58274
58494
|
...durationSeconds !== void 0 ? { durationSeconds } : {},
|
|
58275
|
-
wordCount:
|
|
58495
|
+
wordCount: countWords(text2),
|
|
58276
58496
|
...engine ? { engine } : {}
|
|
58277
58497
|
},
|
|
58278
58498
|
text: text2
|
|
@@ -58299,7 +58519,7 @@ async function transcribeRecording(asset, includeText, ctx) {
|
|
|
58299
58519
|
status,
|
|
58300
58520
|
...status !== "ok" && firstProblem?.code ? { code: firstProblem.code, message: firstProblem.message } : {},
|
|
58301
58521
|
...asset.durationSeconds !== null ? { durationSeconds: Math.round(asset.durationSeconds) } : {},
|
|
58302
|
-
wordCount:
|
|
58522
|
+
wordCount: countWords(transcript),
|
|
58303
58523
|
...includeText ? { transcript } : {},
|
|
58304
58524
|
takes: outcomes.map((o) => o.take)
|
|
58305
58525
|
};
|
|
@@ -58354,24 +58574,29 @@ async function transcribeNoteAudio(noteId3, options = {}) {
|
|
|
58354
58574
|
}
|
|
58355
58575
|
function fitTranscriptions(result, maxBytes, measure = (r) => Buffer.byteLength(JSON.stringify(r))) {
|
|
58356
58576
|
if (measure(result) <= maxBytes) return result;
|
|
58577
|
+
const shorten = (share) => ({
|
|
58578
|
+
...result,
|
|
58579
|
+
recordings: result.recordings.map(
|
|
58580
|
+
(r) => r.transcript ? {
|
|
58581
|
+
...r,
|
|
58582
|
+
transcript: r.transcript.slice(0, Math.floor(r.transcript.length * share)),
|
|
58583
|
+
transcriptTruncated: true
|
|
58584
|
+
} : r
|
|
58585
|
+
)
|
|
58586
|
+
});
|
|
58357
58587
|
let next = result;
|
|
58358
58588
|
for (let share = 0.5; measure(next) > maxBytes && share > 1e-4; share /= 2) {
|
|
58359
|
-
next =
|
|
58360
|
-
...result,
|
|
58361
|
-
recordings: result.recordings.map(
|
|
58362
|
-
(r) => r.transcript ? {
|
|
58363
|
-
...r,
|
|
58364
|
-
transcript: r.transcript.slice(0, Math.floor(r.transcript.length * share)),
|
|
58365
|
-
transcriptTruncated: true
|
|
58366
|
-
} : r
|
|
58367
|
-
)
|
|
58368
|
-
};
|
|
58589
|
+
next = shorten(share);
|
|
58369
58590
|
}
|
|
58591
|
+
if (measure(next) > maxBytes) next = { ...shorten(0), responseOversized: true };
|
|
58370
58592
|
return next;
|
|
58371
58593
|
}
|
|
58372
58594
|
function formatTranscription(result) {
|
|
58373
58595
|
if (result.recordingCount === 0) return `No audio attachments in note ${result.id}.`;
|
|
58374
58596
|
const lines = [
|
|
58597
|
+
...result.responseOversized ? [
|
|
58598
|
+
"The transcripts were dropped: even without them the response exceeds APPLE_NOTES_MCP_EXPORT_MAX_BYTES. Transcribe one recording at a time with attachmentId."
|
|
58599
|
+
] : [],
|
|
58375
58600
|
`${result.recordingCount} audio attachment${result.recordingCount === 1 ? "" : "s"} in note ${result.id} (${result.status}, locale ${result.locale}):`
|
|
58376
58601
|
];
|
|
58377
58602
|
result.recordings.forEach((r, i) => {
|
|
@@ -59243,10 +59468,11 @@ function prepareDeleteGuards(args) {
|
|
|
59243
59468
|
if (guardNoteId === void 0 !== (expectedGuardContentHash === void 0)) {
|
|
59244
59469
|
return { error: "Pass guardNoteId and expectedGuardContentHash together." };
|
|
59245
59470
|
}
|
|
59246
|
-
|
|
59471
|
+
const same = (a, b) => a !== void 0 && b !== void 0 && canonicalCoreDataId(a) === canonicalCoreDataId(b);
|
|
59472
|
+
if (same(guardNoteId, id2) || same(requireActiveNoteId, id2)) {
|
|
59247
59473
|
return { error: "A guard note must be a different note from the one being deleted." };
|
|
59248
59474
|
}
|
|
59249
|
-
if (guardNoteId
|
|
59475
|
+
if (same(guardNoteId, requireActiveNoteId)) {
|
|
59250
59476
|
return { error: "requireActiveNoteId repeats guardNoteId; pass only guardNoteId." };
|
|
59251
59477
|
}
|
|
59252
59478
|
const prepared = { guards: [], labels: [] };
|
|
@@ -59593,7 +59819,7 @@ ${noteList}${truncationNote}${scanNote}${wordCountNote}${syncNote}`,
|
|
|
59593
59819
|
registerTool(
|
|
59594
59820
|
"query-notes",
|
|
59595
59821
|
{
|
|
59596
|
-
description: 'Use when: finding notes with a boolean expression over text and metadata \u2014 e.g. `folder:Work has:checklist -checklist:done`, `(title:invoice OR tag:finance) modified:>=2026-07-01`, `pinned words:>250`. Reads the Notes database directly, so it is fast and can match title OR body in one call.\nSyntax: bare words and "quoted phrases" match title or body (case-insensitive substring); fields title:, body:, text:, folder:, account:, tag: (values may be quoted, e.g. folder:"Work Projects"); facets has:link|attachment|checklist|drawing|image|video|audio|pdf|table|scan|tag; checklist:open|done; flags pinned, locked, shared (or is:pinned); words:>250 and created:/modified: with =, >, >=, <, <= and YYYY-MM-DD local dates. AND is implicit; OR, NOT, leading -, and parentheses are supported; operators are case-insensitive and a quoted "and" searches the literal word.\nReturns: matching notes (most recently modified first) with id, title, folder, account, modified date, snippet, and matchedIn (where the positive text terms occur: title, body, or both; absent when the body is unreadable or the query has no text term), plus scan/match counts; includeWordCount adds wordCount. Ids work with get-note-content and every other id-based tool.\nDo not use when: Full Disk Access is unavailable (use search-notes). Scans the most recent scanLimit notes (default 500); raise it for older notes.\nSafety: read-only; never writes the database. Excludes Recently Deleted and folderless notes unless includeDeleted is true. Locked notes match on title and metadata only; body predicates never match them.',
|
|
59822
|
+
description: 'Use when: finding notes with a boolean expression over text and metadata \u2014 e.g. `folder:Work has:checklist -checklist:done`, `(title:invoice OR tag:finance) modified:>=2026-07-01`, `pinned words:>250`. Reads the Notes database directly, so it is fast and can match title OR body in one call.\nSyntax: bare words and "quoted phrases" match title or body (case-insensitive substring); fields title:, body:, text:, folder:, account:, tag: (values may be quoted, e.g. folder:"Work Projects"); facets has:link|attachment|checklist|drawing|image|video|audio|pdf|table|scan|tag; checklist:open|done; flags pinned, locked, shared (or is:pinned); words:>250 and created:/modified: with =, >, >=, <, <= and YYYY-MM-DD local dates. AND is implicit; OR, NOT, leading -, and parentheses are supported; operators are case-insensitive and a quoted "and" searches the literal word.\nReturns: matching notes (most recently modified first) with id, title, folder, account, modified date, snippet, and matchedIn (where the positive text terms occur: title, body, or both; absent when the body is unreadable or the query has no text term), plus scan/match counts; includeWordCount adds wordCount. Ids work with get-note-content and every other id-based tool.\nDo not use when: Full Disk Access is unavailable (use search-notes). Scans the most recent scanLimit notes (default 500); raise it for older notes.\nSafety: read-only; never writes the database. Excludes Recently Deleted and folderless notes unless includeDeleted is true. Locked notes match on title and metadata only; body predicates (including negated ones such as -body:x) never match them.',
|
|
59597
59823
|
inputSchema: {
|
|
59598
59824
|
query: external_exports.string().min(1, "A query expression is required").max(MAX.QUERY).describe(
|
|
59599
59825
|
'Boolean query expression, e.g. `folder:"Work Projects" has:checklist -checklist:done`'
|
|
@@ -59646,7 +59872,7 @@ registerTool(
|
|
|
59646
59872
|
}
|
|
59647
59873
|
if (result.unreadable > 0) {
|
|
59648
59874
|
notes.push(
|
|
59649
|
-
`\u26A0\uFE0F ${result.unreadable} note bodies could not be decoded, so body predicates did not match them.`
|
|
59875
|
+
`\u26A0\uFE0F ${result.unreadable} note bodies could not be decoded, so body predicates (including negated ones) did not match them.`
|
|
59650
59876
|
);
|
|
59651
59877
|
}
|
|
59652
59878
|
const footer = notes.length ? `
|
|
@@ -60049,7 +60275,7 @@ registerTool(
|
|
|
60049
60275
|
if (!note) return notFoundResponse(`Note with ID "${id2}" not found`);
|
|
60050
60276
|
const body = notesManager.getNoteContentById(id2);
|
|
60051
60277
|
if (!body) return errorResponse(`Failed to read content of note "${note.title}"`);
|
|
60052
|
-
const rich = readRichNote(id2);
|
|
60278
|
+
const rich = readRichNote(id2, { skipUnsafeLinks: true });
|
|
60053
60279
|
const tables = (rich.objectData || []).filter((object3) => object3.type?.includes("table")).map((object3) => {
|
|
60054
60280
|
try {
|
|
60055
60281
|
return {
|
|
@@ -61659,6 +61885,7 @@ registerTool(
|
|
|
61659
61885
|
height: external_exports.number().optional(),
|
|
61660
61886
|
bytes: external_exports.number().optional(),
|
|
61661
61887
|
source: external_exports.enum(["fallback", "preview"]).optional(),
|
|
61888
|
+
stale: external_exports.boolean().optional(),
|
|
61662
61889
|
attachmentId: external_exports.string().optional(),
|
|
61663
61890
|
identifier: external_exports.string().optional(),
|
|
61664
61891
|
kind: external_exports.enum(["paper", "drawing"]).optional(),
|
|
@@ -61667,8 +61894,9 @@ registerTool(
|
|
|
61667
61894
|
},
|
|
61668
61895
|
withErrorHandling(({ noteId: noteId3, savePath, attachmentId }) => {
|
|
61669
61896
|
const r = notesManager.exportPaperImageById(noteId3, savePath, attachmentId);
|
|
61897
|
+
const stale = r.source === "fallback" && r.drawing.fallbackImageStale;
|
|
61670
61898
|
return successResponse(
|
|
61671
|
-
`Saved ${r.format.toUpperCase()} ${r.width}x${r.height} (${r.bytes} bytes, ${r.source === "fallback" ? "Notes' full rendering" : "largest preview"}) to ${r.savedPath}
|
|
61899
|
+
`Saved ${r.format.toUpperCase()} ${r.width}x${r.height} (${r.bytes} bytes, ${r.source === "fallback" ? "Notes' full rendering" : "largest preview"}) to ${r.savedPath}` + (stale ? ". This is an older rendering: the one Notes recorded is not on disk, so it may not show the latest strokes." : ""),
|
|
61672
61900
|
{
|
|
61673
61901
|
savedPath: r.savedPath,
|
|
61674
61902
|
format: r.format,
|
|
@@ -61676,6 +61904,7 @@ registerTool(
|
|
|
61676
61904
|
height: r.height,
|
|
61677
61905
|
bytes: r.bytes,
|
|
61678
61906
|
source: r.source,
|
|
61907
|
+
...stale ? { stale } : {},
|
|
61679
61908
|
attachmentId: attachmentCoreDataId(noteId3, r.drawing.pk),
|
|
61680
61909
|
identifier: r.drawing.identifier,
|
|
61681
61910
|
kind: r.drawing.kind,
|
|
@@ -61688,7 +61917,7 @@ registerTool(
|
|
|
61688
61917
|
"export-attachments",
|
|
61689
61918
|
{
|
|
61690
61919
|
description: `Use when: copying every file attachment of one note (or only its lead visual) into a directory on disk.
|
|
61691
|
-
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.
|
|
61920
|
+
Returns: per attachment its id, kind, exportedTo, and exportedKind: "asset" for the real file, "fallback" for Notes' own full rendering of it (a drawing's PNG or a scan's PDF, named with that format's extension), "preview" when the asset never downloaded and only Notes' rendered thumbnail was available, or null when nothing was on disk; inBody: false marks an attachment no longer shown in the note body.
|
|
61692
61921
|
Do not use when: exporting one attachment to an exact path (save-attachment) or reading bytes inline (fetch-attachment).
|
|
61693
61922
|
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.`,
|
|
61694
61923
|
inputSchema: {
|
|
@@ -61700,6 +61929,7 @@ Safety: writes files; exportDir must be absolute and under the home directory, a
|
|
|
61700
61929
|
exportDir: external_exports.string().optional(),
|
|
61701
61930
|
exported: external_exports.number().optional(),
|
|
61702
61931
|
previews: external_exports.number().optional(),
|
|
61932
|
+
fallbacks: external_exports.number().optional(),
|
|
61703
61933
|
skipped: external_exports.number().optional(),
|
|
61704
61934
|
failed: external_exports.number().optional(),
|
|
61705
61935
|
results: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
@@ -61714,12 +61944,14 @@ Safety: writes files; exportDir must be absolute and under the home directory, a
|
|
|
61714
61944
|
}));
|
|
61715
61945
|
const exported = results.filter((x) => x.exportedKind !== null).length;
|
|
61716
61946
|
const previews = results.filter((x) => x.exportedKind === "preview").length;
|
|
61947
|
+
const fallbacks = results.filter((x) => x.exportedKind === "fallback").length;
|
|
61717
61948
|
const failed = results.filter((x) => x.error).length;
|
|
61718
61949
|
const skipped = results.length - exported - failed;
|
|
61719
61950
|
const structured = {
|
|
61720
61951
|
exportDir: r.exportDir,
|
|
61721
61952
|
exported,
|
|
61722
61953
|
previews,
|
|
61954
|
+
fallbacks,
|
|
61723
61955
|
skipped,
|
|
61724
61956
|
failed,
|
|
61725
61957
|
results
|
|
@@ -61733,7 +61965,7 @@ Safety: writes files; exportDir must be absolute and under the home directory, a
|
|
|
61733
61965
|
);
|
|
61734
61966
|
}
|
|
61735
61967
|
}
|
|
61736
|
-
const summary = `Exported ${exported} file(s) to ${r.exportDir} (${previews} preview-only, ${skipped} with nothing on disk, ${failed} failed).`;
|
|
61968
|
+
const summary = `Exported ${exported} file(s) to ${r.exportDir} (${previews} preview-only, ${fallbacks} Notes rendering(s), ${skipped} with nothing on disk, ${failed} failed).`;
|
|
61737
61969
|
if (exported === 0 && failed > 0) {
|
|
61738
61970
|
const error2 = errorResponse(summary, new CodedError(summary, { code: "operation_failed" }));
|
|
61739
61971
|
return { ...error2, structuredContent: { ...structured, ...error2.structuredContent } };
|
|
@@ -61914,12 +62146,13 @@ var exportStatsSchema = external_exports.object({
|
|
|
61914
62146
|
unavailable: external_exports.number(),
|
|
61915
62147
|
tables: external_exports.number(),
|
|
61916
62148
|
unreadableTables: external_exports.number(),
|
|
61917
|
-
unreferenced: external_exports.number()
|
|
62149
|
+
unreferenced: external_exports.number(),
|
|
62150
|
+
staleRenderings: external_exports.number().optional()
|
|
61918
62151
|
});
|
|
61919
62152
|
registerTool(
|
|
61920
62153
|
"export-notes-markdown",
|
|
61921
62154
|
{
|
|
61922
|
-
description: "Use when: exporting one note (by exact id) or a folder's notes as one Markdown document rendered from the decoded note body: headings, bulleted/dashed/numbered lists with indent, checklists with state, block quotes, monospaced blocks, bold/italic/strikethrough/underline/highlight, links, tables, and attachments in body order.\nReturns: the Markdown inline (capped by APPLE_NOTES_MCP_EXPORT_MAX_BYTES), or with outputPath a receipt {format, count, bytes, output}; plus attachment counts and skipped notes (for example password-protected ones).\nDo not use when: you need the legacy HTML-converted Markdown of one note (get-note-markdown) or a restorable backup (export-notes-json). A folder document separates notes with '---' and is a presentation format, not something to import back.\nSafety: read-only against Notes; requires Full Disk Access. outputPath is create-only (an existing file is refused with [output_exists]); assetsDir copies attachment files without replacing existing ones (collisions get -2, -3 suffixes). Without assetsDir, attachments render as labeled placeholders.\nTemplates: pass template ('standard-markdown' reproduces the default output; 'obsidian' adds YAML front matter and copies attachments beside outputPath) or templateFile (a JSON template) to control how every block, inline style, attachment, per-note header/footer and separator renders. Templated receipts add template, warnings (e.g. missing_asset) and assetFiles; an invalid template is refused with [invalid-template] and every problem's JSON path.",
|
|
62155
|
+
description: "Use when: exporting one note (by exact id) or a folder's notes as one Markdown document rendered from the decoded note body: headings, bulleted/dashed/numbered lists with indent, checklists with state, block quotes, monospaced blocks, bold/italic/strikethrough/underline/highlight, links, tables, and attachments in body order.\nReturns: the Markdown inline (capped by APPLE_NOTES_MCP_EXPORT_MAX_BYTES), or with outputPath a receipt {format, count, bytes, output}; plus attachment counts and skipped notes (for example password-protected ones).\nDo not use when: you need the legacy HTML-converted Markdown of one note (get-note-markdown) or a restorable backup (export-notes-json). A folder document separates notes with '---' and is a presentation format, not something to import back.\nSafety: read-only against Notes; requires Full Disk Access. outputPath is create-only (an existing file is refused with [output_exists]); assetsDir copies attachment files without replacing existing ones (collisions get -2, -3 suffixes). Without assetsDir, attachments render as labeled placeholders.\nTemplates: pass template ('standard-markdown' reproduces the default output; 'obsidian' adds YAML front matter and copies attachments beside outputPath) or templateFile (a JSON template; hidden paths and ~/Library outside iCloud Drive and CloudStorage are refused unless the server sets APPLE_NOTES_MCP_ALLOW_PRIVATE_CONTENT_PATHS=1) to control how every block, inline style, attachment, per-note header/footer and separator renders. Templated receipts add template, warnings (e.g. missing_asset) and assetFiles; an invalid template is refused with [invalid-template] and every problem's JSON path.",
|
|
61923
62156
|
inputSchema: {
|
|
61924
62157
|
id: noteIdInput.optional(),
|
|
61925
62158
|
folder: external_exports.string().min(1).max(MAX.FOLDER).optional().describe("Folder path to export instead of one note (nested paths use '/')"),
|
|
@@ -61934,12 +62167,13 @@ registerTool(
|
|
|
61934
62167
|
`Render through this template: built-in ${BUILTIN_TEMPLATE_NAMES.map((n) => `'${n}'`).join(" or ")}, or a saved template's name (list-markdown-templates). Exclusive with templateFile`
|
|
61935
62168
|
),
|
|
61936
62169
|
templateFile: exportPathInput(
|
|
61937
|
-
"JSON template file (.json) to render through (exclusive with template; at most 256 KiB)"
|
|
62170
|
+
"JSON template file (.json) to render through (exclusive with template; at most 256 KiB; hidden paths and ~/Library outside iCloud Drive and CloudStorage refused unless APPLE_NOTES_MCP_ALLOW_PRIVATE_CONTENT_PATHS=1)"
|
|
61938
62171
|
)
|
|
61939
62172
|
},
|
|
61940
62173
|
outputSchema: {
|
|
61941
62174
|
format: external_exports.string().optional(),
|
|
61942
62175
|
count: external_exports.number().optional(),
|
|
62176
|
+
truncated: external_exports.boolean().optional(),
|
|
61943
62177
|
bytes: external_exports.number().optional(),
|
|
61944
62178
|
markdown: external_exports.string().optional(),
|
|
61945
62179
|
output: external_exports.string().optional(),
|
|
@@ -61978,7 +62212,7 @@ registerTool(
|
|
|
61978
62212
|
);
|
|
61979
62213
|
}
|
|
61980
62214
|
const warned = receipt.warnings?.length ? `; ${receipt.warnings.length + (receipt.warningsOmitted ?? 0)} warning(s)` : "";
|
|
61981
|
-
const skipped = (receipt.skipped.length ? `; skipped ${receipt.skipped.length}` : "") + warned;
|
|
62215
|
+
const skipped = (receipt.skipped.length ? `; skipped ${receipt.skipped.length}` : "") + warned + (receipt.truncated ? "; the folder has more notes than limit, pass a higher limit" : "");
|
|
61982
62216
|
if (receipt.output)
|
|
61983
62217
|
return successResponse(
|
|
61984
62218
|
`Wrote ${receipt.count} note(s) as Markdown (${receipt.bytes} bytes) to ${receipt.output}` + (receipt.assets ? `; copied ${receipt.assets.files} asset file(s) to ${receipt.assets.dir}` : "") + `${skipped}.`,
|
|
@@ -62009,6 +62243,7 @@ registerTool(
|
|
|
62009
62243
|
outputSchema: {
|
|
62010
62244
|
format: external_exports.string().optional(),
|
|
62011
62245
|
count: external_exports.number().optional(),
|
|
62246
|
+
truncated: external_exports.boolean().optional(),
|
|
62012
62247
|
bytes: external_exports.number().optional(),
|
|
62013
62248
|
output: external_exports.string().optional(),
|
|
62014
62249
|
assets: external_exports.object({ dir: external_exports.string(), files: external_exports.number() }).optional(),
|
|
@@ -62031,7 +62266,7 @@ registerTool(
|
|
|
62031
62266
|
return errorResponse(`Error exporting HTML [${error2.code}]: ${error2.message}${hint}`, error2);
|
|
62032
62267
|
}
|
|
62033
62268
|
const assets = receipt.assets ? `; copied ${receipt.assets.files} asset file(s) to ${receipt.assets.dir}` : `; embedded ${receipt.embedded ?? 0} asset(s)`;
|
|
62034
|
-
const skipped = receipt.skipped.length ? `; skipped ${receipt.skipped.length}` : "";
|
|
62269
|
+
const skipped = (receipt.skipped.length ? `; skipped ${receipt.skipped.length}` : "") + (receipt.truncated ? "; the folder has more notes than limit, pass a higher limit" : "");
|
|
62035
62270
|
return successResponse(
|
|
62036
62271
|
`Wrote ${receipt.count} note(s) as HTML (${receipt.bytes} bytes) to ${receipt.output}${assets}${skipped}.`,
|
|
62037
62272
|
{ ...receipt }
|
|
@@ -62214,27 +62449,32 @@ registerTool(
|
|
|
62214
62449
|
drawingCount: external_exports.number().optional(),
|
|
62215
62450
|
status: external_exports.string().optional(),
|
|
62216
62451
|
drawings: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
62217
|
-
pointsOmitted: external_exports.boolean().optional()
|
|
62452
|
+
pointsOmitted: external_exports.boolean().optional(),
|
|
62453
|
+
svgOmitted: external_exports.boolean().optional()
|
|
62218
62454
|
}
|
|
62219
62455
|
},
|
|
62220
62456
|
withErrorHandling(({ id: id2, format, includePoints }) => {
|
|
62221
|
-
|
|
62222
|
-
|
|
62223
|
-
|
|
62224
|
-
|
|
62225
|
-
|
|
62226
|
-
|
|
62227
|
-
|
|
62457
|
+
const limit = exportMaxResponseBytes();
|
|
62458
|
+
const { result, pointsOmitted, svgOmitted, oversized } = fitNoteDrawings(
|
|
62459
|
+
getNoteDrawings(id2, { format, includePoints }),
|
|
62460
|
+
limit
|
|
62461
|
+
);
|
|
62462
|
+
if (oversized)
|
|
62463
|
+
return errorResponse(
|
|
62464
|
+
`The drawings in note "${id2}" are too large to return even without stroke points and SVG (limit ${limit} bytes, APPLE_NOTES_MCP_EXPORT_MAX_BYTES).`
|
|
62465
|
+
);
|
|
62466
|
+
const text2 = formatNoteDrawings(result) + (pointsOmitted ? "\nStroke points were omitted to stay under the response size limit (APPLE_NOTES_MCP_EXPORT_MAX_BYTES)." : "") + (svgOmitted ? "\nSVG documents were omitted to stay under the response size limit (APPLE_NOTES_MCP_EXPORT_MAX_BYTES)." : "");
|
|
62228
62467
|
return successResponse(text2, {
|
|
62229
62468
|
...result,
|
|
62230
|
-
...pointsOmitted ? { pointsOmitted } : {}
|
|
62469
|
+
...pointsOmitted ? { pointsOmitted } : {},
|
|
62470
|
+
...svgOmitted ? { svgOmitted } : {}
|
|
62231
62471
|
});
|
|
62232
62472
|
}, "Error reading drawings")
|
|
62233
62473
|
);
|
|
62234
62474
|
registerTool(
|
|
62235
62475
|
"transcribe-note-audio",
|
|
62236
62476
|
{
|
|
62237
|
-
description: "Use when: you need the words spoken in a note's voice recordings or audio attachments, transcribed now on this Mac, by note id.\nReturns: per audio attachment a status (ok / partial / error / indeterminate), duration, word count, per-take results, and the transcript; overall status ok / partial / error / indeterminate / none.\nDo not use when: you only need the audio file (save-attachment) or a note has no audio.\nNote: read-only; recognition runs entirely on-device (never sent to a server). Needs Full Disk Access and the public native helper built once with `apple-notes-mcp setup --public-helper`. Long recordings take time: pass attachmentId to transcribe one at a time. An indeterminate result means the helper timed out; retrying may succeed. Never prompts: code permission_required means the user must allow the host app under System Settings > Privacy & Security > Speech Recognition. asset_unavailable can mean the language's speech model is not installed; pass downloadAssets: true only if the user agrees to the download.",
|
|
62477
|
+
description: "Use when: you need the words spoken in a note's voice recordings or audio attachments, transcribed now on this Mac, by note id.\nReturns: per audio attachment a status (ok / partial / error / indeterminate), duration, word count, per-take results, and the transcript; overall status ok / partial / error / indeterminate / none.\nDo not use when: you only need the audio file (save-attachment) or a note has no audio.\nNote: read-only; recognition runs entirely on-device (never sent to a server). Needs Full Disk Access and the public native helper built once with `apple-notes-mcp setup --public-helper`. Long recordings take time: pass attachmentId to transcribe one at a time. An indeterminate result means the helper timed out; retrying may succeed. Never prompts: code permission_required means the user must allow the host app under System Settings > Privacy & Security > Speech Recognition; permission_not_requested (before macOS 26) means the host app has never asked for that access, so it is not listed there yet. asset_unavailable can mean the language's speech model is not installed; pass downloadAssets: true only if the user agrees to the download.",
|
|
62238
62478
|
inputSchema: {
|
|
62239
62479
|
id: noteIdInput,
|
|
62240
62480
|
locale: external_exports.string().max(35).optional().describe('BCP-47 language of the speech, e.g. "en-US" (default), "it-IT", "fr-FR"'),
|
|
@@ -62252,11 +62492,13 @@ registerTool(
|
|
|
62252
62492
|
locale: external_exports.string().optional(),
|
|
62253
62493
|
status: external_exports.string().optional(),
|
|
62254
62494
|
recordingCount: external_exports.number().optional(),
|
|
62255
|
-
recordings: external_exports.array(external_exports.object({}).passthrough()).optional()
|
|
62495
|
+
recordings: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
62496
|
+
responseOversized: external_exports.boolean().optional()
|
|
62256
62497
|
}
|
|
62257
62498
|
},
|
|
62258
62499
|
withAsyncErrorHandling(async (params, signal) => {
|
|
62259
62500
|
const { id: id2, locale, attachmentId, includeText, downloadAssets, maxSeconds } = params;
|
|
62501
|
+
const toResponse = (r) => successResponse(formatTranscription(r), r);
|
|
62260
62502
|
const result = fitTranscriptions(
|
|
62261
62503
|
await transcribeNoteAudio(id2, {
|
|
62262
62504
|
locale,
|
|
@@ -62266,12 +62508,10 @@ registerTool(
|
|
|
62266
62508
|
maxSeconds,
|
|
62267
62509
|
signal
|
|
62268
62510
|
}),
|
|
62269
|
-
exportMaxResponseBytes()
|
|
62270
|
-
|
|
62271
|
-
return successResponse(
|
|
62272
|
-
formatTranscription(result),
|
|
62273
|
-
result
|
|
62511
|
+
exportMaxResponseBytes(),
|
|
62512
|
+
(r) => Buffer.byteLength(JSON.stringify(toResponse(r)))
|
|
62274
62513
|
);
|
|
62514
|
+
return toResponse(result);
|
|
62275
62515
|
}, "Error transcribing audio")
|
|
62276
62516
|
);
|
|
62277
62517
|
registerTool(
|
|
@@ -62386,13 +62626,14 @@ process.on("uncaughtException", (err) => {
|
|
|
62386
62626
|
process.on("unhandledRejection", (reason) => {
|
|
62387
62627
|
console.error("[unhandledRejection]", reason);
|
|
62388
62628
|
});
|
|
62389
|
-
var
|
|
62629
|
+
var inFlight = new InFlightRequests();
|
|
62630
|
+
var shutdown = createShutdown(process.stdout, () => process.exit(0), void 0, inFlight);
|
|
62390
62631
|
for (const sig of ["SIGINT", "SIGTERM"]) {
|
|
62391
62632
|
process.on(sig, shutdown);
|
|
62392
62633
|
}
|
|
62393
62634
|
process.stdin.on("end", shutdown);
|
|
62394
62635
|
process.stdin.on("close", shutdown);
|
|
62395
|
-
var transport = withJsonSchema2020_12(new StdioServerTransport());
|
|
62636
|
+
var transport = trackRequests(withJsonSchema2020_12(new StdioServerTransport()), inFlight);
|
|
62396
62637
|
await server.connect(transport);
|
|
62397
62638
|
/*! Bundled license information:
|
|
62398
62639
|
|