apple-notes-mcp 2.8.8 → 2.8.10
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
CHANGED
|
@@ -2242,10 +2242,10 @@ var require_resolve = __commonJS({
|
|
|
2242
2242
|
}
|
|
2243
2243
|
return count;
|
|
2244
2244
|
}
|
|
2245
|
-
function getFullPath(resolver,
|
|
2245
|
+
function getFullPath(resolver, id2 = "", normalize) {
|
|
2246
2246
|
if (normalize !== false)
|
|
2247
|
-
|
|
2248
|
-
const p = resolver.parse(
|
|
2247
|
+
id2 = normalizeId(id2);
|
|
2248
|
+
const p = resolver.parse(id2);
|
|
2249
2249
|
return _getFullPath(resolver, p);
|
|
2250
2250
|
}
|
|
2251
2251
|
exports.getFullPath = getFullPath;
|
|
@@ -2255,13 +2255,13 @@ var require_resolve = __commonJS({
|
|
|
2255
2255
|
}
|
|
2256
2256
|
exports._getFullPath = _getFullPath;
|
|
2257
2257
|
var TRAILING_SLASH_HASH = /#\/?$/;
|
|
2258
|
-
function normalizeId(
|
|
2259
|
-
return
|
|
2258
|
+
function normalizeId(id2) {
|
|
2259
|
+
return id2 ? id2.replace(TRAILING_SLASH_HASH, "") : "";
|
|
2260
2260
|
}
|
|
2261
2261
|
exports.normalizeId = normalizeId;
|
|
2262
|
-
function resolveUrl(resolver, baseId,
|
|
2263
|
-
|
|
2264
|
-
return resolver.resolve(baseId,
|
|
2262
|
+
function resolveUrl(resolver, baseId, id2) {
|
|
2263
|
+
id2 = normalizeId(id2);
|
|
2264
|
+
return resolver.resolve(baseId, id2);
|
|
2265
2265
|
}
|
|
2266
2266
|
exports.resolveUrl = resolveUrl;
|
|
2267
2267
|
var ANCHOR = /^[a-z_][-a-z0-9._]*$/i;
|
|
@@ -2991,7 +2991,7 @@ var require_compile = __commonJS({
|
|
|
2991
2991
|
const schOrFunc = root.refs[ref];
|
|
2992
2992
|
if (schOrFunc)
|
|
2993
2993
|
return schOrFunc;
|
|
2994
|
-
let _sch =
|
|
2994
|
+
let _sch = resolve3.call(this, root, ref);
|
|
2995
2995
|
if (_sch === void 0) {
|
|
2996
2996
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2997
2997
|
const { schemaId } = this.opts;
|
|
@@ -3018,7 +3018,7 @@ var require_compile = __commonJS({
|
|
|
3018
3018
|
function sameSchemaEnv(s1, s2) {
|
|
3019
3019
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3020
3020
|
}
|
|
3021
|
-
function
|
|
3021
|
+
function resolve3(root, ref) {
|
|
3022
3022
|
let sch;
|
|
3023
3023
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3024
3024
|
ref = sch;
|
|
@@ -3031,8 +3031,8 @@ var require_compile = __commonJS({
|
|
|
3031
3031
|
if (Object.keys(root.schema).length > 0 && refPath === baseId) {
|
|
3032
3032
|
return getJsonPointer.call(this, p, root);
|
|
3033
3033
|
}
|
|
3034
|
-
const
|
|
3035
|
-
const schOrRef = this.refs[
|
|
3034
|
+
const id2 = (0, resolve_1.normalizeId)(refPath);
|
|
3035
|
+
const schOrRef = this.refs[id2] || this.schemas[id2];
|
|
3036
3036
|
if (typeof schOrRef == "string") {
|
|
3037
3037
|
const sch = resolveSchema.call(this, root, schOrRef);
|
|
3038
3038
|
if (typeof (sch === null || sch === void 0 ? void 0 : sch.schema) !== "object")
|
|
@@ -3043,7 +3043,7 @@ var require_compile = __commonJS({
|
|
|
3043
3043
|
return;
|
|
3044
3044
|
if (!schOrRef.validate)
|
|
3045
3045
|
compileSchema.call(this, schOrRef);
|
|
3046
|
-
if (
|
|
3046
|
+
if (id2 === (0, resolve_1.normalizeId)(ref)) {
|
|
3047
3047
|
const { schema } = schOrRef;
|
|
3048
3048
|
const { schemaId } = this.opts;
|
|
3049
3049
|
const schId = schema[schemaId];
|
|
@@ -3843,7 +3843,7 @@ var require_fast_uri = __commonJS({
|
|
|
3843
3843
|
}
|
|
3844
3844
|
return uri;
|
|
3845
3845
|
}
|
|
3846
|
-
function
|
|
3846
|
+
function resolve3(baseURI, relativeURI, options) {
|
|
3847
3847
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3848
3848
|
const {
|
|
3849
3849
|
parsed: baseParsed,
|
|
@@ -4205,7 +4205,7 @@ var require_fast_uri = __commonJS({
|
|
|
4205
4205
|
var fastUri = {
|
|
4206
4206
|
SCHEMES,
|
|
4207
4207
|
normalize,
|
|
4208
|
-
resolve:
|
|
4208
|
+
resolve: resolve3,
|
|
4209
4209
|
resolveComponent,
|
|
4210
4210
|
equal,
|
|
4211
4211
|
serialize,
|
|
@@ -4458,15 +4458,15 @@ var require_core = __commonJS({
|
|
|
4458
4458
|
this.addSchema(sch, void 0, _meta, _validateSchema);
|
|
4459
4459
|
return this;
|
|
4460
4460
|
}
|
|
4461
|
-
let
|
|
4461
|
+
let id2;
|
|
4462
4462
|
if (typeof schema === "object") {
|
|
4463
4463
|
const { schemaId } = this.opts;
|
|
4464
|
-
|
|
4465
|
-
if (
|
|
4464
|
+
id2 = schema[schemaId];
|
|
4465
|
+
if (id2 !== void 0 && typeof id2 != "string") {
|
|
4466
4466
|
throw new Error(`schema ${schemaId} must be string`);
|
|
4467
4467
|
}
|
|
4468
4468
|
}
|
|
4469
|
-
key = (0, resolve_1.normalizeId)(key ||
|
|
4469
|
+
key = (0, resolve_1.normalizeId)(key || id2);
|
|
4470
4470
|
this._checkUnique(key);
|
|
4471
4471
|
this.schemas[key] = this._addSchema(schema, _meta, key, _validateSchema, true);
|
|
4472
4472
|
return this;
|
|
@@ -4545,11 +4545,11 @@ var require_core = __commonJS({
|
|
|
4545
4545
|
case "object": {
|
|
4546
4546
|
const cacheKey = schemaKeyRef;
|
|
4547
4547
|
this._cache.delete(cacheKey);
|
|
4548
|
-
let
|
|
4549
|
-
if (
|
|
4550
|
-
|
|
4551
|
-
delete this.schemas[
|
|
4552
|
-
delete this.refs[
|
|
4548
|
+
let id2 = schemaKeyRef[this.opts.schemaId];
|
|
4549
|
+
if (id2) {
|
|
4550
|
+
id2 = (0, resolve_1.normalizeId)(id2);
|
|
4551
|
+
delete this.schemas[id2];
|
|
4552
|
+
delete this.refs[id2];
|
|
4553
4553
|
}
|
|
4554
4554
|
return this;
|
|
4555
4555
|
}
|
|
@@ -4656,10 +4656,10 @@ var require_core = __commonJS({
|
|
|
4656
4656
|
}
|
|
4657
4657
|
}
|
|
4658
4658
|
_addSchema(schema, meta, baseId, validateSchema = this.opts.validateSchema, addSchema = this.opts.addUsedSchema) {
|
|
4659
|
-
let
|
|
4659
|
+
let id2;
|
|
4660
4660
|
const { schemaId } = this.opts;
|
|
4661
4661
|
if (typeof schema == "object") {
|
|
4662
|
-
|
|
4662
|
+
id2 = schema[schemaId];
|
|
4663
4663
|
} else {
|
|
4664
4664
|
if (this.opts.jtd)
|
|
4665
4665
|
throw new Error("schema must be object");
|
|
@@ -4669,7 +4669,7 @@ var require_core = __commonJS({
|
|
|
4669
4669
|
let sch = this._cache.get(schema);
|
|
4670
4670
|
if (sch !== void 0)
|
|
4671
4671
|
return sch;
|
|
4672
|
-
baseId = (0, resolve_1.normalizeId)(
|
|
4672
|
+
baseId = (0, resolve_1.normalizeId)(id2 || baseId);
|
|
4673
4673
|
const localRefs = resolve_1.getSchemaRefs.call(this, schema, baseId);
|
|
4674
4674
|
sch = new compile_1.SchemaEnv({ schema, schemaId, meta, baseId, localRefs });
|
|
4675
4675
|
this._cache.set(sch.schema, sch);
|
|
@@ -4682,9 +4682,9 @@ var require_core = __commonJS({
|
|
|
4682
4682
|
this.validateSchema(schema, true);
|
|
4683
4683
|
return sch;
|
|
4684
4684
|
}
|
|
4685
|
-
_checkUnique(
|
|
4686
|
-
if (this.schemas[
|
|
4687
|
-
throw new Error(`schema with key or id "${
|
|
4685
|
+
_checkUnique(id2) {
|
|
4686
|
+
if (this.schemas[id2] || this.refs[id2]) {
|
|
4687
|
+
throw new Error(`schema with key or id "${id2}" already exists`);
|
|
4688
4688
|
}
|
|
4689
4689
|
}
|
|
4690
4690
|
_compileSchemaEnv(sch) {
|
|
@@ -7952,7 +7952,7 @@ var require_NodeUtils = __commonJS({
|
|
|
7952
7952
|
};
|
|
7953
7953
|
var ESCAPE_REGEXP = /[&<>\u00A0]/g;
|
|
7954
7954
|
var ESCAPE_ATTR_REGEXP = /[&"<>\u00A0]/g;
|
|
7955
|
-
function
|
|
7955
|
+
function escape3(s) {
|
|
7956
7956
|
if (!ESCAPE_REGEXP.test(s)) {
|
|
7957
7957
|
return s;
|
|
7958
7958
|
}
|
|
@@ -8059,7 +8059,7 @@ var require_NodeUtils = __commonJS({
|
|
|
8059
8059
|
if (hasRawContent[parenttag] || parenttag === "NOSCRIPT" && parent.ownerDocument._scripting_enabled) {
|
|
8060
8060
|
s += kid.data;
|
|
8061
8061
|
} else {
|
|
8062
|
-
s +=
|
|
8062
|
+
s += escape3(kid.data);
|
|
8063
8063
|
}
|
|
8064
8064
|
break;
|
|
8065
8065
|
case 8:
|
|
@@ -9922,13 +9922,13 @@ var require_select = __commonJS({
|
|
|
9922
9922
|
return results;
|
|
9923
9923
|
};
|
|
9924
9924
|
module.exports = exports = function(sel, context) {
|
|
9925
|
-
var
|
|
9925
|
+
var id2, r;
|
|
9926
9926
|
if (context.nodeType !== 11 && sel.indexOf(" ") === -1) {
|
|
9927
9927
|
if (sel[0] === "#" && context.rooted && /^#[A-Z_][-A-Z0-9_]*$/i.test(sel)) {
|
|
9928
9928
|
if (context.doc._hasMultipleElementsWithId) {
|
|
9929
|
-
|
|
9930
|
-
if (!context.doc._hasMultipleElementsWithId(
|
|
9931
|
-
r = context.doc.getElementById(
|
|
9929
|
+
id2 = sel.substring(1);
|
|
9930
|
+
if (!context.doc._hasMultipleElementsWithId(id2)) {
|
|
9931
|
+
r = context.doc.getElementById(id2);
|
|
9932
9932
|
return r ? [r] : [];
|
|
9933
9933
|
}
|
|
9934
9934
|
}
|
|
@@ -11110,12 +11110,12 @@ var require_Element = __commonJS({
|
|
|
11110
11110
|
if (c.nodeType === Node.ELEMENT_NODE) {
|
|
11111
11111
|
this[this.childrenByNumber.length] = c;
|
|
11112
11112
|
this.childrenByNumber.push(c);
|
|
11113
|
-
var
|
|
11114
|
-
if (
|
|
11115
|
-
this.childrenByName[
|
|
11113
|
+
var id2 = c.getAttribute("id");
|
|
11114
|
+
if (id2 && !this.childrenByName[id2])
|
|
11115
|
+
this.childrenByName[id2] = c;
|
|
11116
11116
|
var name = c.getAttribute("name");
|
|
11117
11117
|
if (name && this.element.namespaceURI === NAMESPACE.HTML && namedElts.test(this.element.localName) && !this.childrenByName[name])
|
|
11118
|
-
this.childrenByName[
|
|
11118
|
+
this.childrenByName[id2] = c;
|
|
11119
11119
|
}
|
|
11120
11120
|
}
|
|
11121
11121
|
}
|
|
@@ -14845,16 +14845,16 @@ var require_Document = __commonJS({
|
|
|
14845
14845
|
this._updateDocTypeElement();
|
|
14846
14846
|
return child;
|
|
14847
14847
|
} },
|
|
14848
|
-
getElementById: { value: function(
|
|
14849
|
-
var n = this.byId[
|
|
14848
|
+
getElementById: { value: function(id2) {
|
|
14849
|
+
var n = this.byId[id2];
|
|
14850
14850
|
if (!n) return null;
|
|
14851
14851
|
if (n instanceof MultiId) {
|
|
14852
14852
|
return n.getFirst();
|
|
14853
14853
|
}
|
|
14854
14854
|
return n;
|
|
14855
14855
|
} },
|
|
14856
|
-
_hasMultipleElementsWithId: { value: function(
|
|
14857
|
-
return this.byId[
|
|
14856
|
+
_hasMultipleElementsWithId: { value: function(id2) {
|
|
14857
|
+
return this.byId[id2] instanceof MultiId;
|
|
14858
14858
|
} },
|
|
14859
14859
|
// Just copy this method from the Element prototype
|
|
14860
14860
|
getElementsByName: { value: Element.prototype.getElementsByName },
|
|
@@ -15106,29 +15106,29 @@ var require_Document = __commonJS({
|
|
|
15106
15106
|
}
|
|
15107
15107
|
} },
|
|
15108
15108
|
// Add a mapping from id to n for n.ownerDocument
|
|
15109
|
-
addId: { value: function addId(
|
|
15110
|
-
var val = this.byId[
|
|
15109
|
+
addId: { value: function addId(id2, n) {
|
|
15110
|
+
var val = this.byId[id2];
|
|
15111
15111
|
if (!val) {
|
|
15112
|
-
this.byId[
|
|
15112
|
+
this.byId[id2] = n;
|
|
15113
15113
|
} else {
|
|
15114
15114
|
if (!(val instanceof MultiId)) {
|
|
15115
15115
|
val = new MultiId(val);
|
|
15116
|
-
this.byId[
|
|
15116
|
+
this.byId[id2] = val;
|
|
15117
15117
|
}
|
|
15118
15118
|
val.add(n);
|
|
15119
15119
|
}
|
|
15120
15120
|
} },
|
|
15121
15121
|
// Delete the mapping from id to n for n.ownerDocument
|
|
15122
|
-
delId: { value: function delId(
|
|
15123
|
-
var val = this.byId[
|
|
15122
|
+
delId: { value: function delId(id2, n) {
|
|
15123
|
+
var val = this.byId[id2];
|
|
15124
15124
|
utils.assert(val);
|
|
15125
15125
|
if (val instanceof MultiId) {
|
|
15126
15126
|
val.del(n);
|
|
15127
15127
|
if (val.length === 1) {
|
|
15128
|
-
this.byId[
|
|
15128
|
+
this.byId[id2] = val.downgrade();
|
|
15129
15129
|
}
|
|
15130
15130
|
} else {
|
|
15131
|
-
this.byId[
|
|
15131
|
+
this.byId[id2] = void 0;
|
|
15132
15132
|
}
|
|
15133
15133
|
} },
|
|
15134
15134
|
_resolve: { value: function(href) {
|
|
@@ -15238,15 +15238,15 @@ var require_Document = __commonJS({
|
|
|
15238
15238
|
n._nid = n.ownerDocument._nextnid++;
|
|
15239
15239
|
n.ownerDocument._nodes[n._nid] = n;
|
|
15240
15240
|
if (n.nodeType === Node.ELEMENT_NODE) {
|
|
15241
|
-
var
|
|
15242
|
-
if (
|
|
15241
|
+
var id2 = n.getAttribute("id");
|
|
15242
|
+
if (id2) n.ownerDocument.addId(id2, n);
|
|
15243
15243
|
if (n._roothook) n._roothook();
|
|
15244
15244
|
}
|
|
15245
15245
|
}
|
|
15246
15246
|
function uproot(n) {
|
|
15247
15247
|
if (n.nodeType === Node.ELEMENT_NODE) {
|
|
15248
|
-
var
|
|
15249
|
-
if (
|
|
15248
|
+
var id2 = n.getAttribute("id");
|
|
15249
|
+
if (id2) n.ownerDocument.delId(id2, n);
|
|
15250
15250
|
}
|
|
15251
15251
|
n.ownerDocument._nodes[n._nid] = void 0;
|
|
15252
15252
|
n._nid = void 0;
|
|
@@ -23908,8 +23908,8 @@ var require_turndown_cjs = __commonJS({
|
|
|
23908
23908
|
}
|
|
23909
23909
|
var markdownEscapes = [[/\\/g, "\\\\"], [/\*/g, "\\*"], [/^-/g, "\\-"], [/^\+ /g, "\\+ "], [/^(=+)/g, "\\$1"], [/^(#{1,6}) /g, "\\$1 "], [/`/g, "\\`"], [/^~~~/g, "\\~~~"], [/\[/g, "\\["], [/\]/g, "\\]"], [/^>/g, "\\>"], [/_/g, "\\_"], [/^(\d+)\. /g, "$1\\. "]];
|
|
23910
23910
|
function escapeMarkdown(string3) {
|
|
23911
|
-
return markdownEscapes.reduce(function(accumulator,
|
|
23912
|
-
return accumulator.replace(
|
|
23911
|
+
return markdownEscapes.reduce(function(accumulator, escape3) {
|
|
23912
|
+
return accumulator.replace(escape3[0], escape3[1]);
|
|
23913
23913
|
}, string3);
|
|
23914
23914
|
}
|
|
23915
23915
|
var rules = {};
|
|
@@ -24037,9 +24037,9 @@ var require_turndown_cjs = __commonJS({
|
|
|
24037
24037
|
reference = "[" + content + "]: " + href + title;
|
|
24038
24038
|
break;
|
|
24039
24039
|
default:
|
|
24040
|
-
var
|
|
24041
|
-
replacement = "[" + content + "][" +
|
|
24042
|
-
reference = "[" +
|
|
24040
|
+
var id2 = this.references.length + 1;
|
|
24041
|
+
replacement = "[" + content + "][" + id2 + "]";
|
|
24042
|
+
reference = "[" + id2 + "]: " + href + title;
|
|
24043
24043
|
}
|
|
24044
24044
|
this.references.push(reference);
|
|
24045
24045
|
return replacement;
|
|
@@ -24465,14 +24465,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
24465
24465
|
} else if (node.nodeType === 1) {
|
|
24466
24466
|
replacement = replacementForNode.call(self, node);
|
|
24467
24467
|
}
|
|
24468
|
-
return
|
|
24468
|
+
return join11(output, replacement);
|
|
24469
24469
|
}, "");
|
|
24470
24470
|
}
|
|
24471
24471
|
function postProcess(output) {
|
|
24472
24472
|
var self = this;
|
|
24473
24473
|
this.rules.forEach(function(rule) {
|
|
24474
24474
|
if (typeof rule.append === "function") {
|
|
24475
|
-
output =
|
|
24475
|
+
output = join11(output, rule.append(self.options));
|
|
24476
24476
|
}
|
|
24477
24477
|
});
|
|
24478
24478
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -24484,7 +24484,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
24484
24484
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
24485
24485
|
return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
|
|
24486
24486
|
}
|
|
24487
|
-
function
|
|
24487
|
+
function join11(output, replacement) {
|
|
24488
24488
|
var s1 = trimTrailingNewlines(output);
|
|
24489
24489
|
var s2 = trimLeadingNewlines(replacement);
|
|
24490
24490
|
var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
|
@@ -30311,38 +30311,38 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
30311
30311
|
doc.write(`const newResult = {}`);
|
|
30312
30312
|
for (const key of normalized2.keys) {
|
|
30313
30313
|
if (normalized2.optionalKeys.has(key)) {
|
|
30314
|
-
const
|
|
30315
|
-
doc.write(`const ${
|
|
30314
|
+
const id2 = ids[key];
|
|
30315
|
+
doc.write(`const ${id2} = ${parseStr(key)};`);
|
|
30316
30316
|
const k = esc(key);
|
|
30317
30317
|
doc.write(`
|
|
30318
|
-
if (${
|
|
30318
|
+
if (${id2}.issues.length) {
|
|
30319
30319
|
if (input[${k}] === undefined) {
|
|
30320
30320
|
if (${k} in input) {
|
|
30321
30321
|
newResult[${k}] = undefined;
|
|
30322
30322
|
}
|
|
30323
30323
|
} else {
|
|
30324
30324
|
payload.issues = payload.issues.concat(
|
|
30325
|
-
${
|
|
30325
|
+
${id2}.issues.map((iss) => ({
|
|
30326
30326
|
...iss,
|
|
30327
30327
|
path: iss.path ? [${k}, ...iss.path] : [${k}],
|
|
30328
30328
|
}))
|
|
30329
30329
|
);
|
|
30330
30330
|
}
|
|
30331
|
-
} else if (${
|
|
30331
|
+
} else if (${id2}.value === undefined) {
|
|
30332
30332
|
if (${k} in input) newResult[${k}] = undefined;
|
|
30333
30333
|
} else {
|
|
30334
|
-
newResult[${k}] = ${
|
|
30334
|
+
newResult[${k}] = ${id2}.value;
|
|
30335
30335
|
}
|
|
30336
30336
|
`);
|
|
30337
30337
|
} else {
|
|
30338
|
-
const
|
|
30339
|
-
doc.write(`const ${
|
|
30338
|
+
const id2 = ids[key];
|
|
30339
|
+
doc.write(`const ${id2} = ${parseStr(key)};`);
|
|
30340
30340
|
doc.write(`
|
|
30341
|
-
if (${
|
|
30341
|
+
if (${id2}.issues.length) payload.issues = payload.issues.concat(${id2}.issues.map(iss => ({
|
|
30342
30342
|
...iss,
|
|
30343
30343
|
path: iss.path ? [${esc(key)}, ...iss.path] : [${esc(key)}]
|
|
30344
30344
|
})));`);
|
|
30345
|
-
doc.write(`newResult[${esc(key)}] = ${
|
|
30345
|
+
doc.write(`newResult[${esc(key)}] = ${id2}.value`);
|
|
30346
30346
|
}
|
|
30347
30347
|
}
|
|
30348
30348
|
doc.write(`payload.value = newResult;`);
|
|
@@ -32073,13 +32073,13 @@ var JSONSchemaGenerator = class {
|
|
|
32073
32073
|
const defsSegment = this.target === "draft-2020-12" ? "$defs" : "definitions";
|
|
32074
32074
|
if (params.external) {
|
|
32075
32075
|
const externalId = params.external.registry.get(entry[0])?.id;
|
|
32076
|
-
const uriGenerator = params.external.uri ?? ((
|
|
32076
|
+
const uriGenerator = params.external.uri ?? ((id3) => id3);
|
|
32077
32077
|
if (externalId) {
|
|
32078
32078
|
return { ref: uriGenerator(externalId) };
|
|
32079
32079
|
}
|
|
32080
|
-
const
|
|
32081
|
-
entry[1].defId =
|
|
32082
|
-
return { defId:
|
|
32080
|
+
const id2 = entry[1].defId ?? entry[1].schema.id ?? `schema${this.counter++}`;
|
|
32081
|
+
entry[1].defId = id2;
|
|
32082
|
+
return { defId: id2, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id2}` };
|
|
32083
32083
|
}
|
|
32084
32084
|
if (entry[1] === root) {
|
|
32085
32085
|
return { ref: "#" };
|
|
@@ -32127,8 +32127,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
32127
32127
|
continue;
|
|
32128
32128
|
}
|
|
32129
32129
|
}
|
|
32130
|
-
const
|
|
32131
|
-
if (
|
|
32130
|
+
const id2 = this.metadataRegistry.get(entry[0])?.id;
|
|
32131
|
+
if (id2) {
|
|
32132
32132
|
extractToDef(entry);
|
|
32133
32133
|
continue;
|
|
32134
32134
|
}
|
|
@@ -32182,10 +32182,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
32182
32182
|
console.warn(`Invalid target: ${this.target}`);
|
|
32183
32183
|
}
|
|
32184
32184
|
if (params.external?.uri) {
|
|
32185
|
-
const
|
|
32186
|
-
if (!
|
|
32185
|
+
const id2 = params.external.registry.get(schema)?.id;
|
|
32186
|
+
if (!id2)
|
|
32187
32187
|
throw new Error("Schema is missing an `id` property");
|
|
32188
|
-
result.$id = params.external.uri(
|
|
32188
|
+
result.$id = params.external.uri(id2);
|
|
32189
32189
|
}
|
|
32190
32190
|
Object.assign(result, root.def);
|
|
32191
32191
|
const defs = params.external?.defs ?? {};
|
|
@@ -36608,7 +36608,7 @@ var Protocol = class {
|
|
|
36608
36608
|
return;
|
|
36609
36609
|
}
|
|
36610
36610
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
36611
|
-
await new Promise((
|
|
36611
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
36612
36612
|
options?.signal?.throwIfAborted();
|
|
36613
36613
|
}
|
|
36614
36614
|
} catch (error2) {
|
|
@@ -36625,7 +36625,7 @@ var Protocol = class {
|
|
|
36625
36625
|
*/
|
|
36626
36626
|
request(request, resultSchema, options) {
|
|
36627
36627
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
36628
|
-
return new Promise((
|
|
36628
|
+
return new Promise((resolve3, reject) => {
|
|
36629
36629
|
const earlyReject = (error2) => {
|
|
36630
36630
|
reject(error2);
|
|
36631
36631
|
};
|
|
@@ -36703,7 +36703,7 @@ var Protocol = class {
|
|
|
36703
36703
|
if (!parseResult.success) {
|
|
36704
36704
|
reject(parseResult.error);
|
|
36705
36705
|
} else {
|
|
36706
|
-
|
|
36706
|
+
resolve3(parseResult.data);
|
|
36707
36707
|
}
|
|
36708
36708
|
} catch (error2) {
|
|
36709
36709
|
reject(error2);
|
|
@@ -36964,12 +36964,12 @@ var Protocol = class {
|
|
|
36964
36964
|
}
|
|
36965
36965
|
} catch {
|
|
36966
36966
|
}
|
|
36967
|
-
return new Promise((
|
|
36967
|
+
return new Promise((resolve3, reject) => {
|
|
36968
36968
|
if (signal.aborted) {
|
|
36969
36969
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
36970
36970
|
return;
|
|
36971
36971
|
}
|
|
36972
|
-
const timeoutId = setTimeout(
|
|
36972
|
+
const timeoutId = setTimeout(resolve3, interval);
|
|
36973
36973
|
signal.addEventListener("abort", () => {
|
|
36974
36974
|
clearTimeout(timeoutId);
|
|
36975
36975
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -37218,7 +37218,7 @@ var ExperimentalServerTasks = class {
|
|
|
37218
37218
|
if (hasPreviousToolUse) {
|
|
37219
37219
|
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
37220
37220
|
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
37221
|
-
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((
|
|
37221
|
+
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id2) => toolResultIds.has(id2))) {
|
|
37222
37222
|
throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
37223
37223
|
}
|
|
37224
37224
|
}
|
|
@@ -37643,7 +37643,7 @@ var Server = class extends Protocol {
|
|
|
37643
37643
|
if (hasPreviousToolUse) {
|
|
37644
37644
|
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
37645
37645
|
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
37646
|
-
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((
|
|
37646
|
+
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id2) => toolResultIds.has(id2))) {
|
|
37647
37647
|
throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
37648
37648
|
}
|
|
37649
37649
|
}
|
|
@@ -38282,7 +38282,7 @@ var McpServer = class {
|
|
|
38282
38282
|
let task = createTaskResult.task;
|
|
38283
38283
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
38284
38284
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
38285
|
-
await new Promise((
|
|
38285
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
38286
38286
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
38287
38287
|
if (!updatedTask) {
|
|
38288
38288
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -38970,12 +38970,12 @@ var StdioServerTransport = class {
|
|
|
38970
38970
|
this.onclose?.();
|
|
38971
38971
|
}
|
|
38972
38972
|
send(message) {
|
|
38973
|
-
return new Promise((
|
|
38973
|
+
return new Promise((resolve3) => {
|
|
38974
38974
|
const json2 = serializeMessage(message);
|
|
38975
38975
|
if (this._stdout.write(json2)) {
|
|
38976
|
-
|
|
38976
|
+
resolve3();
|
|
38977
38977
|
} else {
|
|
38978
|
-
this._stdout.once("drain",
|
|
38978
|
+
this._stdout.once("drain", resolve3);
|
|
38979
38979
|
}
|
|
38980
38980
|
});
|
|
38981
38981
|
}
|
|
@@ -39604,8 +39604,8 @@ function parseRichNote(data, nativeTags = []) {
|
|
|
39604
39604
|
styleRuns
|
|
39605
39605
|
};
|
|
39606
39606
|
}
|
|
39607
|
-
function readRichNote(
|
|
39608
|
-
const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]+)$/i.exec(
|
|
39607
|
+
function readRichNote(id2) {
|
|
39608
|
+
const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]+)$/i.exec(id2)?.[1];
|
|
39609
39609
|
if (!pk) throw new Error("Invalid exact note ID");
|
|
39610
39610
|
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;`;
|
|
39611
39611
|
const rows = execFileSync3("/usr/bin/sqlite3", ["-readonly", dbPath, sql], {
|
|
@@ -39630,14 +39630,14 @@ function readRichNote(id) {
|
|
|
39630
39630
|
rich.objectData = objectData.filter((row) => rich.nativeObjectIds.includes(row.id)).sort((a, b) => a.id.localeCompare(b.id));
|
|
39631
39631
|
rich.revision = createHash("sha256").update(rich.revision).update(JSON.stringify(rich.objectData)).digest("hex");
|
|
39632
39632
|
rich.nativeTagObjectIds = {};
|
|
39633
|
-
for (const
|
|
39634
|
-
if (tagMap[
|
|
39635
|
-
const tag = tagMap[
|
|
39636
|
-
(rich.nativeTagObjectIds[tag] ||= []).push(
|
|
39633
|
+
for (const id3 of rich.nativeObjectIds)
|
|
39634
|
+
if (tagMap[id3]) {
|
|
39635
|
+
const tag = tagMap[id3].replace(/^#/, "");
|
|
39636
|
+
(rich.nativeTagObjectIds[tag] ||= []).push(id3);
|
|
39637
39637
|
}
|
|
39638
39638
|
rich.nativeTags = [
|
|
39639
39639
|
...new Set(
|
|
39640
|
-
rich.nativeObjectIds.flatMap((
|
|
39640
|
+
rich.nativeObjectIds.flatMap((id3) => tagMap[id3] ? [tagMap[id3].replace(/^#/, "")] : [])
|
|
39641
39641
|
)
|
|
39642
39642
|
];
|
|
39643
39643
|
return rich;
|
|
@@ -39712,10 +39712,10 @@ function restoreNoteLinks(html, rich) {
|
|
|
39712
39712
|
result = result.slice(0, span.start) + `<a href="${escapeAttribute(span.url)}">` + result.slice(span.start, span.end) + "</a>" + result.slice(span.end);
|
|
39713
39713
|
return result;
|
|
39714
39714
|
}
|
|
39715
|
-
function enrichNoteRead(
|
|
39715
|
+
function enrichNoteRead(id2, rawBody) {
|
|
39716
39716
|
let metadata;
|
|
39717
39717
|
try {
|
|
39718
|
-
const rich = readRichNote(
|
|
39718
|
+
const rich = readRichNote(id2);
|
|
39719
39719
|
metadata = rich;
|
|
39720
39720
|
const content = restoreNoteLinks(rawBody, rich);
|
|
39721
39721
|
const writable = !rich.hasNativeObjects && !rich.hasChecklist;
|
|
@@ -39971,24 +39971,24 @@ function validateLength(value, maxLength, label) {
|
|
|
39971
39971
|
}
|
|
39972
39972
|
return value;
|
|
39973
39973
|
}
|
|
39974
|
-
function sanitizeId(
|
|
39974
|
+
function sanitizeId(id2) {
|
|
39975
39975
|
const coreDataPattern = /^x-coredata:\/\/[0-9A-Fa-f-]+\/IC[A-Za-z]+\/p\d+$/;
|
|
39976
39976
|
const tempIdPattern = /^temp-\d+-\d+$/;
|
|
39977
|
-
if (!coreDataPattern.test(
|
|
39977
|
+
if (!coreDataPattern.test(id2) && !tempIdPattern.test(id2)) {
|
|
39978
39978
|
throw new Error(
|
|
39979
|
-
`Invalid note ID format: "${
|
|
39979
|
+
`Invalid note ID format: "${id2.substring(0, 80)}". Expected CoreData URL (x-coredata://...) or temp ID.`
|
|
39980
39980
|
);
|
|
39981
39981
|
}
|
|
39982
|
-
return escapeForAppleScript(
|
|
39982
|
+
return escapeForAppleScript(id2);
|
|
39983
39983
|
}
|
|
39984
|
-
function sanitizeNoteId(
|
|
39984
|
+
function sanitizeNoteId(id2) {
|
|
39985
39985
|
const noteIdPattern = /^x-coredata:\/\/[0-9A-Fa-f-]+\/ICNote\/p\d+$/;
|
|
39986
|
-
if (!noteIdPattern.test(
|
|
39986
|
+
if (!noteIdPattern.test(id2)) {
|
|
39987
39987
|
throw new Error(
|
|
39988
|
-
`Invalid note ID format: "${
|
|
39988
|
+
`Invalid note ID format: "${id2.substring(0, 80)}". Expected canonical Apple Note ID (x-coredata://.../ICNote/p...).`
|
|
39989
39989
|
);
|
|
39990
39990
|
}
|
|
39991
|
-
return escapeForAppleScript(
|
|
39991
|
+
return escapeForAppleScript(id2);
|
|
39992
39992
|
}
|
|
39993
39993
|
function sanitizeAccountName(account) {
|
|
39994
39994
|
validateLength(account, MAX_ACCOUNT_LENGTH, "Account name");
|
|
@@ -40043,10 +40043,10 @@ function parseNotePropertiesOutput(output) {
|
|
|
40043
40043
|
console.error("Unexpected response format: expected 6 delimited note properties");
|
|
40044
40044
|
return null;
|
|
40045
40045
|
}
|
|
40046
|
-
const [title,
|
|
40046
|
+
const [title, id2, createdStr, modifiedStr, sharedStr, ppStr] = parts;
|
|
40047
40047
|
return {
|
|
40048
40048
|
title: title.trim(),
|
|
40049
|
-
id:
|
|
40049
|
+
id: id2.trim(),
|
|
40050
40050
|
created: createdStr?.trim() ? parseAppleScriptDate(createdStr.trim()) : /* @__PURE__ */ new Date(),
|
|
40051
40051
|
modified: modifiedStr?.trim() ? parseAppleScriptDate(modifiedStr.trim()) : /* @__PURE__ */ new Date(),
|
|
40052
40052
|
shared: sharedStr?.trim() === "true",
|
|
@@ -40207,8 +40207,8 @@ var AppleNotesManager = class {
|
|
|
40207
40207
|
* @param id - CoreData URL identifier for the note
|
|
40208
40208
|
* @returns true if the note is password-protected, false otherwise
|
|
40209
40209
|
*/
|
|
40210
|
-
isNotePasswordProtectedById(
|
|
40211
|
-
const note = this.getNoteById(
|
|
40210
|
+
isNotePasswordProtectedById(id2) {
|
|
40211
|
+
const note = this.getNoteById(id2);
|
|
40212
40212
|
return note?.passwordProtected === true;
|
|
40213
40213
|
}
|
|
40214
40214
|
/**
|
|
@@ -40406,9 +40406,9 @@ var AppleNotesManager = class {
|
|
|
40406
40406
|
const seenIds = /* @__PURE__ */ new Set();
|
|
40407
40407
|
const reportedAccount = this.reportedAccount(targetAccount);
|
|
40408
40408
|
for (const item of items) {
|
|
40409
|
-
const [title,
|
|
40409
|
+
const [title, id2, folder2, created, modified] = item.split(FIELD_SEP);
|
|
40410
40410
|
if (!title?.trim()) continue;
|
|
40411
|
-
const noteId3 =
|
|
40411
|
+
const noteId3 = id2?.trim() || generateFallbackId();
|
|
40412
40412
|
if (seenIds.has(noteId3)) continue;
|
|
40413
40413
|
seenIds.add(noteId3);
|
|
40414
40414
|
notes.push({
|
|
@@ -40470,13 +40470,13 @@ var AppleNotesManager = class {
|
|
|
40470
40470
|
* @param id - CoreData URL identifier for the note
|
|
40471
40471
|
* @returns HTML content of the note, or empty string if not found
|
|
40472
40472
|
*/
|
|
40473
|
-
getNoteContentById(
|
|
40474
|
-
const safeId = sanitizeId(
|
|
40473
|
+
getNoteContentById(id2) {
|
|
40474
|
+
const safeId = sanitizeId(id2);
|
|
40475
40475
|
const getCommand = `get body of note id "${safeId}"`;
|
|
40476
40476
|
const script = buildAppLevelScript(getCommand);
|
|
40477
40477
|
const result = executeAppleScript(script);
|
|
40478
40478
|
if (!result.success) {
|
|
40479
|
-
console.error(`Failed to get content of note with ID "${
|
|
40479
|
+
console.error(`Failed to get content of note with ID "${id2}":`, result.error);
|
|
40480
40480
|
return "";
|
|
40481
40481
|
}
|
|
40482
40482
|
return result.output;
|
|
@@ -40518,13 +40518,13 @@ var AppleNotesManager = class {
|
|
|
40518
40518
|
* @param id - CoreData URL identifier for the note
|
|
40519
40519
|
* @returns Plain-text content of the note, or empty string if not found
|
|
40520
40520
|
*/
|
|
40521
|
-
getNotePlaintextById(
|
|
40522
|
-
const safeId = sanitizeId(
|
|
40521
|
+
getNotePlaintextById(id2) {
|
|
40522
|
+
const safeId = sanitizeId(id2);
|
|
40523
40523
|
const getCommand = `get plaintext of note id "${safeId}"`;
|
|
40524
40524
|
const script = buildAppLevelScript(getCommand);
|
|
40525
40525
|
const result = executeAppleScript(script);
|
|
40526
40526
|
if (!result.success) {
|
|
40527
|
-
console.error(`Failed to get plaintext of note with ID "${
|
|
40527
|
+
console.error(`Failed to get plaintext of note with ID "${id2}":`, result.error);
|
|
40528
40528
|
return "";
|
|
40529
40529
|
}
|
|
40530
40530
|
return result.output;
|
|
@@ -40540,8 +40540,8 @@ var AppleNotesManager = class {
|
|
|
40540
40540
|
* @param id - CoreData URL identifier for the note
|
|
40541
40541
|
* @returns Note object with metadata, or null if not found
|
|
40542
40542
|
*/
|
|
40543
|
-
getNoteById(
|
|
40544
|
-
const safeId = sanitizeId(
|
|
40543
|
+
getNoteById(id2) {
|
|
40544
|
+
const safeId = sanitizeId(id2);
|
|
40545
40545
|
const getCommand = `
|
|
40546
40546
|
set n to note id "${safeId}"
|
|
40547
40547
|
set cd to creation date of n
|
|
@@ -40553,7 +40553,7 @@ var AppleNotesManager = class {
|
|
|
40553
40553
|
const script = buildAppLevelScript(getCommand);
|
|
40554
40554
|
const result = executeAppleScript(script);
|
|
40555
40555
|
if (!result.success) {
|
|
40556
|
-
console.error(`Failed to get note with ID "${
|
|
40556
|
+
console.error(`Failed to get note with ID "${id2}":`, result.error);
|
|
40557
40557
|
return null;
|
|
40558
40558
|
}
|
|
40559
40559
|
const parsed = parseNotePropertiesOutput(result.output);
|
|
@@ -40625,10 +40625,10 @@ var AppleNotesManager = class {
|
|
|
40625
40625
|
* race that would exist if JavaScript checked the note and then issued a
|
|
40626
40626
|
* separate unconditional `set body` command.
|
|
40627
40627
|
*/
|
|
40628
|
-
updateNoteByIdIfUnchanged(
|
|
40629
|
-
const safeId = sanitizeNoteId(
|
|
40628
|
+
updateNoteByIdIfUnchanged(id2, currentTitle, expectedBody, newTitle, newContent, format = "plaintext", expectedRichRevision) {
|
|
40629
|
+
const safeId = sanitizeNoteId(id2);
|
|
40630
40630
|
if (expectedRichRevision) {
|
|
40631
|
-
const rich = readRichNote(
|
|
40631
|
+
const rich = readRichNote(id2);
|
|
40632
40632
|
if (rich.revision !== expectedRichRevision) return { status: "conflict" };
|
|
40633
40633
|
if (rich.hasNativeObjects || rich.hasChecklist) return { status: "attachments" };
|
|
40634
40634
|
}
|
|
@@ -40657,7 +40657,7 @@ var AppleNotesManager = class {
|
|
|
40657
40657
|
`);
|
|
40658
40658
|
const result = executeMutationAppleScript(script);
|
|
40659
40659
|
if (!result.success) {
|
|
40660
|
-
console.error(`Failed guarded update for note ID "${
|
|
40660
|
+
console.error(`Failed guarded update for note ID "${id2}":`, result.error);
|
|
40661
40661
|
return { status: "failed" };
|
|
40662
40662
|
}
|
|
40663
40663
|
const status = result.output.trim();
|
|
@@ -40671,8 +40671,8 @@ var AppleNotesManager = class {
|
|
|
40671
40671
|
* the caller reviewed. The comparison and delete are one AppleScript action,
|
|
40672
40672
|
* so a concurrent edit cannot slip between the guard and deletion.
|
|
40673
40673
|
*/
|
|
40674
|
-
deleteNoteByIdIfUnchanged(
|
|
40675
|
-
const safeId = sanitizeNoteId(
|
|
40674
|
+
deleteNoteByIdIfUnchanged(id2, expectedBody) {
|
|
40675
|
+
const safeId = sanitizeNoteId(id2);
|
|
40676
40676
|
validateLength(expectedBody, MAX_CONTENT_LENGTH, "Expected note content");
|
|
40677
40677
|
const safeExpectedBody = escapeHtmlForAppleScript(expectedBody);
|
|
40678
40678
|
const script = buildAppLevelScript(`
|
|
@@ -40686,7 +40686,7 @@ var AppleNotesManager = class {
|
|
|
40686
40686
|
`);
|
|
40687
40687
|
const result = executeMutationAppleScript(script);
|
|
40688
40688
|
if (!result.success) {
|
|
40689
|
-
console.error(`Failed guarded delete for note ID "${
|
|
40689
|
+
console.error(`Failed guarded delete for note ID "${id2}":`, result.error);
|
|
40690
40690
|
return { status: "failed" };
|
|
40691
40691
|
}
|
|
40692
40692
|
const status = result.output.trim();
|
|
@@ -40778,9 +40778,9 @@ var AppleNotesManager = class {
|
|
|
40778
40778
|
const seenIds = /* @__PURE__ */ new Set();
|
|
40779
40779
|
const refs = [];
|
|
40780
40780
|
for (const item of output.split(RECORD_SEP)) {
|
|
40781
|
-
const [title,
|
|
40781
|
+
const [title, id2] = item.split(FIELD_SEP);
|
|
40782
40782
|
if (!title?.trim()) continue;
|
|
40783
|
-
const noteId3 =
|
|
40783
|
+
const noteId3 = id2?.trim() || generateFallbackId();
|
|
40784
40784
|
if (seenIds.has(noteId3)) continue;
|
|
40785
40785
|
seenIds.add(noteId3);
|
|
40786
40786
|
refs.push({ title: title.trim(), id: noteId3 });
|
|
@@ -40906,13 +40906,13 @@ var AppleNotesManager = class {
|
|
|
40906
40906
|
const parts = item.split(FIELD_SEP);
|
|
40907
40907
|
if (parts.length >= 6) {
|
|
40908
40908
|
const title = parts[0].trim();
|
|
40909
|
-
const
|
|
40909
|
+
const id2 = parts[1].trim();
|
|
40910
40910
|
const createdStr = parts[2].trim();
|
|
40911
40911
|
const modifiedStr = parts[3].trim();
|
|
40912
40912
|
const shared = parts[4].trim() === "true";
|
|
40913
40913
|
const passwordProtected = parts[5].trim() === "true";
|
|
40914
40914
|
sharedNotes.push({
|
|
40915
|
-
id,
|
|
40915
|
+
id: id2,
|
|
40916
40916
|
title,
|
|
40917
40917
|
content: "",
|
|
40918
40918
|
tags: [],
|
|
@@ -40931,25 +40931,25 @@ var AppleNotesManager = class {
|
|
|
40931
40931
|
// Folder Operations
|
|
40932
40932
|
// ===========================================================================
|
|
40933
40933
|
/** Rename a folder in place while preserving its identity and contents. */
|
|
40934
|
-
renameFolderById(
|
|
40935
|
-
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICFolder\/p\d+$/i.test(
|
|
40934
|
+
renameFolderById(id2, expectedName, expectedParentId, newName) {
|
|
40935
|
+
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICFolder\/p\d+$/i.test(id2))
|
|
40936
40936
|
throw new Error("An exact folder ID is required");
|
|
40937
40937
|
if (!newName.trim() || newName.length > 1e3 || Array.from(newName).some((char) => char.charCodeAt(0) < 32))
|
|
40938
40938
|
throw new Error("Invalid folder name");
|
|
40939
40939
|
const literal2 = (value) => `"${escapePlainStringForAppleScript(value)}"`;
|
|
40940
40940
|
const script = `tell application "Notes"
|
|
40941
|
-
set targetFolder to folder id ${literal2(
|
|
40941
|
+
set targetFolder to folder id ${literal2(id2)}
|
|
40942
40942
|
set parentRef to container of targetFolder
|
|
40943
40943
|
set parentId to id of parentRef
|
|
40944
40944
|
considering case
|
|
40945
40945
|
if name of targetFolder is not ${literal2(expectedName)} or parentId is not ${literal2(expectedParentId)} then error "Folder changed; read it again"
|
|
40946
40946
|
end considering
|
|
40947
40947
|
repeat with sibling in folders of parentRef
|
|
40948
|
-
if name of sibling is ${literal2(newName)} and id of sibling is not ${literal2(
|
|
40948
|
+
if name of sibling is ${literal2(newName)} and id of sibling is not ${literal2(id2)} then error "A sibling folder already has this name"
|
|
40949
40949
|
end repeat
|
|
40950
40950
|
set name of targetFolder to ${literal2(newName)}
|
|
40951
40951
|
considering case
|
|
40952
|
-
if id of targetFolder is not ${literal2(
|
|
40952
|
+
if id of targetFolder is not ${literal2(id2)} or name of targetFolder is not ${literal2(newName)} or id of container of targetFolder is not parentId then error "Rename readback failed"
|
|
40953
40953
|
end considering
|
|
40954
40954
|
return id of targetFolder
|
|
40955
40955
|
end tell`;
|
|
@@ -40958,28 +40958,28 @@ var AppleNotesManager = class {
|
|
|
40958
40958
|
throw new Error(
|
|
40959
40959
|
result.error || "Rename outcome uncertain; read folder by ID before retrying"
|
|
40960
40960
|
);
|
|
40961
|
-
return { id, name: newName, parentId: expectedParentId };
|
|
40961
|
+
return { id: id2, name: newName, parentId: expectedParentId };
|
|
40962
40962
|
}
|
|
40963
40963
|
/** Read the exact name and parent identity used by guarded folder rename. */
|
|
40964
|
-
getFolderById(
|
|
40965
|
-
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICFolder\/p\d+$/i.test(
|
|
40964
|
+
getFolderById(id2) {
|
|
40965
|
+
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICFolder\/p\d+$/i.test(id2))
|
|
40966
40966
|
throw new Error("An exact folder ID is required");
|
|
40967
40967
|
const result = executeAppleScript(`tell application "Notes"
|
|
40968
|
-
set f to folder id "${
|
|
40968
|
+
set f to folder id "${id2}"
|
|
40969
40969
|
return (name of f) & ${AS_FIELD_SEP} & (id of container of f)
|
|
40970
40970
|
end tell`);
|
|
40971
40971
|
if (!result.success) throw new Error(result.error || "Folder not found");
|
|
40972
40972
|
const [name, parentId] = result.output.replace(/\n$/, "").split(FIELD_SEP);
|
|
40973
40973
|
if (!name || !parentId) throw new Error("Incomplete folder metadata");
|
|
40974
|
-
return { id, name, parentId };
|
|
40974
|
+
return { id: id2, name, parentId };
|
|
40975
40975
|
}
|
|
40976
40976
|
/** Insert one file into an unchanged exact note and return Notes' attachment ID. */
|
|
40977
|
-
addAttachmentById(
|
|
40978
|
-
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(
|
|
40977
|
+
addAttachmentById(id2, expectedBody, filePath) {
|
|
40978
|
+
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(id2))
|
|
40979
40979
|
throw new Error("Exact note ID required");
|
|
40980
40980
|
const quote = (value) => `"${escapePlainStringForAppleScript(value)}"`;
|
|
40981
40981
|
const result = executeMutationAppleScript(`tell application "Notes"
|
|
40982
|
-
set n to note id ${quote(
|
|
40982
|
+
set n to note id ${quote(id2)}
|
|
40983
40983
|
if password protected of n then error "Locked note"
|
|
40984
40984
|
considering case
|
|
40985
40985
|
if body of n is not ${quote(expectedBody)} and body of n is not (${quote(expectedBody)} & linefeed) then error "Note changed before attachment insertion"
|
|
@@ -41151,9 +41151,9 @@ var AppleNotesManager = class {
|
|
|
41151
41151
|
* @param account - Account containing the destination folder (defaults to Notes.app's default account)
|
|
41152
41152
|
* @returns true if the move succeeded, false otherwise
|
|
41153
41153
|
*/
|
|
41154
|
-
moveNoteById(
|
|
41154
|
+
moveNoteById(id2, destinationFolder, account) {
|
|
41155
41155
|
const targetAccount = this.resolveAccount(account);
|
|
41156
|
-
const safeId = sanitizeNoteId(
|
|
41156
|
+
const safeId = sanitizeNoteId(id2);
|
|
41157
41157
|
const destFolderRef = `${buildFolderReference(destinationFolder)} of ${AS_ACCOUNT_REF}`;
|
|
41158
41158
|
const moveCommand = `
|
|
41159
41159
|
${buildAccountResolution(targetAccount)}
|
|
@@ -41177,7 +41177,7 @@ var AppleNotesManager = class {
|
|
|
41177
41177
|
}
|
|
41178
41178
|
if (result.output.trim() !== "SAFETY_MOVED") {
|
|
41179
41179
|
console.error(
|
|
41180
|
-
`Move result for note ID "${
|
|
41180
|
+
`Move result for note ID "${id2}" did not verify destination "${destinationFolder}"`
|
|
41181
41181
|
);
|
|
41182
41182
|
return false;
|
|
41183
41183
|
}
|
|
@@ -41325,14 +41325,14 @@ var AppleNotesManager = class {
|
|
|
41325
41325
|
* @param separately - Whether to open the note in a separate window
|
|
41326
41326
|
* @returns true if Notes.app accepted the show command
|
|
41327
41327
|
*/
|
|
41328
|
-
showNoteById(
|
|
41329
|
-
const safeId = sanitizeId(
|
|
41328
|
+
showNoteById(id2, separately = false) {
|
|
41329
|
+
const safeId = sanitizeId(id2);
|
|
41330
41330
|
const separatelyClause = separately ? " separately true" : "";
|
|
41331
41331
|
const result = executeMutationAppleScript(
|
|
41332
41332
|
buildAppLevelScript(`show note id "${safeId}"${separatelyClause}`)
|
|
41333
41333
|
);
|
|
41334
41334
|
if (!result.success) {
|
|
41335
|
-
console.error(`Failed to show note with ID "${
|
|
41335
|
+
console.error(`Failed to show note with ID "${id2}":`, result.error);
|
|
41336
41336
|
return false;
|
|
41337
41337
|
}
|
|
41338
41338
|
return true;
|
|
@@ -41350,20 +41350,20 @@ var AppleNotesManager = class {
|
|
|
41350
41350
|
* @param id - CoreData URL identifier for the note
|
|
41351
41351
|
* @returns notes://showNote?identifier=<uuid> string, or null on failure
|
|
41352
41352
|
*/
|
|
41353
|
-
getNoteLinkById(
|
|
41354
|
-
const note = this.getNoteById(
|
|
41353
|
+
getNoteLinkById(id2) {
|
|
41354
|
+
const note = this.getNoteById(id2);
|
|
41355
41355
|
if (!note) return null;
|
|
41356
41356
|
if (note.passwordProtected) return null;
|
|
41357
|
-
const sqliteLink = getNoteLinkFromDB(
|
|
41357
|
+
const sqliteLink = getNoteLinkFromDB(id2);
|
|
41358
41358
|
if (sqliteLink) return sqliteLink;
|
|
41359
|
-
const safeId = sanitizeId(
|
|
41359
|
+
const safeId = sanitizeId(id2);
|
|
41360
41360
|
const result = executeAppleScript(
|
|
41361
41361
|
buildAppLevelScript(`return note link of (note id "${safeId}")`)
|
|
41362
41362
|
);
|
|
41363
41363
|
if (result.success && result.output.trim()) {
|
|
41364
41364
|
return result.output.trim();
|
|
41365
41365
|
}
|
|
41366
|
-
console.error(`Failed to get note link for ID "${
|
|
41366
|
+
console.error(`Failed to get note link for ID "${id2}":`, result.error);
|
|
41367
41367
|
return null;
|
|
41368
41368
|
}
|
|
41369
41369
|
/**
|
|
@@ -41388,14 +41388,14 @@ var AppleNotesManager = class {
|
|
|
41388
41388
|
* @param separately - Open in a separate window when supported by Notes.app
|
|
41389
41389
|
* @returns true if Notes.app accepted the show command, false otherwise
|
|
41390
41390
|
*/
|
|
41391
|
-
showFolderById(
|
|
41392
|
-
const safeId = sanitizeId(
|
|
41391
|
+
showFolderById(id2, separately = false) {
|
|
41392
|
+
const safeId = sanitizeId(id2);
|
|
41393
41393
|
const separatelyClause = separately ? " separately true" : "";
|
|
41394
41394
|
const result = executeMutationAppleScript(
|
|
41395
41395
|
buildAppLevelScript(`show folder id "${safeId}"${separatelyClause}`)
|
|
41396
41396
|
);
|
|
41397
41397
|
if (!result.success) {
|
|
41398
|
-
console.error(`Failed to show folder with ID "${
|
|
41398
|
+
console.error(`Failed to show folder with ID "${id2}":`, result.error);
|
|
41399
41399
|
return false;
|
|
41400
41400
|
}
|
|
41401
41401
|
return true;
|
|
@@ -41410,14 +41410,14 @@ var AppleNotesManager = class {
|
|
|
41410
41410
|
* @param separately - Open in a separate window when supported by Notes.app
|
|
41411
41411
|
* @returns true if Notes.app accepted the show command, false otherwise
|
|
41412
41412
|
*/
|
|
41413
|
-
showAccountById(
|
|
41414
|
-
const safeId = sanitizeId(
|
|
41413
|
+
showAccountById(id2, separately = false) {
|
|
41414
|
+
const safeId = sanitizeId(id2);
|
|
41415
41415
|
const separatelyClause = separately ? " separately true" : "";
|
|
41416
41416
|
const result = executeMutationAppleScript(
|
|
41417
41417
|
buildAppLevelScript(`show account id "${safeId}"${separatelyClause}`)
|
|
41418
41418
|
);
|
|
41419
41419
|
if (!result.success) {
|
|
41420
|
-
console.error(`Failed to show account with ID "${
|
|
41420
|
+
console.error(`Failed to show account with ID "${id2}":`, result.error);
|
|
41421
41421
|
return false;
|
|
41422
41422
|
}
|
|
41423
41423
|
return true;
|
|
@@ -41692,8 +41692,8 @@ var AppleNotesManager = class {
|
|
|
41692
41692
|
* attachments.forEach(a => console.log(`${a.name}: ${a.contentType}`));
|
|
41693
41693
|
* ```
|
|
41694
41694
|
*/
|
|
41695
|
-
listAttachmentsById(
|
|
41696
|
-
const safeId = sanitizeId(
|
|
41695
|
+
listAttachmentsById(id2) {
|
|
41696
|
+
const safeId = sanitizeId(id2);
|
|
41697
41697
|
const script = `
|
|
41698
41698
|
tell application "Notes"
|
|
41699
41699
|
set theNote to note id "${safeId}"
|
|
@@ -41738,7 +41738,7 @@ var AppleNotesManager = class {
|
|
|
41738
41738
|
const result = executeAppleScript(script);
|
|
41739
41739
|
if (!result.success) {
|
|
41740
41740
|
throw new Error(
|
|
41741
|
-
`Failed to list attachments for note ID "${
|
|
41741
|
+
`Failed to list attachments for note ID "${id2}": ${result.error ?? "unknown AppleScript error"}`
|
|
41742
41742
|
);
|
|
41743
41743
|
}
|
|
41744
41744
|
if (!result.output) {
|
|
@@ -41962,32 +41962,32 @@ var AppleNotesManager = class {
|
|
|
41962
41962
|
/**
|
|
41963
41963
|
* Result of a batch operation on a single item.
|
|
41964
41964
|
*/
|
|
41965
|
-
createBatchResult(
|
|
41966
|
-
return error2 ? { id, success, error: error2 } : { id, success };
|
|
41965
|
+
createBatchResult(id2, success, error2) {
|
|
41966
|
+
return error2 ? { id: id2, success, error: error2 } : { id: id2, success };
|
|
41967
41967
|
}
|
|
41968
41968
|
/**
|
|
41969
41969
|
* Maps a per-item status token emitted by a batch AppleScript loop to a
|
|
41970
41970
|
* BatchResult, preserving the human-readable error messages of the original
|
|
41971
41971
|
* per-note implementation. See {@link batchMoveNotes}.
|
|
41972
41972
|
*/
|
|
41973
|
-
mapBatchStatus(
|
|
41973
|
+
mapBatchStatus(id2, status, op) {
|
|
41974
41974
|
switch (status) {
|
|
41975
41975
|
case "ok":
|
|
41976
|
-
return this.createBatchResult(
|
|
41976
|
+
return this.createBatchResult(id2, true);
|
|
41977
41977
|
case "pw":
|
|
41978
|
-
return this.createBatchResult(
|
|
41978
|
+
return this.createBatchResult(id2, false, "Note is password-protected");
|
|
41979
41979
|
case "missing":
|
|
41980
|
-
return this.createBatchResult(
|
|
41980
|
+
return this.createBatchResult(id2, false, "Note not found");
|
|
41981
41981
|
case "fail":
|
|
41982
41982
|
return this.createBatchResult(
|
|
41983
|
-
|
|
41983
|
+
id2,
|
|
41984
41984
|
false,
|
|
41985
41985
|
op === "delete" ? "Deletion failed" : "Move failed"
|
|
41986
41986
|
);
|
|
41987
41987
|
case "wrongfolder":
|
|
41988
|
-
return this.createBatchResult(
|
|
41988
|
+
return this.createBatchResult(id2, false, "Destination folder verification failed");
|
|
41989
41989
|
default:
|
|
41990
|
-
return this.createBatchResult(
|
|
41990
|
+
return this.createBatchResult(id2, false, "Unknown error");
|
|
41991
41991
|
}
|
|
41992
41992
|
}
|
|
41993
41993
|
/**
|
|
@@ -42015,12 +42015,12 @@ var AppleNotesManager = class {
|
|
|
42015
42015
|
const destFolderRef = `${buildFolderReference(folder)} of ${AS_ACCOUNT_REF}`;
|
|
42016
42016
|
const results = new Array(ids.length);
|
|
42017
42017
|
const runnable = [];
|
|
42018
|
-
ids.forEach((
|
|
42018
|
+
ids.forEach((id2, i) => {
|
|
42019
42019
|
try {
|
|
42020
|
-
runnable.push({ index: i, safe: sanitizeNoteId(
|
|
42020
|
+
runnable.push({ index: i, safe: sanitizeNoteId(id2) });
|
|
42021
42021
|
} catch (e) {
|
|
42022
42022
|
results[i] = this.createBatchResult(
|
|
42023
|
-
|
|
42023
|
+
id2,
|
|
42024
42024
|
false,
|
|
42025
42025
|
e instanceof Error ? e.message : "Invalid note ID"
|
|
42026
42026
|
);
|
|
@@ -42284,12 +42284,12 @@ var AppleNotesManager = class {
|
|
|
42284
42284
|
* @param id - CoreData URL identifier for the note
|
|
42285
42285
|
* @returns Markdown content, or empty string if not found
|
|
42286
42286
|
*/
|
|
42287
|
-
getNoteMarkdownById(
|
|
42288
|
-
const html = this.getNoteContentById(
|
|
42287
|
+
getNoteMarkdownById(id2) {
|
|
42288
|
+
const html = this.getNoteContentById(id2);
|
|
42289
42289
|
if (!html) return "";
|
|
42290
|
-
const rich = enrichNoteRead(
|
|
42290
|
+
const rich = enrichNoteRead(id2, html);
|
|
42291
42291
|
let markdown = this.htmlToMarkdown(rich.content);
|
|
42292
|
-
const result = getChecklistItems(
|
|
42292
|
+
const result = getChecklistItems(id2);
|
|
42293
42293
|
if (result.items) {
|
|
42294
42294
|
markdown = this.enrichMarkdownWithChecklists(markdown, result.items);
|
|
42295
42295
|
}
|
|
@@ -42621,6 +42621,469 @@ function describeSearchScope(searchContent, resultCount) {
|
|
|
42621
42621
|
|
|
42622
42622
|
// src/tools/doctor.ts
|
|
42623
42623
|
import { spawnSync } from "child_process";
|
|
42624
|
+
|
|
42625
|
+
// src/services/nativeTags.ts
|
|
42626
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
42627
|
+
import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
|
|
42628
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
42629
|
+
import { join as join7 } from "node:path";
|
|
42630
|
+
var NATIVE_TAGS_SHORTCUT = "Apple Notes MCP - Native Tags";
|
|
42631
|
+
function normalizeNativeTags(tags) {
|
|
42632
|
+
if (!tags.length || tags.length > 100) throw new Error("Provide between 1 and 100 tags");
|
|
42633
|
+
return [
|
|
42634
|
+
...new Set(
|
|
42635
|
+
tags.map((value) => {
|
|
42636
|
+
const tag = value.normalize("NFC").replace(/^#/, "");
|
|
42637
|
+
if (tag.length > 100 || !new RegExp("^(?=.*\\p{L})[\\p{L}\\p{N}_-]+$", "u").test(tag))
|
|
42638
|
+
throw new Error(
|
|
42639
|
+
"Tags must contain a letter and only letters, digits, hyphens or underscores"
|
|
42640
|
+
);
|
|
42641
|
+
return tag;
|
|
42642
|
+
})
|
|
42643
|
+
)
|
|
42644
|
+
];
|
|
42645
|
+
}
|
|
42646
|
+
function addNativeTags(request, deps) {
|
|
42647
|
+
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(request.id))
|
|
42648
|
+
throw new Error("An exact CoreData note ID is required");
|
|
42649
|
+
if (request.scopeText.length < 12 || request.scopeText.length > 500 || /[\r\n\0]/u.test(request.scopeText))
|
|
42650
|
+
throw new Error("scopeText must be a stable, single-line project marker of 12\u2013500 characters");
|
|
42651
|
+
const tags = normalizeNativeTags(request.tags);
|
|
42652
|
+
const before = deps.read(request.id);
|
|
42653
|
+
if (before.contentHash !== request.expectedContentHash)
|
|
42654
|
+
throw new Error("Note revision changed; read it again");
|
|
42655
|
+
if (before.title !== request.title || !before.plaintext.includes(request.scopeText))
|
|
42656
|
+
throw new Error("The exact note does not match the title and project marker");
|
|
42657
|
+
const missing = tags.filter((tag) => !before.rich.nativeTags.includes(tag));
|
|
42658
|
+
if (!missing.length)
|
|
42659
|
+
return { nativeTags: before.rich.nativeTags, added: [], contentHash: before.contentHash };
|
|
42660
|
+
const candidates = deps.candidates(request.title, request.scopeText);
|
|
42661
|
+
if (candidates.length !== 1 || candidates[0] !== request.id)
|
|
42662
|
+
throw new Error(
|
|
42663
|
+
"Shortcuts selection is ambiguous or points to a different note; nothing was changed"
|
|
42664
|
+
);
|
|
42665
|
+
if (deps.read(request.id).contentHash !== request.expectedContentHash)
|
|
42666
|
+
throw new Error("Note revision changed during preflight; nothing was changed");
|
|
42667
|
+
let transportWarning;
|
|
42668
|
+
try {
|
|
42669
|
+
deps.run({ title: request.title, scopeText: request.scopeText, tags: missing });
|
|
42670
|
+
} catch (error2) {
|
|
42671
|
+
transportWarning = error2 instanceof Error ? error2.message : "Shortcuts completion was uncertain";
|
|
42672
|
+
}
|
|
42673
|
+
const after = deps.read(request.id);
|
|
42674
|
+
const allTags = [.../* @__PURE__ */ new Set([...before.rich.nativeTags, ...tags])];
|
|
42675
|
+
const textWithoutTags = (text) => {
|
|
42676
|
+
for (const tag of [...allTags].sort((a, b) => b.length - a.length)) {
|
|
42677
|
+
const escaped = tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
42678
|
+
text = text.replace(new RegExp(`#${escaped}(?![\\p{L}\\p{N}_-])`, "gu"), "");
|
|
42679
|
+
}
|
|
42680
|
+
return text.replace(/[\s\ufffc]/gu, "");
|
|
42681
|
+
};
|
|
42682
|
+
if (after.title !== before.title || allTags.some((tag) => !after.rich.nativeTags.includes(tag)) || textWithoutTags(before.rich.text) !== textWithoutTags(after.rich.text) || linkSignature(before.rich.links) !== linkSignature(after.rich.links) || before.rich.nativeObjectIds.some((id2) => !after.rich.nativeObjectIds.includes(id2)) || before.rich.hasChecklist !== after.rich.hasChecklist) {
|
|
42683
|
+
throw new Error(
|
|
42684
|
+
"Shortcuts ran, but exact-ID tags/text/links readback was not verified. Read the note before any retry"
|
|
42685
|
+
);
|
|
42686
|
+
}
|
|
42687
|
+
return {
|
|
42688
|
+
nativeTags: after.rich.nativeTags,
|
|
42689
|
+
added: missing,
|
|
42690
|
+
contentHash: after.contentHash,
|
|
42691
|
+
...transportWarning ? {
|
|
42692
|
+
transportWarning: "Shortcuts completion was uncertain, but all requested native tags and preserved content were verified by exact ID."
|
|
42693
|
+
} : {}
|
|
42694
|
+
};
|
|
42695
|
+
}
|
|
42696
|
+
function nativeTagsStatus(shortcut = process.env.APPLE_NOTES_MCP_TAGS_SHORTCUT || NATIVE_TAGS_SHORTCUT) {
|
|
42697
|
+
const lines = execFileSync6("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
|
|
42698
|
+
encoding: "utf8",
|
|
42699
|
+
timeout: 15e3,
|
|
42700
|
+
maxBuffer: 1024 * 1024,
|
|
42701
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
42702
|
+
}).split(/\r?\n/u);
|
|
42703
|
+
const matches = lines.flatMap((line) => {
|
|
42704
|
+
const match = /^(.*) \(([0-9A-Fa-f-]{36})\)$/.exec(line);
|
|
42705
|
+
return match && (match[1] === shortcut || match[2].toLowerCase() === shortcut.toLowerCase()) ? [{ name: match[1], identifier: match[2] }] : [];
|
|
42706
|
+
});
|
|
42707
|
+
return {
|
|
42708
|
+
shortcut,
|
|
42709
|
+
installed: matches.length === 1,
|
|
42710
|
+
identifier: matches.length === 1 ? matches[0].identifier : void 0
|
|
42711
|
+
};
|
|
42712
|
+
}
|
|
42713
|
+
function runNativeTagsShortcut(input) {
|
|
42714
|
+
const status = nativeTagsStatus();
|
|
42715
|
+
if (!status.installed)
|
|
42716
|
+
throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
|
|
42717
|
+
const directory = mkdtempSync2(join7(tmpdir2(), "apple-notes-native-tags-"));
|
|
42718
|
+
try {
|
|
42719
|
+
const path4 = join7(directory, "request.json");
|
|
42720
|
+
writeFileSync(path4, JSON.stringify(input), { mode: 384 });
|
|
42721
|
+
execFileSync6("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path4], {
|
|
42722
|
+
encoding: "utf8",
|
|
42723
|
+
timeout: 6e4,
|
|
42724
|
+
maxBuffer: 1024 * 1024,
|
|
42725
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
42726
|
+
});
|
|
42727
|
+
} catch {
|
|
42728
|
+
throw new Error(
|
|
42729
|
+
"Native tag operation did not complete cleanly (possibly waiting for macOS permission). Do not retry automatically; read the exact note and check Shortcuts"
|
|
42730
|
+
);
|
|
42731
|
+
} finally {
|
|
42732
|
+
rmSync2(directory, { recursive: true, force: true });
|
|
42733
|
+
}
|
|
42734
|
+
}
|
|
42735
|
+
|
|
42736
|
+
// src/services/backgroundNotes.ts
|
|
42737
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
42738
|
+
import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
|
|
42739
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
42740
|
+
import { join as join8 } from "node:path";
|
|
42741
|
+
|
|
42742
|
+
// src/utils/appendMarkdown.ts
|
|
42743
|
+
var escape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
42744
|
+
function appendMarkdownHtml(markdown) {
|
|
42745
|
+
if (markdown.includes("\uE000") || markdown.includes("\uE001"))
|
|
42746
|
+
throw new Error("Unsupported reserved characters");
|
|
42747
|
+
if (Array.from(markdown).some((c) => c.charCodeAt(0) < 32 && !["\n", "\r", " "].includes(c)))
|
|
42748
|
+
throw new Error("Unsupported control characters");
|
|
42749
|
+
if (/[`~|]|^#{4,}\s|^\s*>|^\s*\[.+\]:|!\[|<\/?[a-z]/im.test(markdown))
|
|
42750
|
+
throw new Error(
|
|
42751
|
+
"Markdown append supports paragraphs, headings, flat lists, emphasis and inline links; use semantic HTML for other formatting"
|
|
42752
|
+
);
|
|
42753
|
+
const inline = (text) => {
|
|
42754
|
+
const links = [];
|
|
42755
|
+
let value = text.replace(/\[([^\]\n]+)\]\(([^()\s]+)\)/g, (_s, label, url) => {
|
|
42756
|
+
if (!/^(https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url))
|
|
42757
|
+
throw new Error("Unsupported Markdown link URL");
|
|
42758
|
+
links.push(`<a href="${escape2(url)}">${escape2(label)}</a>`);
|
|
42759
|
+
return `\uE000${links.length - 1}\uE001`;
|
|
42760
|
+
});
|
|
42761
|
+
value = escape2(value).replace(/\*\*([^*\n]+)\*\*/g, "<b>$1</b>").replace(/\*([^*\n]+)\*/g, "<i>$1</i>");
|
|
42762
|
+
if (value.includes("[") || value.includes("]") || value.includes("*"))
|
|
42763
|
+
throw new Error("Unsupported or unbalanced Markdown inline syntax");
|
|
42764
|
+
return value.replace(/\uE000(\d+)\uE001/g, (_s, i) => links[Number(i)]);
|
|
42765
|
+
};
|
|
42766
|
+
let html = "", list;
|
|
42767
|
+
const close = () => {
|
|
42768
|
+
if (list) {
|
|
42769
|
+
html += `</${list}>`;
|
|
42770
|
+
list = void 0;
|
|
42771
|
+
}
|
|
42772
|
+
};
|
|
42773
|
+
for (const line of markdown.replace(/\r\n/g, "\n").split("\n")) {
|
|
42774
|
+
if (/^\s{2,}\S/.test(line)) throw new Error("Nested lists and indented code are unsupported");
|
|
42775
|
+
const heading = /^(#{1,3})\s+(.+)$/.exec(line), item = /^(?:([-+*])|\d+\.)\s+(.+)$/.exec(line);
|
|
42776
|
+
if (item) {
|
|
42777
|
+
const kind = item[1] ? "ul" : "ol";
|
|
42778
|
+
if (list !== kind) {
|
|
42779
|
+
close();
|
|
42780
|
+
list = kind;
|
|
42781
|
+
html += `<${kind}>`;
|
|
42782
|
+
}
|
|
42783
|
+
html += `<li>${inline(item[2])}</li>`;
|
|
42784
|
+
continue;
|
|
42785
|
+
}
|
|
42786
|
+
close();
|
|
42787
|
+
html += heading ? `<h${heading[1].length}>${inline(heading[2])}</h${heading[1].length}>` : line.trim() ? `<div>${inline(line)}</div>` : "<div><br></div>";
|
|
42788
|
+
}
|
|
42789
|
+
close();
|
|
42790
|
+
return html;
|
|
42791
|
+
}
|
|
42792
|
+
|
|
42793
|
+
// src/utils/noteRevision.ts
|
|
42794
|
+
var INLINE_TAG = /^<\/?(?:b|i|u|s|strike|em|strong|span|a|font|sub|sup|code|tt|small|big|mark)\b/i;
|
|
42795
|
+
function comparableVisibleText(html) {
|
|
42796
|
+
return html.replace(/<br\s*\/?\s*>/gi, " ").replace(/<[^>]*>/g, (tag) => INLINE_TAG.test(tag) ? "" : " ").replace(/ | /gi, " ").replace(/"/gi, '"').replace(/'|'/gi, "'").replace(/</gi, "<").replace(/>/gi, ">").replace(/&/gi, "&").replace(/&#(\d+);/g, (_match, codePoint) => String.fromCodePoint(Number(codePoint))).replace(
|
|
42797
|
+
/&#x([0-9a-f]+);/gi,
|
|
42798
|
+
(_match, codePoint) => String.fromCodePoint(Number.parseInt(codePoint, 16))
|
|
42799
|
+
).replace(/\s+/g, " ").trim();
|
|
42800
|
+
}
|
|
42801
|
+
|
|
42802
|
+
// src/services/backgroundNotes.ts
|
|
42803
|
+
var BACKGROUND_SHORTCUT = "Apple Notes MCP - Background Operations v5";
|
|
42804
|
+
var backgroundStatus = () => nativeTagsStatus(process.env.APPLE_NOTES_MCP_BACKGROUND_SHORTCUT || BACKGROUND_SHORTCUT);
|
|
42805
|
+
var nativeTagBridgeStatus = () => nativeTagsStatus();
|
|
42806
|
+
function readBackgroundSnapshot(manager, id2) {
|
|
42807
|
+
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(id2))
|
|
42808
|
+
throw new Error("Exact note ID required");
|
|
42809
|
+
const note = manager.getNoteById(id2);
|
|
42810
|
+
if (!note) throw new Error("Note not found");
|
|
42811
|
+
if (note.passwordProtected) throw new Error("Locked notes are unavailable in background mode");
|
|
42812
|
+
const html = manager.getNoteContentById(id2);
|
|
42813
|
+
const enriched = enrichNoteRead(id2, html);
|
|
42814
|
+
const rich = readRichNote(id2);
|
|
42815
|
+
if (enriched.revision !== rich.revision)
|
|
42816
|
+
throw new Error("Note changed during read; read it again");
|
|
42817
|
+
return {
|
|
42818
|
+
id: id2,
|
|
42819
|
+
title: note.title,
|
|
42820
|
+
hash: richContentHash(html, enriched),
|
|
42821
|
+
html,
|
|
42822
|
+
rich,
|
|
42823
|
+
pinned: getNoteMetadata(id2).metadata?.pinned,
|
|
42824
|
+
checklist: getChecklistItems(id2).items || []
|
|
42825
|
+
};
|
|
42826
|
+
}
|
|
42827
|
+
function validateAppendContent(content, format) {
|
|
42828
|
+
if (!content || content.length > 1024 * 1024 || content.includes("\0"))
|
|
42829
|
+
throw new Error("Invalid append content (limit 1 MiB)");
|
|
42830
|
+
if (format === "html") {
|
|
42831
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
42832
|
+
"div",
|
|
42833
|
+
"p",
|
|
42834
|
+
"br",
|
|
42835
|
+
"b",
|
|
42836
|
+
"strong",
|
|
42837
|
+
"i",
|
|
42838
|
+
"em",
|
|
42839
|
+
"u",
|
|
42840
|
+
"s",
|
|
42841
|
+
"del",
|
|
42842
|
+
"h1",
|
|
42843
|
+
"h2",
|
|
42844
|
+
"h3",
|
|
42845
|
+
"ul",
|
|
42846
|
+
"ol",
|
|
42847
|
+
"li",
|
|
42848
|
+
"a",
|
|
42849
|
+
"table",
|
|
42850
|
+
"thead",
|
|
42851
|
+
"tbody",
|
|
42852
|
+
"tr",
|
|
42853
|
+
"td",
|
|
42854
|
+
"th"
|
|
42855
|
+
]);
|
|
42856
|
+
for (const tag of content.matchAll(/<\/?\s*([a-z][a-z0-9]*)\b([^>]*)>/gi)) {
|
|
42857
|
+
if (!allowed.has(tag[1].toLowerCase()))
|
|
42858
|
+
throw new Error(`Unsupported HTML element: ${tag[1]}`);
|
|
42859
|
+
let attrs = tag[2].replace(/\/$/, "").trim();
|
|
42860
|
+
if (tag[1].toLowerCase() === "a")
|
|
42861
|
+
attrs = attrs.replace(/\bhref\s*=\s*(["'])(.*?)\1/gi, (_s, _q, url) => {
|
|
42862
|
+
if (!/^(https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url) || Array.from(url).some((c) => c.charCodeAt(0) < 33))
|
|
42863
|
+
throw new Error("Unsupported link URL");
|
|
42864
|
+
return "";
|
|
42865
|
+
});
|
|
42866
|
+
if (attrs.trim())
|
|
42867
|
+
throw new Error(
|
|
42868
|
+
"Unsupported HTML attributes; use semantic formatting and explicit blank paragraphs"
|
|
42869
|
+
);
|
|
42870
|
+
}
|
|
42871
|
+
if (/<!--|<!|<\?|<[^>]*$/u.test(content)) throw new Error("Unsupported HTML markup");
|
|
42872
|
+
}
|
|
42873
|
+
if (format === "markdown" && /!\[|<\/?[a-z]|\]\(\s*(?:javascript|data|file):/i.test(content))
|
|
42874
|
+
throw new Error("Markdown images, raw HTML and local/executable links are unsupported");
|
|
42875
|
+
}
|
|
42876
|
+
function runBackgroundShortcut(input) {
|
|
42877
|
+
const status = backgroundStatus();
|
|
42878
|
+
if (!status.installed)
|
|
42879
|
+
throw new Error("Install the supplied Background Operations shortcut once");
|
|
42880
|
+
const directory = mkdtempSync3(join8(tmpdir3(), "apple-notes-background-"));
|
|
42881
|
+
try {
|
|
42882
|
+
const file = join8(directory, "request.json");
|
|
42883
|
+
writeFileSync2(
|
|
42884
|
+
file,
|
|
42885
|
+
JSON.stringify({
|
|
42886
|
+
operation: "",
|
|
42887
|
+
title: "",
|
|
42888
|
+
scopeText: "",
|
|
42889
|
+
text: "",
|
|
42890
|
+
objectId: "",
|
|
42891
|
+
objectName: "",
|
|
42892
|
+
change: "",
|
|
42893
|
+
otherTitle: "",
|
|
42894
|
+
otherScope: "",
|
|
42895
|
+
tag: "",
|
|
42896
|
+
size: "",
|
|
42897
|
+
...input
|
|
42898
|
+
}),
|
|
42899
|
+
{ mode: 384 }
|
|
42900
|
+
);
|
|
42901
|
+
execFileSync7("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
|
|
42902
|
+
encoding: "utf8",
|
|
42903
|
+
timeout: 6e4,
|
|
42904
|
+
maxBuffer: 1024 * 1024,
|
|
42905
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
42906
|
+
});
|
|
42907
|
+
} finally {
|
|
42908
|
+
rmSync3(directory, { recursive: true, force: true });
|
|
42909
|
+
}
|
|
42910
|
+
}
|
|
42911
|
+
function backgroundDependencies(manager) {
|
|
42912
|
+
return {
|
|
42913
|
+
read: (id2) => readBackgroundSnapshot(manager, id2),
|
|
42914
|
+
run: runBackgroundShortcut,
|
|
42915
|
+
candidates: (title, scope) => manager.listAccounts().flatMap(
|
|
42916
|
+
(account) => manager.searchNotes(title, false, account.name).filter(
|
|
42917
|
+
(note) => note.title === title && !note.passwordProtected && manager.getNotePlaintextById(note.id).includes(scope)
|
|
42918
|
+
).map((note) => note.id)
|
|
42919
|
+
)
|
|
42920
|
+
};
|
|
42921
|
+
}
|
|
42922
|
+
function assertPreserved(before, after, options = {}) {
|
|
42923
|
+
if (before.id !== after.id || before.title !== after.title)
|
|
42924
|
+
throw new Error("Note identity changed");
|
|
42925
|
+
const tidy = (s) => s.replace(/\r\n/g, "\n").replace(/[\s\ufffc]+$/gu, "");
|
|
42926
|
+
let oldText = tidy(before.rich.text), newText = tidy(after.rich.text);
|
|
42927
|
+
if (options.tagChange) {
|
|
42928
|
+
const escaped = options.tagChange.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
42929
|
+
const strip = (s) => s.replace(new RegExp(`#${escaped}(?![\\p{L}\\p{N}_-])`, "gu"), "").replace(/[\s\ufffc]/gu, "");
|
|
42930
|
+
oldText = strip(oldText);
|
|
42931
|
+
newText = strip(newText);
|
|
42932
|
+
}
|
|
42933
|
+
if (options.append ? !newText.startsWith(oldText) : newText !== oldText)
|
|
42934
|
+
throw new Error("Existing note text was not preserved");
|
|
42935
|
+
const links = options.append ? after.rich.links.slice(0, before.rich.links.length) : after.rich.links;
|
|
42936
|
+
if (linkSignature(before.rich.links) !== linkSignature(links))
|
|
42937
|
+
throw new Error("Existing links were not preserved");
|
|
42938
|
+
if (!options.tagChange && before.rich.nativeObjectIds.some((id2) => !after.rich.nativeObjectIds.includes(id2)))
|
|
42939
|
+
throw new Error("Existing native object was lost");
|
|
42940
|
+
if (JSON.stringify(before.checklist) !== JSON.stringify(
|
|
42941
|
+
options.append ? after.checklist.slice(0, before.checklist.length) : after.checklist
|
|
42942
|
+
))
|
|
42943
|
+
throw new Error("Existing checklist items changed");
|
|
42944
|
+
for (const item of before.rich.checklistItems || []) {
|
|
42945
|
+
const actual = after.rich.checklistItems?.find((current) => current.id === item.id);
|
|
42946
|
+
if (!actual || actual.text !== item.text || actual.done !== item.done)
|
|
42947
|
+
throw new Error("Existing checklist item identity or state changed");
|
|
42948
|
+
}
|
|
42949
|
+
if (before.rich.nativeTags.filter((t) => t !== options.tagChange).some((t) => !after.rich.nativeTags.includes(t)))
|
|
42950
|
+
throw new Error("Existing native tag was lost");
|
|
42951
|
+
const allowedRemoved = options.tagChange ? before.rich.nativeTagObjectIds?.[options.tagChange] || [] : [];
|
|
42952
|
+
for (const object3 of before.rich.objectData || []) {
|
|
42953
|
+
if (allowedRemoved.includes(object3.id)) continue;
|
|
42954
|
+
const actual = after.rich.objectData?.find((o) => o.id === object3.id);
|
|
42955
|
+
if (!actual || actual.mergeable !== object3.mergeable || actual.view !== object3.view)
|
|
42956
|
+
throw new Error("Existing native object content or presentation changed");
|
|
42957
|
+
}
|
|
42958
|
+
if (!options.tagChange && before.rich.styleRuns && after.rich.styleRuns) {
|
|
42959
|
+
const end = before.rich.text.trimEnd().length;
|
|
42960
|
+
for (const oldRun of before.rich.styleRuns)
|
|
42961
|
+
for (const newRun of after.rich.styleRuns) {
|
|
42962
|
+
const overlapStart = Math.max(oldRun.start, newRun.start);
|
|
42963
|
+
const overlapEnd = Math.min(
|
|
42964
|
+
end,
|
|
42965
|
+
oldRun.start + oldRun.length,
|
|
42966
|
+
newRun.start + newRun.length
|
|
42967
|
+
);
|
|
42968
|
+
if (overlapStart < overlapEnd && /[^\s\ufffc]/u.test(before.rich.text.slice(overlapStart, overlapEnd)) && oldRun.signature !== newRun.signature)
|
|
42969
|
+
throw new Error("Existing rich formatting changed");
|
|
42970
|
+
}
|
|
42971
|
+
}
|
|
42972
|
+
}
|
|
42973
|
+
function mutateBackground(request, operation, data, verify, deps) {
|
|
42974
|
+
if (request.scopeText.length < 12 || request.scopeText.length > 500 || /[\r\n\0]/u.test(request.scopeText))
|
|
42975
|
+
throw new Error("Use a distinctive existing single-line scope of 12\u2013500 characters");
|
|
42976
|
+
const before = deps.read(request.id);
|
|
42977
|
+
if (before.hash !== request.expectedContentHash)
|
|
42978
|
+
throw new Error("Note revision changed; read it again");
|
|
42979
|
+
if (!before.rich.text.includes(request.scopeText))
|
|
42980
|
+
throw new Error("Scope is absent from exact note");
|
|
42981
|
+
const candidates = deps.candidates(before.title, request.scopeText);
|
|
42982
|
+
if (candidates.length !== 1 || candidates[0] !== request.id)
|
|
42983
|
+
throw new Error("Ambiguous note selection; nothing changed");
|
|
42984
|
+
if (deps.read(request.id).hash !== before.hash)
|
|
42985
|
+
throw new Error("Note revision changed during preflight");
|
|
42986
|
+
let transportUncertain = false;
|
|
42987
|
+
let transportMessage = "";
|
|
42988
|
+
try {
|
|
42989
|
+
deps.run({ ...data, operation, title: before.title, scopeText: request.scopeText });
|
|
42990
|
+
} catch (error2) {
|
|
42991
|
+
transportUncertain = true;
|
|
42992
|
+
const detail = error2;
|
|
42993
|
+
transportMessage = detail?.code === "ETIMEDOUT" ? "Shortcuts timed out; check for an interactive parameter or permission request" : String(detail?.stderr || detail?.message || "Shortcuts failed").trim().slice(0, 500);
|
|
42994
|
+
}
|
|
42995
|
+
const after = deps.read(request.id);
|
|
42996
|
+
try {
|
|
42997
|
+
verify(before, after);
|
|
42998
|
+
} catch (error2) {
|
|
42999
|
+
throw new Error(
|
|
43000
|
+
`Operation outcome uncertain; read exact note before any retry: ${error2 instanceof Error ? error2.message : "readback failed"}${transportMessage ? "; " + transportMessage : ""}`
|
|
43001
|
+
);
|
|
43002
|
+
}
|
|
43003
|
+
return {
|
|
43004
|
+
ok: true,
|
|
43005
|
+
id: request.id,
|
|
43006
|
+
previousContentHash: before.hash,
|
|
43007
|
+
contentHash: after.hash,
|
|
43008
|
+
...transportUncertain ? {
|
|
43009
|
+
transportWarning: "Transport was uncertain; exact-ID readback verified the requested result"
|
|
43010
|
+
} : {}
|
|
43011
|
+
};
|
|
43012
|
+
}
|
|
43013
|
+
function assertAppendedHtmlLinks(previousCount, links, html) {
|
|
43014
|
+
const remaining = links.slice(previousCount);
|
|
43015
|
+
for (const expected of htmlLinks(html)) {
|
|
43016
|
+
const index = remaining.findIndex(
|
|
43017
|
+
(link) => linkSignature([link]) === linkSignature([expected])
|
|
43018
|
+
);
|
|
43019
|
+
if (index < 0) throw new Error("Appended HTML link not verified");
|
|
43020
|
+
remaining.splice(index, 1);
|
|
43021
|
+
}
|
|
43022
|
+
}
|
|
43023
|
+
function assertAppendedVisibleText(beforeHtml, afterHtml, expected) {
|
|
43024
|
+
const before = comparableVisibleText(beforeHtml);
|
|
43025
|
+
const after = comparableVisibleText(afterHtml);
|
|
43026
|
+
if (!after.startsWith(before)) throw new Error("Appended text not verified");
|
|
43027
|
+
const suffix = after.slice(before.length).replace(/\s+/gu, " ").trim();
|
|
43028
|
+
const wanted = expected.replace(/\s+/gu, " ").trim();
|
|
43029
|
+
if (!suffix || !suffix.includes(wanted)) throw new Error("Appended text not verified");
|
|
43030
|
+
}
|
|
43031
|
+
function appendNative(manager, request) {
|
|
43032
|
+
validateAppendContent(request.content, request.format);
|
|
43033
|
+
if (request.format === "markdown")
|
|
43034
|
+
return appendNative(manager, {
|
|
43035
|
+
...request,
|
|
43036
|
+
content: appendMarkdownHtml(request.content),
|
|
43037
|
+
format: "html"
|
|
43038
|
+
});
|
|
43039
|
+
if (request.format === "html" && /<table\b/i.test(request.content))
|
|
43040
|
+
throw new Error("Use create-table for verified native table insertion");
|
|
43041
|
+
const op = request.format === "plaintext" ? "append-text" : request.format === "html" ? "append-html" : "append-markdown";
|
|
43042
|
+
const text = request.format === "html" ? "<div><br></div>" + request.content : "\n\n" + request.content;
|
|
43043
|
+
return mutateBackground(
|
|
43044
|
+
request,
|
|
43045
|
+
op,
|
|
43046
|
+
{ text },
|
|
43047
|
+
(before, after) => {
|
|
43048
|
+
assertPreserved(before, after, { append: true });
|
|
43049
|
+
const expected = request.format === "html" ? comparableVisibleText(request.content) : request.format === "plaintext" ? request.content : null;
|
|
43050
|
+
if (expected) assertAppendedVisibleText(before.html, after.html, expected);
|
|
43051
|
+
if (request.format === "html")
|
|
43052
|
+
assertAppendedHtmlLinks(before.rich.links.length, after.rich.links, request.content);
|
|
43053
|
+
},
|
|
43054
|
+
backgroundDependencies(manager)
|
|
43055
|
+
);
|
|
43056
|
+
}
|
|
43057
|
+
function setNativeTag(manager, request) {
|
|
43058
|
+
const tag = normalizeNativeTags([request.tag])[0];
|
|
43059
|
+
const initial = readBackgroundSnapshot(manager, request.id);
|
|
43060
|
+
if (initial.hash !== request.expectedContentHash)
|
|
43061
|
+
throw new Error("Note revision changed; read it again");
|
|
43062
|
+
if (initial.rich.nativeTags.includes(tag) === request.present)
|
|
43063
|
+
return { ok: true, id: request.id, contentHash: initial.hash, changed: false };
|
|
43064
|
+
const deps = backgroundDependencies(manager);
|
|
43065
|
+
if (request.present)
|
|
43066
|
+
deps.run = ({ title, scopeText }) => runNativeTagsShortcut({ title, scopeText, tags: [tag] });
|
|
43067
|
+
return mutateBackground(
|
|
43068
|
+
request,
|
|
43069
|
+
request.present ? "add-tag" : "remove-tag",
|
|
43070
|
+
{ tag },
|
|
43071
|
+
(before, after) => {
|
|
43072
|
+
assertPreserved(before, after, { tagChange: tag });
|
|
43073
|
+
if (after.rich.nativeTags.includes(tag) !== request.present)
|
|
43074
|
+
throw new Error("Native tag state not verified");
|
|
43075
|
+
const removed = before.rich.nativeObjectIds.filter(
|
|
43076
|
+
(id2) => !after.rich.nativeObjectIds.includes(id2)
|
|
43077
|
+
);
|
|
43078
|
+
const allowed = request.present ? [] : before.rich.nativeTagObjectIds?.[tag] || [];
|
|
43079
|
+
if (removed.some((id2) => !allowed.includes(id2)))
|
|
43080
|
+
throw new Error("Unrelated native object was lost");
|
|
43081
|
+
},
|
|
43082
|
+
deps
|
|
43083
|
+
);
|
|
43084
|
+
}
|
|
43085
|
+
|
|
43086
|
+
// src/tools/doctor.ts
|
|
42624
43087
|
function runDoctor(manager) {
|
|
42625
43088
|
const checks = [];
|
|
42626
43089
|
const hc = manager.healthCheck();
|
|
@@ -42651,6 +43114,23 @@ function runDoctor(manager) {
|
|
|
42651
43114
|
status: fda ? "ok" : "warn",
|
|
42652
43115
|
detail: fda ? "granted \u2014 the Notes database is readable (checklist state, note metadata, note links, sync detail)" : `not granted \u2014 get-checklist-state, get-note-metadata, and the checklist annotations in get-note-markdown won't work; get-note-link fails on macOS 26+ (macOS 12-15 falls back to AppleScript); get-sync-status still answers but cannot see pending uploads. Everything else is pure AppleScript and is unaffected. In System Settings > Privacy & Security > Full Disk Access, grant access to the app that launches this server (Claude Desktop / Terminal / iTerm2), then fully quit and relaunch it and re-run doctor. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL}`
|
|
42653
43116
|
});
|
|
43117
|
+
try {
|
|
43118
|
+
const bridgeStatuses = [NATIVE_TAGS_SHORTCUT, BACKGROUND_SHORTCUT].map(
|
|
43119
|
+
(name) => nativeTagsStatus(name)
|
|
43120
|
+
);
|
|
43121
|
+
const missing = bridgeStatuses.filter((status) => !status.installed);
|
|
43122
|
+
checks.push({
|
|
43123
|
+
name: "Native write Shortcuts",
|
|
43124
|
+
status: missing.length ? "warn" : "ok",
|
|
43125
|
+
detail: missing.length ? `missing: ${missing.map((status) => status.shortcut).join(", ")}. Run apple-notes-mcp setup and approve Add Shortcut in macOS` : "both native-write bridges are installed"
|
|
43126
|
+
});
|
|
43127
|
+
} catch (error2) {
|
|
43128
|
+
checks.push({
|
|
43129
|
+
name: "Native write Shortcuts",
|
|
43130
|
+
status: "warn",
|
|
43131
|
+
detail: `could not inspect Shortcuts: ${String(error2)}. Run apple-notes-mcp setup --check`
|
|
43132
|
+
});
|
|
43133
|
+
}
|
|
42654
43134
|
checks.push(checkNodeRuntimeSignature());
|
|
42655
43135
|
const healthy = !checks.some((c) => c.status === "fail");
|
|
42656
43136
|
return { healthy, checks };
|
|
@@ -42694,12 +43174,12 @@ function formatDoctorReport(r) {
|
|
|
42694
43174
|
|
|
42695
43175
|
// src/services/fileConfig.ts
|
|
42696
43176
|
import { existsSync as existsSync6, readFileSync as readFileSync2 } from "fs";
|
|
42697
|
-
import { join as
|
|
43177
|
+
import { join as join9 } from "path";
|
|
42698
43178
|
import { homedir as homedir7 } from "os";
|
|
42699
43179
|
function fileConfigPath(env = process.env) {
|
|
42700
43180
|
const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
|
|
42701
43181
|
if (override && override.trim()) return override.trim();
|
|
42702
|
-
return
|
|
43182
|
+
return join9(homedir7(), "Library", "Application Support", "apple-notes-mcp", "config.json");
|
|
42703
43183
|
}
|
|
42704
43184
|
function loadFileConfig(env = process.env, path4 = fileConfigPath(env)) {
|
|
42705
43185
|
const applied = [];
|
|
@@ -42739,8 +43219,8 @@ function registerResourcesAndPrompts(server2, manager) {
|
|
|
42739
43219
|
"note",
|
|
42740
43220
|
new ResourceTemplate("notes://note/{id}", { list: void 0 }),
|
|
42741
43221
|
(uri, variables) => {
|
|
42742
|
-
const
|
|
42743
|
-
const markdown = manager.getNoteMarkdownById(
|
|
43222
|
+
const id2 = decodeURIComponent(String(variables.id));
|
|
43223
|
+
const markdown = manager.getNoteMarkdownById(id2);
|
|
42744
43224
|
return {
|
|
42745
43225
|
contents: [{ uri: uri.href, mimeType: "text/markdown", text: markdown || "(not found)" }]
|
|
42746
43226
|
};
|
|
@@ -42906,15 +43386,6 @@ function withJsonSchema2020_12(transport2) {
|
|
|
42906
43386
|
return transport2;
|
|
42907
43387
|
}
|
|
42908
43388
|
|
|
42909
|
-
// src/utils/noteRevision.ts
|
|
42910
|
-
var INLINE_TAG = /^<\/?(?:b|i|u|s|strike|em|strong|span|a|font|sub|sup|code|tt|small|big|mark)\b/i;
|
|
42911
|
-
function comparableVisibleText(html) {
|
|
42912
|
-
return html.replace(/<br\s*\/?\s*>/gi, " ").replace(/<[^>]*>/g, (tag) => INLINE_TAG.test(tag) ? "" : " ").replace(/ | /gi, " ").replace(/"/gi, '"').replace(/'|'/gi, "'").replace(/</gi, "<").replace(/>/gi, ">").replace(/&/gi, "&").replace(/&#(\d+);/g, (_match, codePoint) => String.fromCodePoint(Number(codePoint))).replace(
|
|
42913
|
-
/&#x([0-9a-f]+);/gi,
|
|
42914
|
-
(_match, codePoint) => String.fromCodePoint(Number.parseInt(codePoint, 16))
|
|
42915
|
-
).replace(/\s+/g, " ").trim();
|
|
42916
|
-
}
|
|
42917
|
-
|
|
42918
43389
|
// src/utils/noteTables.ts
|
|
42919
43390
|
import { gunzipSync as gunzipSync3 } from "node:zlib";
|
|
42920
43391
|
var sub = (f, n) => {
|
|
@@ -42960,8 +43431,8 @@ function parseNoteTable(compressed) {
|
|
|
42960
43431
|
const ordering = sub(sub(entry(ref), 16), 1), array2 = sub(ordering, 1);
|
|
42961
43432
|
const ids = many(array2, 2).map((a) => hex(getField(a, 2)));
|
|
42962
43433
|
const map = /* @__PURE__ */ new Map();
|
|
42963
|
-
ids.forEach((
|
|
42964
|
-
const index = uuids.indexOf(
|
|
43434
|
+
ids.forEach((id2, i) => {
|
|
43435
|
+
const index = uuids.indexOf(id2);
|
|
42965
43436
|
if (index < 0) throw new Error("Missing dimension UUID");
|
|
42966
43437
|
map.set(index, i);
|
|
42967
43438
|
});
|
|
@@ -43018,27 +43489,27 @@ import {
|
|
|
43018
43489
|
closeSync,
|
|
43019
43490
|
constants,
|
|
43020
43491
|
fstatSync,
|
|
43021
|
-
mkdtempSync as
|
|
43492
|
+
mkdtempSync as mkdtempSync4,
|
|
43022
43493
|
openSync,
|
|
43023
43494
|
readFileSync as readFileSync3,
|
|
43024
|
-
rmSync as
|
|
43025
|
-
writeFileSync
|
|
43495
|
+
rmSync as rmSync4,
|
|
43496
|
+
writeFileSync as writeFileSync3
|
|
43026
43497
|
} from "node:fs";
|
|
43027
|
-
import { tmpdir as
|
|
43028
|
-
import { basename, isAbsolute as isAbsolute2, join as
|
|
43498
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
43499
|
+
import { basename, isAbsolute as isAbsolute2, join as join10 } from "node:path";
|
|
43029
43500
|
var noteId = external_exports.string().regex(/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i);
|
|
43030
43501
|
var revision = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
43031
|
-
function readSnapshot(manager,
|
|
43032
|
-
const note = manager.getNoteById(
|
|
43502
|
+
function readSnapshot(manager, id2) {
|
|
43503
|
+
const note = manager.getNoteById(id2);
|
|
43033
43504
|
if (!note) throw new Error("Note not found");
|
|
43034
43505
|
if (note.passwordProtected) throw new Error("Locked notes cannot receive attachments");
|
|
43035
|
-
const body = manager.getNoteContentById(
|
|
43506
|
+
const body = manager.getNoteContentById(id2);
|
|
43036
43507
|
if (!body) throw new Error("Note content is unavailable");
|
|
43037
|
-
const enriched = enrichNoteRead(
|
|
43038
|
-
const rich = readRichNote(
|
|
43508
|
+
const enriched = enrichNoteRead(id2, body);
|
|
43509
|
+
const rich = readRichNote(id2);
|
|
43039
43510
|
if (!enriched.complete || enriched.revision !== rich.revision)
|
|
43040
43511
|
throw new Error("Native metadata changed during read; read the note again");
|
|
43041
|
-
return { id, title: note.title, body, rich, hash: richContentHash(body, enriched) };
|
|
43512
|
+
return { id: id2, title: note.title, body, rich, hash: richContentHash(body, enriched) };
|
|
43042
43513
|
}
|
|
43043
43514
|
function assertExistingContentPreserved(before, after) {
|
|
43044
43515
|
if (before.id !== after.id || before.title !== after.title)
|
|
@@ -43048,7 +43519,7 @@ function assertExistingContentPreserved(before, after) {
|
|
|
43048
43519
|
throw new Error("Existing note text was not preserved");
|
|
43049
43520
|
if (linkSignature(before.rich.links) !== linkSignature(after.rich.links.slice(0, before.rich.links.length)))
|
|
43050
43521
|
throw new Error("Existing links were not preserved");
|
|
43051
|
-
if (before.rich.nativeObjectIds.some((
|
|
43522
|
+
if (before.rich.nativeObjectIds.some((id2) => !after.rich.nativeObjectIds.includes(id2)))
|
|
43052
43523
|
throw new Error("Existing native object was lost");
|
|
43053
43524
|
if (before.rich.nativeTags.some((tag) => !after.rich.nativeTags.includes(tag)))
|
|
43054
43525
|
throw new Error("Existing native tag was lost");
|
|
@@ -43113,7 +43584,7 @@ function registerDirectOperations(server2, manager) {
|
|
|
43113
43584
|
"get-folder-by-id",
|
|
43114
43585
|
"Use when: reading the exact folder name and parent before a guarded rename.\nReturns: folder id, current name, and parent id.\nDo not use when: listing folders by path (list-folders).\nSafety: read-only.",
|
|
43115
43586
|
{ id: external_exports.string().max(2e3) },
|
|
43116
|
-
({ id }) => manager.getFolderById(
|
|
43587
|
+
({ id: id2 }) => manager.getFolderById(id2),
|
|
43117
43588
|
true
|
|
43118
43589
|
);
|
|
43119
43590
|
tool(
|
|
@@ -43134,28 +43605,28 @@ function registerDirectOperations(server2, manager) {
|
|
|
43134
43605
|
"add-attachment",
|
|
43135
43606
|
"Use when: adding one local file to an exact note without replacing its body.\nReturns: the new attachment id, byte count, and post-write content hash after exact byte verification.\nDo not use when: reading or exporting an existing attachment.\nSafety: requires a fresh rich revision, copies at most 64 MiB through a private temporary file, never retries insertion, and verifies existing content plus fetched bytes.",
|
|
43136
43607
|
{ id: noteId, expectedContentHash: revision, path: external_exports.string().min(1).max(4096) },
|
|
43137
|
-
({ id, expectedContentHash, path: path4 }) => {
|
|
43138
|
-
const before = readSnapshot(manager,
|
|
43608
|
+
({ id: id2, expectedContentHash, path: path4 }) => {
|
|
43609
|
+
const before = readSnapshot(manager, id2);
|
|
43139
43610
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
43140
43611
|
const bytes = localAttachment(path4);
|
|
43141
|
-
const beforeAttachments = manager.listAttachmentsById(
|
|
43142
|
-
const directory =
|
|
43143
|
-
const temporaryFile =
|
|
43612
|
+
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
43613
|
+
const directory = mkdtempSync4(join10(tmpdir4(), "notes-attachment-add-"));
|
|
43614
|
+
const temporaryFile = join10(directory, basename(path4));
|
|
43144
43615
|
try {
|
|
43145
|
-
|
|
43146
|
-
if (readSnapshot(manager,
|
|
43616
|
+
writeFileSync3(temporaryFile, bytes, { mode: 384 });
|
|
43617
|
+
if (readSnapshot(manager, id2).hash !== before.hash)
|
|
43147
43618
|
throw new Error("Note revision changed");
|
|
43148
43619
|
let returnedId;
|
|
43149
43620
|
let transportUncertain = false;
|
|
43150
43621
|
try {
|
|
43151
|
-
returnedId = manager.addAttachmentById(
|
|
43622
|
+
returnedId = manager.addAttachmentById(id2, before.body, temporaryFile);
|
|
43152
43623
|
} catch {
|
|
43153
43624
|
transportUncertain = true;
|
|
43154
43625
|
}
|
|
43155
|
-
const after = readSnapshot(manager,
|
|
43626
|
+
const after = readSnapshot(manager, id2);
|
|
43156
43627
|
assertExistingContentPreserved(before, after);
|
|
43157
43628
|
const readInserted = () => [
|
|
43158
|
-
...new Map(manager.listAttachmentsById(
|
|
43629
|
+
...new Map(manager.listAttachmentsById(id2).map((item) => [item.id, item])).values()
|
|
43159
43630
|
].filter((item) => !beforeAttachments.some((existing) => existing.id === item.id));
|
|
43160
43631
|
let inserted = readInserted();
|
|
43161
43632
|
for (let attempt = 0; attempt < 4 && (inserted.length !== 1 || returnedId && returnedId !== inserted[0].id); attempt++) {
|
|
@@ -43168,7 +43639,7 @@ function registerDirectOperations(server2, manager) {
|
|
|
43168
43639
|
"Attachment insertion outcome uncertain; read the exact note before retrying"
|
|
43169
43640
|
);
|
|
43170
43641
|
const attachmentId = inserted[0].id;
|
|
43171
|
-
const fetched = manager.getAttachmentBase64ById(
|
|
43642
|
+
const fetched = manager.getAttachmentBase64ById(id2, attachmentId);
|
|
43172
43643
|
const actual = typeof fetched.base64 === "string" ? Buffer.from(fetched.base64, "base64") : null;
|
|
43173
43644
|
if (!actual || createHash2("sha256").update(actual).digest("hex") !== createHash2("sha256").update(bytes).digest("hex"))
|
|
43174
43645
|
throw new Error(
|
|
@@ -43176,7 +43647,7 @@ function registerDirectOperations(server2, manager) {
|
|
|
43176
43647
|
);
|
|
43177
43648
|
return {
|
|
43178
43649
|
ok: true,
|
|
43179
|
-
id,
|
|
43650
|
+
id: id2,
|
|
43180
43651
|
attachmentId,
|
|
43181
43652
|
contentHash: after.hash,
|
|
43182
43653
|
bytes: bytes.length,
|
|
@@ -43185,123 +43656,12 @@ function registerDirectOperations(server2, manager) {
|
|
|
43185
43656
|
} : {}
|
|
43186
43657
|
};
|
|
43187
43658
|
} finally {
|
|
43188
|
-
|
|
43659
|
+
rmSync4(directory, { recursive: true, force: true });
|
|
43189
43660
|
}
|
|
43190
43661
|
}
|
|
43191
43662
|
);
|
|
43192
43663
|
}
|
|
43193
43664
|
|
|
43194
|
-
// src/services/nativeTags.ts
|
|
43195
|
-
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
43196
|
-
import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
|
|
43197
|
-
import { tmpdir as tmpdir3 } from "node:os";
|
|
43198
|
-
import { join as join9 } from "node:path";
|
|
43199
|
-
var NATIVE_TAGS_SHORTCUT = "Apple Notes MCP - Native Tags";
|
|
43200
|
-
function normalizeNativeTags(tags) {
|
|
43201
|
-
if (!tags.length || tags.length > 100) throw new Error("Provide between 1 and 100 tags");
|
|
43202
|
-
return [
|
|
43203
|
-
...new Set(
|
|
43204
|
-
tags.map((value) => {
|
|
43205
|
-
const tag = value.normalize("NFC").replace(/^#/, "");
|
|
43206
|
-
if (tag.length > 100 || !new RegExp("^(?=.*\\p{L})[\\p{L}\\p{N}_-]+$", "u").test(tag))
|
|
43207
|
-
throw new Error(
|
|
43208
|
-
"Tags must contain a letter and only letters, digits, hyphens or underscores"
|
|
43209
|
-
);
|
|
43210
|
-
return tag;
|
|
43211
|
-
})
|
|
43212
|
-
)
|
|
43213
|
-
];
|
|
43214
|
-
}
|
|
43215
|
-
function addNativeTags(request, deps) {
|
|
43216
|
-
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(request.id))
|
|
43217
|
-
throw new Error("An exact CoreData note ID is required");
|
|
43218
|
-
if (request.scopeText.length < 12 || request.scopeText.length > 500 || /[\r\n\0]/u.test(request.scopeText))
|
|
43219
|
-
throw new Error("scopeText must be a stable, single-line project marker of 12\u2013500 characters");
|
|
43220
|
-
const tags = normalizeNativeTags(request.tags);
|
|
43221
|
-
const before = deps.read(request.id);
|
|
43222
|
-
if (before.contentHash !== request.expectedContentHash)
|
|
43223
|
-
throw new Error("Note revision changed; read it again");
|
|
43224
|
-
if (before.title !== request.title || !before.plaintext.includes(request.scopeText))
|
|
43225
|
-
throw new Error("The exact note does not match the title and project marker");
|
|
43226
|
-
const missing = tags.filter((tag) => !before.rich.nativeTags.includes(tag));
|
|
43227
|
-
if (!missing.length)
|
|
43228
|
-
return { nativeTags: before.rich.nativeTags, added: [], contentHash: before.contentHash };
|
|
43229
|
-
const candidates = deps.candidates(request.title, request.scopeText);
|
|
43230
|
-
if (candidates.length !== 1 || candidates[0] !== request.id)
|
|
43231
|
-
throw new Error(
|
|
43232
|
-
"Shortcuts selection is ambiguous or points to a different note; nothing was changed"
|
|
43233
|
-
);
|
|
43234
|
-
if (deps.read(request.id).contentHash !== request.expectedContentHash)
|
|
43235
|
-
throw new Error("Note revision changed during preflight; nothing was changed");
|
|
43236
|
-
let transportWarning;
|
|
43237
|
-
try {
|
|
43238
|
-
deps.run({ title: request.title, scopeText: request.scopeText, tags: missing });
|
|
43239
|
-
} catch (error2) {
|
|
43240
|
-
transportWarning = error2 instanceof Error ? error2.message : "Shortcuts completion was uncertain";
|
|
43241
|
-
}
|
|
43242
|
-
const after = deps.read(request.id);
|
|
43243
|
-
const allTags = [.../* @__PURE__ */ new Set([...before.rich.nativeTags, ...tags])];
|
|
43244
|
-
const textWithoutTags = (text) => {
|
|
43245
|
-
for (const tag of [...allTags].sort((a, b) => b.length - a.length)) {
|
|
43246
|
-
const escaped = tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
43247
|
-
text = text.replace(new RegExp(`#${escaped}(?![\\p{L}\\p{N}_-])`, "gu"), "");
|
|
43248
|
-
}
|
|
43249
|
-
return text.replace(/[\s\ufffc]/gu, "");
|
|
43250
|
-
};
|
|
43251
|
-
if (after.title !== before.title || allTags.some((tag) => !after.rich.nativeTags.includes(tag)) || textWithoutTags(before.rich.text) !== textWithoutTags(after.rich.text) || linkSignature(before.rich.links) !== linkSignature(after.rich.links) || before.rich.nativeObjectIds.some((id) => !after.rich.nativeObjectIds.includes(id)) || before.rich.hasChecklist !== after.rich.hasChecklist) {
|
|
43252
|
-
throw new Error(
|
|
43253
|
-
"Shortcuts ran, but exact-ID tags/text/links readback was not verified. Read the note before any retry"
|
|
43254
|
-
);
|
|
43255
|
-
}
|
|
43256
|
-
return {
|
|
43257
|
-
nativeTags: after.rich.nativeTags,
|
|
43258
|
-
added: missing,
|
|
43259
|
-
contentHash: after.contentHash,
|
|
43260
|
-
...transportWarning ? {
|
|
43261
|
-
transportWarning: "Shortcuts completion was uncertain, but all requested native tags and preserved content were verified by exact ID."
|
|
43262
|
-
} : {}
|
|
43263
|
-
};
|
|
43264
|
-
}
|
|
43265
|
-
function nativeTagsStatus(shortcut = process.env.APPLE_NOTES_MCP_TAGS_SHORTCUT || NATIVE_TAGS_SHORTCUT) {
|
|
43266
|
-
const lines = execFileSync6("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
|
|
43267
|
-
encoding: "utf8",
|
|
43268
|
-
timeout: 15e3,
|
|
43269
|
-
maxBuffer: 1024 * 1024,
|
|
43270
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
43271
|
-
}).split(/\r?\n/u);
|
|
43272
|
-
const matches = lines.flatMap((line) => {
|
|
43273
|
-
const match = /^(.*) \(([0-9A-Fa-f-]{36})\)$/.exec(line);
|
|
43274
|
-
return match && (match[1] === shortcut || match[2].toLowerCase() === shortcut.toLowerCase()) ? [{ name: match[1], identifier: match[2] }] : [];
|
|
43275
|
-
});
|
|
43276
|
-
return {
|
|
43277
|
-
shortcut,
|
|
43278
|
-
installed: matches.length === 1,
|
|
43279
|
-
identifier: matches.length === 1 ? matches[0].identifier : void 0
|
|
43280
|
-
};
|
|
43281
|
-
}
|
|
43282
|
-
function runNativeTagsShortcut(input) {
|
|
43283
|
-
const status = nativeTagsStatus();
|
|
43284
|
-
if (!status.installed)
|
|
43285
|
-
throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
|
|
43286
|
-
const directory = mkdtempSync3(join9(tmpdir3(), "apple-notes-native-tags-"));
|
|
43287
|
-
try {
|
|
43288
|
-
const path4 = join9(directory, "request.json");
|
|
43289
|
-
writeFileSync2(path4, JSON.stringify(input), { mode: 384 });
|
|
43290
|
-
execFileSync6("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path4], {
|
|
43291
|
-
encoding: "utf8",
|
|
43292
|
-
timeout: 6e4,
|
|
43293
|
-
maxBuffer: 1024 * 1024,
|
|
43294
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
43295
|
-
});
|
|
43296
|
-
} catch {
|
|
43297
|
-
throw new Error(
|
|
43298
|
-
"Native tag operation did not complete cleanly (possibly waiting for macOS permission). Do not retry automatically; read the exact note and check Shortcuts"
|
|
43299
|
-
);
|
|
43300
|
-
} finally {
|
|
43301
|
-
rmSync3(directory, { recursive: true, force: true });
|
|
43302
|
-
}
|
|
43303
|
-
}
|
|
43304
|
-
|
|
43305
43665
|
// src/tools/nativeTagsBridge.ts
|
|
43306
43666
|
var noteId2 = external_exports.string().regex(/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i);
|
|
43307
43667
|
var revision2 = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
@@ -43335,20 +43695,20 @@ function registerNativeTagsBridge(server2, manager) {
|
|
|
43335
43695
|
})
|
|
43336
43696
|
);
|
|
43337
43697
|
}
|
|
43338
|
-
const read = (
|
|
43339
|
-
const note = manager.getNoteById(
|
|
43698
|
+
const read = (id2) => {
|
|
43699
|
+
const note = manager.getNoteById(id2);
|
|
43340
43700
|
if (!note) throw new Error("Note not found");
|
|
43341
43701
|
if (note.passwordProtected) throw new Error("Locked notes cannot receive native tags");
|
|
43342
|
-
const body = manager.getNoteContentById(
|
|
43702
|
+
const body = manager.getNoteContentById(id2);
|
|
43343
43703
|
if (!body) throw new Error("Note content is unavailable");
|
|
43344
|
-
const enriched = enrichNoteRead(
|
|
43345
|
-
const rich = readRichNote(
|
|
43704
|
+
const enriched = enrichNoteRead(id2, body);
|
|
43705
|
+
const rich = readRichNote(id2);
|
|
43346
43706
|
if (!enriched.complete || enriched.revision !== rich.revision)
|
|
43347
43707
|
throw new Error("Native metadata changed during read; read the note again");
|
|
43348
43708
|
return {
|
|
43349
43709
|
contentHash: richContentHash(body, enriched),
|
|
43350
43710
|
title: note.title,
|
|
43351
|
-
plaintext: manager.getNotePlaintextById(
|
|
43711
|
+
plaintext: manager.getNotePlaintextById(id2),
|
|
43352
43712
|
rich
|
|
43353
43713
|
};
|
|
43354
43714
|
};
|
|
@@ -43356,7 +43716,10 @@ function registerNativeTagsBridge(server2, manager) {
|
|
|
43356
43716
|
"native-tags-status",
|
|
43357
43717
|
"Use when: checking whether the Native Tags Shortcut is installed before a tag write.\nReturns: the configured Shortcut name, unique installed identifier, and installed state.\nDo not use when: listing tags on notes (list-native-tags).\nSafety: read-only; installation does not by itself prove Notes permission or a successful live mutation.",
|
|
43358
43718
|
{},
|
|
43359
|
-
() =>
|
|
43719
|
+
() => {
|
|
43720
|
+
const status = nativeTagsStatus();
|
|
43721
|
+
return status.installed ? status : { ...status, setupCommand: "apple-notes-mcp setup" };
|
|
43722
|
+
}
|
|
43360
43723
|
);
|
|
43361
43724
|
tool(
|
|
43362
43725
|
"add-native-tags",
|
|
@@ -43367,10 +43730,10 @@ function registerNativeTagsBridge(server2, manager) {
|
|
|
43367
43730
|
scopeText: external_exports.string().min(12).max(500),
|
|
43368
43731
|
tags: external_exports.array(external_exports.string().min(1).max(101)).min(1).max(100)
|
|
43369
43732
|
},
|
|
43370
|
-
({ id, expectedContentHash, scopeText, tags }) => {
|
|
43371
|
-
const initial = read(
|
|
43733
|
+
({ id: id2, expectedContentHash, scopeText, tags }) => {
|
|
43734
|
+
const initial = read(id2);
|
|
43372
43735
|
const result = addNativeTags(
|
|
43373
|
-
{ id, expectedContentHash, scopeText, tags, title: initial.title },
|
|
43736
|
+
{ id: id2, expectedContentHash, scopeText, tags, title: initial.title },
|
|
43374
43737
|
{
|
|
43375
43738
|
read,
|
|
43376
43739
|
candidates: (title, scope) => manager.listAccounts().flatMap(
|
|
@@ -43381,15 +43744,410 @@ function registerNativeTagsBridge(server2, manager) {
|
|
|
43381
43744
|
run: runNativeTagsShortcut
|
|
43382
43745
|
}
|
|
43383
43746
|
);
|
|
43384
|
-
return { ok: true, id, ...result };
|
|
43747
|
+
return { ok: true, id: id2, ...result };
|
|
43748
|
+
}
|
|
43749
|
+
);
|
|
43750
|
+
}
|
|
43751
|
+
|
|
43752
|
+
// src/tools/nativeOperations.ts
|
|
43753
|
+
var VERIFIED_BACKGROUND = /* @__PURE__ */ new Set([
|
|
43754
|
+
"append-native",
|
|
43755
|
+
"create-checklist-item",
|
|
43756
|
+
"create-table",
|
|
43757
|
+
"insert-note-link",
|
|
43758
|
+
"set-note-pinned",
|
|
43759
|
+
"remove-native-tags",
|
|
43760
|
+
"replace-native-tag"
|
|
43761
|
+
]);
|
|
43762
|
+
var LIVE_VALIDATION_BLOCKERS = {};
|
|
43763
|
+
var signingRefusal = "Installed Shortcuts refuses to sign this Notes action (unsupported features); no background fallback is enabled";
|
|
43764
|
+
var UNAVAILABLE = {
|
|
43765
|
+
"set-checklist-item": signingRefusal,
|
|
43766
|
+
"delete-checklist-item": signingRefusal,
|
|
43767
|
+
"set-attachment-size": signingRefusal,
|
|
43768
|
+
"insert-mention": signingRefusal,
|
|
43769
|
+
"update-table-cells": "No background cell-editing action; replacing a table would change its identity and position",
|
|
43770
|
+
"edit-rich-note": "Native text replacement needs current UI selection; full-body rewriting is protected",
|
|
43771
|
+
"smart-folders": "No supported background interface for Smart Folder rules",
|
|
43772
|
+
"rename-tag-globally": "Cannot update Smart Folder references in background; use replace-native-tag on explicit notes",
|
|
43773
|
+
"sharing-permissions": "No supported background interface for sharing invitations or permissions",
|
|
43774
|
+
"note-lock": "Notes lock actions require the foreground and may require user authentication",
|
|
43775
|
+
"record-audio": "Recording requires the Notes UI",
|
|
43776
|
+
"transcribe-audio": "No supported background Notes transcription action",
|
|
43777
|
+
"scan-and-markup": "No supported background scanning or graphical markup action"
|
|
43778
|
+
};
|
|
43779
|
+
function requireValidated(name) {
|
|
43780
|
+
if (!VERIFIED_BACKGROUND.has(name) && process.env.APPLE_NOTES_MCP_ALLOW_UNVERIFIED !== "1")
|
|
43781
|
+
throw new Error(
|
|
43782
|
+
UNAVAILABLE[name] || LIVE_VALIDATION_BLOCKERS[name] || `${name} has not passed live background validation in this build; see get-capabilities`
|
|
43783
|
+
);
|
|
43784
|
+
}
|
|
43785
|
+
var id = external_exports.string().regex(/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i);
|
|
43786
|
+
var revision3 = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
43787
|
+
var common = {
|
|
43788
|
+
id,
|
|
43789
|
+
expectedContentHash: revision3,
|
|
43790
|
+
scopeText: external_exports.string().min(12).max(500).describe(
|
|
43791
|
+
"Distinctive existing phrase used by Notes search. Prefer plain words without punctuation, hashtags, or paths."
|
|
43792
|
+
)
|
|
43793
|
+
};
|
|
43794
|
+
var htmlEscape = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
43795
|
+
function registerNativeOperations(server2, manager) {
|
|
43796
|
+
function tool(name, description, input, handler, readOnly = false) {
|
|
43797
|
+
server2.registerTool(
|
|
43798
|
+
name,
|
|
43799
|
+
{
|
|
43800
|
+
description,
|
|
43801
|
+
inputSchema: input,
|
|
43802
|
+
outputSchema: external_exports.object({ ok: external_exports.boolean().optional() }).passthrough(),
|
|
43803
|
+
annotations: { readOnlyHint: readOnly }
|
|
43804
|
+
},
|
|
43805
|
+
(async (args) => {
|
|
43806
|
+
try {
|
|
43807
|
+
const result = handler(args);
|
|
43808
|
+
return {
|
|
43809
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
43810
|
+
structuredContent: result
|
|
43811
|
+
};
|
|
43812
|
+
} catch (error2) {
|
|
43813
|
+
return {
|
|
43814
|
+
content: [
|
|
43815
|
+
{
|
|
43816
|
+
type: "text",
|
|
43817
|
+
text: error2 instanceof Error ? error2.message : "Operation failed"
|
|
43818
|
+
}
|
|
43819
|
+
],
|
|
43820
|
+
isError: true
|
|
43821
|
+
};
|
|
43822
|
+
}
|
|
43823
|
+
})
|
|
43824
|
+
);
|
|
43825
|
+
}
|
|
43826
|
+
tool(
|
|
43827
|
+
"get-capabilities",
|
|
43828
|
+
"Use when: checking native background-edit support before calling a write tool.\nReturns: bridge installation, implementation, verification, availability, and specific limitations per operation.\nDo not use when: checking only the Native Tags bridge (native-tags-status).\nSafety: read-only; does not open Notes or run a mutation.",
|
|
43829
|
+
{},
|
|
43830
|
+
() => {
|
|
43831
|
+
let bridge;
|
|
43832
|
+
try {
|
|
43833
|
+
bridge = backgroundStatus();
|
|
43834
|
+
} catch {
|
|
43835
|
+
bridge = {
|
|
43836
|
+
installed: false,
|
|
43837
|
+
shortcut: "Apple Notes MCP - Background Operations v5",
|
|
43838
|
+
error: "Shortcuts helper unavailable"
|
|
43839
|
+
};
|
|
43840
|
+
}
|
|
43841
|
+
const native = [
|
|
43842
|
+
"append-native",
|
|
43843
|
+
"create-checklist-item",
|
|
43844
|
+
"create-table",
|
|
43845
|
+
"set-note-pinned",
|
|
43846
|
+
"remove-native-tags",
|
|
43847
|
+
"replace-native-tag",
|
|
43848
|
+
"insert-note-link"
|
|
43849
|
+
];
|
|
43850
|
+
let tagBridgeInstalled = false;
|
|
43851
|
+
try {
|
|
43852
|
+
tagBridgeInstalled = nativeTagBridgeStatus().installed;
|
|
43853
|
+
} catch {
|
|
43854
|
+
}
|
|
43855
|
+
return {
|
|
43856
|
+
bridge,
|
|
43857
|
+
nativeTagBridgeInstalled: tagBridgeInstalled,
|
|
43858
|
+
mode: "background-only",
|
|
43859
|
+
operations: Object.fromEntries(
|
|
43860
|
+
native.map((name) => [
|
|
43861
|
+
name,
|
|
43862
|
+
{
|
|
43863
|
+
implemented: true,
|
|
43864
|
+
verified: VERIFIED_BACKGROUND.has(name),
|
|
43865
|
+
available: VERIFIED_BACKGROUND.has(name) && (!native.includes(name) || bridge.installed) && (name !== "replace-native-tag" || tagBridgeInstalled),
|
|
43866
|
+
reason: !VERIFIED_BACKGROUND.has(name) ? UNAVAILABLE[name] || LIVE_VALIDATION_BLOCKERS[name] || "Live validation pending; install the shortcut and complete the isolated acceptance tests" : native.includes(name) && !bridge.installed ? "Run apple-notes-mcp setup and approve Add Shortcut in macOS" : name === "replace-native-tag" && !tagBridgeInstalled ? "Run apple-notes-mcp setup to install the Native Tags bridge for the addition phase" : void 0
|
|
43867
|
+
}
|
|
43868
|
+
])
|
|
43869
|
+
),
|
|
43870
|
+
unavailable: UNAVAILABLE
|
|
43871
|
+
};
|
|
43872
|
+
},
|
|
43873
|
+
true
|
|
43874
|
+
);
|
|
43875
|
+
tool(
|
|
43876
|
+
"append-native",
|
|
43877
|
+
"Use when: appending formatted content to a native-object note without replacing its body.\nReturns: exact-ID preservation and appended-content readback.\nDo not use when: the existing note can be safely handled by append-to-note or the scope phrase is ambiguous.\nSafety: requires a fresh revision and unique existing scope phrase of plain words; avoid punctuation, hashtags, and paths because Notes search may not resolve them literally. Supports bounded plaintext, semantic HTML, and Markdown without external fetching or automatic retries.",
|
|
43878
|
+
{
|
|
43879
|
+
...common,
|
|
43880
|
+
content: external_exports.string().min(1).max(1024 * 1024),
|
|
43881
|
+
format: external_exports.enum(["plaintext", "html", "markdown"]).default("plaintext")
|
|
43882
|
+
},
|
|
43883
|
+
(args) => {
|
|
43884
|
+
requireValidated("append-native");
|
|
43885
|
+
return appendNative(manager, args);
|
|
43886
|
+
}
|
|
43887
|
+
);
|
|
43888
|
+
tool(
|
|
43889
|
+
"create-checklist-item",
|
|
43890
|
+
"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.",
|
|
43891
|
+
{
|
|
43892
|
+
...common,
|
|
43893
|
+
text: external_exports.string().min(1).max(1e4).refine((s) => !/[\r\n\0]/u.test(s), "One line per checklist item")
|
|
43894
|
+
},
|
|
43895
|
+
(args) => {
|
|
43896
|
+
requireValidated("create-checklist-item");
|
|
43897
|
+
const result = mutateBackground(
|
|
43898
|
+
args,
|
|
43899
|
+
"create-checklist-item",
|
|
43900
|
+
{ text: args.text },
|
|
43901
|
+
(before, after) => {
|
|
43902
|
+
assertPreserved(before, after, { append: true });
|
|
43903
|
+
const added = after.checklist.slice(before.checklist.length);
|
|
43904
|
+
if (added.length !== 1 || added[0].text !== args.text || added[0].done)
|
|
43905
|
+
throw new Error("Native checklist item not verified");
|
|
43906
|
+
},
|
|
43907
|
+
backgroundDependencies(manager)
|
|
43908
|
+
);
|
|
43909
|
+
return { ...result, items: readRichNote(args.id).checklistItems };
|
|
43910
|
+
}
|
|
43911
|
+
);
|
|
43912
|
+
tool(
|
|
43913
|
+
"create-table",
|
|
43914
|
+
"Use when: appending one native Notes table from a rectangular array of strings.\nReturns: the native table identity and decoded cells after readback.\nDo not use when: a text table is acceptable or the rows are not rectangular.\nSafety: never substitutes text; preserves existing rich content and reports failure unless the native table is verified.",
|
|
43915
|
+
{
|
|
43916
|
+
...common,
|
|
43917
|
+
rows: external_exports.array(external_exports.array(external_exports.string().max(1e4)).min(1).max(100)).min(1).max(1e3)
|
|
43918
|
+
},
|
|
43919
|
+
(args) => {
|
|
43920
|
+
requireValidated("create-table");
|
|
43921
|
+
if (args.rows.some((row) => row.length !== args.rows[0].length))
|
|
43922
|
+
throw new Error("Table rows must have equal cell counts");
|
|
43923
|
+
const before = readRichNote(args.id);
|
|
43924
|
+
const content = "<table>" + args.rows.map(
|
|
43925
|
+
(row) => "<tr>" + row.map((v) => "<td>" + htmlEscape(v) + "</td>").join("") + "</tr>"
|
|
43926
|
+
).join("") + "</table>";
|
|
43927
|
+
const result = mutateBackground(
|
|
43928
|
+
args,
|
|
43929
|
+
"append-html",
|
|
43930
|
+
{ text: "<div><br></div>" + content },
|
|
43931
|
+
(oldNote, newNote) => {
|
|
43932
|
+
assertPreserved(oldNote, newNote, { append: true });
|
|
43933
|
+
const inserted = newNote.rich.objectData?.filter(
|
|
43934
|
+
(o) => o.type?.includes("table") && !oldNote.rich.nativeObjectIds.includes(o.id)
|
|
43935
|
+
) || [];
|
|
43936
|
+
if (inserted.length !== 1 || JSON.stringify(parseNoteTable(Buffer.from(inserted[0].mergeable, "hex")).rows) !== JSON.stringify(args.rows))
|
|
43937
|
+
throw new Error("Native table cells not verified");
|
|
43938
|
+
},
|
|
43939
|
+
backgroundDependencies(manager)
|
|
43940
|
+
);
|
|
43941
|
+
const current = readRichNote(args.id);
|
|
43942
|
+
const added = current.objectData?.filter(
|
|
43943
|
+
(obj) => obj.type?.includes("table") && !before.nativeObjectIds.includes(obj.id)
|
|
43944
|
+
) || [];
|
|
43945
|
+
if (added.length !== 1)
|
|
43946
|
+
throw new Error(
|
|
43947
|
+
"Table result uncertain; native table not verified. Read the note before retrying"
|
|
43948
|
+
);
|
|
43949
|
+
const table = parseNoteTable(Buffer.from(added[0].mergeable, "hex"));
|
|
43950
|
+
if (JSON.stringify(table.rows) !== JSON.stringify(args.rows))
|
|
43951
|
+
throw new Error("Native table cells not verified; read before retrying");
|
|
43952
|
+
return { ...result, table: { id: added[0].id, ...table } };
|
|
43953
|
+
}
|
|
43954
|
+
);
|
|
43955
|
+
tool(
|
|
43956
|
+
"set-note-pinned",
|
|
43957
|
+
"Use when: setting one exact note's pinned state.\nReturns: the verified requested state and new revision.\nDo not use when: only reading pinned metadata (get-note-metadata).\nSafety: checks expectedPinned and a fresh content revision before changing metadata; never rewrites the body.",
|
|
43958
|
+
{ ...common, expectedPinned: external_exports.boolean(), pinned: external_exports.boolean() },
|
|
43959
|
+
(args) => {
|
|
43960
|
+
requireValidated("set-note-pinned");
|
|
43961
|
+
const deps = backgroundDependencies(manager);
|
|
43962
|
+
const s = deps.read(args.id);
|
|
43963
|
+
if (s.hash !== args.expectedContentHash || s.pinned !== args.expectedPinned)
|
|
43964
|
+
throw new Error("Note or pinned state changed; read it again");
|
|
43965
|
+
if (s.pinned === args.pinned)
|
|
43966
|
+
return { ok: true, id: args.id, pinned: s.pinned, contentHash: s.hash, changed: false };
|
|
43967
|
+
const run = deps.run;
|
|
43968
|
+
deps.run = (input) => {
|
|
43969
|
+
if (deps.read(args.id).pinned !== args.expectedPinned)
|
|
43970
|
+
throw new Error("Pinned state changed during preflight");
|
|
43971
|
+
run(input);
|
|
43972
|
+
};
|
|
43973
|
+
return {
|
|
43974
|
+
...mutateBackground(
|
|
43975
|
+
args,
|
|
43976
|
+
"set-pinned",
|
|
43977
|
+
{ change: args.pinned ? "add" : "remove" },
|
|
43978
|
+
(before, after) => {
|
|
43979
|
+
assertPreserved(before, after);
|
|
43980
|
+
if (after.pinned !== args.pinned) throw new Error("Pinned state not verified");
|
|
43981
|
+
},
|
|
43982
|
+
deps
|
|
43983
|
+
),
|
|
43984
|
+
pinned: args.pinned
|
|
43985
|
+
};
|
|
43385
43986
|
}
|
|
43386
43987
|
);
|
|
43988
|
+
tool(
|
|
43989
|
+
"remove-native-tags",
|
|
43990
|
+
"Use when: removing specified native tags from one exact note.\nReturns: verified remaining tags and new revision.\nDo not use when: deleting a global tag definition or changing Smart Folder rules.\nSafety: preserves unrelated tags and native objects and never retries an uncertain mutation.",
|
|
43991
|
+
{ ...common, tags: external_exports.array(external_exports.string().min(1).max(101)).min(1).max(100) },
|
|
43992
|
+
(args) => {
|
|
43993
|
+
requireValidated("remove-native-tags");
|
|
43994
|
+
let hash = args.expectedContentHash;
|
|
43995
|
+
const completed = [];
|
|
43996
|
+
for (const tag of normalizeNativeTags(args.tags)) {
|
|
43997
|
+
try {
|
|
43998
|
+
const result = setNativeTag(manager, {
|
|
43999
|
+
...args,
|
|
44000
|
+
expectedContentHash: hash,
|
|
44001
|
+
tag,
|
|
44002
|
+
present: false
|
|
44003
|
+
});
|
|
44004
|
+
hash = result.contentHash;
|
|
44005
|
+
completed.push(tag);
|
|
44006
|
+
} catch (error2) {
|
|
44007
|
+
throw new Error(
|
|
44008
|
+
`Stopped after tags ${JSON.stringify(completed)}; read exact note before retry: ${String(error2)}`
|
|
44009
|
+
);
|
|
44010
|
+
}
|
|
44011
|
+
}
|
|
44012
|
+
return { ok: true, id: args.id, removed: completed, contentHash: hash };
|
|
44013
|
+
}
|
|
44014
|
+
);
|
|
44015
|
+
tool(
|
|
44016
|
+
"replace-native-tag",
|
|
44017
|
+
"Use when: replacing one native tag across an explicit list of freshly read notes.\nReturns: per-note results and remaining work after any failure.\nDo not use when: renaming global tag definitions or Smart Folder rules.\nSafety: adds and verifies the new tag before removing the old one and stops on the first uncertain result.",
|
|
44018
|
+
{
|
|
44019
|
+
notes: external_exports.array(external_exports.object(common)).min(1).max(100),
|
|
44020
|
+
oldTag: external_exports.string().min(1).max(101),
|
|
44021
|
+
newTag: external_exports.string().min(1).max(101)
|
|
44022
|
+
},
|
|
44023
|
+
(args) => {
|
|
44024
|
+
requireValidated("replace-native-tag");
|
|
44025
|
+
const [oldTag] = normalizeNativeTags([args.oldTag]);
|
|
44026
|
+
const [newTag] = normalizeNativeTags([args.newTag]);
|
|
44027
|
+
if (new Set(args.notes.map((n) => n.id)).size !== args.notes.length)
|
|
44028
|
+
throw new Error("Duplicate note IDs");
|
|
44029
|
+
for (const n of args.notes)
|
|
44030
|
+
if (readBackgroundSnapshot(manager, n.id).hash !== n.expectedContentHash)
|
|
44031
|
+
throw new Error("Note revision changed; no replacement started");
|
|
44032
|
+
const results = [];
|
|
44033
|
+
for (const n of args.notes) {
|
|
44034
|
+
try {
|
|
44035
|
+
const initial = readBackgroundSnapshot(manager, n.id);
|
|
44036
|
+
if (!initial.rich.nativeTags.includes(oldTag) || oldTag === newTag) {
|
|
44037
|
+
results.push({ id: n.id, changed: false });
|
|
44038
|
+
continue;
|
|
44039
|
+
}
|
|
44040
|
+
const added = setNativeTag(manager, { ...n, tag: newTag, present: true });
|
|
44041
|
+
const removed = setNativeTag(manager, {
|
|
44042
|
+
...n,
|
|
44043
|
+
expectedContentHash: added.contentHash,
|
|
44044
|
+
tag: oldTag,
|
|
44045
|
+
present: false
|
|
44046
|
+
});
|
|
44047
|
+
results.push(removed);
|
|
44048
|
+
} catch (error2) {
|
|
44049
|
+
results.push({ id: n.id, ok: false, error: String(error2) });
|
|
44050
|
+
return { ok: false, results, remaining: args.notes.length - results.length };
|
|
44051
|
+
}
|
|
44052
|
+
}
|
|
44053
|
+
return { ok: true, results };
|
|
44054
|
+
}
|
|
44055
|
+
);
|
|
44056
|
+
tool(
|
|
44057
|
+
"insert-note-link",
|
|
44058
|
+
"Use when: appending a real Notes deep link to a native-object note.\nReturns: verified link destination and label after exact-note readback.\nDo not use when: only retrieving a deep link (get-note-link).\nSafety: retrieves the real target URL, preserves existing rich content, and creates a static label rather than a dynamic-title object.",
|
|
44059
|
+
{ ...common, linkedNoteId: id, label: external_exports.string().min(1).max(2e3).optional() },
|
|
44060
|
+
(args) => {
|
|
44061
|
+
requireValidated("insert-note-link");
|
|
44062
|
+
const link = manager.getNoteLinkById(args.linkedNoteId);
|
|
44063
|
+
if (!link) throw new Error("Real Notes link unavailable");
|
|
44064
|
+
const linked = manager.getNoteById(args.linkedNoteId);
|
|
44065
|
+
if (!linked) throw new Error("Linked note not found");
|
|
44066
|
+
const result = appendNative(manager, {
|
|
44067
|
+
...args,
|
|
44068
|
+
content: `<div><a href="${htmlEscape(link)}">${htmlEscape(args.label || linked.title)}</a></div>`,
|
|
44069
|
+
format: "html"
|
|
44070
|
+
});
|
|
44071
|
+
if (!readRichNote(args.id).links.some(
|
|
44072
|
+
(l) => l.url === link && l.text === (args.label || linked.title)
|
|
44073
|
+
))
|
|
44074
|
+
throw new Error("Link result uncertain; read note before retrying");
|
|
44075
|
+
return result;
|
|
44076
|
+
}
|
|
44077
|
+
);
|
|
44078
|
+
}
|
|
44079
|
+
|
|
44080
|
+
// src/setupShortcuts.ts
|
|
44081
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
44082
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
44083
|
+
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
44084
|
+
import { fileURLToPath } from "node:url";
|
|
44085
|
+
var shortcutFiles = [
|
|
44086
|
+
{ name: NATIVE_TAGS_SHORTCUT, file: "Apple Notes MCP - Native Tags.shortcut" },
|
|
44087
|
+
{
|
|
44088
|
+
name: BACKGROUND_SHORTCUT,
|
|
44089
|
+
file: "Apple Notes MCP - Background Operations v5.shortcut"
|
|
44090
|
+
}
|
|
44091
|
+
];
|
|
44092
|
+
function setupShortcuts(checkOnly, dependencies = {}) {
|
|
44093
|
+
const status = dependencies.status || nativeTagsStatus;
|
|
44094
|
+
const exists = dependencies.exists || existsSync7;
|
|
44095
|
+
const open = dependencies.open || ((path4) => {
|
|
44096
|
+
const result = spawnSync2("/usr/bin/open", [path4], { encoding: "utf8" });
|
|
44097
|
+
return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
|
|
44098
|
+
});
|
|
44099
|
+
const baseDirectory = dependencies.baseDirectory || resolve2(dirname2(fileURLToPath(import.meta.url)), "../shortcuts");
|
|
44100
|
+
const items = shortcutFiles.map(({ name, file }) => {
|
|
44101
|
+
const path4 = resolve2(baseDirectory, file);
|
|
44102
|
+
let installed = false;
|
|
44103
|
+
let identifier;
|
|
44104
|
+
let error2;
|
|
44105
|
+
try {
|
|
44106
|
+
const current = status(name);
|
|
44107
|
+
installed = current.installed;
|
|
44108
|
+
identifier = current.identifier;
|
|
44109
|
+
} catch (cause) {
|
|
44110
|
+
error2 = cause instanceof Error ? cause.message : String(cause);
|
|
44111
|
+
}
|
|
44112
|
+
let opened = false;
|
|
44113
|
+
if (!installed && !checkOnly) {
|
|
44114
|
+
if (!exists(path4)) error2 = `Packaged Shortcut is missing: ${path4}`;
|
|
44115
|
+
else {
|
|
44116
|
+
const result = open(path4);
|
|
44117
|
+
opened = result.ok;
|
|
44118
|
+
if (!result.ok) error2 = result.error || `Could not open ${file}`;
|
|
44119
|
+
}
|
|
44120
|
+
}
|
|
44121
|
+
return { name, installed, identifier, file: path4, opened, ...error2 ? { error: error2 } : {} };
|
|
44122
|
+
});
|
|
44123
|
+
return { ready: items.every((item) => item.installed), checkOnly, items };
|
|
44124
|
+
}
|
|
44125
|
+
function formatShortcutSetup(report) {
|
|
44126
|
+
const lines = ["Apple Notes MCP Shortcut setup", ""];
|
|
44127
|
+
for (const item of report.items) {
|
|
44128
|
+
if (item.installed) lines.push(`\u2713 ${item.name} (${item.identifier})`);
|
|
44129
|
+
else if (item.opened) lines.push(`\u2192 ${item.name}: confirm \u201CAdd Shortcut\u201D in macOS`);
|
|
44130
|
+
else lines.push(`\u2717 ${item.name}: ${item.error || "not installed"}`);
|
|
44131
|
+
}
|
|
44132
|
+
lines.push("");
|
|
44133
|
+
if (report.ready) lines.push("Both Shortcut bridges are installed.");
|
|
44134
|
+
else if (report.checkOnly) lines.push("Run `apple-notes-mcp setup` to open missing workflows.");
|
|
44135
|
+
else
|
|
44136
|
+
lines.push(
|
|
44137
|
+
"After approving the macOS dialogs, run `apple-notes-mcp setup --check` or the MCP doctor tool."
|
|
44138
|
+
);
|
|
44139
|
+
return lines.join("\n");
|
|
43387
44140
|
}
|
|
43388
44141
|
|
|
43389
44142
|
// src/index.ts
|
|
43390
44143
|
loadFileConfig();
|
|
43391
44144
|
var require2 = createRequire(import.meta.url);
|
|
43392
44145
|
var { version: version2 } = require2("../package.json");
|
|
44146
|
+
if (process.argv[2] === "setup") {
|
|
44147
|
+
const report = setupShortcuts(process.argv.slice(3).includes("--check"));
|
|
44148
|
+
process.stdout.write(formatShortcutSetup(report) + "\n");
|
|
44149
|
+
process.exit(report.ready || !report.checkOnly ? 0 : 1);
|
|
44150
|
+
}
|
|
43393
44151
|
var server = new McpServer({
|
|
43394
44152
|
name: "apple-notes",
|
|
43395
44153
|
version: version2,
|
|
@@ -43398,6 +44156,7 @@ var server = new McpServer({
|
|
|
43398
44156
|
var notesManager = new AppleNotesManager();
|
|
43399
44157
|
registerDirectOperations(server, notesManager);
|
|
43400
44158
|
registerNativeTagsBridge(server, notesManager);
|
|
44159
|
+
registerNativeOperations(server, notesManager);
|
|
43401
44160
|
function successResponse(message, structured) {
|
|
43402
44161
|
const res = { content: [{ type: "text", text: message }] };
|
|
43403
44162
|
if (structured) res.structuredContent = structured;
|
|
@@ -43445,17 +44204,17 @@ var noteIdInput = external_exports.string().min(1, "Note ID is required").max(MA
|
|
|
43445
44204
|
var expectedContentHashInput = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/, "expectedContentHash must come from get-note-content").describe(
|
|
43446
44205
|
"Revision token returned by get-note-content for this exact ID. The mutation stops if the note changed since that read."
|
|
43447
44206
|
);
|
|
43448
|
-
function readExactNoteSnapshot(
|
|
43449
|
-
const note = notesManager.getNoteById(
|
|
43450
|
-
if (!note) return { error: `Note with ID "${
|
|
44207
|
+
function readExactNoteSnapshot(id2) {
|
|
44208
|
+
const note = notesManager.getNoteById(id2);
|
|
44209
|
+
if (!note) return { error: `Note with ID "${id2}" not found` };
|
|
43451
44210
|
if (note.passwordProtected) {
|
|
43452
44211
|
return {
|
|
43453
44212
|
error: `Note "${note.title}" is password-protected and cannot be changed. Unlock it in Notes.app first.`
|
|
43454
44213
|
};
|
|
43455
44214
|
}
|
|
43456
|
-
const body = notesManager.getNoteContentById(
|
|
44215
|
+
const body = notesManager.getNoteContentById(id2);
|
|
43457
44216
|
if (!body) return { error: `Failed to read content of note "${note.title}"` };
|
|
43458
|
-
const rich = enrichNoteRead(
|
|
44217
|
+
const rich = enrichNoteRead(id2, body);
|
|
43459
44218
|
return { note, body, rich, contentHash: richContentHash(body, rich) };
|
|
43460
44219
|
}
|
|
43461
44220
|
function revisionConflictMessage(title) {
|
|
@@ -43636,28 +44395,28 @@ registerTool(
|
|
|
43636
44395
|
truncated: external_exports.boolean().optional()
|
|
43637
44396
|
}
|
|
43638
44397
|
},
|
|
43639
|
-
withErrorHandling(({ id, title, account }) => {
|
|
43640
|
-
if (
|
|
43641
|
-
const note2 = notesManager.getNoteById(
|
|
44398
|
+
withErrorHandling(({ id: id2, title, account }) => {
|
|
44399
|
+
if (id2) {
|
|
44400
|
+
const note2 = notesManager.getNoteById(id2);
|
|
43642
44401
|
if (!note2) {
|
|
43643
|
-
return errorResponse(`Note with ID "${
|
|
44402
|
+
return errorResponse(`Note with ID "${id2}" not found`);
|
|
43644
44403
|
}
|
|
43645
44404
|
if (note2.passwordProtected) {
|
|
43646
44405
|
return errorResponse(
|
|
43647
44406
|
`Note "${note2.title}" is password-protected and cannot be read. Unlock it in Notes.app first.`
|
|
43648
44407
|
);
|
|
43649
44408
|
}
|
|
43650
|
-
const rawContent2 = notesManager.getNoteContentById(
|
|
44409
|
+
const rawContent2 = notesManager.getNoteContentById(id2);
|
|
43651
44410
|
if (!rawContent2) {
|
|
43652
44411
|
return errorResponse(`Failed to read content of note "${note2.title}"`);
|
|
43653
44412
|
}
|
|
43654
|
-
const rich2 = enrichNoteRead(
|
|
44413
|
+
const rich2 = enrichNoteRead(id2, rawContent2);
|
|
43655
44414
|
const stripped2 = stripLargeInlineImages(rich2.content);
|
|
43656
44415
|
const content2 = stripped2.html;
|
|
43657
44416
|
const hashtags2 = parseHashtags(content2);
|
|
43658
44417
|
const warning2 = [strippedImagesWarning(stripped2), rich2.warning].filter(Boolean).join("\n\n");
|
|
43659
44418
|
return successResponse(warning2 ? content2 + warning2 : content2, {
|
|
43660
|
-
id,
|
|
44419
|
+
id: id2,
|
|
43661
44420
|
title: note2.title,
|
|
43662
44421
|
content: content2,
|
|
43663
44422
|
contentHash: richContentHash(rawContent2, rich2),
|
|
@@ -43665,6 +44424,11 @@ registerTool(
|
|
|
43665
44424
|
nativeTags: rich2.nativeTags,
|
|
43666
44425
|
richContentComplete: rich2.complete,
|
|
43667
44426
|
writable: rich2.writable && stripped2.strippedCount === 0,
|
|
44427
|
+
supportedOperations: {
|
|
44428
|
+
fullBodyReplace: rich2.writable && stripped2.strippedCount === 0,
|
|
44429
|
+
nativeAppendImplemented: VERIFIED_BACKGROUND.has("append-native"),
|
|
44430
|
+
requiresShortcut: !rich2.writable
|
|
44431
|
+
},
|
|
43668
44432
|
warning: rich2.warning,
|
|
43669
44433
|
hashtags: hashtags2,
|
|
43670
44434
|
strippedImages: stripped2.strippedCount,
|
|
@@ -43701,6 +44465,11 @@ registerTool(
|
|
|
43701
44465
|
nativeTags: rich.nativeTags,
|
|
43702
44466
|
richContentComplete: rich.complete,
|
|
43703
44467
|
writable: rich.writable && stripped.strippedCount === 0,
|
|
44468
|
+
supportedOperations: {
|
|
44469
|
+
fullBodyReplace: rich.writable && stripped.strippedCount === 0,
|
|
44470
|
+
nativeAppendImplemented: VERIFIED_BACKGROUND.has("append-native"),
|
|
44471
|
+
requiresShortcut: !rich.writable
|
|
44472
|
+
},
|
|
43704
44473
|
warning: rich.warning,
|
|
43705
44474
|
hashtags,
|
|
43706
44475
|
strippedImages: stripped.strippedCount,
|
|
@@ -43724,18 +44493,18 @@ registerTool(
|
|
|
43724
44493
|
plaintext: external_exports.string().optional()
|
|
43725
44494
|
}
|
|
43726
44495
|
},
|
|
43727
|
-
withErrorHandling(({ id, title, account }) => {
|
|
43728
|
-
if (
|
|
43729
|
-
const note2 = notesManager.getNoteById(
|
|
44496
|
+
withErrorHandling(({ id: id2, title, account }) => {
|
|
44497
|
+
if (id2) {
|
|
44498
|
+
const note2 = notesManager.getNoteById(id2);
|
|
43730
44499
|
if (!note2) {
|
|
43731
|
-
return errorResponse(`Note with ID "${
|
|
44500
|
+
return errorResponse(`Note with ID "${id2}" not found`);
|
|
43732
44501
|
}
|
|
43733
44502
|
if (note2.passwordProtected) {
|
|
43734
44503
|
return errorResponse(
|
|
43735
44504
|
`Note "${note2.title}" is password-protected and cannot be read. Unlock it in Notes.app first.`
|
|
43736
44505
|
);
|
|
43737
44506
|
}
|
|
43738
|
-
const plaintext2 = notesManager.getNotePlaintextById(
|
|
44507
|
+
const plaintext2 = notesManager.getNotePlaintextById(id2);
|
|
43739
44508
|
if (!plaintext2) {
|
|
43740
44509
|
return errorResponse(`Failed to read plaintext of note "${note2.title}"`);
|
|
43741
44510
|
}
|
|
@@ -43776,10 +44545,10 @@ registerTool(
|
|
|
43776
44545
|
passwordProtected: external_exports.boolean().optional()
|
|
43777
44546
|
}
|
|
43778
44547
|
},
|
|
43779
|
-
withErrorHandling(({ id }) => {
|
|
43780
|
-
const note = notesManager.getNoteById(
|
|
44548
|
+
withErrorHandling(({ id: id2 }) => {
|
|
44549
|
+
const note = notesManager.getNoteById(id2);
|
|
43781
44550
|
if (!note) {
|
|
43782
|
-
return errorResponse(`Note with ID "${
|
|
44551
|
+
return errorResponse(`Note with ID "${id2}" not found`);
|
|
43783
44552
|
}
|
|
43784
44553
|
const metadata = {
|
|
43785
44554
|
id: note.id,
|
|
@@ -43837,12 +44606,12 @@ registerTool(
|
|
|
43837
44606
|
separately: external_exports.boolean().optional()
|
|
43838
44607
|
}
|
|
43839
44608
|
},
|
|
43840
|
-
withErrorHandling(({ id, separately = false }) => {
|
|
43841
|
-
const success = notesManager.showNoteById(
|
|
44609
|
+
withErrorHandling(({ id: id2, separately = false }) => {
|
|
44610
|
+
const success = notesManager.showNoteById(id2, separately);
|
|
43842
44611
|
if (!success) {
|
|
43843
|
-
return errorResponse(`Failed to show note with ID "${
|
|
44612
|
+
return errorResponse(`Failed to show note with ID "${id2}"`);
|
|
43844
44613
|
}
|
|
43845
|
-
return successResponse(`Shown note with ID "${
|
|
44614
|
+
return successResponse(`Shown note with ID "${id2}" in Notes.app`, { id: id2, separately });
|
|
43846
44615
|
}, "Error showing note")
|
|
43847
44616
|
);
|
|
43848
44617
|
registerTool(
|
|
@@ -43860,24 +44629,24 @@ registerTool(
|
|
|
43860
44629
|
url: external_exports.string().optional()
|
|
43861
44630
|
}
|
|
43862
44631
|
},
|
|
43863
|
-
withErrorHandling(({ id, title, account }) => {
|
|
43864
|
-
if (
|
|
43865
|
-
const note2 = notesManager.getNoteById(
|
|
44632
|
+
withErrorHandling(({ id: id2, title, account }) => {
|
|
44633
|
+
if (id2) {
|
|
44634
|
+
const note2 = notesManager.getNoteById(id2);
|
|
43866
44635
|
if (!note2) {
|
|
43867
|
-
return errorResponse(`Note with ID "${
|
|
44636
|
+
return errorResponse(`Note with ID "${id2}" not found`);
|
|
43868
44637
|
}
|
|
43869
44638
|
if (note2.passwordProtected) {
|
|
43870
44639
|
return errorResponse(
|
|
43871
44640
|
`Note "${note2.title}" is password-protected. Unlock it in Notes.app first.`
|
|
43872
44641
|
);
|
|
43873
44642
|
}
|
|
43874
|
-
const url2 = notesManager.getNoteLinkById(
|
|
44643
|
+
const url2 = notesManager.getNoteLinkById(id2);
|
|
43875
44644
|
if (!url2) {
|
|
43876
44645
|
return errorResponse(
|
|
43877
44646
|
`Failed to get note link for "${note2.title}". The Notes database may not be accessible \u2014 grant Full Disk Access to the app that launches the server, fully quit and relaunch, then run the doctor tool. See: ${FULL_DISK_ACCESS_GUIDE_URL}. (On macOS 12\u201315 this also falls back to the AppleScript note link property.)`
|
|
43878
44647
|
);
|
|
43879
44648
|
}
|
|
43880
|
-
return successResponse(`Note link: ${url2}`, { id, title: note2.title, url: url2 });
|
|
44649
|
+
return successResponse(`Note link: ${url2}`, { id: id2, title: note2.title, url: url2 });
|
|
43881
44650
|
}
|
|
43882
44651
|
if (!title) {
|
|
43883
44652
|
return errorResponse("Either 'id' or 'title' is required");
|
|
@@ -43913,12 +44682,12 @@ registerTool(
|
|
|
43913
44682
|
separately: external_exports.boolean().optional()
|
|
43914
44683
|
}
|
|
43915
44684
|
},
|
|
43916
|
-
withErrorHandling(({ id, separately = false }) => {
|
|
43917
|
-
const success = notesManager.showFolderById(
|
|
44685
|
+
withErrorHandling(({ id: id2, separately = false }) => {
|
|
44686
|
+
const success = notesManager.showFolderById(id2, separately);
|
|
43918
44687
|
if (!success) {
|
|
43919
|
-
return errorResponse(`Failed to show folder with ID "${
|
|
44688
|
+
return errorResponse(`Failed to show folder with ID "${id2}"`);
|
|
43920
44689
|
}
|
|
43921
|
-
return successResponse(`Shown folder with ID "${
|
|
44690
|
+
return successResponse(`Shown folder with ID "${id2}" in Notes.app`, { id: id2, separately });
|
|
43922
44691
|
}, "Error showing folder")
|
|
43923
44692
|
);
|
|
43924
44693
|
registerTool(
|
|
@@ -43934,12 +44703,12 @@ registerTool(
|
|
|
43934
44703
|
separately: external_exports.boolean().optional()
|
|
43935
44704
|
}
|
|
43936
44705
|
},
|
|
43937
|
-
withErrorHandling(({ id, separately = false }) => {
|
|
43938
|
-
const success = notesManager.showAccountById(
|
|
44706
|
+
withErrorHandling(({ id: id2, separately = false }) => {
|
|
44707
|
+
const success = notesManager.showAccountById(id2, separately);
|
|
43939
44708
|
if (!success) {
|
|
43940
|
-
return errorResponse(`Failed to show account with ID "${
|
|
44709
|
+
return errorResponse(`Failed to show account with ID "${id2}"`);
|
|
43941
44710
|
}
|
|
43942
|
-
return successResponse(`Shown account with ID "${
|
|
44711
|
+
return successResponse(`Shown account with ID "${id2}" in Notes.app`, { id: id2, separately });
|
|
43943
44712
|
}, "Error showing account")
|
|
43944
44713
|
);
|
|
43945
44714
|
registerTool(
|
|
@@ -43958,17 +44727,17 @@ registerTool(
|
|
|
43958
44727
|
},
|
|
43959
44728
|
annotations: { readOnlyHint: true }
|
|
43960
44729
|
},
|
|
43961
|
-
withErrorHandling(({ id }) => {
|
|
43962
|
-
const note = notesManager.getNoteById(
|
|
43963
|
-
if (!note) return errorResponse(`Note with ID "${
|
|
43964
|
-
const body = notesManager.getNoteContentById(
|
|
44730
|
+
withErrorHandling(({ id: id2 }) => {
|
|
44731
|
+
const note = notesManager.getNoteById(id2);
|
|
44732
|
+
if (!note) return errorResponse(`Note with ID "${id2}" not found`);
|
|
44733
|
+
const body = notesManager.getNoteContentById(id2);
|
|
43965
44734
|
if (!body) return errorResponse(`Failed to read content of note "${note.title}"`);
|
|
43966
|
-
const rich = readRichNote(
|
|
44735
|
+
const rich = readRichNote(id2);
|
|
43967
44736
|
const tables = (rich.objectData || []).filter((object3) => object3.type?.includes("table")).map((object3) => {
|
|
43968
44737
|
try {
|
|
43969
44738
|
return {
|
|
43970
44739
|
id: object3.id,
|
|
43971
|
-
attachmentId:
|
|
44740
|
+
attachmentId: id2.replace(/ICNote\/p\d+$/, `ICAttachment/p${object3.pk}`),
|
|
43972
44741
|
complete: true,
|
|
43973
44742
|
...parseNoteTable(Buffer.from(object3.mergeable, "hex"))
|
|
43974
44743
|
};
|
|
@@ -43994,7 +44763,7 @@ registerTool(
|
|
|
43994
44763
|
revision: rich.revision
|
|
43995
44764
|
};
|
|
43996
44765
|
return successResponse("Native objects read from the exact note", {
|
|
43997
|
-
id,
|
|
44766
|
+
id: id2,
|
|
43998
44767
|
contentHash: richContentHash(body, richRead),
|
|
43999
44768
|
objects: rich.objects,
|
|
44000
44769
|
checklistItems: rich.checklistItems,
|
|
@@ -44066,27 +44835,27 @@ registerTool(
|
|
|
44066
44835
|
},
|
|
44067
44836
|
withErrorHandling(
|
|
44068
44837
|
({
|
|
44069
|
-
id,
|
|
44838
|
+
id: id2,
|
|
44070
44839
|
expectedContentHash,
|
|
44071
44840
|
newTitle,
|
|
44072
44841
|
newContent,
|
|
44073
44842
|
format = "plaintext",
|
|
44074
44843
|
allowLinkChanges = false
|
|
44075
44844
|
}) => {
|
|
44076
|
-
const snapshot = readExactNoteSnapshot(
|
|
44845
|
+
const snapshot = readExactNoteSnapshot(id2);
|
|
44077
44846
|
if ("error" in snapshot) return errorResponse(snapshot.error);
|
|
44078
44847
|
if (snapshot.contentHash !== expectedContentHash) {
|
|
44079
44848
|
return errorResponse(revisionConflictMessage(snapshot.note.title));
|
|
44080
44849
|
}
|
|
44081
44850
|
assertLinkedWrite(snapshot.rich, newContent, format, allowLinkChanges);
|
|
44082
|
-
const attachments = notesManager.listAttachmentsById(
|
|
44851
|
+
const attachments = notesManager.listAttachmentsById(id2);
|
|
44083
44852
|
if (attachments.length > 0) {
|
|
44084
44853
|
return errorResponse(
|
|
44085
44854
|
`Note "${snapshot.note.title}" has ${attachments.length} attachment(s). Full-body replacement is blocked; edit it in Notes.app.`
|
|
44086
44855
|
);
|
|
44087
44856
|
}
|
|
44088
44857
|
const result = notesManager.updateNoteByIdIfUnchanged(
|
|
44089
|
-
|
|
44858
|
+
id2,
|
|
44090
44859
|
snapshot.note.title,
|
|
44091
44860
|
snapshot.body,
|
|
44092
44861
|
newTitle,
|
|
@@ -44107,8 +44876,8 @@ registerTool(
|
|
|
44107
44876
|
`The update result for note "${snapshot.note.title}" is uncertain. Read the exact ID before retrying.`
|
|
44108
44877
|
);
|
|
44109
44878
|
}
|
|
44110
|
-
const readback = notesManager.getNoteContentById(
|
|
44111
|
-
const richReadback = enrichNoteRead(
|
|
44879
|
+
const readback = notesManager.getNoteContentById(id2);
|
|
44880
|
+
const richReadback = enrichNoteRead(id2, readback || "");
|
|
44112
44881
|
const contentHash = readback ? richContentHash(readback, richReadback) : "";
|
|
44113
44882
|
if (!richReadback.complete || linkSignature(richReadback.links) !== linkSignature(htmlLinks(result.writtenBody))) {
|
|
44114
44883
|
return errorResponse(
|
|
@@ -44117,7 +44886,7 @@ registerTool(
|
|
|
44117
44886
|
}
|
|
44118
44887
|
if (!readback || comparableVisibleText(readback) !== comparableVisibleText(result.writtenBody)) {
|
|
44119
44888
|
return errorResponse(
|
|
44120
|
-
`The note accepted an update, but exact-ID readback visible text did not match. Do not retry automatically; inspect note ID ${
|
|
44889
|
+
`The note accepted an update, but exact-ID readback visible text did not match. Do not retry automatically; inspect note ID ${id2} in Notes.app.`
|
|
44121
44890
|
);
|
|
44122
44891
|
}
|
|
44123
44892
|
const displayTitle = resolveUpdateResponseTitle(
|
|
@@ -44129,10 +44898,10 @@ registerTool(
|
|
|
44129
44898
|
const sharedWarning = snapshot.note.shared ? "\n\n\u26A0\uFE0F This note is shared with collaborators. Your changes are visible to them." : "";
|
|
44130
44899
|
const checklistWarning = detectChecklistAttempt(newContent) ?? "";
|
|
44131
44900
|
return successResponse(
|
|
44132
|
-
`Note updated; visible text verified: "${displayTitle}" [id: ${
|
|
44901
|
+
`Note updated; visible text verified: "${displayTitle}" [id: ${id2}]${sharedWarning}${checklistWarning}`,
|
|
44133
44902
|
{
|
|
44134
44903
|
ok: true,
|
|
44135
|
-
id,
|
|
44904
|
+
id: id2,
|
|
44136
44905
|
title: displayTitle,
|
|
44137
44906
|
shared: snapshot.note.shared ?? false,
|
|
44138
44907
|
previousContentHash: expectedContentHash,
|
|
@@ -44147,11 +44916,12 @@ registerTool(
|
|
|
44147
44916
|
registerTool(
|
|
44148
44917
|
"append-to-note",
|
|
44149
44918
|
{
|
|
44150
|
-
description: "Use when: adding content to one exact note after reading it by id.\nReturns: exact id, new content hash, and visible-text readback verification.\nDo not use when: you only have a title
|
|
44919
|
+
description: "Use when: adding content to one exact note after reading it by id.\nReturns: exact id, new content hash, and visible-text readback verification.\nDo not use when: you only have a title or the note changed since the read.\nSafety: protected native-object notes use native end-append with scopeText; ordinary notes retain guarded HTML editing. Notes.app normalizes HTML, so rich formatting is not claimed as byte-identical.",
|
|
44151
44920
|
inputSchema: {
|
|
44152
44921
|
id: noteIdInput,
|
|
44153
44922
|
expectedContentHash: expectedContentHashInput,
|
|
44154
44923
|
content: external_exports.string().min(1, "Content to append is required").max(MAX.CONTENT).describe("Text to append to the note body"),
|
|
44924
|
+
scopeText: external_exports.string().min(12).max(500).optional().describe("Existing unique phrase required for native append to protected notes"),
|
|
44155
44925
|
position: external_exports.enum(["after", "before"]).optional().default("after").describe(
|
|
44156
44926
|
"Where to insert: 'after' appends to the end (default), 'before' prepends to the start"
|
|
44157
44927
|
),
|
|
@@ -44170,12 +44940,13 @@ registerTool(
|
|
|
44170
44940
|
},
|
|
44171
44941
|
withErrorHandling(
|
|
44172
44942
|
({
|
|
44173
|
-
id,
|
|
44943
|
+
id: id2,
|
|
44174
44944
|
expectedContentHash,
|
|
44175
44945
|
content,
|
|
44176
44946
|
position = "after",
|
|
44177
44947
|
separator = "\n\n",
|
|
44178
|
-
format = "plaintext"
|
|
44948
|
+
format = "plaintext",
|
|
44949
|
+
scopeText
|
|
44179
44950
|
}) => {
|
|
44180
44951
|
const contentToHtml = (text) => {
|
|
44181
44952
|
if (format === "html") return text;
|
|
@@ -44190,17 +44961,32 @@ registerTool(
|
|
|
44190
44961
|
const escaped = sep2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
44191
44962
|
return `<div>${escaped}</div>`;
|
|
44192
44963
|
};
|
|
44193
|
-
const snapshot = readExactNoteSnapshot(
|
|
44964
|
+
const snapshot = readExactNoteSnapshot(id2);
|
|
44194
44965
|
if ("error" in snapshot) return errorResponse(snapshot.error);
|
|
44195
44966
|
if (snapshot.contentHash !== expectedContentHash) {
|
|
44196
44967
|
return errorResponse(revisionConflictMessage(snapshot.note.title));
|
|
44197
44968
|
}
|
|
44198
44969
|
if (!snapshot.rich.writable) {
|
|
44199
|
-
|
|
44200
|
-
|
|
44201
|
-
)
|
|
44970
|
+
if (separator !== "\n\n")
|
|
44971
|
+
return errorResponse("Native append supports the default blank-line separator only");
|
|
44972
|
+
if (position !== "after")
|
|
44973
|
+
return errorResponse("Native append supports the end of a note only");
|
|
44974
|
+
if (!scopeText)
|
|
44975
|
+
return errorResponse("Provide scopeText: a unique existing phrase for native append");
|
|
44976
|
+
if (!VERIFIED_BACKGROUND.has("append-native"))
|
|
44977
|
+
return errorResponse(
|
|
44978
|
+
"Native append has not passed live validation; see get-capabilities"
|
|
44979
|
+
);
|
|
44980
|
+
const result2 = appendNative(notesManager, {
|
|
44981
|
+
id: id2,
|
|
44982
|
+
expectedContentHash,
|
|
44983
|
+
scopeText,
|
|
44984
|
+
content,
|
|
44985
|
+
format
|
|
44986
|
+
});
|
|
44987
|
+
return successResponse("Native append verified without replacing existing objects", result2);
|
|
44202
44988
|
}
|
|
44203
|
-
const attachments = notesManager.listAttachmentsById(
|
|
44989
|
+
const attachments = notesManager.listAttachmentsById(id2);
|
|
44204
44990
|
if (attachments.length > 0) {
|
|
44205
44991
|
return errorResponse(
|
|
44206
44992
|
`Note "${snapshot.note.title}" has ${attachments.length} attachment(s). Append is blocked because it rewrites the full body; edit it in Notes.app.`
|
|
@@ -44214,7 +45000,7 @@ registerTool(
|
|
|
44214
45000
|
const sepHtml = separatorToHtml(separator);
|
|
44215
45001
|
const combinedBody = position === "before" ? titleDiv + newBlock + sepHtml + bodyHtml : titleDiv + bodyHtml + sepHtml + newBlock;
|
|
44216
45002
|
const result = notesManager.updateNoteByIdIfUnchanged(
|
|
44217
|
-
|
|
45003
|
+
id2,
|
|
44218
45004
|
snapshot.note.title,
|
|
44219
45005
|
snapshot.body,
|
|
44220
45006
|
void 0,
|
|
@@ -44235,8 +45021,8 @@ registerTool(
|
|
|
44235
45021
|
`The append result for note "${snapshot.note.title}" is uncertain. Read the exact ID before retrying.`
|
|
44236
45022
|
);
|
|
44237
45023
|
}
|
|
44238
|
-
const readback = notesManager.getNoteContentById(
|
|
44239
|
-
const richReadback = enrichNoteRead(
|
|
45024
|
+
const readback = notesManager.getNoteContentById(id2);
|
|
45025
|
+
const richReadback = enrichNoteRead(id2, readback || "");
|
|
44240
45026
|
const contentHash = readback ? richContentHash(readback, richReadback) : "";
|
|
44241
45027
|
if (!richReadback.complete || linkSignature(richReadback.links) !== linkSignature(htmlLinks(result.writtenBody))) {
|
|
44242
45028
|
return errorResponse(
|
|
@@ -44245,7 +45031,7 @@ registerTool(
|
|
|
44245
45031
|
}
|
|
44246
45032
|
if (!readback || comparableVisibleText(readback) !== comparableVisibleText(result.writtenBody)) {
|
|
44247
45033
|
return errorResponse(
|
|
44248
|
-
`The note accepted an append, but exact-ID readback visible text did not match. Do not retry automatically; inspect note ID ${
|
|
45034
|
+
`The note accepted an append, but exact-ID readback visible text did not match. Do not retry automatically; inspect note ID ${id2} in Notes.app.`
|
|
44249
45035
|
);
|
|
44250
45036
|
}
|
|
44251
45037
|
const sharedWarning = snapshot.note.shared ? "\n\n\u26A0\uFE0F This note is shared with collaborators. Your changes are visible to them." : "";
|
|
@@ -44253,7 +45039,7 @@ registerTool(
|
|
|
44253
45039
|
`Note appended; visible text verified: "${snapshot.note.title}"${sharedWarning}`,
|
|
44254
45040
|
{
|
|
44255
45041
|
ok: true,
|
|
44256
|
-
id,
|
|
45042
|
+
id: id2,
|
|
44257
45043
|
title: snapshot.note.title,
|
|
44258
45044
|
shared: snapshot.note.shared ?? false,
|
|
44259
45045
|
previousContentHash: expectedContentHash,
|
|
@@ -44281,19 +45067,19 @@ registerTool(
|
|
|
44281
45067
|
previousContentHash: external_exports.string().optional()
|
|
44282
45068
|
}
|
|
44283
45069
|
},
|
|
44284
|
-
withErrorHandling(({ id, expectedContentHash }) => {
|
|
44285
|
-
const snapshot = readExactNoteSnapshot(
|
|
45070
|
+
withErrorHandling(({ id: id2, expectedContentHash }) => {
|
|
45071
|
+
const snapshot = readExactNoteSnapshot(id2);
|
|
44286
45072
|
if ("error" in snapshot) return errorResponse(snapshot.error);
|
|
44287
45073
|
if (snapshot.contentHash !== expectedContentHash) {
|
|
44288
45074
|
return errorResponse(revisionConflictMessage(snapshot.note.title));
|
|
44289
45075
|
}
|
|
44290
|
-
const result = notesManager.deleteNoteByIdIfUnchanged(
|
|
45076
|
+
const result = notesManager.deleteNoteByIdIfUnchanged(id2, snapshot.body);
|
|
44291
45077
|
if (result.status === "conflict") {
|
|
44292
45078
|
return errorResponse(revisionConflictMessage(snapshot.note.title));
|
|
44293
45079
|
}
|
|
44294
45080
|
if (result.status !== "deleted") {
|
|
44295
45081
|
return errorResponse(
|
|
44296
|
-
`The delete result for note "${snapshot.note.title}" is uncertain. Inspect exact ID ${
|
|
45082
|
+
`The delete result for note "${snapshot.note.title}" is uncertain. Inspect exact ID ${id2} before retrying.`
|
|
44297
45083
|
);
|
|
44298
45084
|
}
|
|
44299
45085
|
const sharedWarning = snapshot.note.shared ? "\n\n\u26A0\uFE0F This note was shared with collaborators. They will no longer have access." : "";
|
|
@@ -44301,7 +45087,7 @@ registerTool(
|
|
|
44301
45087
|
`Note moved to Recently Deleted: "${snapshot.note.title}"${sharedWarning}`,
|
|
44302
45088
|
{
|
|
44303
45089
|
ok: true,
|
|
44304
|
-
id,
|
|
45090
|
+
id: id2,
|
|
44305
45091
|
title: snapshot.note.title,
|
|
44306
45092
|
wasShared: snapshot.note.shared ?? false,
|
|
44307
45093
|
previousContentHash: expectedContentHash
|
|
@@ -44326,26 +45112,26 @@ registerTool(
|
|
|
44326
45112
|
verified: external_exports.boolean().optional()
|
|
44327
45113
|
}
|
|
44328
45114
|
},
|
|
44329
|
-
withErrorHandling(({ id, folder, account }) => {
|
|
44330
|
-
const note = notesManager.getNoteById(
|
|
45115
|
+
withErrorHandling(({ id: id2, folder, account }) => {
|
|
45116
|
+
const note = notesManager.getNoteById(id2);
|
|
44331
45117
|
if (!note) {
|
|
44332
|
-
return errorResponse(`Note with ID "${
|
|
45118
|
+
return errorResponse(`Note with ID "${id2}" not found`);
|
|
44333
45119
|
}
|
|
44334
|
-
const success = notesManager.moveNoteById(
|
|
45120
|
+
const success = notesManager.moveNoteById(id2, folder, account);
|
|
44335
45121
|
if (!success) {
|
|
44336
45122
|
return errorResponse(
|
|
44337
45123
|
`Failed to move note "${note.title}" to folder "${folder}". Folder may not exist.`
|
|
44338
45124
|
);
|
|
44339
45125
|
}
|
|
44340
|
-
const readback = notesManager.getNoteById(
|
|
44341
|
-
if (!readback || readback.id !==
|
|
45126
|
+
const readback = notesManager.getNoteById(id2);
|
|
45127
|
+
if (!readback || readback.id !== id2) {
|
|
44342
45128
|
return errorResponse(
|
|
44343
|
-
`The move may have succeeded, but exact-ID readback failed. Inspect note ID ${
|
|
45129
|
+
`The move may have succeeded, but exact-ID readback failed. Inspect note ID ${id2} before retrying.`
|
|
44344
45130
|
);
|
|
44345
45131
|
}
|
|
44346
45132
|
return successResponse(`Note moved and verified: "${readback.title}" -> "${folder}"`, {
|
|
44347
45133
|
ok: true,
|
|
44348
|
-
id,
|
|
45134
|
+
id: id2,
|
|
44349
45135
|
title: readback.title,
|
|
44350
45136
|
folder,
|
|
44351
45137
|
verified: true
|
|
@@ -44740,13 +45526,13 @@ registerTool(
|
|
|
44740
45526
|
count: external_exports.number().optional()
|
|
44741
45527
|
}
|
|
44742
45528
|
},
|
|
44743
|
-
withErrorHandling(({ id, title, account }) => {
|
|
44744
|
-
if (
|
|
44745
|
-
const note2 = notesManager.getNoteById(
|
|
45529
|
+
withErrorHandling(({ id: id2, title, account }) => {
|
|
45530
|
+
if (id2) {
|
|
45531
|
+
const note2 = notesManager.getNoteById(id2);
|
|
44746
45532
|
if (!note2) {
|
|
44747
|
-
return errorResponse(`Note with ID "${
|
|
45533
|
+
return errorResponse(`Note with ID "${id2}" not found`);
|
|
44748
45534
|
}
|
|
44749
|
-
const attachments2 = notesManager.listAttachmentsById(
|
|
45535
|
+
const attachments2 = notesManager.listAttachmentsById(id2);
|
|
44750
45536
|
if (attachments2.length === 0) {
|
|
44751
45537
|
return successResponse(`Note "${note2.title}" has no attachments`, {
|
|
44752
45538
|
attachments: [],
|
|
@@ -44804,18 +45590,18 @@ registerTool(
|
|
|
44804
45590
|
if (notes.length === 0) {
|
|
44805
45591
|
return errorResponse("No reviewed notes provided");
|
|
44806
45592
|
}
|
|
44807
|
-
const results = notes.map(({ id, expectedContentHash }) => {
|
|
44808
|
-
const snapshot = readExactNoteSnapshot(
|
|
44809
|
-
if ("error" in snapshot) return { id, success: false, error: snapshot.error };
|
|
45593
|
+
const results = notes.map(({ id: id2, expectedContentHash }) => {
|
|
45594
|
+
const snapshot = readExactNoteSnapshot(id2);
|
|
45595
|
+
if ("error" in snapshot) return { id: id2, success: false, error: snapshot.error };
|
|
44810
45596
|
if (snapshot.contentHash !== expectedContentHash) {
|
|
44811
|
-
return { id, success: false, error: revisionConflictMessage(snapshot.note.title) };
|
|
45597
|
+
return { id: id2, success: false, error: revisionConflictMessage(snapshot.note.title) };
|
|
44812
45598
|
}
|
|
44813
|
-
const result = notesManager.deleteNoteByIdIfUnchanged(
|
|
44814
|
-
if (result.status === "deleted") return { id, success: true };
|
|
45599
|
+
const result = notesManager.deleteNoteByIdIfUnchanged(id2, snapshot.body);
|
|
45600
|
+
if (result.status === "deleted") return { id: id2, success: true };
|
|
44815
45601
|
if (result.status === "conflict") {
|
|
44816
|
-
return { id, success: false, error: revisionConflictMessage(snapshot.note.title) };
|
|
45602
|
+
return { id: id2, success: false, error: revisionConflictMessage(snapshot.note.title) };
|
|
44817
45603
|
}
|
|
44818
|
-
return { id, success: false, error: "Delete result uncertain; inspect this exact ID" };
|
|
45604
|
+
return { id: id2, success: false, error: "Delete result uncertain; inspect this exact ID" };
|
|
44819
45605
|
});
|
|
44820
45606
|
const succeeded = results.filter((r) => r.success).length;
|
|
44821
45607
|
const failed = results.filter((r) => !r.success).length;
|
|
@@ -45003,11 +45789,11 @@ registerTool(
|
|
|
45003
45789
|
markdown: external_exports.string().optional()
|
|
45004
45790
|
}
|
|
45005
45791
|
},
|
|
45006
|
-
withErrorHandling(({ id, title, account }) => {
|
|
45007
|
-
if (
|
|
45008
|
-
const markdown2 = notesManager.getNoteMarkdownById(
|
|
45792
|
+
withErrorHandling(({ id: id2, title, account }) => {
|
|
45793
|
+
if (id2) {
|
|
45794
|
+
const markdown2 = notesManager.getNoteMarkdownById(id2);
|
|
45009
45795
|
if (!markdown2) {
|
|
45010
|
-
return errorResponse(`Note with ID "${
|
|
45796
|
+
return errorResponse(`Note with ID "${id2}" not found or has no content`);
|
|
45011
45797
|
}
|
|
45012
45798
|
return successResponse(markdown2, { markdown: markdown2 });
|
|
45013
45799
|
}
|
|
@@ -45036,17 +45822,17 @@ registerTool(
|
|
|
45036
45822
|
total: external_exports.number().optional()
|
|
45037
45823
|
}
|
|
45038
45824
|
},
|
|
45039
|
-
withErrorHandling(({ id }) => {
|
|
45040
|
-
const note = notesManager.getNoteById(
|
|
45825
|
+
withErrorHandling(({ id: id2 }) => {
|
|
45826
|
+
const note = notesManager.getNoteById(id2);
|
|
45041
45827
|
if (!note) {
|
|
45042
|
-
return errorResponse(`Note with ID "${
|
|
45828
|
+
return errorResponse(`Note with ID "${id2}" not found`);
|
|
45043
45829
|
}
|
|
45044
45830
|
if (note.passwordProtected) {
|
|
45045
45831
|
return errorResponse(
|
|
45046
45832
|
`Note "${note.title}" is password-protected and cannot be read. Unlock it in Notes.app first.`
|
|
45047
45833
|
);
|
|
45048
45834
|
}
|
|
45049
|
-
const result = getChecklistItems(
|
|
45835
|
+
const result = getChecklistItems(id2);
|
|
45050
45836
|
if (!result.items) {
|
|
45051
45837
|
return errorResponse(result.message || "Failed to read checklist state.");
|
|
45052
45838
|
}
|
|
@@ -45078,13 +45864,13 @@ registerTool(
|
|
|
45078
45864
|
smartFolderQuery: external_exports.string().optional()
|
|
45079
45865
|
}
|
|
45080
45866
|
},
|
|
45081
|
-
withErrorHandling(({ id }) => {
|
|
45082
|
-
const { metadata, message } = getNoteMetadata(
|
|
45867
|
+
withErrorHandling(({ id: id2 }) => {
|
|
45868
|
+
const { metadata, message } = getNoteMetadata(id2);
|
|
45083
45869
|
if (!metadata) {
|
|
45084
|
-
return errorResponse(message || `Failed to read metadata for note "${
|
|
45870
|
+
return errorResponse(message || `Failed to read metadata for note "${id2}"`);
|
|
45085
45871
|
}
|
|
45086
45872
|
const keys = Object.keys(metadata);
|
|
45087
|
-
const summary = keys.length === 0 ? `No additional metadata is available for note "${
|
|
45873
|
+
const summary = keys.length === 0 ? `No additional metadata is available for note "${id2}" on this macOS version.` : keys.map((k) => `${k}: ${String(metadata[k])}`).join("\n");
|
|
45088
45874
|
return successResponse(summary, metadata);
|
|
45089
45875
|
}, "Error reading note metadata")
|
|
45090
45876
|
);
|