apple-notes-mcp 2.15.4 → 2.15.5
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/build/index.js +241 -100
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -48713,33 +48713,11 @@ import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as
|
|
|
48713
48713
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
48714
48714
|
import { join as join17 } from "node:path";
|
|
48715
48715
|
|
|
48716
|
-
// src/
|
|
48717
|
-
function shortcutConsentHint(shortcut) {
|
|
48718
|
-
const target = shortcut ? `"${shortcut}"` : "the bridge Shortcut";
|
|
48719
|
-
return `This may be an unanswered first-run Shortcuts consent prompt, which a background run cannot display: run ${target} once in the foreground in Shortcuts.app and choose Always Allow.`;
|
|
48720
|
-
}
|
|
48721
|
-
|
|
48722
|
-
// src/services/nativeTags.ts
|
|
48723
|
-
var NATIVE_TAGS_SHORTCUT = "Apple Notes MCP - Native Tags";
|
|
48724
|
-
function normalizeNativeTags(tags) {
|
|
48725
|
-
if (!tags.length || tags.length > 100) throw new Error("Provide between 1 and 100 tags");
|
|
48726
|
-
return [
|
|
48727
|
-
...new Set(
|
|
48728
|
-
tags.map((value) => {
|
|
48729
|
-
const tag = value.normalize("NFC").replace(/^#/, "");
|
|
48730
|
-
if (tag.length > 100 || !new RegExp("^(?=.*\\p{L})[\\p{L}\\p{N}_-]+$", "u").test(tag))
|
|
48731
|
-
throw new Error(
|
|
48732
|
-
"Tags must contain a letter and only letters, digits, hyphens or underscores"
|
|
48733
|
-
);
|
|
48734
|
-
return tag;
|
|
48735
|
-
})
|
|
48736
|
-
)
|
|
48737
|
-
];
|
|
48738
|
-
}
|
|
48716
|
+
// src/utils/richContentPreservation.ts
|
|
48739
48717
|
var TAG_OBJECT_TYPE = "com.apple.notes.inlinetextattachment.hashtag";
|
|
48740
48718
|
function requirePreservationMetadata(rich) {
|
|
48741
|
-
const unavailable = () => {
|
|
48742
|
-
throw new Error(
|
|
48719
|
+
const unavailable = (message = "Native object, checklist or formatting preservation metadata is unavailable") => {
|
|
48720
|
+
throw new Error(message);
|
|
48743
48721
|
};
|
|
48744
48722
|
const { objects, objectData, checklistItems, styleRuns, nativeTagObjectIds } = rich;
|
|
48745
48723
|
if (!objects || !objectData || !checklistItems || !styleRuns || !nativeTagObjectIds || rich.nativeObjectDataComplete !== true)
|
|
@@ -48774,16 +48752,173 @@ function requirePreservationMetadata(rich) {
|
|
|
48774
48752
|
return unavailable();
|
|
48775
48753
|
}
|
|
48776
48754
|
let position = 0;
|
|
48755
|
+
const firstReferences = /* @__PURE__ */ new Map();
|
|
48777
48756
|
for (const run of styleRuns) {
|
|
48778
48757
|
if (run.start !== position || !Number.isInteger(run.length) || run.length < 0 || typeof run.signature !== "string" || run.nativeSemantics?.complete !== true || run.length === 0 && (run.nativeSemantics.unknown || run.nativeSemantics.structuredParagraph || run.nativeSemantics.links || run.nativeSemantics.objects.length > 0))
|
|
48779
48758
|
return unavailable();
|
|
48759
|
+
const references = run.nativeSemantics.objects;
|
|
48760
|
+
if (references.length > 1 || rich.text.slice(run.start, run.start + run.length).includes("\uFFFC") && references.length !== 1)
|
|
48761
|
+
return unavailable("Native object placement preservation metadata is unavailable");
|
|
48762
|
+
for (const reference of references) {
|
|
48763
|
+
if (!objectData.some((data) => data.id === reference.id && data.type === reference.type))
|
|
48764
|
+
return unavailable("Native object placement preservation metadata is unavailable");
|
|
48765
|
+
if (!firstReferences.has(reference.id))
|
|
48766
|
+
firstReferences.set(reference.id, {
|
|
48767
|
+
start: run.start,
|
|
48768
|
+
length: run.length,
|
|
48769
|
+
type: reference.type
|
|
48770
|
+
});
|
|
48771
|
+
}
|
|
48780
48772
|
position += run.length;
|
|
48781
48773
|
}
|
|
48782
48774
|
if (position !== rich.text.length) return unavailable();
|
|
48775
|
+
if (objects.some((object3) => {
|
|
48776
|
+
const reference = firstReferences.get(object3.id);
|
|
48777
|
+
return !reference || reference.start !== object3.start || reference.length !== object3.length || reference.type !== object3.type;
|
|
48778
|
+
}))
|
|
48779
|
+
return unavailable("Native object placement preservation metadata is unavailable");
|
|
48783
48780
|
for (const link of rich.links)
|
|
48784
48781
|
if (!Number.isInteger(link.start) || !Number.isInteger(link.length) || link.start < 0 || link.length < 1 || link.start + link.length > rich.text.length || rich.text.slice(link.start, link.start + link.length) !== link.text)
|
|
48785
48782
|
return unavailable();
|
|
48786
48783
|
}
|
|
48784
|
+
function assertZeroLengthFormatting(before, after, spans) {
|
|
48785
|
+
const oldEmpty = before.styleRuns.filter((run) => run.length === 0).map((run) => {
|
|
48786
|
+
const span = spans.find(
|
|
48787
|
+
(span2) => run.start >= span2.before && run.start <= span2.before + span2.length
|
|
48788
|
+
);
|
|
48789
|
+
if (!span) throw new Error("Zero-length formatting falls inside the requested removal");
|
|
48790
|
+
return { ...run, start: span.after + run.start - span.before };
|
|
48791
|
+
});
|
|
48792
|
+
const newEmpty = after.styleRuns.filter(
|
|
48793
|
+
(run) => run.length === 0 && spans.some((span) => run.start >= span.after && run.start <= span.after + span.length)
|
|
48794
|
+
);
|
|
48795
|
+
if (JSON.stringify(oldEmpty) !== JSON.stringify(newEmpty))
|
|
48796
|
+
throw new Error("Existing zero-length formatting metadata changed");
|
|
48797
|
+
}
|
|
48798
|
+
function retainedSpans(before, after, change) {
|
|
48799
|
+
if (change.kind !== "remove-tag") {
|
|
48800
|
+
if (change.kind === "append" ? !after.text.startsWith(before.text) : after.text !== before.text)
|
|
48801
|
+
throw new Error("Existing note text or whitespace was not preserved");
|
|
48802
|
+
return {
|
|
48803
|
+
spans: [{ before: 0, after: 0, length: before.text.length }],
|
|
48804
|
+
removed: /* @__PURE__ */ new Set()
|
|
48805
|
+
};
|
|
48806
|
+
}
|
|
48807
|
+
const removed = new Set(before.nativeTagObjectIds[change.tag]);
|
|
48808
|
+
if (!removed.size || after.nativeTags.includes(change.tag))
|
|
48809
|
+
throw new Error("Native tag removal was not verified");
|
|
48810
|
+
const spans = [];
|
|
48811
|
+
let position = 0, output = "";
|
|
48812
|
+
for (const object3 of before.objects.filter((object4) => removed.has(object4.id))) {
|
|
48813
|
+
const text2 = before.text.slice(object3.start, object3.start + object3.length);
|
|
48814
|
+
if (text2 !== "\uFFFC" && text2 !== `#${change.tag}`)
|
|
48815
|
+
throw new Error("Native tag removal range is unsupported");
|
|
48816
|
+
const prefix = before.text.slice(position, object3.start);
|
|
48817
|
+
spans.push({ before: position, after: output.length, length: prefix.length });
|
|
48818
|
+
output += prefix;
|
|
48819
|
+
position = object3.start + object3.length;
|
|
48820
|
+
}
|
|
48821
|
+
spans.push({ before: position, after: output.length, length: before.text.length - position });
|
|
48822
|
+
output += before.text.slice(position);
|
|
48823
|
+
if (after.text !== output)
|
|
48824
|
+
throw new Error("Existing note text or whitespace was not preserved around the removed tag");
|
|
48825
|
+
return { spans, removed };
|
|
48826
|
+
}
|
|
48827
|
+
function assertRetainedRichContent(before, after, change = { kind: "unchanged" }) {
|
|
48828
|
+
requirePreservationMetadata(before);
|
|
48829
|
+
requirePreservationMetadata(after);
|
|
48830
|
+
const { spans, removed } = retainedSpans(before, after, change);
|
|
48831
|
+
const rangeStart = (start, length) => {
|
|
48832
|
+
const span = spans.find(
|
|
48833
|
+
(span2) => start >= span2.before && start + length <= span2.before + span2.length
|
|
48834
|
+
);
|
|
48835
|
+
return span && span.after + start - span.before;
|
|
48836
|
+
};
|
|
48837
|
+
const retainedIds = before.nativeObjectIds.filter((id2) => !removed.has(id2));
|
|
48838
|
+
if (retainedIds.some((id2) => !after.nativeObjectIds.includes(id2)) || [...removed].some((id2) => after.nativeObjectIds.includes(id2)) || change.kind !== "append" && after.nativeObjectIds.length !== retainedIds.length || change.kind === "append" && after.objects.some(
|
|
48839
|
+
(object3) => !retainedIds.includes(object3.id) && object3.start < before.text.length
|
|
48840
|
+
))
|
|
48841
|
+
throw new Error("Existing native object identity or placement changed");
|
|
48842
|
+
for (const object3 of before.objects.filter((object4) => !removed.has(object4.id))) {
|
|
48843
|
+
const actual = after.objects.find((current) => current.id === object3.id);
|
|
48844
|
+
if (actual.type !== object3.type || actual.length !== object3.length || actual.start !== rangeStart(object3.start, object3.length))
|
|
48845
|
+
throw new Error("Existing native object range changed");
|
|
48846
|
+
const oldData = before.objectData.find((row) => row.id === object3.id), newData = after.objectData.find((row) => row.id === object3.id);
|
|
48847
|
+
if (oldData.pk !== newData.pk || oldData.type !== newData.type || oldData.mergeable !== newData.mergeable || oldData.view !== newData.view || oldData.altText !== newData.altText)
|
|
48848
|
+
throw new Error("Existing native object payload changed");
|
|
48849
|
+
}
|
|
48850
|
+
const tags = before.nativeTags.filter(
|
|
48851
|
+
(tag) => change.kind !== "remove-tag" || tag !== change.tag
|
|
48852
|
+
);
|
|
48853
|
+
if (tags.some(
|
|
48854
|
+
(tag) => JSON.stringify(before.nativeTagObjectIds[tag]) !== JSON.stringify(after.nativeTagObjectIds[tag])
|
|
48855
|
+
) || change.kind !== "append" && after.nativeTags.length !== tags.length)
|
|
48856
|
+
throw new Error("Existing native tag identities changed");
|
|
48857
|
+
const links = change.kind === "append" ? after.links.filter((link) => link.start < before.text.length) : after.links;
|
|
48858
|
+
if (links.length !== before.links.length || before.links.some((link, index) => {
|
|
48859
|
+
const actual = links[index];
|
|
48860
|
+
return actual.text !== link.text || actual.url !== link.url || actual.length !== link.length || actual.start !== rangeStart(link.start, link.length);
|
|
48861
|
+
}))
|
|
48862
|
+
throw new Error("Existing links or their ranges were not preserved");
|
|
48863
|
+
const items = change.kind === "append" ? after.checklistItems.filter((item) => item.start < before.text.length) : after.checklistItems;
|
|
48864
|
+
if (items.length !== before.checklistItems.length || before.checklistItems.some((item, index) => {
|
|
48865
|
+
const actual = items[index];
|
|
48866
|
+
const retained = spans.flatMap((span) => {
|
|
48867
|
+
const start2 = Math.max(item.start, span.before), end = Math.min(item.start + item.text.length, span.before + span.length);
|
|
48868
|
+
return start2 < end ? [before.text.slice(start2, end)] : [];
|
|
48869
|
+
}).join("");
|
|
48870
|
+
const startSpan = spans.find(
|
|
48871
|
+
(span) => item.start >= span.before && item.start <= span.before + span.length
|
|
48872
|
+
);
|
|
48873
|
+
const start = startSpan && startSpan.after + item.start - startSpan.before;
|
|
48874
|
+
return actual.id !== item.id || actual.text !== retained || actual.done !== item.done || actual.start !== start;
|
|
48875
|
+
}))
|
|
48876
|
+
throw new Error("Existing checklist item identity, state or range changed");
|
|
48877
|
+
for (const span of spans) {
|
|
48878
|
+
let offset = 0, oldIndex = 0, newIndex = 0;
|
|
48879
|
+
while (offset < span.length) {
|
|
48880
|
+
const oldPosition = span.before + offset, newPosition = span.after + offset;
|
|
48881
|
+
while (before.styleRuns[oldIndex].start + before.styleRuns[oldIndex].length <= oldPosition)
|
|
48882
|
+
oldIndex++;
|
|
48883
|
+
while (after.styleRuns[newIndex].start + after.styleRuns[newIndex].length <= newPosition)
|
|
48884
|
+
newIndex++;
|
|
48885
|
+
const oldRun = before.styleRuns[oldIndex], newRun = after.styleRuns[newIndex];
|
|
48886
|
+
if (oldRun.signature !== newRun.signature || oldRun.paragraphStyle !== newRun.paragraphStyle || oldRun.blockQuote !== newRun.blockQuote || oldRun.highlight !== newRun.highlight)
|
|
48887
|
+
throw new Error("Existing rich formatting changed");
|
|
48888
|
+
offset += Math.min(
|
|
48889
|
+
span.length - offset,
|
|
48890
|
+
oldRun.start + oldRun.length - oldPosition,
|
|
48891
|
+
newRun.start + newRun.length - newPosition
|
|
48892
|
+
);
|
|
48893
|
+
}
|
|
48894
|
+
}
|
|
48895
|
+
assertZeroLengthFormatting(before, after, spans);
|
|
48896
|
+
}
|
|
48897
|
+
|
|
48898
|
+
// src/services/shortcutConsent.ts
|
|
48899
|
+
function shortcutConsentHint(shortcut) {
|
|
48900
|
+
const target = shortcut ? `"${shortcut}"` : "the bridge Shortcut";
|
|
48901
|
+
return `This may be an unanswered first-run Shortcuts consent prompt, which a background run cannot display: run ${target} once in the foreground in Shortcuts.app and choose Always Allow.`;
|
|
48902
|
+
}
|
|
48903
|
+
|
|
48904
|
+
// src/services/nativeTags.ts
|
|
48905
|
+
var NATIVE_TAGS_SHORTCUT = "Apple Notes MCP - Native Tags";
|
|
48906
|
+
function normalizeNativeTags(tags) {
|
|
48907
|
+
if (!tags.length || tags.length > 100) throw new Error("Provide between 1 and 100 tags");
|
|
48908
|
+
return [
|
|
48909
|
+
...new Set(
|
|
48910
|
+
tags.map((value) => {
|
|
48911
|
+
const tag = value.normalize("NFC").replace(/^#/, "");
|
|
48912
|
+
if (tag.length > 100 || !new RegExp("^(?=.*\\p{L})[\\p{L}\\p{N}_-]+$", "u").test(tag))
|
|
48913
|
+
throw new Error(
|
|
48914
|
+
"Tags must contain a letter and only letters, digits, hyphens or underscores"
|
|
48915
|
+
);
|
|
48916
|
+
return tag;
|
|
48917
|
+
})
|
|
48918
|
+
)
|
|
48919
|
+
];
|
|
48920
|
+
}
|
|
48921
|
+
var TAG_OBJECT_TYPE2 = "com.apple.notes.inlinetextattachment.hashtag";
|
|
48787
48922
|
function plainNativeRange(rich, start, length) {
|
|
48788
48923
|
return rich.styleRuns.every((run) => {
|
|
48789
48924
|
if (run.start >= start + length || run.start + run.length <= start) return true;
|
|
@@ -48799,7 +48934,7 @@ function preservedTextSpans(before, after, missing) {
|
|
|
48799
48934
|
([tag, ids]) => ids.map((id2) => [id2, tag])
|
|
48800
48935
|
)
|
|
48801
48936
|
);
|
|
48802
|
-
if (!added.length || added.some((o) => o.type !==
|
|
48937
|
+
if (!added.length || added.some((o) => o.type !== TAG_OBJECT_TYPE2 || !missing.includes(tagsById.get(o.id))) || missing.some((tag) => !added.some((o) => tagsById.get(o.id) === tag)))
|
|
48803
48938
|
return void 0;
|
|
48804
48939
|
const spans = [];
|
|
48805
48940
|
let oldPosition = 0, newPosition = 0, appended = false;
|
|
@@ -48856,6 +48991,11 @@ function preservesAddedSemantics(before, after, spans) {
|
|
|
48856
48991
|
return true;
|
|
48857
48992
|
}
|
|
48858
48993
|
function preservesNativeContent(before, after, spans) {
|
|
48994
|
+
try {
|
|
48995
|
+
assertZeroLengthFormatting(before, after, spans);
|
|
48996
|
+
} catch {
|
|
48997
|
+
return false;
|
|
48998
|
+
}
|
|
48859
48999
|
const rangeStart = (start, length) => {
|
|
48860
49000
|
const span = spans.find((s) => start >= s.before && start + length <= s.before + s.length);
|
|
48861
49001
|
return span && span.after + start - span.before;
|
|
@@ -48906,6 +49046,16 @@ function preservesNativeContent(before, after, spans) {
|
|
|
48906
49046
|
}
|
|
48907
49047
|
return true;
|
|
48908
49048
|
}
|
|
49049
|
+
function nativeTagAdditionPreserved(before, after, tags) {
|
|
49050
|
+
requirePreservationMetadata(before);
|
|
49051
|
+
requirePreservationMetadata(after);
|
|
49052
|
+
const missing = tags.filter((tag) => !before.nativeTags.includes(tag));
|
|
49053
|
+
const allTags = [.../* @__PURE__ */ new Set([...before.nativeTags, ...tags])];
|
|
49054
|
+
const spans = preservedTextSpans(before, after, missing);
|
|
49055
|
+
return after.nativeTags.length === allTags.length && allTags.every((tag) => after.nativeTags.includes(tag)) && before.nativeObjectIds.every((id2) => after.nativeObjectIds.includes(id2)) && before.hasChecklist === after.hasChecklist && Boolean(
|
|
49056
|
+
spans && preservesAddedSemantics(before, after, spans) && preservesNativeContent(before, after, spans)
|
|
49057
|
+
);
|
|
49058
|
+
}
|
|
48909
49059
|
function addNativeTags(request, deps) {
|
|
48910
49060
|
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(request.id))
|
|
48911
49061
|
throw new Error("An exact CoreData note ID is required");
|
|
@@ -48947,9 +49097,7 @@ function addNativeTags(request, deps) {
|
|
|
48947
49097
|
} catch (error2) {
|
|
48948
49098
|
throw verificationFailure(error2 instanceof Error ? error2.message : String(error2));
|
|
48949
49099
|
}
|
|
48950
|
-
|
|
48951
|
-
const spans = preservedTextSpans(before.rich, after.rich, missing);
|
|
48952
|
-
if (after.title !== before.title || after.rich.nativeTags.length !== allTags.length || allTags.some((tag) => !after.rich.nativeTags.includes(tag)) || before.rich.nativeObjectIds.some((id2) => !after.rich.nativeObjectIds.includes(id2)) || before.rich.hasChecklist !== after.rich.hasChecklist || !spans || !preservesAddedSemantics(before.rich, after.rich, spans) || !preservesNativeContent(before.rich, after.rich, spans)) {
|
|
49100
|
+
if (after.title !== before.title || !nativeTagAdditionPreserved(before.rich, after.rich, tags)) {
|
|
48953
49101
|
throw verificationFailure();
|
|
48954
49102
|
}
|
|
48955
49103
|
return {
|
|
@@ -49389,53 +49537,25 @@ function backgroundDependencies(manager) {
|
|
|
49389
49537
|
function assertPreserved(before, after, options = {}) {
|
|
49390
49538
|
if (before.id !== after.id || before.title !== after.title)
|
|
49391
49539
|
throw new Error("Note identity changed");
|
|
49392
|
-
|
|
49393
|
-
|
|
49394
|
-
|
|
49395
|
-
|
|
49396
|
-
|
|
49397
|
-
|
|
49398
|
-
|
|
49399
|
-
|
|
49400
|
-
|
|
49401
|
-
|
|
49402
|
-
|
|
49403
|
-
|
|
49404
|
-
|
|
49405
|
-
|
|
49406
|
-
|
|
49407
|
-
if (JSON.stringify(before.checklist) !== JSON.stringify(
|
|
49408
|
-
options.append ? after.checklist.slice(0, before.checklist.length) : after.checklist
|
|
49409
|
-
))
|
|
49410
|
-
throw new Error("Existing checklist items changed");
|
|
49411
|
-
for (const item of before.rich.checklistItems || []) {
|
|
49412
|
-
const actual = after.rich.checklistItems?.find((current) => current.id === item.id);
|
|
49413
|
-
if (!actual || actual.text !== item.text || actual.done !== item.done)
|
|
49414
|
-
throw new Error("Existing checklist item identity or state changed");
|
|
49415
|
-
}
|
|
49416
|
-
if (before.rich.nativeTags.filter((t) => t !== options.tagChange).some((t) => !after.rich.nativeTags.includes(t)))
|
|
49417
|
-
throw new Error("Existing native tag was lost");
|
|
49418
|
-
const allowedRemoved = options.tagChange ? before.rich.nativeTagObjectIds?.[options.tagChange] || [] : [];
|
|
49419
|
-
for (const object3 of before.rich.objectData || []) {
|
|
49420
|
-
if (allowedRemoved.includes(object3.id)) continue;
|
|
49421
|
-
const actual = after.rich.objectData?.find((o) => o.id === object3.id);
|
|
49422
|
-
if (!actual || actual.mergeable !== object3.mergeable || actual.view !== object3.view)
|
|
49423
|
-
throw new Error("Existing native object content or presentation changed");
|
|
49424
|
-
}
|
|
49425
|
-
if (!options.tagChange && before.rich.styleRuns && after.rich.styleRuns) {
|
|
49426
|
-
const end = before.rich.text.trimEnd().length;
|
|
49427
|
-
for (const oldRun of before.rich.styleRuns)
|
|
49428
|
-
for (const newRun of after.rich.styleRuns) {
|
|
49429
|
-
const overlapStart = Math.max(oldRun.start, newRun.start);
|
|
49430
|
-
const overlapEnd = Math.min(
|
|
49431
|
-
end,
|
|
49432
|
-
oldRun.start + oldRun.length,
|
|
49433
|
-
newRun.start + newRun.length
|
|
49434
|
-
);
|
|
49435
|
-
if (overlapStart < overlapEnd && /[^\s\ufffc]/u.test(before.rich.text.slice(overlapStart, overlapEnd)) && oldRun.signature !== newRun.signature)
|
|
49436
|
-
throw new Error("Existing rich formatting changed");
|
|
49437
|
-
}
|
|
49540
|
+
if (options.tagChange && !options.removeTag) {
|
|
49541
|
+
if (!nativeTagAdditionPreserved(before.rich, after.rich, [options.tagChange]))
|
|
49542
|
+
throw new Error("Native tag addition or retained content was not verified");
|
|
49543
|
+
} else {
|
|
49544
|
+
try {
|
|
49545
|
+
assertRetainedRichContent(
|
|
49546
|
+
before.rich,
|
|
49547
|
+
after.rich,
|
|
49548
|
+
options.tagChange ? { kind: "remove-tag", tag: options.tagChange } : options.append ? { kind: "append" } : { kind: "unchanged" }
|
|
49549
|
+
);
|
|
49550
|
+
} catch (error2) {
|
|
49551
|
+
if (error2 instanceof Error && error2.message === "Existing note text or whitespace was not preserved")
|
|
49552
|
+
throw new Error("Existing note text was not preserved", { cause: error2 });
|
|
49553
|
+
throw error2;
|
|
49554
|
+
}
|
|
49438
49555
|
}
|
|
49556
|
+
for (const snapshot of [before, after])
|
|
49557
|
+
if (JSON.stringify(snapshot.checklist) !== JSON.stringify(snapshot.rich.checklistItems.map(({ text: text2, done }) => ({ text: text2, done }))))
|
|
49558
|
+
throw new Error("Native checklist readers disagree about item text or state");
|
|
49439
49559
|
}
|
|
49440
49560
|
function describeTransportFailure(error2) {
|
|
49441
49561
|
const detail = error2;
|
|
@@ -49449,12 +49569,37 @@ function textBelowTitle(text2, title) {
|
|
|
49449
49569
|
if (firstLine.trim() !== title.trim()) return text2;
|
|
49450
49570
|
return lineEnd < 0 ? "" : text2.slice(lineEnd + 1);
|
|
49451
49571
|
}
|
|
49572
|
+
function assertChecklistEndAppendAvailable(rich) {
|
|
49573
|
+
requirePreservationMetadata(rich);
|
|
49574
|
+
const items = [...rich.checklistItems].sort((a, b) => a.start - b.start);
|
|
49575
|
+
if (!items.length) return;
|
|
49576
|
+
const last = items.at(-1);
|
|
49577
|
+
if (items.some(
|
|
49578
|
+
(item, index) => item.start > 0 && rich.text[item.start - 1] !== "\n" || index > 0 && item.start !== items[index - 1].start + items[index - 1].text.length + 1
|
|
49579
|
+
) || !/^\n?$/u.test(rich.text.slice(last.start + last.text.length)))
|
|
49580
|
+
throw new CodedError(
|
|
49581
|
+
"Checklist end-append requires one existing checklist group at the end of the note. Notes may insert into an earlier group when text or native objects follow it, or when groups are separate. Nothing changed",
|
|
49582
|
+
{ code: "validation_error", committed: false, indeterminate: false }
|
|
49583
|
+
);
|
|
49584
|
+
}
|
|
49585
|
+
function requireBackgroundPreflightMetadata(rich) {
|
|
49586
|
+
try {
|
|
49587
|
+
requirePreservationMetadata(rich);
|
|
49588
|
+
} catch (error2) {
|
|
49589
|
+
throw new CodedError(error2 instanceof Error ? error2.message : String(error2), {
|
|
49590
|
+
code: "validation_error",
|
|
49591
|
+
committed: false,
|
|
49592
|
+
indeterminate: false
|
|
49593
|
+
});
|
|
49594
|
+
}
|
|
49595
|
+
}
|
|
49452
49596
|
function mutateBackground(request, operation, data, verify, deps) {
|
|
49453
49597
|
if (request.scopeText.length < 12 || request.scopeText.length > 500 || /[\r\n\0]/u.test(request.scopeText))
|
|
49454
49598
|
throw new Error("Use a distinctive existing single-line scope of 12\u2013500 characters");
|
|
49455
49599
|
const before = deps.read(request.id);
|
|
49456
49600
|
if (before.hash !== request.expectedContentHash)
|
|
49457
49601
|
throw new Error("Note revision changed; read it again");
|
|
49602
|
+
requireBackgroundPreflightMetadata(before.rich);
|
|
49458
49603
|
if (!before.rich.text.includes(request.scopeText))
|
|
49459
49604
|
throw new Error("Scope is absent from exact note");
|
|
49460
49605
|
if (!textBelowTitle(before.rich.text, before.title).includes(request.scopeText))
|
|
@@ -49465,8 +49610,10 @@ function mutateBackground(request, operation, data, verify, deps) {
|
|
|
49465
49610
|
const candidates = deps.candidates(before.title, request.scopeText);
|
|
49466
49611
|
if (candidates.length !== 1 || candidates[0] !== request.id)
|
|
49467
49612
|
throw new Error("Ambiguous note selection; nothing changed");
|
|
49468
|
-
|
|
49469
|
-
|
|
49613
|
+
const current = deps.read(request.id);
|
|
49614
|
+
if (current.hash !== before.hash) throw new Error("Note revision changed during preflight");
|
|
49615
|
+
requireBackgroundPreflightMetadata(current.rich);
|
|
49616
|
+
if (operation === "create-checklist-item") assertChecklistEndAppendAvailable(current.rich);
|
|
49470
49617
|
let transportUncertain = false;
|
|
49471
49618
|
let transportMessage = "";
|
|
49472
49619
|
let transportError;
|
|
@@ -49570,7 +49717,7 @@ function setNativeTag(manager, request) {
|
|
|
49570
49717
|
request.present ? "add-tag" : "remove-tag",
|
|
49571
49718
|
{ tag },
|
|
49572
49719
|
(before, after) => {
|
|
49573
|
-
assertPreserved(before, after, { tagChange: tag });
|
|
49720
|
+
assertPreserved(before, after, { tagChange: tag, removeTag: !request.present });
|
|
49574
49721
|
if (after.rich.nativeTags.includes(tag) !== request.present)
|
|
49575
49722
|
throw new Error("Native tag state not verified");
|
|
49576
49723
|
const removed = before.rich.nativeObjectIds.filter(
|
|
@@ -55509,30 +55656,13 @@ function readSnapshot(manager, id2) {
|
|
|
55509
55656
|
const rich = readRichNote(id2);
|
|
55510
55657
|
if (enriched.revision !== rich.revision)
|
|
55511
55658
|
throw new Error("Native metadata changed during read; read the note again");
|
|
55659
|
+
requirePreservationMetadata(rich);
|
|
55512
55660
|
return { id: id2, title: note.title, body, rich, hash: richContentHash(body, enriched) };
|
|
55513
55661
|
}
|
|
55514
55662
|
function assertExistingContentPreserved(before, after) {
|
|
55515
55663
|
if (before.id !== after.id || before.title !== after.title)
|
|
55516
55664
|
throw new Error("Note identity changed");
|
|
55517
|
-
|
|
55518
|
-
if (!tidy(after.rich.text).startsWith(tidy(before.rich.text)))
|
|
55519
|
-
throw new Error("Existing note text was not preserved");
|
|
55520
|
-
if (linkSignature(before.rich.links) !== linkSignature(after.rich.links.slice(0, before.rich.links.length)))
|
|
55521
|
-
throw new Error("Existing links were not preserved");
|
|
55522
|
-
if (before.rich.nativeObjectIds.some((id2) => !after.rich.nativeObjectIds.includes(id2)))
|
|
55523
|
-
throw new Error("Existing native object was lost");
|
|
55524
|
-
if (before.rich.nativeTags.some((tag) => !after.rich.nativeTags.includes(tag)))
|
|
55525
|
-
throw new Error("Existing native tag was lost");
|
|
55526
|
-
for (const item of before.rich.checklistItems || []) {
|
|
55527
|
-
const actual = after.rich.checklistItems?.find((current) => current.id === item.id);
|
|
55528
|
-
if (!actual || actual.text !== item.text || actual.done !== item.done)
|
|
55529
|
-
throw new Error("Existing checklist item identity or state changed");
|
|
55530
|
-
}
|
|
55531
|
-
for (const object3 of before.rich.objectData || []) {
|
|
55532
|
-
const actual = after.rich.objectData?.find((current) => current.id === object3.id);
|
|
55533
|
-
if (!actual || actual.mergeable !== object3.mergeable || actual.view !== object3.view)
|
|
55534
|
-
throw new Error("Existing native object content or presentation changed");
|
|
55535
|
-
}
|
|
55665
|
+
assertRetainedRichContent(before.rich, after.rich, { kind: "append" });
|
|
55536
55666
|
}
|
|
55537
55667
|
function localAttachment(path10) {
|
|
55538
55668
|
if (!isAbsolute4(path10)) throw new Error("An absolute local file path is required");
|
|
@@ -55909,8 +56039,19 @@ function attachFile(manager, args, checked, progress = { insertionStarted: false
|
|
|
55909
56039
|
transportWarning: "Transport was uncertain; exact bytes and prior content were verified"
|
|
55910
56040
|
} : {}
|
|
55911
56041
|
};
|
|
56042
|
+
} catch (error2) {
|
|
56043
|
+
if (progress.insertionStarted)
|
|
56044
|
+
throw new CodedError(
|
|
56045
|
+
`Attachment outcome uncertain; read the exact note before any retry: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
56046
|
+
{ code: "verification_failed", indeterminate: true }
|
|
56047
|
+
);
|
|
56048
|
+
throw error2;
|
|
55912
56049
|
} finally {
|
|
55913
|
-
|
|
56050
|
+
try {
|
|
56051
|
+
rmSync7(directory, { recursive: true, force: true });
|
|
56052
|
+
} catch {
|
|
56053
|
+
console.error("Could not remove private temporary attachment files");
|
|
56054
|
+
}
|
|
55914
56055
|
}
|
|
55915
56056
|
}
|
|
55916
56057
|
|
|
@@ -59994,7 +60135,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
59994
60135
|
);
|
|
59995
60136
|
tool(
|
|
59996
60137
|
"create-checklist-item",
|
|
59997
|
-
"Use when: appending one real unchecked Notes checklist item.\nReturns: the created checklist identity after exact-note readback.\nDo not use when: plain text is sufficient or more than one item is requested.\nSafety: preserves existing text, links, native objects, checklist identities, and formatting.",
|
|
60138
|
+
"Use when: appending one real unchecked Notes checklist item.\nReturns: the created checklist identity after exact-note readback.\nDo not use when: plain text is sufficient or more than one item is requested.\nSafety: preserves existing text, links, native objects, checklist identities, and formatting. If a checklist already exists, requires one contiguous group at the end of the note; otherwise refuses before running the bridge.",
|
|
59998
60139
|
{
|
|
59999
60140
|
...common,
|
|
60000
60141
|
text: external_exports.string().min(1).max(1e4).refine((s) => !/[\r\n\0]/u.test(s), "One line per checklist item")
|
|
@@ -60018,7 +60159,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
60018
60159
|
);
|
|
60019
60160
|
tool(
|
|
60020
60161
|
"create-checklist-items",
|
|
60021
|
-
"Use when: appending several real unchecked Notes checklist items to one note, in order.\nReturns: each landed item's native identity and text, the final order check, and the new revision; on a stop, which items landed, the item whose outcome is uncertain, and the items not attempted.\nDo not use when: one item is enough (create-checklist-item) or plain text is sufficient.\nSafety: runs the verified single-item bridge once per item, chaining each verified revision into the next;
|
|
60162
|
+
"Use when: appending several real unchecked Notes checklist items to one note, in order.\nReturns: each landed item's native identity and text, the final order check, and the new revision; on a stop, which items landed, the item whose outcome is uncertain, and the items not attempted.\nDo not use when: one item is enough (create-checklist-item) or plain text is sufficient.\nSafety: runs the verified single-item bridge once per item, chaining each verified revision into the next; requires any existing checklist to be one contiguous group at the end of the note before dispatch. Checks after every item that exactly one new unchecked item with that text and a new identity appeared and that earlier items kept theirs, and stops at the first uncertain result without retrying. Each item takes a few seconds, so a full batch can run about a minute; if the call times out on the client side, read the note before retrying and send only items that are not present.",
|
|
60022
60163
|
{
|
|
60023
60164
|
...common,
|
|
60024
60165
|
items: external_exports.array(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apple-notes-mcp",
|
|
3
|
-
"version": "2.15.
|
|
3
|
+
"version": "2.15.5",
|
|
4
4
|
"description": "MCP server for Apple Notes - read, search, create, edit, organize, and export notes on macOS via Claude and other AI assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|