apple-notes-mcp 2.8.22 → 2.8.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -0
- package/build/index.js +1233 -103
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -3268,8 +3268,8 @@ var require_utils = __commonJS({
|
|
|
3268
3268
|
}
|
|
3269
3269
|
return ind;
|
|
3270
3270
|
}
|
|
3271
|
-
function removeDotSegments(
|
|
3272
|
-
let input =
|
|
3271
|
+
function removeDotSegments(path5) {
|
|
3272
|
+
let input = path5;
|
|
3273
3273
|
const output = [];
|
|
3274
3274
|
let nextSlash = -1;
|
|
3275
3275
|
let len = 0;
|
|
@@ -3674,8 +3674,8 @@ var require_schemes = __commonJS({
|
|
|
3674
3674
|
}
|
|
3675
3675
|
if (wsComponent.resourceName) {
|
|
3676
3676
|
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
3677
|
-
const
|
|
3678
|
-
wsComponent.path =
|
|
3677
|
+
const path5 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
3678
|
+
wsComponent.path = path5 && path5 !== "/" ? path5 : void 0;
|
|
3679
3679
|
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
3680
3680
|
wsComponent.resourceName = void 0;
|
|
3681
3681
|
}
|
|
@@ -6883,12 +6883,12 @@ var require_formats = __commonJS({
|
|
|
6883
6883
|
"use strict";
|
|
6884
6884
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6885
6885
|
exports.formatNames = exports.fastFormats = exports.fullFormats = void 0;
|
|
6886
|
-
function fmtDef(validate,
|
|
6887
|
-
return { validate, compare };
|
|
6886
|
+
function fmtDef(validate, compare2) {
|
|
6887
|
+
return { validate, compare: compare2 };
|
|
6888
6888
|
}
|
|
6889
6889
|
exports.fullFormats = {
|
|
6890
6890
|
// date: http://tools.ietf.org/html/rfc3339#section-5.6
|
|
6891
|
-
date: fmtDef(date3,
|
|
6891
|
+
date: fmtDef(date3, compareDate2),
|
|
6892
6892
|
// date-time: http://tools.ietf.org/html/rfc3339#section-5.6
|
|
6893
6893
|
time: fmtDef(getTime(true), compareTime),
|
|
6894
6894
|
"date-time": fmtDef(getDateTime(true), compareDateTime),
|
|
@@ -6935,7 +6935,7 @@ var require_formats = __commonJS({
|
|
|
6935
6935
|
};
|
|
6936
6936
|
exports.fastFormats = {
|
|
6937
6937
|
...exports.fullFormats,
|
|
6938
|
-
date: fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\d$/,
|
|
6938
|
+
date: fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\d$/, compareDate2),
|
|
6939
6939
|
time: fmtDef(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i, compareTime),
|
|
6940
6940
|
"date-time": fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i, compareDateTime),
|
|
6941
6941
|
"iso-time": fmtDef(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i, compareIsoTime),
|
|
@@ -6963,7 +6963,7 @@ var require_formats = __commonJS({
|
|
|
6963
6963
|
const day = +matches[3];
|
|
6964
6964
|
return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month]);
|
|
6965
6965
|
}
|
|
6966
|
-
function
|
|
6966
|
+
function compareDate2(d1, d2) {
|
|
6967
6967
|
if (!(d1 && d2))
|
|
6968
6968
|
return void 0;
|
|
6969
6969
|
if (d1 > d2)
|
|
@@ -7040,7 +7040,7 @@ var require_formats = __commonJS({
|
|
|
7040
7040
|
return void 0;
|
|
7041
7041
|
const [d1, t1] = dt1.split(DATE_TIME_SEPARATOR);
|
|
7042
7042
|
const [d2, t2] = dt2.split(DATE_TIME_SEPARATOR);
|
|
7043
|
-
const res =
|
|
7043
|
+
const res = compareDate2(d1, d2);
|
|
7044
7044
|
if (res === void 0)
|
|
7045
7045
|
return void 0;
|
|
7046
7046
|
return res || compareTime(t1, t2);
|
|
@@ -7181,12 +7181,12 @@ var require_dist = __commonJS({
|
|
|
7181
7181
|
throw new Error(`Unknown format "${name}"`);
|
|
7182
7182
|
return f;
|
|
7183
7183
|
};
|
|
7184
|
-
function addFormats(ajv, list,
|
|
7184
|
+
function addFormats(ajv, list, fs5, exportName) {
|
|
7185
7185
|
var _a;
|
|
7186
7186
|
var _b;
|
|
7187
7187
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
7188
7188
|
for (const f of list)
|
|
7189
|
-
ajv.addFormat(f,
|
|
7189
|
+
ajv.addFormat(f, fs5[f]);
|
|
7190
7190
|
}
|
|
7191
7191
|
module.exports = exports = formatsPlugin;
|
|
7192
7192
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -12238,32 +12238,32 @@ var require_URL = __commonJS({
|
|
|
12238
12238
|
else
|
|
12239
12239
|
return basepath.substring(0, lastslash + 1) + refpath;
|
|
12240
12240
|
}
|
|
12241
|
-
function remove_dot_segments(
|
|
12242
|
-
if (!
|
|
12241
|
+
function remove_dot_segments(path5) {
|
|
12242
|
+
if (!path5) return path5;
|
|
12243
12243
|
var output = "";
|
|
12244
|
-
while (
|
|
12245
|
-
if (
|
|
12246
|
-
|
|
12244
|
+
while (path5.length > 0) {
|
|
12245
|
+
if (path5 === "." || path5 === "..") {
|
|
12246
|
+
path5 = "";
|
|
12247
12247
|
break;
|
|
12248
12248
|
}
|
|
12249
|
-
var twochars =
|
|
12250
|
-
var threechars =
|
|
12251
|
-
var fourchars =
|
|
12249
|
+
var twochars = path5.substring(0, 2);
|
|
12250
|
+
var threechars = path5.substring(0, 3);
|
|
12251
|
+
var fourchars = path5.substring(0, 4);
|
|
12252
12252
|
if (threechars === "../") {
|
|
12253
|
-
|
|
12253
|
+
path5 = path5.substring(3);
|
|
12254
12254
|
} else if (twochars === "./") {
|
|
12255
|
-
|
|
12255
|
+
path5 = path5.substring(2);
|
|
12256
12256
|
} else if (threechars === "/./") {
|
|
12257
|
-
|
|
12258
|
-
} else if (twochars === "/." &&
|
|
12259
|
-
|
|
12260
|
-
} else if (fourchars === "/../" || threechars === "/.." &&
|
|
12261
|
-
|
|
12257
|
+
path5 = "/" + path5.substring(3);
|
|
12258
|
+
} else if (twochars === "/." && path5.length === 2) {
|
|
12259
|
+
path5 = "/";
|
|
12260
|
+
} else if (fourchars === "/../" || threechars === "/.." && path5.length === 3) {
|
|
12261
|
+
path5 = "/" + path5.substring(4);
|
|
12262
12262
|
output = output.replace(/\/?[^\/]*$/, "");
|
|
12263
12263
|
} else {
|
|
12264
|
-
var segment =
|
|
12264
|
+
var segment = path5.match(/(\/?([^\/]*))/)[0];
|
|
12265
12265
|
output += segment;
|
|
12266
|
-
|
|
12266
|
+
path5 = path5.substring(segment.length);
|
|
12267
12267
|
}
|
|
12268
12268
|
}
|
|
12269
12269
|
return output;
|
|
@@ -24232,10 +24232,10 @@ var require_turndown_cjs = __commonJS({
|
|
|
24232
24232
|
}
|
|
24233
24233
|
var root = typeof window !== "undefined" ? window : {};
|
|
24234
24234
|
function canParseHTMLNatively() {
|
|
24235
|
-
var
|
|
24235
|
+
var Parser2 = root.DOMParser;
|
|
24236
24236
|
var canParse = false;
|
|
24237
24237
|
try {
|
|
24238
|
-
if (new
|
|
24238
|
+
if (new Parser2().parseFromString("", "text/html")) {
|
|
24239
24239
|
canParse = true;
|
|
24240
24240
|
}
|
|
24241
24241
|
} catch (e) {
|
|
@@ -24243,15 +24243,15 @@ var require_turndown_cjs = __commonJS({
|
|
|
24243
24243
|
return canParse;
|
|
24244
24244
|
}
|
|
24245
24245
|
function createHTMLParser() {
|
|
24246
|
-
var
|
|
24246
|
+
var Parser2 = function() {
|
|
24247
24247
|
};
|
|
24248
24248
|
{
|
|
24249
24249
|
var domino = require_lib();
|
|
24250
|
-
|
|
24250
|
+
Parser2.prototype.parseFromString = function(string3) {
|
|
24251
24251
|
return domino.createDocument(string3);
|
|
24252
24252
|
};
|
|
24253
24253
|
}
|
|
24254
|
-
return
|
|
24254
|
+
return Parser2;
|
|
24255
24255
|
}
|
|
24256
24256
|
var HTMLParser = canParseHTMLNatively() ? root.DOMParser : createHTMLParser();
|
|
24257
24257
|
function RootNode(input, options) {
|
|
@@ -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 join12(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 = join12(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 join12(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);
|
|
@@ -24979,8 +24979,8 @@ function getErrorMap() {
|
|
|
24979
24979
|
|
|
24980
24980
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
24981
24981
|
var makeIssue = (params) => {
|
|
24982
|
-
const { data, path:
|
|
24983
|
-
const fullPath = [...
|
|
24982
|
+
const { data, path: path5, errorMaps, issueData } = params;
|
|
24983
|
+
const fullPath = [...path5, ...issueData.path || []];
|
|
24984
24984
|
const fullIssue = {
|
|
24985
24985
|
...issueData,
|
|
24986
24986
|
path: fullPath
|
|
@@ -25096,11 +25096,11 @@ var errorUtil;
|
|
|
25096
25096
|
|
|
25097
25097
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
25098
25098
|
var ParseInputLazyPath = class {
|
|
25099
|
-
constructor(parent, value,
|
|
25099
|
+
constructor(parent, value, path5, key) {
|
|
25100
25100
|
this._cachedPath = [];
|
|
25101
25101
|
this.parent = parent;
|
|
25102
25102
|
this.data = value;
|
|
25103
|
-
this._path =
|
|
25103
|
+
this._path = path5;
|
|
25104
25104
|
this._key = key;
|
|
25105
25105
|
}
|
|
25106
25106
|
get path() {
|
|
@@ -28737,10 +28737,10 @@ function assignProp(target, prop, value) {
|
|
|
28737
28737
|
configurable: true
|
|
28738
28738
|
});
|
|
28739
28739
|
}
|
|
28740
|
-
function getElementAtPath(obj,
|
|
28741
|
-
if (!
|
|
28740
|
+
function getElementAtPath(obj, path5) {
|
|
28741
|
+
if (!path5)
|
|
28742
28742
|
return obj;
|
|
28743
|
-
return
|
|
28743
|
+
return path5.reduce((acc, key) => acc?.[key], obj);
|
|
28744
28744
|
}
|
|
28745
28745
|
function promiseAllObject(promisesObj) {
|
|
28746
28746
|
const keys = Object.keys(promisesObj);
|
|
@@ -29060,11 +29060,11 @@ function aborted(x, startIndex = 0) {
|
|
|
29060
29060
|
}
|
|
29061
29061
|
return false;
|
|
29062
29062
|
}
|
|
29063
|
-
function prefixIssues(
|
|
29063
|
+
function prefixIssues(path5, issues) {
|
|
29064
29064
|
return issues.map((iss) => {
|
|
29065
29065
|
var _a;
|
|
29066
29066
|
(_a = iss).path ?? (_a.path = []);
|
|
29067
|
-
iss.path.unshift(
|
|
29067
|
+
iss.path.unshift(path5);
|
|
29068
29068
|
return iss;
|
|
29069
29069
|
});
|
|
29070
29070
|
}
|
|
@@ -32475,11 +32475,11 @@ function normalizeObjectSchema(schema) {
|
|
|
32475
32475
|
}
|
|
32476
32476
|
return void 0;
|
|
32477
32477
|
}
|
|
32478
|
-
function getDotPath(
|
|
32479
|
-
if (
|
|
32478
|
+
function getDotPath(path5) {
|
|
32479
|
+
if (path5.length === 0) {
|
|
32480
32480
|
return "object root";
|
|
32481
32481
|
}
|
|
32482
|
-
return
|
|
32482
|
+
return path5.reduce((acc, seg, index) => {
|
|
32483
32483
|
if (index === 0) {
|
|
32484
32484
|
return String(seg);
|
|
32485
32485
|
}
|
|
@@ -39862,8 +39862,8 @@ function allowedSaveRoots() {
|
|
|
39862
39862
|
"/private/tmp"
|
|
39863
39863
|
];
|
|
39864
39864
|
}
|
|
39865
|
-
function canonicalize(
|
|
39866
|
-
return realpathSync.native(
|
|
39865
|
+
function canonicalize(path5) {
|
|
39866
|
+
return realpathSync.native(path5);
|
|
39867
39867
|
}
|
|
39868
39868
|
function isWithinRoots(candidate, roots) {
|
|
39869
39869
|
return roots.some((root) => {
|
|
@@ -39885,9 +39885,9 @@ function canonicalRoots(roots) {
|
|
|
39885
39885
|
}
|
|
39886
39886
|
return canonical;
|
|
39887
39887
|
}
|
|
39888
|
-
function entryExists(
|
|
39888
|
+
function entryExists(path5) {
|
|
39889
39889
|
try {
|
|
39890
|
-
lstatSync(
|
|
39890
|
+
lstatSync(path5);
|
|
39891
39891
|
return true;
|
|
39892
39892
|
} catch (e) {
|
|
39893
39893
|
const code = e.code;
|
|
@@ -42733,14 +42733,797 @@ function describeSearchScope(searchContent, resultCount) {
|
|
|
42733
42733
|
return "\n\n\u2139\uFE0F Only note titles were searched, so a term that appears in note bodies would not match. Retry with `searchContent: true` to search bodies instead.";
|
|
42734
42734
|
}
|
|
42735
42735
|
|
|
42736
|
+
// src/utils/noteQuery.ts
|
|
42737
|
+
var QUERY_LIMITS = {
|
|
42738
|
+
/** Maximum number of tokens (terms, operators, parentheses). */
|
|
42739
|
+
MAX_TOKENS: 256,
|
|
42740
|
+
/** Maximum nesting depth of parentheses and NOT operators combined. */
|
|
42741
|
+
MAX_DEPTH: 64
|
|
42742
|
+
};
|
|
42743
|
+
var FACETS = [
|
|
42744
|
+
"link",
|
|
42745
|
+
"attachment",
|
|
42746
|
+
"checklist",
|
|
42747
|
+
"drawing",
|
|
42748
|
+
"image",
|
|
42749
|
+
"video",
|
|
42750
|
+
"audio",
|
|
42751
|
+
"pdf",
|
|
42752
|
+
"table",
|
|
42753
|
+
"scan",
|
|
42754
|
+
"tag"
|
|
42755
|
+
];
|
|
42756
|
+
var FLAGS = ["pinned", "locked", "shared"];
|
|
42757
|
+
var NoteQueryError = class extends Error {
|
|
42758
|
+
constructor(message, position) {
|
|
42759
|
+
super(position === void 0 ? message : `${message} (at position ${position + 1})`);
|
|
42760
|
+
this.position = position;
|
|
42761
|
+
this.name = "NoteQueryError";
|
|
42762
|
+
}
|
|
42763
|
+
position;
|
|
42764
|
+
};
|
|
42765
|
+
var FIELDS = /* @__PURE__ */ new Set([
|
|
42766
|
+
"title",
|
|
42767
|
+
"body",
|
|
42768
|
+
"text",
|
|
42769
|
+
"folder",
|
|
42770
|
+
"account",
|
|
42771
|
+
"tag",
|
|
42772
|
+
"has",
|
|
42773
|
+
"is",
|
|
42774
|
+
"checklist",
|
|
42775
|
+
"words",
|
|
42776
|
+
"created",
|
|
42777
|
+
"modified"
|
|
42778
|
+
]);
|
|
42779
|
+
function readQuoted(input, start) {
|
|
42780
|
+
let value = "";
|
|
42781
|
+
let i = start + 1;
|
|
42782
|
+
while (i < input.length) {
|
|
42783
|
+
const ch = input[i];
|
|
42784
|
+
if (ch === "\\" && (input[i + 1] === '"' || input[i + 1] === "\\")) {
|
|
42785
|
+
value += input[i + 1];
|
|
42786
|
+
i += 2;
|
|
42787
|
+
continue;
|
|
42788
|
+
}
|
|
42789
|
+
if (ch === '"') return [value, i + 1];
|
|
42790
|
+
value += ch;
|
|
42791
|
+
i++;
|
|
42792
|
+
}
|
|
42793
|
+
throw new NoteQueryError("Unterminated quoted phrase", start);
|
|
42794
|
+
}
|
|
42795
|
+
var isSpace = (ch) => /\s/u.test(ch);
|
|
42796
|
+
function tokenize(input) {
|
|
42797
|
+
const tokens = [];
|
|
42798
|
+
const push = (token) => {
|
|
42799
|
+
if (tokens.length >= QUERY_LIMITS.MAX_TOKENS) {
|
|
42800
|
+
throw new NoteQueryError(
|
|
42801
|
+
`Query has more than ${QUERY_LIMITS.MAX_TOKENS} tokens; simplify it`,
|
|
42802
|
+
token.pos
|
|
42803
|
+
);
|
|
42804
|
+
}
|
|
42805
|
+
tokens.push(token);
|
|
42806
|
+
};
|
|
42807
|
+
let i = 0;
|
|
42808
|
+
while (i < input.length) {
|
|
42809
|
+
const ch = input[i];
|
|
42810
|
+
if (isSpace(ch)) {
|
|
42811
|
+
i++;
|
|
42812
|
+
continue;
|
|
42813
|
+
}
|
|
42814
|
+
if (ch === "(" || ch === ")") {
|
|
42815
|
+
push({ kind: ch === "(" ? "lparen" : "rparen", pos: i });
|
|
42816
|
+
i++;
|
|
42817
|
+
continue;
|
|
42818
|
+
}
|
|
42819
|
+
if (ch === '"') {
|
|
42820
|
+
const [value, next] = readQuoted(input, i);
|
|
42821
|
+
push({ kind: "term", pos: i, value, quoted: true });
|
|
42822
|
+
i = next;
|
|
42823
|
+
continue;
|
|
42824
|
+
}
|
|
42825
|
+
if (ch === "-" && i + 1 < input.length && !isSpace(input[i + 1]) && input[i + 1] !== ")") {
|
|
42826
|
+
push({ kind: "not", pos: i });
|
|
42827
|
+
i++;
|
|
42828
|
+
continue;
|
|
42829
|
+
}
|
|
42830
|
+
let j = i;
|
|
42831
|
+
while (j < input.length && !isSpace(input[j]) && !'()"'.includes(input[j])) j++;
|
|
42832
|
+
const word = input.slice(i, j);
|
|
42833
|
+
const colon = word.indexOf(":");
|
|
42834
|
+
if (colon > 0) {
|
|
42835
|
+
const prefix = word.slice(0, colon);
|
|
42836
|
+
const field = prefix.toLowerCase();
|
|
42837
|
+
let value = word.slice(colon + 1);
|
|
42838
|
+
if (FIELDS.has(field)) {
|
|
42839
|
+
let quoted = false;
|
|
42840
|
+
if (value === "" && input[j] === '"') {
|
|
42841
|
+
[value, j] = readQuoted(input, j);
|
|
42842
|
+
quoted = true;
|
|
42843
|
+
}
|
|
42844
|
+
push({ kind: "term", pos: i, field, value, quoted });
|
|
42845
|
+
i = j;
|
|
42846
|
+
continue;
|
|
42847
|
+
}
|
|
42848
|
+
if (/^[a-z]+$/i.test(prefix) && !value.startsWith("//")) {
|
|
42849
|
+
throw new NoteQueryError(
|
|
42850
|
+
`Unknown field "${prefix}:". Known fields: ${[...FIELDS].join(", ")}. Quote the term to search it literally`,
|
|
42851
|
+
i
|
|
42852
|
+
);
|
|
42853
|
+
}
|
|
42854
|
+
}
|
|
42855
|
+
const upper = word.toUpperCase();
|
|
42856
|
+
if (upper === "AND" || upper === "OR" || upper === "NOT") {
|
|
42857
|
+
push({ kind: upper.toLowerCase(), pos: i });
|
|
42858
|
+
} else {
|
|
42859
|
+
push({ kind: "term", pos: i, value: word, quoted: false });
|
|
42860
|
+
}
|
|
42861
|
+
i = j;
|
|
42862
|
+
}
|
|
42863
|
+
return tokens;
|
|
42864
|
+
}
|
|
42865
|
+
var COMPARE_RE = /^(>=|<=|>|<|=)?(.*)$/s;
|
|
42866
|
+
function splitComparison(value) {
|
|
42867
|
+
const match = COMPARE_RE.exec(value);
|
|
42868
|
+
return [match[1] ?? "=", match[2]];
|
|
42869
|
+
}
|
|
42870
|
+
function parseLocalDate(text, pos) {
|
|
42871
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(text);
|
|
42872
|
+
if (!match) {
|
|
42873
|
+
throw new NoteQueryError(`Expected a date as YYYY-MM-DD, got "${text}"`, pos);
|
|
42874
|
+
}
|
|
42875
|
+
const [year, month, day] = [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
42876
|
+
const start = new Date(year, month - 1, day);
|
|
42877
|
+
if (start.getFullYear() !== year || start.getMonth() !== month - 1 || start.getDate() !== day) {
|
|
42878
|
+
throw new NoteQueryError(`"${text}" is not a valid calendar date`, pos);
|
|
42879
|
+
}
|
|
42880
|
+
return { start: start.getTime(), end: new Date(year, month - 1, day + 1).getTime() };
|
|
42881
|
+
}
|
|
42882
|
+
function termNode(token) {
|
|
42883
|
+
const { field, value, quoted, pos } = token;
|
|
42884
|
+
if (field === void 0) {
|
|
42885
|
+
if (value === "") throw new NoteQueryError("Empty quoted phrase", pos);
|
|
42886
|
+
const lower2 = value.toLowerCase();
|
|
42887
|
+
if (!quoted && FLAGS.includes(lower2)) {
|
|
42888
|
+
return { type: "flag", flag: lower2 };
|
|
42889
|
+
}
|
|
42890
|
+
return { type: "text", field: "any", value };
|
|
42891
|
+
}
|
|
42892
|
+
if (value === "") throw new NoteQueryError(`"${field}:" needs a value`, pos);
|
|
42893
|
+
const lower = value.toLowerCase();
|
|
42894
|
+
switch (field) {
|
|
42895
|
+
case "title":
|
|
42896
|
+
case "body":
|
|
42897
|
+
return { type: "text", field, value };
|
|
42898
|
+
case "text":
|
|
42899
|
+
return { type: "text", field: "any", value };
|
|
42900
|
+
case "folder":
|
|
42901
|
+
case "account":
|
|
42902
|
+
return { type: field, value };
|
|
42903
|
+
case "tag": {
|
|
42904
|
+
const tag = value.replace(/^#/, "");
|
|
42905
|
+
if (!tag) throw new NoteQueryError(`"tag:" needs a tag name`, pos);
|
|
42906
|
+
return { type: "tag", value: tag };
|
|
42907
|
+
}
|
|
42908
|
+
case "has":
|
|
42909
|
+
if (!FACETS.includes(lower)) {
|
|
42910
|
+
throw new NoteQueryError(
|
|
42911
|
+
`Unknown facet "has:${value}". Supported: ${FACETS.map((f) => `has:${f}`).join(", ")}`,
|
|
42912
|
+
pos
|
|
42913
|
+
);
|
|
42914
|
+
}
|
|
42915
|
+
return { type: "has", facet: lower };
|
|
42916
|
+
case "is":
|
|
42917
|
+
if (!FLAGS.includes(lower)) {
|
|
42918
|
+
throw new NoteQueryError(`Unknown flag "is:${value}". Supported: ${FLAGS.join(", ")}`, pos);
|
|
42919
|
+
}
|
|
42920
|
+
return { type: "flag", flag: lower };
|
|
42921
|
+
case "checklist":
|
|
42922
|
+
if (lower !== "open" && lower !== "done") {
|
|
42923
|
+
throw new NoteQueryError(`"checklist:" accepts open or done, got "${value}"`, pos);
|
|
42924
|
+
}
|
|
42925
|
+
return { type: "checklist", state: lower };
|
|
42926
|
+
case "words": {
|
|
42927
|
+
const [op, number3] = splitComparison(value);
|
|
42928
|
+
if (!/^\d{1,9}$/.test(number3)) {
|
|
42929
|
+
throw new NoteQueryError(`"words:" needs a whole number, e.g. words:>250`, pos);
|
|
42930
|
+
}
|
|
42931
|
+
return { type: "words", op, value: Number(number3) };
|
|
42932
|
+
}
|
|
42933
|
+
case "created":
|
|
42934
|
+
case "modified": {
|
|
42935
|
+
const [op, date3] = splitComparison(value);
|
|
42936
|
+
return { type: "date", field, op, date: date3, ...parseLocalDate(date3, pos) };
|
|
42937
|
+
}
|
|
42938
|
+
}
|
|
42939
|
+
throw new NoteQueryError(`Unknown field "${field}:"`, pos);
|
|
42940
|
+
}
|
|
42941
|
+
var Parser = class {
|
|
42942
|
+
constructor(tokens, length) {
|
|
42943
|
+
this.tokens = tokens;
|
|
42944
|
+
this.length = length;
|
|
42945
|
+
}
|
|
42946
|
+
tokens;
|
|
42947
|
+
length;
|
|
42948
|
+
index = 0;
|
|
42949
|
+
depth = 0;
|
|
42950
|
+
parse() {
|
|
42951
|
+
if (this.tokens.length === 0) throw new NoteQueryError("Query is empty");
|
|
42952
|
+
const node = this.parseOr();
|
|
42953
|
+
const extra = this.peek();
|
|
42954
|
+
if (extra) {
|
|
42955
|
+
throw new NoteQueryError(
|
|
42956
|
+
extra.kind === "rparen" ? "Unmatched closing parenthesis" : "Unexpected token",
|
|
42957
|
+
extra.pos
|
|
42958
|
+
);
|
|
42959
|
+
}
|
|
42960
|
+
return node;
|
|
42961
|
+
}
|
|
42962
|
+
peek() {
|
|
42963
|
+
return this.tokens[this.index];
|
|
42964
|
+
}
|
|
42965
|
+
enter(pos) {
|
|
42966
|
+
this.depth++;
|
|
42967
|
+
if (this.depth > QUERY_LIMITS.MAX_DEPTH) {
|
|
42968
|
+
throw new NoteQueryError(
|
|
42969
|
+
`Query nests deeper than ${QUERY_LIMITS.MAX_DEPTH} levels; simplify it`,
|
|
42970
|
+
pos
|
|
42971
|
+
);
|
|
42972
|
+
}
|
|
42973
|
+
}
|
|
42974
|
+
missingOperand(after) {
|
|
42975
|
+
const next = this.peek();
|
|
42976
|
+
throw new NoteQueryError(`${after} needs a search term after it`, next?.pos ?? this.length);
|
|
42977
|
+
}
|
|
42978
|
+
parseOr() {
|
|
42979
|
+
const children = [this.parseAnd()];
|
|
42980
|
+
while (this.peek()?.kind === "or") {
|
|
42981
|
+
this.index++;
|
|
42982
|
+
children.push(this.parseAnd());
|
|
42983
|
+
}
|
|
42984
|
+
return children.length === 1 ? children[0] : { type: "or", children };
|
|
42985
|
+
}
|
|
42986
|
+
parseAnd() {
|
|
42987
|
+
const children = [this.parseUnary()];
|
|
42988
|
+
for (; ; ) {
|
|
42989
|
+
const next = this.peek();
|
|
42990
|
+
if (next?.kind === "and") {
|
|
42991
|
+
this.index++;
|
|
42992
|
+
children.push(this.parseUnary());
|
|
42993
|
+
} else if (next && (next.kind === "term" || next.kind === "not" || next.kind === "lparen")) {
|
|
42994
|
+
children.push(this.parseUnary());
|
|
42995
|
+
} else {
|
|
42996
|
+
break;
|
|
42997
|
+
}
|
|
42998
|
+
}
|
|
42999
|
+
return children.length === 1 ? children[0] : { type: "and", children };
|
|
43000
|
+
}
|
|
43001
|
+
parseUnary() {
|
|
43002
|
+
const token = this.peek();
|
|
43003
|
+
if (token?.kind === "not") {
|
|
43004
|
+
this.index++;
|
|
43005
|
+
this.enter(token.pos);
|
|
43006
|
+
const child = this.parseUnary();
|
|
43007
|
+
this.depth--;
|
|
43008
|
+
return { type: "not", child };
|
|
43009
|
+
}
|
|
43010
|
+
return this.parsePrimary();
|
|
43011
|
+
}
|
|
43012
|
+
parsePrimary() {
|
|
43013
|
+
const token = this.peek();
|
|
43014
|
+
if (!token) {
|
|
43015
|
+
const previous2 = this.tokens[this.index - 1];
|
|
43016
|
+
this.missingOperand(previous2 ? previous2.kind.toUpperCase().replace("LPAREN", "(") : "Query");
|
|
43017
|
+
}
|
|
43018
|
+
if (token.kind === "lparen") {
|
|
43019
|
+
this.index++;
|
|
43020
|
+
this.enter(token.pos);
|
|
43021
|
+
if (this.peek()?.kind === "rparen") {
|
|
43022
|
+
throw new NoteQueryError("Empty parentheses", token.pos);
|
|
43023
|
+
}
|
|
43024
|
+
const node = this.parseOr();
|
|
43025
|
+
if (this.peek()?.kind !== "rparen") {
|
|
43026
|
+
throw new NoteQueryError("Missing closing parenthesis", token.pos);
|
|
43027
|
+
}
|
|
43028
|
+
this.index++;
|
|
43029
|
+
this.depth--;
|
|
43030
|
+
return node;
|
|
43031
|
+
}
|
|
43032
|
+
if (token.kind === "term") {
|
|
43033
|
+
this.index++;
|
|
43034
|
+
return termNode(token);
|
|
43035
|
+
}
|
|
43036
|
+
if (token.kind === "rparen") {
|
|
43037
|
+
throw new NoteQueryError("Unexpected closing parenthesis", token.pos);
|
|
43038
|
+
}
|
|
43039
|
+
const previous = this.tokens[this.index - 1];
|
|
43040
|
+
if (!previous || previous.kind === "lparen") {
|
|
43041
|
+
throw new NoteQueryError(
|
|
43042
|
+
`${token.kind.toUpperCase()} needs a search term before it`,
|
|
43043
|
+
token.pos
|
|
43044
|
+
);
|
|
43045
|
+
}
|
|
43046
|
+
this.missingOperand(previous.kind === "term" ? "Operator" : previous.kind.toUpperCase());
|
|
43047
|
+
}
|
|
43048
|
+
};
|
|
43049
|
+
function parseNoteQuery(input) {
|
|
43050
|
+
return new Parser(tokenize(input), input.length).parse();
|
|
43051
|
+
}
|
|
43052
|
+
function normalizeForMatch(text) {
|
|
43053
|
+
return text.normalize("NFC").replace(/[\ufffc\u00a0]/gu, " ").toLowerCase();
|
|
43054
|
+
}
|
|
43055
|
+
function needsContent(node) {
|
|
43056
|
+
switch (node.type) {
|
|
43057
|
+
case "and":
|
|
43058
|
+
case "or":
|
|
43059
|
+
return node.children.some(needsContent);
|
|
43060
|
+
case "not":
|
|
43061
|
+
return needsContent(node.child);
|
|
43062
|
+
case "text":
|
|
43063
|
+
return node.field !== "title";
|
|
43064
|
+
case "tag":
|
|
43065
|
+
case "has":
|
|
43066
|
+
case "checklist":
|
|
43067
|
+
case "words":
|
|
43068
|
+
return true;
|
|
43069
|
+
default:
|
|
43070
|
+
return false;
|
|
43071
|
+
}
|
|
43072
|
+
}
|
|
43073
|
+
function needsTags(node) {
|
|
43074
|
+
switch (node.type) {
|
|
43075
|
+
case "and":
|
|
43076
|
+
case "or":
|
|
43077
|
+
return node.children.some(needsTags);
|
|
43078
|
+
case "not":
|
|
43079
|
+
return needsTags(node.child);
|
|
43080
|
+
case "tag":
|
|
43081
|
+
return true;
|
|
43082
|
+
case "has":
|
|
43083
|
+
return node.facet === "tag";
|
|
43084
|
+
default:
|
|
43085
|
+
return false;
|
|
43086
|
+
}
|
|
43087
|
+
}
|
|
43088
|
+
function compare(actual, op, expected) {
|
|
43089
|
+
switch (op) {
|
|
43090
|
+
case "=":
|
|
43091
|
+
return actual === expected;
|
|
43092
|
+
case ">":
|
|
43093
|
+
return actual > expected;
|
|
43094
|
+
case ">=":
|
|
43095
|
+
return actual >= expected;
|
|
43096
|
+
case "<":
|
|
43097
|
+
return actual < expected;
|
|
43098
|
+
case "<=":
|
|
43099
|
+
return actual <= expected;
|
|
43100
|
+
}
|
|
43101
|
+
}
|
|
43102
|
+
function compareDate(actual, node) {
|
|
43103
|
+
if (actual === void 0) return false;
|
|
43104
|
+
switch (node.op) {
|
|
43105
|
+
case "=":
|
|
43106
|
+
return actual >= node.start && actual < node.end;
|
|
43107
|
+
case ">":
|
|
43108
|
+
return actual >= node.end;
|
|
43109
|
+
case ">=":
|
|
43110
|
+
return actual >= node.start;
|
|
43111
|
+
case "<":
|
|
43112
|
+
return actual < node.start;
|
|
43113
|
+
case "<=":
|
|
43114
|
+
return actual < node.end;
|
|
43115
|
+
}
|
|
43116
|
+
}
|
|
43117
|
+
var orderCache = /* @__PURE__ */ new WeakMap();
|
|
43118
|
+
function cheapFirst(children) {
|
|
43119
|
+
let ordered = orderCache.get(children);
|
|
43120
|
+
if (!ordered) {
|
|
43121
|
+
ordered = [...children].sort((a, b) => Number(needsContent(a)) - Number(needsContent(b)));
|
|
43122
|
+
orderCache.set(children, ordered);
|
|
43123
|
+
}
|
|
43124
|
+
return ordered;
|
|
43125
|
+
}
|
|
43126
|
+
function evaluateNoteQuery(node, note) {
|
|
43127
|
+
switch (node.type) {
|
|
43128
|
+
case "and":
|
|
43129
|
+
return cheapFirst(node.children).every((child) => evaluateNoteQuery(child, note));
|
|
43130
|
+
case "or":
|
|
43131
|
+
return cheapFirst(node.children).some((child) => evaluateNoteQuery(child, note));
|
|
43132
|
+
case "not":
|
|
43133
|
+
return !evaluateNoteQuery(node.child, note);
|
|
43134
|
+
case "text": {
|
|
43135
|
+
const needle = normalizeForMatch(node.value);
|
|
43136
|
+
if (node.field === "title") return note.titleLower.includes(needle);
|
|
43137
|
+
if (node.field === "any" && note.titleLower.includes(needle)) return true;
|
|
43138
|
+
const content = note.content();
|
|
43139
|
+
if (!content) return false;
|
|
43140
|
+
return (node.field === "body" ? content.bodyLower : content.textLower).includes(needle);
|
|
43141
|
+
}
|
|
43142
|
+
case "folder":
|
|
43143
|
+
return note.folderKeys.includes(normalizeForMatch(node.value));
|
|
43144
|
+
case "account":
|
|
43145
|
+
return note.accountLower === normalizeForMatch(node.value);
|
|
43146
|
+
case "flag":
|
|
43147
|
+
return note[node.flag];
|
|
43148
|
+
case "date":
|
|
43149
|
+
return compareDate(node.field === "created" ? note.created : note.modified, node);
|
|
43150
|
+
case "tag": {
|
|
43151
|
+
const content = note.content();
|
|
43152
|
+
return Boolean(content?.tags.includes(normalizeForMatch(node.value)));
|
|
43153
|
+
}
|
|
43154
|
+
case "has":
|
|
43155
|
+
return Boolean(note.content()?.facets.has(node.facet));
|
|
43156
|
+
case "checklist": {
|
|
43157
|
+
const content = note.content();
|
|
43158
|
+
if (!content || content.checklist.total === 0) return false;
|
|
43159
|
+
return node.state === "open" ? content.checklist.open > 0 : content.checklist.open === 0;
|
|
43160
|
+
}
|
|
43161
|
+
case "words": {
|
|
43162
|
+
const content = note.content();
|
|
43163
|
+
return content ? compare(content.words, node.op, node.value) : false;
|
|
43164
|
+
}
|
|
43165
|
+
}
|
|
43166
|
+
}
|
|
43167
|
+
function positiveTextTerms(node, negated = false) {
|
|
43168
|
+
switch (node.type) {
|
|
43169
|
+
case "and":
|
|
43170
|
+
case "or":
|
|
43171
|
+
return node.children.flatMap((child) => positiveTextTerms(child, negated));
|
|
43172
|
+
case "not":
|
|
43173
|
+
return positiveTextTerms(node.child, !negated);
|
|
43174
|
+
case "text":
|
|
43175
|
+
return negated ? [] : [node.value];
|
|
43176
|
+
default:
|
|
43177
|
+
return [];
|
|
43178
|
+
}
|
|
43179
|
+
}
|
|
43180
|
+
|
|
43181
|
+
// src/utils/noteQueryStore.ts
|
|
43182
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
43183
|
+
import * as fs4 from "fs";
|
|
43184
|
+
import * as os4 from "os";
|
|
43185
|
+
import * as path4 from "path";
|
|
43186
|
+
import { gunzipSync as gunzipSync3 } from "zlib";
|
|
43187
|
+
var NOTES_DB_PATH4 = path4.join(
|
|
43188
|
+
os4.homedir(),
|
|
43189
|
+
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
43190
|
+
);
|
|
43191
|
+
var QUERY_SCAN = { DEFAULT: 500, MAX: 5e3 };
|
|
43192
|
+
var QUERY_RESULTS = { DEFAULT: 50, MAX: 500 };
|
|
43193
|
+
var CORE_DATA_EPOCH_MS = Date.UTC(2001, 0, 1);
|
|
43194
|
+
var SNIPPET_BEFORE = 60;
|
|
43195
|
+
var SNIPPET_LENGTH = 180;
|
|
43196
|
+
var QUERY_FDA_MESSAGE = `Full Disk Access is required to query notes. 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. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL} \u2014 run the doctor tool to verify.`;
|
|
43197
|
+
var NoteQueryStoreError = class extends Error {
|
|
43198
|
+
constructor(message, kind) {
|
|
43199
|
+
super(message);
|
|
43200
|
+
this.kind = kind;
|
|
43201
|
+
this.name = "NoteQueryStoreError";
|
|
43202
|
+
}
|
|
43203
|
+
kind;
|
|
43204
|
+
};
|
|
43205
|
+
function facetsForAttachmentType(uti) {
|
|
43206
|
+
const type = uti.toLowerCase();
|
|
43207
|
+
if (type === "com.apple.notes.inlinetextattachment.hashtag") return [];
|
|
43208
|
+
if (type === "com.apple.notes.inlinetextattachment.link") return ["link"];
|
|
43209
|
+
if (type.startsWith("com.apple.notes.inlinetextattachment.")) return [];
|
|
43210
|
+
if (type === "com.apple.notes.table") return ["table"];
|
|
43211
|
+
const facets = ["attachment"];
|
|
43212
|
+
if (type === "public.url") facets.push("link");
|
|
43213
|
+
else if (type === "com.apple.paper.doc.scan" || type === "com.apple.notes.gallery")
|
|
43214
|
+
facets.push("scan");
|
|
43215
|
+
else if (type === "com.adobe.pdf" || type === "com.apple.paper.doc.pdf") facets.push("pdf");
|
|
43216
|
+
else if (type === "com.apple.paper" || type === "com.apple.drawing" || type === "com.apple.drawing.2" || type === "com.apple.notes.sketch")
|
|
43217
|
+
facets.push("drawing");
|
|
43218
|
+
else if (/^public\.(jpeg|png|heic|heif|tiff|gif|image|camera-raw-image|webp|bmp|svg-image)$/.test(
|
|
43219
|
+
type
|
|
43220
|
+
) || type === "com.adobe.raw-image" || type === "com.compuserve.gif" || type === "org.webmproject.webp" || type === "com.microsoft.bmp")
|
|
43221
|
+
facets.push("image");
|
|
43222
|
+
else if (/^public\.(movie|video|mpeg-4|mpeg|avi|3gpp|3gpp2)$/.test(type) || type === "com.apple.quicktime-movie" || type === "com.apple.m4v-video")
|
|
43223
|
+
facets.push("video");
|
|
43224
|
+
else if (/^public\.(audio|mp3|mpeg-4-audio|aiff-audio|aifc-audio)$/.test(type) || type === "com.apple.m4a-audio" || type === "com.apple.coreaudio-format" || type === "com.microsoft.waveform-audio")
|
|
43225
|
+
facets.push("audio");
|
|
43226
|
+
return facets;
|
|
43227
|
+
}
|
|
43228
|
+
var CHECKLIST_STYLE = 103;
|
|
43229
|
+
function decodeNoteBody(data) {
|
|
43230
|
+
const wrapper = embeddedMessage(getField(decodeMessage(data), 2));
|
|
43231
|
+
const body = wrapper && embeddedMessage(getField(wrapper, 3));
|
|
43232
|
+
const text = body && stringValue(getField(body, 2));
|
|
43233
|
+
if (!body || text === void 0) return null;
|
|
43234
|
+
const facets = /* @__PURE__ */ new Set();
|
|
43235
|
+
const objectIds = /* @__PURE__ */ new Set();
|
|
43236
|
+
const checklist = /* @__PURE__ */ new Map();
|
|
43237
|
+
let position = 0;
|
|
43238
|
+
for (const run of getFields(body, 5)) {
|
|
43239
|
+
const fields = embeddedMessage(run);
|
|
43240
|
+
if (!fields) continue;
|
|
43241
|
+
const length = varintValue(getField(fields, 1)) ?? 0;
|
|
43242
|
+
if (stringValue(getField(fields, 9))) facets.add("link");
|
|
43243
|
+
const attachment = embeddedMessage(getField(fields, 12));
|
|
43244
|
+
if (attachment) {
|
|
43245
|
+
const id2 = stringValue(getField(attachment, 1));
|
|
43246
|
+
if (id2) objectIds.add(id2);
|
|
43247
|
+
for (const facet of facetsForAttachmentType(stringValue(getField(attachment, 2)) ?? ""))
|
|
43248
|
+
facets.add(facet);
|
|
43249
|
+
}
|
|
43250
|
+
const paragraph = embeddedMessage(getField(fields, 2));
|
|
43251
|
+
if (paragraph && varintValue(getField(paragraph, 1)) === CHECKLIST_STYLE) {
|
|
43252
|
+
const item = embeddedMessage(getField(paragraph, 5));
|
|
43253
|
+
const rawId = item && getField(item, 1)?.value;
|
|
43254
|
+
const key = rawId instanceof Uint8Array ? Buffer.from(rawId).toString("hex") : `line:${text.lastIndexOf("\n", position - 1) + 1}`;
|
|
43255
|
+
checklist.set(key, (item && varintValue(getField(item, 2))) === 1);
|
|
43256
|
+
}
|
|
43257
|
+
position += length;
|
|
43258
|
+
}
|
|
43259
|
+
if (checklist.size > 0) facets.add("checklist");
|
|
43260
|
+
const done = [...checklist.values()].filter(Boolean).length;
|
|
43261
|
+
return {
|
|
43262
|
+
text,
|
|
43263
|
+
facets,
|
|
43264
|
+
checklist: { total: checklist.size, open: checklist.size - done },
|
|
43265
|
+
objectIds
|
|
43266
|
+
};
|
|
43267
|
+
}
|
|
43268
|
+
function countWords(text) {
|
|
43269
|
+
let count = 0;
|
|
43270
|
+
for (const word of text.replace(/\ufffc/gu, " ").split(/\s+/u)) {
|
|
43271
|
+
if (/[\p{L}\p{N}]/u.test(word)) count++;
|
|
43272
|
+
}
|
|
43273
|
+
return count;
|
|
43274
|
+
}
|
|
43275
|
+
var REQUIRED_COLUMNS = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
|
|
43276
|
+
function col(available, alias, name) {
|
|
43277
|
+
return available.has(name) ? `${alias}.${name}` : "NULL";
|
|
43278
|
+
}
|
|
43279
|
+
var entity = (name) => `(SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME='${name}')`;
|
|
43280
|
+
function buildScanSql(available, options) {
|
|
43281
|
+
const missing = REQUIRED_COLUMNS.filter((c) => !available.has(c));
|
|
43282
|
+
if (missing.length) {
|
|
43283
|
+
throw new NoteQueryStoreError(
|
|
43284
|
+
`This macOS version's Notes database lacks columns query-notes needs (${missing.join(", ")}).`,
|
|
43285
|
+
"schema"
|
|
43286
|
+
);
|
|
43287
|
+
}
|
|
43288
|
+
const scan = Math.trunc(options.scanLimit);
|
|
43289
|
+
if (!Number.isInteger(scan) || scan < 1 || scan > QUERY_SCAN.MAX) {
|
|
43290
|
+
throw new NoteQueryStoreError(`scanLimit must be 1\u2013${QUERY_SCAN.MAX}`, "query_error");
|
|
43291
|
+
}
|
|
43292
|
+
const created = ["ZCREATIONDATE3", "ZCREATIONDATE1", "ZCREATIONDATE"].filter((c) => available.has(c)).map((c) => `n.${c}`).join(", ") || "NULL";
|
|
43293
|
+
const createdExpr = created.includes(",") ? `COALESCE(${created})` : created;
|
|
43294
|
+
const bool = (name) => available.has(name) ? `COALESCE(n.${name}, 0)` : "0";
|
|
43295
|
+
const notNull = (alias, name) => available.has(name) ? `${alias}.${name} IS NOT NULL` : "0";
|
|
43296
|
+
const tagsExpr = options.withTags && ["ZNOTE1", "ZTYPEUTI1", "ZALTTEXT", "ZIDENTIFIER"].every((c) => available.has(c)) ? `(SELECT json_group_array(json_array(t.ZIDENTIFIER, t.ZALTTEXT)) FROM ZICCLOUDSYNCINGOBJECT t WHERE t.ZNOTE1 = n.Z_PK AND t.ZTYPEUTI1 = 'com.apple.notes.inlinetextattachment.hashtag')` : "NULL";
|
|
43297
|
+
const where = [`n.Z_ENT = ${entity("ICNote")}`];
|
|
43298
|
+
if (!options.includeDeleted) {
|
|
43299
|
+
where.push("n.ZFOLDER IS NOT NULL");
|
|
43300
|
+
if (available.has("ZMARKEDFORDELETION")) where.push("COALESCE(n.ZMARKEDFORDELETION, 0) = 0");
|
|
43301
|
+
if (available.has("ZFOLDERTYPE")) {
|
|
43302
|
+
where.push(
|
|
43303
|
+
`n.ZFOLDER NOT IN (SELECT Z_PK FROM ZICCLOUDSYNCINGOBJECT WHERE Z_ENT = ${entity("ICFolder")} AND ZFOLDERTYPE = 1)`
|
|
43304
|
+
);
|
|
43305
|
+
}
|
|
43306
|
+
}
|
|
43307
|
+
const whereSql = where.join(" AND ");
|
|
43308
|
+
const folderRow = `SELECT json_object('k', 'folder', 'pk', f.Z_PK, 'name', ${col(available, "f", "ZTITLE2")}, 'parent', ${col(available, "f", "ZPARENT")}, 'type', ${col(available, "f", "ZFOLDERTYPE")}, 'owner', ${col(available, "f", "ZOWNER")}, 'shared', ${notNull("f", "ZSERVERSHAREDATA")}) FROM ZICCLOUDSYNCINGOBJECT f WHERE f.Z_ENT = ${entity("ICFolder")};`;
|
|
43309
|
+
const accountRow = `SELECT json_object('k', 'account', 'pk', a.Z_PK, 'name', ${col(available, "a", "ZNAME")}) FROM ZICCLOUDSYNCINGOBJECT a WHERE a.Z_ENT = ${entity("ICAccount")};`;
|
|
43310
|
+
const dataExpr = options.withBodies ? "(SELECT hex(d.ZDATA) FROM ZICNOTEDATA d WHERE d.ZNOTE = n.Z_PK ORDER BY d.Z_PK DESC LIMIT 1)" : "NULL";
|
|
43311
|
+
const noteRow = `SELECT json_object('k', 'note', 'pk', n.Z_PK, 'title', n.ZTITLE1, 'folder', n.ZFOLDER, 'created', ${createdExpr}, 'modified', n.ZMODIFICATIONDATE1, 'pinned', ${bool("ZISPINNED")}, 'locked', ${bool("ZISPASSWORDPROTECTED")}, 'shared', ${notNull("n", "ZSERVERSHAREDATA")}, 'snippet', ${col(available, "n", "ZSNIPPET")}, 'data', ${dataExpr}, 'tags', ${tagsExpr}) FROM ZICCLOUDSYNCINGOBJECT n WHERE ${whereSql} ORDER BY n.ZMODIFICATIONDATE1 DESC, n.Z_PK DESC LIMIT ${scan};`;
|
|
43312
|
+
return [
|
|
43313
|
+
"BEGIN;",
|
|
43314
|
+
"SELECT json_object('k', 'meta', 'uuid', (SELECT Z_UUID FROM Z_METADATA LIMIT 1));",
|
|
43315
|
+
`SELECT json_object('k', 'total', 'n', COUNT(*)) FROM ZICCLOUDSYNCINGOBJECT n WHERE ${whereSql};`,
|
|
43316
|
+
folderRow,
|
|
43317
|
+
accountRow,
|
|
43318
|
+
noteRow,
|
|
43319
|
+
"COMMIT;"
|
|
43320
|
+
].join(" ");
|
|
43321
|
+
}
|
|
43322
|
+
function runSqlite2(dbPath2, query) {
|
|
43323
|
+
return execFileSync6("sqlite3", ["-readonly", dbPath2, query], {
|
|
43324
|
+
encoding: "utf8",
|
|
43325
|
+
timeout: 3e4,
|
|
43326
|
+
maxBuffer: 512 * 1024 * 1024,
|
|
43327
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
43328
|
+
});
|
|
43329
|
+
}
|
|
43330
|
+
function presentColumns2(dbPath2) {
|
|
43331
|
+
const cols = /* @__PURE__ */ new Set();
|
|
43332
|
+
for (const line of runSqlite2(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
|
|
43333
|
+
const name = line.split("|")[1];
|
|
43334
|
+
if (name) cols.add(name);
|
|
43335
|
+
}
|
|
43336
|
+
return cols;
|
|
43337
|
+
}
|
|
43338
|
+
var escapeSegment = escapeFolderName;
|
|
43339
|
+
function resolveFolders(rows) {
|
|
43340
|
+
const byPk = new Map(rows.map((row) => [row.pk, row]));
|
|
43341
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
43342
|
+
const resolve3 = (pk, seen) => {
|
|
43343
|
+
const cached2 = resolved.get(pk);
|
|
43344
|
+
if (cached2) return cached2;
|
|
43345
|
+
const row = byPk.get(pk);
|
|
43346
|
+
if (!row || seen.has(pk)) return void 0;
|
|
43347
|
+
seen.add(pk);
|
|
43348
|
+
const parent = row.parent !== null ? resolve3(row.parent, seen) : void 0;
|
|
43349
|
+
const name = row.name ?? "";
|
|
43350
|
+
const path5 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
|
|
43351
|
+
const plainPath = parent ? `${parent.plainPath}/${name}` : name;
|
|
43352
|
+
const info = {
|
|
43353
|
+
path: path5,
|
|
43354
|
+
plainPath,
|
|
43355
|
+
keys: [...new Set([name, plainPath, path5].map(normalizeForMatch))],
|
|
43356
|
+
accountPk: row.owner ?? parent?.accountPk ?? null,
|
|
43357
|
+
shared: Boolean(row.shared) || Boolean(parent?.shared)
|
|
43358
|
+
};
|
|
43359
|
+
resolved.set(pk, info);
|
|
43360
|
+
return info;
|
|
43361
|
+
};
|
|
43362
|
+
for (const row of rows) resolve3(row.pk, /* @__PURE__ */ new Set());
|
|
43363
|
+
return resolved;
|
|
43364
|
+
}
|
|
43365
|
+
var coreDataMs = (seconds) => seconds === null || !Number.isFinite(seconds) ? void 0 : CORE_DATA_EPOCH_MS + seconds * 1e3;
|
|
43366
|
+
function buildSnippet(text, terms) {
|
|
43367
|
+
const clean = text.replace(/\ufffc/gu, " ");
|
|
43368
|
+
const firstBreak = clean.indexOf("\n");
|
|
43369
|
+
const body = firstBreak === -1 ? clean : clean.slice(firstBreak + 1);
|
|
43370
|
+
const lower = normalizeForMatch(body);
|
|
43371
|
+
let start = 0;
|
|
43372
|
+
if (lower.length === body.length) {
|
|
43373
|
+
const hits = terms.map((term) => lower.indexOf(normalizeForMatch(term))).filter((index) => index >= 0);
|
|
43374
|
+
if (hits.length) start = Math.max(0, Math.min(...hits) - SNIPPET_BEFORE);
|
|
43375
|
+
}
|
|
43376
|
+
const slice = body.slice(start, start + SNIPPET_LENGTH).replace(/\s+/gu, " ").trim();
|
|
43377
|
+
if (!slice) return "";
|
|
43378
|
+
return `${start > 0 ? "\u2026" : ""}${slice}${start + SNIPPET_LENGTH < body.length ? "\u2026" : ""}`;
|
|
43379
|
+
}
|
|
43380
|
+
function queryNotes(expression, options = {}) {
|
|
43381
|
+
const ast = parseNoteQuery(expression);
|
|
43382
|
+
const limit = Math.min(options.limit ?? QUERY_RESULTS.DEFAULT, QUERY_RESULTS.MAX);
|
|
43383
|
+
const scanLimit = Math.min(options.scanLimit ?? QUERY_SCAN.DEFAULT, QUERY_SCAN.MAX);
|
|
43384
|
+
const includeDeleted = options.includeDeleted ?? false;
|
|
43385
|
+
const withBodies = needsContent(ast);
|
|
43386
|
+
const withTags = needsTags(ast);
|
|
43387
|
+
const dbPath2 = options.dbPath ?? NOTES_DB_PATH4;
|
|
43388
|
+
if (!fs4.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
|
|
43389
|
+
let output;
|
|
43390
|
+
try {
|
|
43391
|
+
const available = presentColumns2(dbPath2);
|
|
43392
|
+
output = runSqlite2(
|
|
43393
|
+
dbPath2,
|
|
43394
|
+
buildScanSql(available, { scanLimit, includeDeleted, withBodies, withTags })
|
|
43395
|
+
);
|
|
43396
|
+
} catch (error2) {
|
|
43397
|
+
if (error2 instanceof NoteQueryStoreError) throw error2;
|
|
43398
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
43399
|
+
if (message.includes("authorization denied") || message.includes("unable to open database")) {
|
|
43400
|
+
throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
|
|
43401
|
+
}
|
|
43402
|
+
console.error(`query-notes: database read failed: ${message}`);
|
|
43403
|
+
throw new NoteQueryStoreError("Failed to read the Notes database.", "query_error");
|
|
43404
|
+
}
|
|
43405
|
+
let uuid2;
|
|
43406
|
+
let eligible = 0;
|
|
43407
|
+
const folderRows = [];
|
|
43408
|
+
const accounts = /* @__PURE__ */ new Map();
|
|
43409
|
+
const notes = [];
|
|
43410
|
+
for (const line of output.split("\n")) {
|
|
43411
|
+
if (!line.trim()) continue;
|
|
43412
|
+
const row = JSON.parse(line);
|
|
43413
|
+
if (row.k === "meta") uuid2 = typeof row.uuid === "string" ? row.uuid : void 0;
|
|
43414
|
+
else if (row.k === "total") eligible = Number(row.n) || 0;
|
|
43415
|
+
else if (row.k === "folder") folderRows.push(row);
|
|
43416
|
+
else if (row.k === "account" && typeof row.name === "string")
|
|
43417
|
+
accounts.set(Number(row.pk), row.name);
|
|
43418
|
+
else if (row.k === "note") notes.push(row);
|
|
43419
|
+
}
|
|
43420
|
+
if (!uuid2 || !/^[0-9A-Fa-f-]+$/.test(uuid2)) {
|
|
43421
|
+
throw new NoteQueryStoreError(
|
|
43422
|
+
"The Notes database has no store identifier, so note IDs cannot be formed.",
|
|
43423
|
+
"schema"
|
|
43424
|
+
);
|
|
43425
|
+
}
|
|
43426
|
+
const folders = resolveFolders(folderRows);
|
|
43427
|
+
const textTerms = positiveTextTerms(ast);
|
|
43428
|
+
const hits = [];
|
|
43429
|
+
let matched = 0;
|
|
43430
|
+
let unreadable = 0;
|
|
43431
|
+
for (const row of notes) {
|
|
43432
|
+
const folder = row.folder !== null ? folders.get(row.folder) : void 0;
|
|
43433
|
+
const account = folder?.accountPk !== null && folder?.accountPk !== void 0 ? accounts.get(folder.accountPk) : void 0;
|
|
43434
|
+
const locked = Boolean(row.locked);
|
|
43435
|
+
let decoded;
|
|
43436
|
+
let content;
|
|
43437
|
+
const decode = () => {
|
|
43438
|
+
if (decoded !== void 0) return decoded;
|
|
43439
|
+
decoded = null;
|
|
43440
|
+
if (locked) return decoded;
|
|
43441
|
+
if (!row.data) {
|
|
43442
|
+
if (withBodies) unreadable++;
|
|
43443
|
+
return decoded;
|
|
43444
|
+
}
|
|
43445
|
+
try {
|
|
43446
|
+
decoded = decodeNoteBody(
|
|
43447
|
+
new Uint8Array(
|
|
43448
|
+
gunzipSync3(Buffer.from(row.data, "hex"), { maxOutputLength: 32 * 1024 * 1024 })
|
|
43449
|
+
)
|
|
43450
|
+
);
|
|
43451
|
+
} catch {
|
|
43452
|
+
decoded = null;
|
|
43453
|
+
}
|
|
43454
|
+
if (!decoded) unreadable++;
|
|
43455
|
+
return decoded;
|
|
43456
|
+
};
|
|
43457
|
+
const note = {
|
|
43458
|
+
titleLower: normalizeForMatch(row.title ?? ""),
|
|
43459
|
+
folderKeys: folder?.keys ?? [],
|
|
43460
|
+
accountLower: account !== void 0 ? normalizeForMatch(account) : void 0,
|
|
43461
|
+
pinned: Boolean(row.pinned),
|
|
43462
|
+
locked,
|
|
43463
|
+
shared: Boolean(row.shared) || Boolean(folder?.shared),
|
|
43464
|
+
created: coreDataMs(row.created),
|
|
43465
|
+
modified: coreDataMs(row.modified),
|
|
43466
|
+
content: () => {
|
|
43467
|
+
if (content !== void 0) return content;
|
|
43468
|
+
const body2 = decode();
|
|
43469
|
+
if (!body2) return content = null;
|
|
43470
|
+
const text = body2.text;
|
|
43471
|
+
const firstBreak = text.indexOf("\n");
|
|
43472
|
+
const tags = /* @__PURE__ */ new Set();
|
|
43473
|
+
for (const [id2, alt] of row.tags ?? []) {
|
|
43474
|
+
if (id2 && alt && body2.objectIds.has(id2))
|
|
43475
|
+
tags.add(normalizeForMatch(alt.replace(/^#/, "")));
|
|
43476
|
+
}
|
|
43477
|
+
const facets = new Set(body2.facets);
|
|
43478
|
+
if (tags.size) facets.add("tag");
|
|
43479
|
+
content = {
|
|
43480
|
+
textLower: normalizeForMatch(text),
|
|
43481
|
+
bodyLower: normalizeForMatch(firstBreak === -1 ? "" : text.slice(firstBreak + 1)),
|
|
43482
|
+
words: countWords(text),
|
|
43483
|
+
facets,
|
|
43484
|
+
checklist: body2.checklist,
|
|
43485
|
+
tags: [...tags]
|
|
43486
|
+
};
|
|
43487
|
+
return content;
|
|
43488
|
+
}
|
|
43489
|
+
};
|
|
43490
|
+
if (!evaluateNoteQuery(ast, note)) continue;
|
|
43491
|
+
matched++;
|
|
43492
|
+
if (hits.length >= limit) continue;
|
|
43493
|
+
const body = locked ? null : decode();
|
|
43494
|
+
hits.push({
|
|
43495
|
+
id: `x-coredata://${uuid2}/ICNote/p${row.pk}`,
|
|
43496
|
+
title: row.title ?? "",
|
|
43497
|
+
...folder ? { folder: folder.path } : {},
|
|
43498
|
+
...account !== void 0 ? { account } : {},
|
|
43499
|
+
...note.modified !== void 0 ? { modified: new Date(note.modified).toISOString() } : {},
|
|
43500
|
+
...note.created !== void 0 ? { created: new Date(note.created).toISOString() } : {},
|
|
43501
|
+
snippet: locked ? "" : body ? buildSnippet(body.text, textTerms) : (row.snippet ?? "").replace(/\s+/gu, " ").trim(),
|
|
43502
|
+
...locked ? { locked: true } : {}
|
|
43503
|
+
});
|
|
43504
|
+
}
|
|
43505
|
+
return {
|
|
43506
|
+
notes: hits,
|
|
43507
|
+
count: hits.length,
|
|
43508
|
+
matched,
|
|
43509
|
+
scanned: notes.length,
|
|
43510
|
+
eligible,
|
|
43511
|
+
scanLimit,
|
|
43512
|
+
scanTruncated: eligible > notes.length,
|
|
43513
|
+
limit,
|
|
43514
|
+
truncated: matched > hits.length,
|
|
43515
|
+
unreadable
|
|
43516
|
+
};
|
|
43517
|
+
}
|
|
43518
|
+
|
|
42736
43519
|
// src/tools/doctor.ts
|
|
42737
43520
|
import { spawnSync } from "child_process";
|
|
42738
43521
|
|
|
42739
43522
|
// src/services/nativeTags.ts
|
|
42740
|
-
import { execFileSync as
|
|
43523
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
42741
43524
|
import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
|
|
42742
43525
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
42743
|
-
import { join as
|
|
43526
|
+
import { join as join8 } from "node:path";
|
|
42744
43527
|
|
|
42745
43528
|
// src/services/shortcutConsent.ts
|
|
42746
43529
|
function shortcutConsentHint(shortcut) {
|
|
@@ -42815,13 +43598,19 @@ function addNativeTags(request, deps) {
|
|
|
42815
43598
|
} : {}
|
|
42816
43599
|
};
|
|
42817
43600
|
}
|
|
42818
|
-
function
|
|
42819
|
-
|
|
43601
|
+
function listInstalledShortcuts() {
|
|
43602
|
+
return execFileSync7("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
|
|
42820
43603
|
encoding: "utf8",
|
|
42821
43604
|
timeout: 15e3,
|
|
42822
43605
|
maxBuffer: 1024 * 1024,
|
|
42823
43606
|
stdio: ["ignore", "pipe", "pipe"]
|
|
42824
43607
|
}).split(/\r?\n/u);
|
|
43608
|
+
}
|
|
43609
|
+
var nativeTagsShortcutName = () => process.env.APPLE_NOTES_MCP_TAGS_SHORTCUT || NATIVE_TAGS_SHORTCUT;
|
|
43610
|
+
function nativeTagsStatus(shortcut = nativeTagsShortcutName()) {
|
|
43611
|
+
return resolveShortcut(listInstalledShortcuts(), shortcut);
|
|
43612
|
+
}
|
|
43613
|
+
function resolveShortcut(lines, shortcut) {
|
|
42825
43614
|
const matches = lines.flatMap((line) => {
|
|
42826
43615
|
const match = /^(.*) \(([0-9A-Fa-f-]{36})\)$/.exec(line);
|
|
42827
43616
|
return match && (match[1] === shortcut || match[2].toLowerCase() === shortcut.toLowerCase()) ? [{ name: match[1], identifier: match[2] }] : [];
|
|
@@ -42836,11 +43625,11 @@ function runNativeTagsShortcut(input) {
|
|
|
42836
43625
|
const status = nativeTagsStatus();
|
|
42837
43626
|
if (!status.installed)
|
|
42838
43627
|
throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
|
|
42839
|
-
const directory = mkdtempSync2(
|
|
43628
|
+
const directory = mkdtempSync2(join8(tmpdir2(), "apple-notes-native-tags-"));
|
|
42840
43629
|
try {
|
|
42841
|
-
const
|
|
42842
|
-
writeFileSync(
|
|
42843
|
-
|
|
43630
|
+
const path5 = join8(directory, "request.json");
|
|
43631
|
+
writeFileSync(path5, JSON.stringify(input), { mode: 384 });
|
|
43632
|
+
execFileSync7("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path5], {
|
|
42844
43633
|
encoding: "utf8",
|
|
42845
43634
|
timeout: 6e4,
|
|
42846
43635
|
maxBuffer: 1024 * 1024,
|
|
@@ -42859,10 +43648,10 @@ function runNativeTagsShortcut(input) {
|
|
|
42859
43648
|
}
|
|
42860
43649
|
|
|
42861
43650
|
// src/services/backgroundNotes.ts
|
|
42862
|
-
import { execFileSync as
|
|
43651
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
42863
43652
|
import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
|
|
42864
43653
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
42865
|
-
import { join as
|
|
43654
|
+
import { join as join9 } from "node:path";
|
|
42866
43655
|
|
|
42867
43656
|
// src/utils/appendMarkdown.ts
|
|
42868
43657
|
var escape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -42938,10 +43727,12 @@ function comparableVisibleText(html) {
|
|
|
42938
43727
|
|
|
42939
43728
|
// src/services/backgroundNotes.ts
|
|
42940
43729
|
var BACKGROUND_SHORTCUT = "Apple Notes MCP - Background Operations v5";
|
|
42941
|
-
var
|
|
43730
|
+
var backgroundShortcutName = () => process.env.APPLE_NOTES_MCP_BACKGROUND_SHORTCUT || BACKGROUND_SHORTCUT;
|
|
43731
|
+
var backgroundStatus = () => nativeTagsStatus(backgroundShortcutName());
|
|
42942
43732
|
var nativeTagBridgeStatus = () => nativeTagsStatus();
|
|
42943
43733
|
var MARKDOWN_NOTE_SHORTCUT = "Apple Notes MCP - Create Markdown Note";
|
|
42944
|
-
var
|
|
43734
|
+
var markdownShortcutName = () => process.env.APPLE_NOTES_MCP_MARKDOWN_SHORTCUT || MARKDOWN_NOTE_SHORTCUT;
|
|
43735
|
+
var markdownNoteStatus = () => nativeTagsStatus(markdownShortcutName());
|
|
42945
43736
|
function readBackgroundSnapshot(manager, id2) {
|
|
42946
43737
|
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(id2))
|
|
42947
43738
|
throw new Error("Exact note ID required");
|
|
@@ -43049,9 +43840,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
43049
43840
|
throw new Error(
|
|
43050
43841
|
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
43051
43842
|
);
|
|
43052
|
-
const directory = mkdtempSync3(
|
|
43843
|
+
const directory = mkdtempSync3(join9(tmpdir3(), "apple-notes-background-"));
|
|
43053
43844
|
try {
|
|
43054
|
-
const file =
|
|
43845
|
+
const file = join9(directory, "request.json");
|
|
43055
43846
|
writeFileSync2(
|
|
43056
43847
|
file,
|
|
43057
43848
|
JSON.stringify({
|
|
@@ -43071,7 +43862,7 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
43071
43862
|
{ mode: 384 }
|
|
43072
43863
|
);
|
|
43073
43864
|
try {
|
|
43074
|
-
|
|
43865
|
+
execFileSync8("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
|
|
43075
43866
|
encoding: "utf8",
|
|
43076
43867
|
timeout: 6e4,
|
|
43077
43868
|
maxBuffer: 1024 * 1024,
|
|
@@ -43274,7 +44065,7 @@ function createMarkdownNote(manager, request, run = runBackgroundShortcut) {
|
|
|
43274
44065
|
throw new Error(
|
|
43275
44066
|
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
43276
44067
|
);
|
|
43277
|
-
const segments = (
|
|
44068
|
+
const segments = (path5) => JSON.stringify(splitFolderPath(path5).map((part) => part.toLocaleLowerCase()));
|
|
43278
44069
|
if (request.folder) {
|
|
43279
44070
|
const wanted = segments(request.folder);
|
|
43280
44071
|
if (!manager.listAccounts().some(
|
|
@@ -43362,8 +44153,261 @@ ${request.content}`
|
|
|
43362
44153
|
}
|
|
43363
44154
|
}
|
|
43364
44155
|
|
|
44156
|
+
// src/services/capabilityMatrix.ts
|
|
44157
|
+
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
44158
|
+
import { release } from "node:os";
|
|
44159
|
+
var MACOS_SHORTCUTS_CLI = "12.0";
|
|
44160
|
+
var MACOS_MARKDOWN_IMPORT = "26.0";
|
|
44161
|
+
var FEATURES = [
|
|
44162
|
+
{
|
|
44163
|
+
name: "applescriptCore",
|
|
44164
|
+
description: "Create, read, search, update, move, and delete notes and folders through Notes.app's AppleScript interface",
|
|
44165
|
+
tools: [
|
|
44166
|
+
"create-note",
|
|
44167
|
+
"search-notes",
|
|
44168
|
+
"get-note-content",
|
|
44169
|
+
"update-note",
|
|
44170
|
+
"append-to-note",
|
|
44171
|
+
"insert-link",
|
|
44172
|
+
"delete-note",
|
|
44173
|
+
"move-note",
|
|
44174
|
+
"list-notes",
|
|
44175
|
+
"list-folders",
|
|
44176
|
+
"create-folder",
|
|
44177
|
+
"list-accounts"
|
|
44178
|
+
],
|
|
44179
|
+
minimumMacOSVersion: null,
|
|
44180
|
+
requirements: [{ kind: "notes_automation" }]
|
|
44181
|
+
},
|
|
44182
|
+
{
|
|
44183
|
+
name: "fullDiskAccessReads",
|
|
44184
|
+
description: "Read-only reads of the Notes database: query-notes, checklist state, note metadata, note links, native objects, native tags, and sync detail",
|
|
44185
|
+
tools: [
|
|
44186
|
+
"query-notes",
|
|
44187
|
+
"get-checklist-state",
|
|
44188
|
+
"get-note-metadata",
|
|
44189
|
+
"get-note-link",
|
|
44190
|
+
"get-native-objects",
|
|
44191
|
+
"list-native-tags",
|
|
44192
|
+
"get-note-markdown (checklist annotations)",
|
|
44193
|
+
"get-sync-status (pending uploads)"
|
|
44194
|
+
],
|
|
44195
|
+
minimumMacOSVersion: null,
|
|
44196
|
+
requirements: [{ kind: "full_disk_access" }]
|
|
44197
|
+
},
|
|
44198
|
+
{
|
|
44199
|
+
name: "shortcutsBridges",
|
|
44200
|
+
description: "Run the optional native-write Shortcut bridges in the background",
|
|
44201
|
+
tools: [],
|
|
44202
|
+
minimumMacOSVersion: MACOS_SHORTCUTS_CLI,
|
|
44203
|
+
requirements: [{ kind: "shortcuts_cli" }]
|
|
44204
|
+
},
|
|
44205
|
+
{
|
|
44206
|
+
name: "backgroundOperationsBridge",
|
|
44207
|
+
description: "Native background edits through the Background Operations bridge, verified by exact-ID database readback",
|
|
44208
|
+
tools: [
|
|
44209
|
+
"append-native",
|
|
44210
|
+
"create-checklist-item",
|
|
44211
|
+
"create-checklist-items",
|
|
44212
|
+
"create-table",
|
|
44213
|
+
"insert-note-link",
|
|
44214
|
+
"set-note-pinned",
|
|
44215
|
+
"remove-native-tags"
|
|
44216
|
+
],
|
|
44217
|
+
minimumMacOSVersion: MACOS_SHORTCUTS_CLI,
|
|
44218
|
+
requirements: [
|
|
44219
|
+
{ kind: "notes_automation" },
|
|
44220
|
+
{ kind: "full_disk_access" },
|
|
44221
|
+
{ kind: "shortcuts_cli" },
|
|
44222
|
+
{ kind: "shortcut", name: backgroundShortcutName }
|
|
44223
|
+
]
|
|
44224
|
+
},
|
|
44225
|
+
{
|
|
44226
|
+
name: "nativeTagsBridge",
|
|
44227
|
+
description: "Add real Notes tags through the Native Tags bridge",
|
|
44228
|
+
tools: ["add-native-tags", "replace-native-tag"],
|
|
44229
|
+
minimumMacOSVersion: MACOS_SHORTCUTS_CLI,
|
|
44230
|
+
requirements: [
|
|
44231
|
+
{ kind: "notes_automation" },
|
|
44232
|
+
{ kind: "full_disk_access" },
|
|
44233
|
+
{ kind: "shortcuts_cli" },
|
|
44234
|
+
{ kind: "shortcut", name: nativeTagsShortcutName }
|
|
44235
|
+
]
|
|
44236
|
+
},
|
|
44237
|
+
{
|
|
44238
|
+
name: "markdownNoteBridge",
|
|
44239
|
+
description: "Create a note from Markdown with real Title, Heading, and Subheading styles through the Create Markdown Note bridge",
|
|
44240
|
+
tools: ['create-note (format: "markdown")'],
|
|
44241
|
+
minimumMacOSVersion: MACOS_MARKDOWN_IMPORT,
|
|
44242
|
+
requirements: [
|
|
44243
|
+
{ kind: "notes_automation" },
|
|
44244
|
+
{ kind: "full_disk_access" },
|
|
44245
|
+
{ kind: "shortcuts_cli" },
|
|
44246
|
+
{ kind: "shortcut", name: markdownShortcutName }
|
|
44247
|
+
]
|
|
44248
|
+
},
|
|
44249
|
+
// Placeholders for features that need a native helper this server does not
|
|
44250
|
+
// ship. They always report `not_implemented` so clients can detect them.
|
|
44251
|
+
{
|
|
44252
|
+
name: "checklistToggle",
|
|
44253
|
+
description: "Check or uncheck an existing checklist item in place",
|
|
44254
|
+
tools: [],
|
|
44255
|
+
minimumMacOSVersion: null,
|
|
44256
|
+
requirements: [{ kind: "native_helper" }]
|
|
44257
|
+
},
|
|
44258
|
+
{
|
|
44259
|
+
name: "smartFolders",
|
|
44260
|
+
description: "Create or edit Smart Folders and their tag rules",
|
|
44261
|
+
tools: [],
|
|
44262
|
+
minimumMacOSVersion: null,
|
|
44263
|
+
requirements: [{ kind: "native_helper" }]
|
|
44264
|
+
},
|
|
44265
|
+
{
|
|
44266
|
+
name: "paragraphLinks",
|
|
44267
|
+
description: "Link to a specific paragraph or heading inside a note",
|
|
44268
|
+
tools: [],
|
|
44269
|
+
minimumMacOSVersion: null,
|
|
44270
|
+
requirements: [{ kind: "native_helper" }]
|
|
44271
|
+
},
|
|
44272
|
+
{
|
|
44273
|
+
name: "audioTranscription",
|
|
44274
|
+
description: "Transcribe audio recordings attached to a note",
|
|
44275
|
+
tools: [],
|
|
44276
|
+
minimumMacOSVersion: null,
|
|
44277
|
+
requirements: [{ kind: "native_helper" }]
|
|
44278
|
+
}
|
|
44279
|
+
];
|
|
44280
|
+
function requirementLabel(requirement) {
|
|
44281
|
+
return requirement.kind === "shortcut" ? `shortcut:${requirement.name()}` : requirement.kind;
|
|
44282
|
+
}
|
|
44283
|
+
function compareVersions(a, b) {
|
|
44284
|
+
const pa = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
44285
|
+
const pb = b.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
44286
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
44287
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
44288
|
+
if (diff !== 0) return diff;
|
|
44289
|
+
}
|
|
44290
|
+
return 0;
|
|
44291
|
+
}
|
|
44292
|
+
function evaluateFeature(feature, env) {
|
|
44293
|
+
const isMac = env.platform === "darwin";
|
|
44294
|
+
const floor = feature.minimumMacOSVersion;
|
|
44295
|
+
const versionKnown = env.macOSVersion !== null;
|
|
44296
|
+
const osSupported = isMac && (floor === null || versionKnown && compareVersions(env.macOSVersion, floor) >= 0);
|
|
44297
|
+
const missing = [];
|
|
44298
|
+
const unverified = [];
|
|
44299
|
+
const failures = /* @__PURE__ */ new Set();
|
|
44300
|
+
for (const requirement of feature.requirements) {
|
|
44301
|
+
const label = requirementLabel(requirement);
|
|
44302
|
+
switch (requirement.kind) {
|
|
44303
|
+
case "notes_automation":
|
|
44304
|
+
unverified.push(label);
|
|
44305
|
+
break;
|
|
44306
|
+
case "full_disk_access":
|
|
44307
|
+
if (!env.fullDiskAccess) {
|
|
44308
|
+
missing.push(label);
|
|
44309
|
+
failures.add("full_disk_access_missing");
|
|
44310
|
+
}
|
|
44311
|
+
break;
|
|
44312
|
+
case "shortcuts_cli":
|
|
44313
|
+
if (env.shortcutLines === null) {
|
|
44314
|
+
missing.push(label);
|
|
44315
|
+
failures.add("shortcuts_unavailable");
|
|
44316
|
+
}
|
|
44317
|
+
break;
|
|
44318
|
+
case "shortcut":
|
|
44319
|
+
if (env.shortcutLines === null) unverified.push(label);
|
|
44320
|
+
else if (!resolveShortcut(env.shortcutLines, requirement.name()).installed) {
|
|
44321
|
+
missing.push(label);
|
|
44322
|
+
failures.add("shortcut_not_installed");
|
|
44323
|
+
}
|
|
44324
|
+
break;
|
|
44325
|
+
case "native_helper":
|
|
44326
|
+
missing.push(label);
|
|
44327
|
+
failures.add("not_implemented");
|
|
44328
|
+
break;
|
|
44329
|
+
}
|
|
44330
|
+
}
|
|
44331
|
+
let reason = null;
|
|
44332
|
+
if (!isMac) reason = "unsupported_platform";
|
|
44333
|
+
else if (failures.has("not_implemented")) reason = "not_implemented";
|
|
44334
|
+
else if (floor !== null && !versionKnown) reason = "unknown_os_version";
|
|
44335
|
+
else if (!osSupported) reason = `requires_macos_${Number.parseInt(floor, 10)}`;
|
|
44336
|
+
else if (failures.has("full_disk_access_missing")) reason = "full_disk_access_missing";
|
|
44337
|
+
else if (failures.has("shortcuts_unavailable")) reason = "shortcuts_unavailable";
|
|
44338
|
+
else if (failures.has("shortcut_not_installed")) reason = "shortcut_not_installed";
|
|
44339
|
+
return {
|
|
44340
|
+
description: feature.description,
|
|
44341
|
+
tools: feature.tools,
|
|
44342
|
+
available: reason === null,
|
|
44343
|
+
osSupported,
|
|
44344
|
+
minimumMacOSVersion: floor,
|
|
44345
|
+
requirements: feature.requirements.map(requirementLabel),
|
|
44346
|
+
missing,
|
|
44347
|
+
unverified,
|
|
44348
|
+
reason
|
|
44349
|
+
};
|
|
44350
|
+
}
|
|
44351
|
+
function evaluateFeatures(env, features = FEATURES) {
|
|
44352
|
+
return {
|
|
44353
|
+
runtimeOS: {
|
|
44354
|
+
platform: env.platform,
|
|
44355
|
+
macOSVersion: env.macOSVersion,
|
|
44356
|
+
darwinRelease: env.darwinRelease
|
|
44357
|
+
},
|
|
44358
|
+
features: Object.fromEntries(features.map((f) => [f.name, evaluateFeature(f, env)]))
|
|
44359
|
+
};
|
|
44360
|
+
}
|
|
44361
|
+
function readMacOSVersion() {
|
|
44362
|
+
if (process.platform !== "darwin") return null;
|
|
44363
|
+
try {
|
|
44364
|
+
const out = execFileSync9("/usr/bin/sw_vers", ["-productVersion"], {
|
|
44365
|
+
encoding: "utf8",
|
|
44366
|
+
timeout: 3e3,
|
|
44367
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
44368
|
+
}).trim();
|
|
44369
|
+
return /^\d+(\.\d+)*$/.test(out) ? out : null;
|
|
44370
|
+
} catch {
|
|
44371
|
+
return null;
|
|
44372
|
+
}
|
|
44373
|
+
}
|
|
44374
|
+
function probeCapabilityEnvironment() {
|
|
44375
|
+
const platform = process.platform;
|
|
44376
|
+
const isMac = platform === "darwin";
|
|
44377
|
+
let shortcutLines = null;
|
|
44378
|
+
if (isMac) {
|
|
44379
|
+
try {
|
|
44380
|
+
shortcutLines = listInstalledShortcuts();
|
|
44381
|
+
} catch {
|
|
44382
|
+
shortcutLines = null;
|
|
44383
|
+
}
|
|
44384
|
+
}
|
|
44385
|
+
return {
|
|
44386
|
+
platform,
|
|
44387
|
+
macOSVersion: readMacOSVersion(),
|
|
44388
|
+
darwinRelease: release(),
|
|
44389
|
+
fullDiskAccess: isMac && hasFullDiskAccess(),
|
|
44390
|
+
shortcutLines
|
|
44391
|
+
};
|
|
44392
|
+
}
|
|
44393
|
+
function getCapabilityMatrix(probe = probeCapabilityEnvironment) {
|
|
44394
|
+
return evaluateFeatures(probe());
|
|
44395
|
+
}
|
|
44396
|
+
function formatCapabilityMatrix(matrix) {
|
|
44397
|
+
const os5 = matrix.runtimeOS;
|
|
44398
|
+
const lines = [
|
|
44399
|
+
`Feature matrix (macOS ${os5.macOSVersion ?? "unknown"}, Darwin ${os5.darwinRelease}):`
|
|
44400
|
+
];
|
|
44401
|
+
for (const [name, status] of Object.entries(matrix.features)) {
|
|
44402
|
+
lines.push(
|
|
44403
|
+
status.available ? ` \u2713 ${name}: available${status.unverified.length ? ` (unverified: ${status.unverified.join(", ")})` : ""}` : ` \u2717 ${name}: ${status.reason}${status.missing.length ? ` (missing: ${status.missing.join(", ")})` : ""}`
|
|
44404
|
+
);
|
|
44405
|
+
}
|
|
44406
|
+
return lines.join("\n");
|
|
44407
|
+
}
|
|
44408
|
+
|
|
43365
44409
|
// src/tools/doctor.ts
|
|
43366
|
-
function runDoctor(manager) {
|
|
44410
|
+
function runDoctor(manager, capabilityMatrix = getCapabilityMatrix) {
|
|
43367
44411
|
const checks = [];
|
|
43368
44412
|
const hc = manager.healthCheck();
|
|
43369
44413
|
for (const c of hc.checks) {
|
|
@@ -43414,7 +44458,13 @@ function runDoctor(manager) {
|
|
|
43414
44458
|
}
|
|
43415
44459
|
checks.push(checkNodeRuntimeSignature());
|
|
43416
44460
|
const healthy = !checks.some((c) => c.status === "fail");
|
|
43417
|
-
|
|
44461
|
+
let matrix;
|
|
44462
|
+
try {
|
|
44463
|
+
matrix = capabilityMatrix();
|
|
44464
|
+
} catch {
|
|
44465
|
+
matrix = void 0;
|
|
44466
|
+
}
|
|
44467
|
+
return matrix ? { healthy, checks, runtimeOS: matrix.runtimeOS, features: matrix.features } : { healthy, checks };
|
|
43418
44468
|
}
|
|
43419
44469
|
function markdownBridgeDetail() {
|
|
43420
44470
|
const purpose = 'needed only for create-note format: "markdown" on macOS 26+';
|
|
@@ -43458,23 +44508,25 @@ function formatDoctorReport(r) {
|
|
|
43458
44508
|
const icon = (s) => s === "ok" ? "\u2705" : s === "warn" ? "\u26A0\uFE0F " : "\u274C";
|
|
43459
44509
|
const lines = [`\u{1FA7A} apple-notes-mcp doctor \u2014 ${r.healthy ? "healthy" : "ISSUES FOUND"}`, ""];
|
|
43460
44510
|
for (const c of r.checks) lines.push(`${icon(c.status)} ${c.name}: ${c.detail}`);
|
|
44511
|
+
if (r.runtimeOS && r.features)
|
|
44512
|
+
lines.push("", formatCapabilityMatrix({ runtimeOS: r.runtimeOS, features: r.features }));
|
|
43461
44513
|
return lines.join("\n");
|
|
43462
44514
|
}
|
|
43463
44515
|
|
|
43464
44516
|
// src/services/fileConfig.ts
|
|
43465
|
-
import { existsSync as
|
|
43466
|
-
import { join as
|
|
43467
|
-
import { homedir as
|
|
44517
|
+
import { existsSync as existsSync7, readFileSync as readFileSync2 } from "fs";
|
|
44518
|
+
import { join as join10 } from "path";
|
|
44519
|
+
import { homedir as homedir8 } from "os";
|
|
43468
44520
|
function fileConfigPath(env = process.env) {
|
|
43469
44521
|
const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
|
|
43470
44522
|
if (override && override.trim()) return override.trim();
|
|
43471
|
-
return
|
|
44523
|
+
return join10(homedir8(), "Library", "Application Support", "apple-notes-mcp", "config.json");
|
|
43472
44524
|
}
|
|
43473
|
-
function loadFileConfig(env = process.env,
|
|
44525
|
+
function loadFileConfig(env = process.env, path5 = fileConfigPath(env)) {
|
|
43474
44526
|
const applied = [];
|
|
43475
44527
|
try {
|
|
43476
|
-
if (!
|
|
43477
|
-
const parsed = JSON.parse(readFileSync2(
|
|
44528
|
+
if (!existsSync7(path5)) return applied;
|
|
44529
|
+
const parsed = JSON.parse(readFileSync2(path5, "utf8"));
|
|
43478
44530
|
if (!parsed || typeof parsed !== "object") return applied;
|
|
43479
44531
|
for (const [k, v] of Object.entries(parsed)) {
|
|
43480
44532
|
if (typeof v !== "string") continue;
|
|
@@ -43484,7 +44536,7 @@ function loadFileConfig(env = process.env, path4 = fileConfigPath(env)) {
|
|
|
43484
44536
|
}
|
|
43485
44537
|
}
|
|
43486
44538
|
} catch (e) {
|
|
43487
|
-
console.error(`Failed to load apple-notes-mcp config file ${
|
|
44539
|
+
console.error(`Failed to load apple-notes-mcp config file ${path5}: ${String(e)}`);
|
|
43488
44540
|
}
|
|
43489
44541
|
return applied;
|
|
43490
44542
|
}
|
|
@@ -43700,7 +44752,7 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
|
|
|
43700
44752
|
}
|
|
43701
44753
|
|
|
43702
44754
|
// src/utils/noteTables.ts
|
|
43703
|
-
import { gunzipSync as
|
|
44755
|
+
import { gunzipSync as gunzipSync4 } from "node:zlib";
|
|
43704
44756
|
var sub = (f, n) => {
|
|
43705
44757
|
const value = embeddedMessage(getField(f, n));
|
|
43706
44758
|
if (!value) throw new Error(`Missing table field ${n}`);
|
|
@@ -43721,7 +44773,7 @@ var hex = (f) => {
|
|
|
43721
44773
|
return Buffer.from(f.value).toString("hex");
|
|
43722
44774
|
};
|
|
43723
44775
|
function parseNoteTable(compressed) {
|
|
43724
|
-
const root = decodeMessage(
|
|
44776
|
+
const root = decodeMessage(gunzipSync4(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
|
|
43725
44777
|
const data = sub(sub(root, 2), 3), entries = many(data, 3);
|
|
43726
44778
|
if (entries.length > 1e5) throw new Error("Table too large");
|
|
43727
44779
|
const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
|
|
@@ -43924,7 +44976,7 @@ import {
|
|
|
43924
44976
|
writeFileSync as writeFileSync3
|
|
43925
44977
|
} from "node:fs";
|
|
43926
44978
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
43927
|
-
import { basename, isAbsolute as isAbsolute2, join as
|
|
44979
|
+
import { basename, isAbsolute as isAbsolute2, join as join11 } from "node:path";
|
|
43928
44980
|
var noteId = external_exports.string().regex(/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i);
|
|
43929
44981
|
var revision = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
43930
44982
|
function readSnapshot(manager, id2) {
|
|
@@ -43962,9 +45014,9 @@ function assertExistingContentPreserved(before, after) {
|
|
|
43962
45014
|
throw new Error("Existing native object content or presentation changed");
|
|
43963
45015
|
}
|
|
43964
45016
|
}
|
|
43965
|
-
function localAttachment(
|
|
43966
|
-
if (!isAbsolute2(
|
|
43967
|
-
const descriptor = openSync(
|
|
45017
|
+
function localAttachment(path5) {
|
|
45018
|
+
if (!isAbsolute2(path5)) throw new Error("An absolute local file path is required");
|
|
45019
|
+
const descriptor = openSync(path5, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
43968
45020
|
try {
|
|
43969
45021
|
const stat = fstatSync(descriptor);
|
|
43970
45022
|
if (!stat.isFile() || stat.size === 0 || stat.size > 64 * 1024 * 1024)
|
|
@@ -44033,13 +45085,13 @@ function registerDirectOperations(server2, manager) {
|
|
|
44033
45085
|
"add-attachment",
|
|
44034
45086
|
"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.",
|
|
44035
45087
|
{ id: noteId, expectedContentHash: revision, path: external_exports.string().min(1).max(4096) },
|
|
44036
|
-
({ id: id2, expectedContentHash, path:
|
|
45088
|
+
({ id: id2, expectedContentHash, path: path5 }) => {
|
|
44037
45089
|
const before = readSnapshot(manager, id2);
|
|
44038
45090
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
44039
|
-
const bytes = localAttachment(
|
|
45091
|
+
const bytes = localAttachment(path5);
|
|
44040
45092
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
44041
|
-
const directory = mkdtempSync4(
|
|
44042
|
-
const temporaryFile =
|
|
45093
|
+
const directory = mkdtempSync4(join11(tmpdir4(), "notes-attachment-add-"));
|
|
45094
|
+
const temporaryFile = join11(directory, basename(path5));
|
|
44043
45095
|
try {
|
|
44044
45096
|
writeFileSync3(temporaryFile, bytes, { mode: 384 });
|
|
44045
45097
|
if (readSnapshot(manager, id2).hash !== before.hash)
|
|
@@ -44320,7 +45372,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
44320
45372
|
}
|
|
44321
45373
|
tool(
|
|
44322
45374
|
"get-capabilities",
|
|
44323
|
-
"Use when: checking native background-edit support before calling a
|
|
45375
|
+
"Use when: checking native background-edit support or which feature groups this Mac supports before calling a tool.\nReturns: bridge installation, implementation, verification, availability, and specific limitations per operation; plus runtimeOS and an OS-version-aware features matrix (available, osSupported, minimumMacOSVersion, requirements, missing, unverified, machine reason code) per feature group.\nDo not use when: checking only the Native Tags bridge (native-tags-status).\nSafety: read-only; does not open Notes or run a mutation.",
|
|
44324
45376
|
{},
|
|
44325
45377
|
() => {
|
|
44326
45378
|
let bridge;
|
|
@@ -44370,7 +45422,8 @@ function registerNativeOperations(server2, manager) {
|
|
|
44370
45422
|
}
|
|
44371
45423
|
])
|
|
44372
45424
|
),
|
|
44373
|
-
unavailable: UNAVAILABLE
|
|
45425
|
+
unavailable: UNAVAILABLE,
|
|
45426
|
+
...getCapabilityMatrix()
|
|
44374
45427
|
};
|
|
44375
45428
|
},
|
|
44376
45429
|
true
|
|
@@ -44596,8 +45649,8 @@ function registerNativeOperations(server2, manager) {
|
|
|
44596
45649
|
|
|
44597
45650
|
// src/setupShortcuts.ts
|
|
44598
45651
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
44599
|
-
import { existsSync as
|
|
44600
|
-
import { release } from "node:os";
|
|
45652
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
45653
|
+
import { release as release2 } from "node:os";
|
|
44601
45654
|
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
44602
45655
|
import { fileURLToPath } from "node:url";
|
|
44603
45656
|
var OPTIONAL_BRIDGE_NOTE = "(optional \u2014 needed only for create-note format: markdown, macOS 26+)";
|
|
@@ -44616,16 +45669,16 @@ var shortcutFiles = [
|
|
|
44616
45669
|
];
|
|
44617
45670
|
function setupShortcuts(checkOnly, dependencies = {}) {
|
|
44618
45671
|
const status = dependencies.status || nativeTagsStatus;
|
|
44619
|
-
const exists = dependencies.exists ||
|
|
44620
|
-
const open = dependencies.open || ((
|
|
44621
|
-
const result = spawnSync2("/usr/bin/open", [
|
|
45672
|
+
const exists = dependencies.exists || existsSync8;
|
|
45673
|
+
const open = dependencies.open || ((path5) => {
|
|
45674
|
+
const result = spawnSync2("/usr/bin/open", [path5], { encoding: "utf8" });
|
|
44622
45675
|
return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
|
|
44623
45676
|
});
|
|
44624
45677
|
const baseDirectory = dependencies.baseDirectory || resolve2(dirname2(fileURLToPath(import.meta.url)), "../shortcuts");
|
|
44625
|
-
const osRelease = (dependencies.osRelease ||
|
|
45678
|
+
const osRelease = (dependencies.osRelease || release2)();
|
|
44626
45679
|
const darwinMajor = Number.parseInt(osRelease.split(".")[0], 10);
|
|
44627
45680
|
const items = shortcutFiles.map(({ name, file, optional: optional2 }) => {
|
|
44628
|
-
const
|
|
45681
|
+
const path5 = resolve2(baseDirectory, file);
|
|
44629
45682
|
let installed = false;
|
|
44630
45683
|
let identifier;
|
|
44631
45684
|
let error2;
|
|
@@ -44641,9 +45694,9 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
44641
45694
|
if (!installed && !checkOnly) {
|
|
44642
45695
|
if (optional2 && !(darwinMajor >= MARKDOWN_MIN_DARWIN_MAJOR))
|
|
44643
45696
|
skipped = `requires macOS 26 or later (this Mac reports Darwin ${osRelease})`;
|
|
44644
|
-
else if (!exists(
|
|
45697
|
+
else if (!exists(path5)) error2 = `Packaged Shortcut is missing: ${path5}`;
|
|
44645
45698
|
else {
|
|
44646
|
-
const result = open(
|
|
45699
|
+
const result = open(path5);
|
|
44647
45700
|
opened = result.ok;
|
|
44648
45701
|
if (!result.ok) error2 = result.error || `Could not open ${file}`;
|
|
44649
45702
|
}
|
|
@@ -44652,7 +45705,7 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
44652
45705
|
name,
|
|
44653
45706
|
installed,
|
|
44654
45707
|
identifier,
|
|
44655
|
-
file:
|
|
45708
|
+
file: path5,
|
|
44656
45709
|
opened,
|
|
44657
45710
|
...optional2 ? { optional: optional2 } : {},
|
|
44658
45711
|
...skipped ? { skipped } : {},
|
|
@@ -44924,6 +45977,81 @@ ${noteList}${truncationNote}${syncNote}`,
|
|
|
44924
45977
|
);
|
|
44925
45978
|
}, "Error searching notes")
|
|
44926
45979
|
);
|
|
45980
|
+
registerTool(
|
|
45981
|
+
"query-notes",
|
|
45982
|
+
{
|
|
45983
|
+
description: 'Use when: finding notes with a boolean expression over text and metadata \u2014 e.g. `folder:Work has:checklist -checklist:done`, `(title:invoice OR tag:finance) modified:>=2026-07-01`, `pinned words:>250`. Reads the Notes database directly, so it is fast and can match title OR body in one call.\nSyntax: bare words and "quoted phrases" match title or body (case-insensitive substring); fields title:, body:, text:, folder:, account:, tag: (values may be quoted, e.g. folder:"Work Projects"); facets has:link|attachment|checklist|drawing|image|video|audio|pdf|table|scan|tag; checklist:open|done; flags pinned, locked, shared (or is:pinned); words:>250 and created:/modified: with =, >, >=, <, <= and YYYY-MM-DD local dates. AND is implicit; OR, NOT, leading -, and parentheses are supported; operators are case-insensitive and a quoted "and" searches the literal word.\nReturns: matching notes (most recently modified first) with id, title, folder, account, modified date, and snippet, plus scan/match counts. Ids work with get-note-content and every other id-based tool.\nDo not use when: Full Disk Access is unavailable (use search-notes). Scans the most recent scanLimit notes (default 500); raise it for older notes.\nSafety: read-only; never writes the database. Excludes Recently Deleted and folderless notes unless includeDeleted is true. Locked notes match on title and metadata only; body predicates never match them.',
|
|
45984
|
+
inputSchema: {
|
|
45985
|
+
query: external_exports.string().min(1, "A query expression is required").max(MAX.QUERY).describe(
|
|
45986
|
+
'Boolean query expression, e.g. `folder:"Work Projects" has:checklist -checklist:done`'
|
|
45987
|
+
),
|
|
45988
|
+
limit: external_exports.number().int().positive().max(QUERY_RESULTS.MAX).optional().describe(
|
|
45989
|
+
`Maximum notes to return (default ${QUERY_RESULTS.DEFAULT}, max ${QUERY_RESULTS.MAX}). The response reports how many matched in total.`
|
|
45990
|
+
),
|
|
45991
|
+
scanLimit: external_exports.number().int().positive().max(QUERY_SCAN.MAX).optional().describe(
|
|
45992
|
+
`How many of the most recently modified notes to examine (default ${QUERY_SCAN.DEFAULT}, max ${QUERY_SCAN.MAX}). The response says when older notes were left unscanned.`
|
|
45993
|
+
),
|
|
45994
|
+
includeDeleted: external_exports.boolean().optional().describe(
|
|
45995
|
+
"Also scan notes in Recently Deleted, notes pending deletion, and folderless notes (default false)"
|
|
45996
|
+
)
|
|
45997
|
+
},
|
|
45998
|
+
outputSchema: {
|
|
45999
|
+
notes: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
46000
|
+
count: external_exports.number().optional(),
|
|
46001
|
+
matched: external_exports.number().optional(),
|
|
46002
|
+
scanned: external_exports.number().optional(),
|
|
46003
|
+
eligible: external_exports.number().optional(),
|
|
46004
|
+
scanLimit: external_exports.number().optional(),
|
|
46005
|
+
scanTruncated: external_exports.boolean().optional(),
|
|
46006
|
+
limit: external_exports.number().optional(),
|
|
46007
|
+
truncated: external_exports.boolean().optional(),
|
|
46008
|
+
unreadable: external_exports.number().optional()
|
|
46009
|
+
},
|
|
46010
|
+
annotations: { readOnlyHint: true }
|
|
46011
|
+
},
|
|
46012
|
+
withErrorHandling(({ query, limit, scanLimit, includeDeleted }) => {
|
|
46013
|
+
let result;
|
|
46014
|
+
try {
|
|
46015
|
+
result = queryNotes(query, { limit, scanLimit, includeDeleted });
|
|
46016
|
+
} catch (error2) {
|
|
46017
|
+
if (error2 instanceof NoteQueryError || error2 instanceof NoteQueryStoreError) {
|
|
46018
|
+
return errorResponse(
|
|
46019
|
+
error2 instanceof NoteQueryError ? `Invalid query: ${error2.message}` : error2.message
|
|
46020
|
+
);
|
|
46021
|
+
}
|
|
46022
|
+
throw error2;
|
|
46023
|
+
}
|
|
46024
|
+
const scope = `scanned ${result.scanned} of ${result.eligible} notes` + (result.scanTruncated ? `, the most recent ${result.scanLimit}; pass a higher scanLimit to include older notes` : "");
|
|
46025
|
+
const notes = [];
|
|
46026
|
+
if (result.truncated) {
|
|
46027
|
+
notes.push(
|
|
46028
|
+
`\u2139\uFE0F ${result.matched} notes matched; showing the first ${result.count}. Pass a higher limit or narrow the query.`
|
|
46029
|
+
);
|
|
46030
|
+
}
|
|
46031
|
+
if (result.unreadable > 0) {
|
|
46032
|
+
notes.push(
|
|
46033
|
+
`\u26A0\uFE0F ${result.unreadable} note bodies could not be decoded, so body predicates did not match them.`
|
|
46034
|
+
);
|
|
46035
|
+
}
|
|
46036
|
+
const footer = notes.length ? `
|
|
46037
|
+
|
|
46038
|
+
${notes.join("\n")}` : "";
|
|
46039
|
+
if (result.count === 0) {
|
|
46040
|
+
return successResponse(`No notes matched (${scope}).${footer}`, { ...result });
|
|
46041
|
+
}
|
|
46042
|
+
const lines = result.notes.map((n) => {
|
|
46043
|
+
const where = [n.account, n.folder].filter(Boolean).join(" / ");
|
|
46044
|
+
const snippet = n.snippet ? `
|
|
46045
|
+
${n.snippet}` : "";
|
|
46046
|
+
return ` - ${n.title}${where ? ` (${where})` : ""}${n.locked ? " [locked]" : ""} [id: ${n.id}]${snippet}`;
|
|
46047
|
+
}).join("\n");
|
|
46048
|
+
return successResponse(
|
|
46049
|
+
`Found ${result.matched} matching notes (${scope}):
|
|
46050
|
+
${lines}${footer}`,
|
|
46051
|
+
{ ...result }
|
|
46052
|
+
);
|
|
46053
|
+
}, "Error querying notes")
|
|
46054
|
+
);
|
|
44927
46055
|
registerTool(
|
|
44928
46056
|
"get-note-content",
|
|
44929
46057
|
{
|
|
@@ -46081,11 +47209,13 @@ ${fdaLine}`, {
|
|
|
46081
47209
|
registerTool(
|
|
46082
47210
|
"doctor",
|
|
46083
47211
|
{
|
|
46084
|
-
description: "Use when: diagnosing setup problems (Notes.app automation permission, account state, Full Disk Access) with actionable guidance.\nReturns: a detailed report plus structured fields.\nDo not use when: you just need a quick pass/fail (health-check).\nRead-only.",
|
|
47212
|
+
description: "Use when: diagnosing setup problems (Notes.app automation permission, account state, Full Disk Access) with actionable guidance.\nReturns: a detailed report plus structured fields, including runtimeOS and the same OS-version-aware features matrix as get-capabilities.\nDo not use when: you just need a quick pass/fail (health-check).\nRead-only.",
|
|
46085
47213
|
inputSchema: {},
|
|
46086
47214
|
outputSchema: {
|
|
46087
47215
|
healthy: external_exports.boolean().optional(),
|
|
46088
|
-
checks: external_exports.array(external_exports.object({}).passthrough()).optional()
|
|
47216
|
+
checks: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
47217
|
+
runtimeOS: external_exports.object({}).passthrough().optional(),
|
|
47218
|
+
features: external_exports.record(external_exports.string(), external_exports.object({}).passthrough()).optional()
|
|
46089
47219
|
}
|
|
46090
47220
|
},
|
|
46091
47221
|
withErrorHandling(() => {
|