apple-notes-mcp 2.8.38 → 2.8.40
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 +14 -2
- package/build/index.js +516 -341
- 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(path9) {
|
|
3272
|
+
let input = path9;
|
|
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 path9 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
3678
|
+
wsComponent.path = path9 && path9 !== "/" ? path9 : void 0;
|
|
3679
3679
|
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
3680
3680
|
wsComponent.resourceName = void 0;
|
|
3681
3681
|
}
|
|
@@ -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, fs9, 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, fs9[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(path9) {
|
|
12242
|
+
if (!path9) return path9;
|
|
12243
12243
|
var output = "";
|
|
12244
|
-
while (
|
|
12245
|
-
if (
|
|
12246
|
-
|
|
12244
|
+
while (path9.length > 0) {
|
|
12245
|
+
if (path9 === "." || path9 === "..") {
|
|
12246
|
+
path9 = "";
|
|
12247
12247
|
break;
|
|
12248
12248
|
}
|
|
12249
|
-
var twochars =
|
|
12250
|
-
var threechars =
|
|
12251
|
-
var fourchars =
|
|
12249
|
+
var twochars = path9.substring(0, 2);
|
|
12250
|
+
var threechars = path9.substring(0, 3);
|
|
12251
|
+
var fourchars = path9.substring(0, 4);
|
|
12252
12252
|
if (threechars === "../") {
|
|
12253
|
-
|
|
12253
|
+
path9 = path9.substring(3);
|
|
12254
12254
|
} else if (twochars === "./") {
|
|
12255
|
-
|
|
12255
|
+
path9 = path9.substring(2);
|
|
12256
12256
|
} else if (threechars === "/./") {
|
|
12257
|
-
|
|
12258
|
-
} else if (twochars === "/." &&
|
|
12259
|
-
|
|
12260
|
-
} else if (fourchars === "/../" || threechars === "/.." &&
|
|
12261
|
-
|
|
12257
|
+
path9 = "/" + path9.substring(3);
|
|
12258
|
+
} else if (twochars === "/." && path9.length === 2) {
|
|
12259
|
+
path9 = "/";
|
|
12260
|
+
} else if (fourchars === "/../" || threechars === "/.." && path9.length === 3) {
|
|
12261
|
+
path9 = "/" + path9.substring(4);
|
|
12262
12262
|
output = output.replace(/\/?[^\/]*$/, "");
|
|
12263
12263
|
} else {
|
|
12264
|
-
var segment =
|
|
12264
|
+
var segment = path9.match(/(\/?([^\/]*))/)[0];
|
|
12265
12265
|
output += segment;
|
|
12266
|
-
|
|
12266
|
+
path9 = path9.substring(segment.length);
|
|
12267
12267
|
}
|
|
12268
12268
|
}
|
|
12269
12269
|
return output;
|
|
@@ -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 join23(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 = join23(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 join23(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: path9, errorMaps, issueData } = params;
|
|
24983
|
+
const fullPath = [...path9, ...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, path9, key) {
|
|
25100
25100
|
this._cachedPath = [];
|
|
25101
25101
|
this.parent = parent;
|
|
25102
25102
|
this.data = value;
|
|
25103
|
-
this._path =
|
|
25103
|
+
this._path = path9;
|
|
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, path9) {
|
|
28741
|
+
if (!path9)
|
|
28742
28742
|
return obj;
|
|
28743
|
-
return
|
|
28743
|
+
return path9.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(path9, 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(path9);
|
|
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(path9) {
|
|
32479
|
+
if (path9.length === 0) {
|
|
32480
32480
|
return "object root";
|
|
32481
32481
|
}
|
|
32482
|
-
return
|
|
32482
|
+
return path9.reduce((acc, seg, index) => {
|
|
32483
32483
|
if (index === 0) {
|
|
32484
32484
|
return String(seg);
|
|
32485
32485
|
}
|
|
@@ -39275,21 +39275,13 @@ var WIRE_TYPE = {
|
|
|
39275
39275
|
};
|
|
39276
39276
|
function decodeVarint(buf, offset) {
|
|
39277
39277
|
let result = 0;
|
|
39278
|
-
let shift = 0;
|
|
39279
|
-
|
|
39280
|
-
while (pos < buf.length) {
|
|
39281
|
-
const byte = buf[pos];
|
|
39278
|
+
for (let pos = offset, shift = 0; pos < buf.length && shift < 28; shift += 7) {
|
|
39279
|
+
const byte = buf[pos++];
|
|
39282
39280
|
result |= (byte & 127) << shift;
|
|
39283
|
-
pos
|
|
39284
|
-
if ((byte & 128) === 0) {
|
|
39285
|
-
return [result, pos];
|
|
39286
|
-
}
|
|
39287
|
-
shift += 7;
|
|
39288
|
-
if (shift > 35) {
|
|
39289
|
-
throw new Error(`Varint too long at offset ${offset}`);
|
|
39290
|
-
}
|
|
39281
|
+
if ((byte & 128) === 0) return [result, pos];
|
|
39291
39282
|
}
|
|
39292
|
-
|
|
39283
|
+
const [value, next] = decodeVarint64(buf, offset);
|
|
39284
|
+
return [Number(BigInt.asIntN(64, value)), next];
|
|
39293
39285
|
}
|
|
39294
39286
|
function decodeMessage(buf, options = {}) {
|
|
39295
39287
|
const fields = [];
|
|
@@ -39306,7 +39298,7 @@ function decodeMessage(buf, options = {}) {
|
|
|
39306
39298
|
} else if (wireType === WIRE_TYPE.LENGTH_DELIMITED) {
|
|
39307
39299
|
let length;
|
|
39308
39300
|
[length, offset] = decodeVarint(buf, offset);
|
|
39309
|
-
if (offset + length > buf.length) {
|
|
39301
|
+
if (length < 0 || offset + length > buf.length) {
|
|
39310
39302
|
break;
|
|
39311
39303
|
}
|
|
39312
39304
|
const value = buf.slice(offset, offset + length);
|
|
@@ -39452,9 +39444,9 @@ function queryNoteData(noteId3) {
|
|
|
39452
39444
|
return { hex: null, error: "invalid_id" };
|
|
39453
39445
|
}
|
|
39454
39446
|
const pk = pkMatch[1];
|
|
39455
|
-
const
|
|
39447
|
+
const query2 = `SELECT hex(nd.ZDATA) FROM ZICNOTEDATA nd JOIN ZICCLOUDSYNCINGOBJECT n ON nd.ZNOTE = n.Z_PK WHERE n.Z_PK = ${pk};`;
|
|
39456
39448
|
try {
|
|
39457
|
-
const result = execFileSync2("sqlite3", ["-readonly", NOTES_DB_PATH,
|
|
39449
|
+
const result = execFileSync2("sqlite3", ["-readonly", NOTES_DB_PATH, query2], {
|
|
39458
39450
|
encoding: "utf8",
|
|
39459
39451
|
timeout: 5e3,
|
|
39460
39452
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -39623,6 +39615,12 @@ import { createHash } from "node:crypto";
|
|
|
39623
39615
|
import { homedir as homedir2 } from "node:os";
|
|
39624
39616
|
import { join as join2 } from "node:path";
|
|
39625
39617
|
import { gunzipSync as gunzipSync2 } from "node:zlib";
|
|
39618
|
+
var HTML_LOSSY_ORDER = [
|
|
39619
|
+
"superscript",
|
|
39620
|
+
"subscript",
|
|
39621
|
+
"alignment",
|
|
39622
|
+
"highlight"
|
|
39623
|
+
];
|
|
39626
39624
|
var dbPath = join2(homedir2(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
|
|
39627
39625
|
var safeUrl = (url) => /^(?:https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url) && !Array.from(url).some((char) => char.charCodeAt(0) < 32);
|
|
39628
39626
|
var escapeAttribute = (text2) => text2.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
@@ -39643,6 +39641,7 @@ function styleValue(field) {
|
|
|
39643
39641
|
else if (wire === 5) offset += 4;
|
|
39644
39642
|
else if (wire === 2) {
|
|
39645
39643
|
[length, offset] = decodeVarint(data, offset);
|
|
39644
|
+
if (length < 0) return Buffer.from(data).toString("hex");
|
|
39646
39645
|
offset += length;
|
|
39647
39646
|
} else return Buffer.from(data).toString("hex");
|
|
39648
39647
|
if (offset > data.length) return Buffer.from(data).toString("hex");
|
|
@@ -39669,6 +39668,7 @@ function parseRichNote(data, nativeTags = []) {
|
|
|
39669
39668
|
let position = 0;
|
|
39670
39669
|
let hasNativeObjects = false;
|
|
39671
39670
|
let hasChecklist = false;
|
|
39671
|
+
const lossy = /* @__PURE__ */ new Set();
|
|
39672
39672
|
for (const run of getFields(body, 5)) {
|
|
39673
39673
|
const fields = embeddedMessage(run);
|
|
39674
39674
|
if (!fields) throw new Error("Invalid Notes attribute run");
|
|
@@ -39686,6 +39686,13 @@ function parseRichNote(data, nativeTags = []) {
|
|
|
39686
39686
|
fields.filter((f) => f.fieldNumber >= 2 && f.fieldNumber <= 12 || f.fieldNumber === 14).map((f) => [f.fieldNumber, styleValue(f)])
|
|
39687
39687
|
)
|
|
39688
39688
|
});
|
|
39689
|
+
if (/[^\s\ufffc]/u.test(text2.slice(position, position + length))) {
|
|
39690
|
+
const baseline = varintValue(getField(fields, 8)) ?? 0;
|
|
39691
|
+
if (baseline > 0) lossy.add("superscript");
|
|
39692
|
+
if (baseline < 0) lossy.add("subscript");
|
|
39693
|
+
if (paragraph && (varintValue(getField(paragraph, 2)) ?? 0) !== 0) lossy.add("alignment");
|
|
39694
|
+
if (varintValue(getField(fields, 14))) lossy.add("highlight");
|
|
39695
|
+
}
|
|
39689
39696
|
const url = stringValue(getField(fields, 9));
|
|
39690
39697
|
if (url) {
|
|
39691
39698
|
if (!safeUrl(url)) throw new Error("Unsupported link scheme in note");
|
|
@@ -39737,7 +39744,8 @@ function parseRichNote(data, nativeTags = []) {
|
|
|
39737
39744
|
revision: createHash("sha256").update(data).digest("hex"),
|
|
39738
39745
|
objects,
|
|
39739
39746
|
checklistItems,
|
|
39740
|
-
styleRuns
|
|
39747
|
+
styleRuns,
|
|
39748
|
+
...lossy.size ? { htmlLossyFormatting: HTML_LOSSY_ORDER.filter((f) => lossy.has(f)) } : {}
|
|
39741
39749
|
};
|
|
39742
39750
|
}
|
|
39743
39751
|
function readRichNote(id2) {
|
|
@@ -39854,17 +39862,26 @@ function enrichNoteRead(id2, rawBody) {
|
|
|
39854
39862
|
const rich = readRichNote(id2);
|
|
39855
39863
|
metadata = rich;
|
|
39856
39864
|
const content = restoreNoteLinks(rawBody, rich);
|
|
39857
|
-
const
|
|
39865
|
+
const complete = !rich.hasNativeObjects && !rich.hasChecklist;
|
|
39866
|
+
const lossy = rich.htmlLossyFormatting ?? [];
|
|
39867
|
+
const writable = complete && lossy.length === 0;
|
|
39868
|
+
const warnings = [];
|
|
39869
|
+
if (!complete)
|
|
39870
|
+
warnings.push(
|
|
39871
|
+
"Native tags, inline objects or checklists are present. Their state is not writable through AppleScript; full-body edits are blocked to preserve them."
|
|
39872
|
+
);
|
|
39873
|
+
if (lossy.length)
|
|
39874
|
+
warnings.push(
|
|
39875
|
+
`This note uses formatting that Notes' AppleScript HTML does not carry (${lossy.join(", ")}); a full-body edit would silently drop it, so full-body edits are blocked. Edit it in Notes.app, or use append-to-note with scopeText, which appends natively and verifies existing formatting.`
|
|
39876
|
+
);
|
|
39858
39877
|
return {
|
|
39859
39878
|
content,
|
|
39860
39879
|
links: rich.links,
|
|
39861
39880
|
nativeTags: rich.nativeTags,
|
|
39862
|
-
complete
|
|
39881
|
+
complete,
|
|
39863
39882
|
writable,
|
|
39864
39883
|
revision: rich.revision,
|
|
39865
|
-
|
|
39866
|
-
warning: "Native tags, inline objects or checklists are present. Their state is not writable through AppleScript; full-body edits are blocked to preserve them."
|
|
39867
|
-
} : {}
|
|
39884
|
+
...warnings.length ? { warning: warnings.join(" ") } : {}
|
|
39868
39885
|
};
|
|
39869
39886
|
} catch (err) {
|
|
39870
39887
|
const detail = err instanceof Error ? err.message : String(err);
|
|
@@ -40713,7 +40730,7 @@ function decodeSmartFolderQuery(raw, context = {}) {
|
|
|
40713
40730
|
inner = type.and.filter((clause) => !isDeletedWrapper(clause));
|
|
40714
40731
|
}
|
|
40715
40732
|
}
|
|
40716
|
-
const
|
|
40733
|
+
const query2 = inner.length === 1 ? inner[0] : { and: inner };
|
|
40717
40734
|
let match = "all";
|
|
40718
40735
|
let filters;
|
|
40719
40736
|
const only = inner.length === 1 && isClause(inner[0]) ? inner[0] : void 0;
|
|
@@ -40732,7 +40749,7 @@ function decodeSmartFolderQuery(raw, context = {}) {
|
|
|
40732
40749
|
return {
|
|
40733
40750
|
match,
|
|
40734
40751
|
filters,
|
|
40735
|
-
query,
|
|
40752
|
+
query: query2,
|
|
40736
40753
|
...includesRecentlyDeleted === void 0 ? {} : { includesRecentlyDeleted },
|
|
40737
40754
|
fullyDecoded: decoder.fullyDecoded
|
|
40738
40755
|
};
|
|
@@ -40854,11 +40871,55 @@ function readSmartFolders(dbPath2 = NOTES_DB_PATH3) {
|
|
|
40854
40871
|
}
|
|
40855
40872
|
}
|
|
40856
40873
|
|
|
40874
|
+
// src/utils/trashFolders.ts
|
|
40875
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
40876
|
+
import * as fs3 from "fs";
|
|
40877
|
+
import * as os3 from "os";
|
|
40878
|
+
import * as path3 from "path";
|
|
40879
|
+
var NOTES_DB_PATH4 = path3.join(
|
|
40880
|
+
os3.homedir(),
|
|
40881
|
+
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
40882
|
+
);
|
|
40883
|
+
var RECENTLY_DELETED_FOLDER_NAME = "Recently Deleted";
|
|
40884
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
40885
|
+
var cache = null;
|
|
40886
|
+
function query(dbPath2, sql) {
|
|
40887
|
+
return execFileSync6("sqlite3", ["-readonly", dbPath2, sql], {
|
|
40888
|
+
encoding: "utf8",
|
|
40889
|
+
timeout: 5e3,
|
|
40890
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
40891
|
+
});
|
|
40892
|
+
}
|
|
40893
|
+
function readTrashFolderIds(dbPath2 = NOTES_DB_PATH4) {
|
|
40894
|
+
if (cache && cache.dbPath === dbPath2 && Date.now() - cache.at < CACHE_TTL_MS) {
|
|
40895
|
+
return cache.ids;
|
|
40896
|
+
}
|
|
40897
|
+
try {
|
|
40898
|
+
if (!fs3.existsSync(dbPath2)) return [];
|
|
40899
|
+
const columns = new Set(
|
|
40900
|
+
query(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").split("\n").map((line) => line.trim()).filter(Boolean)
|
|
40901
|
+
);
|
|
40902
|
+
const predicates = [];
|
|
40903
|
+
if (columns.has("ZFOLDERTYPE")) predicates.push("COALESCE(ZFOLDERTYPE, 0) = 1");
|
|
40904
|
+
if (columns.has("ZIDENTIFIER"))
|
|
40905
|
+
predicates.push("COALESCE(ZIDENTIFIER, '') LIKE 'TrashFolder%'");
|
|
40906
|
+
if (!predicates.length) return [];
|
|
40907
|
+
const sql = `SELECT (SELECT Z_UUID FROM Z_METADATA LIMIT 1); SELECT Z_PK FROM ZICCLOUDSYNCINGOBJECT WHERE Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICFolder') AND (${predicates.join(" OR ")}) ORDER BY Z_PK;`;
|
|
40908
|
+
const [uuid2, ...pks] = query(dbPath2, sql).split("\n").map((line) => line.trim()).filter(Boolean);
|
|
40909
|
+
if (!uuid2 || !/^[0-9A-F-]+$/i.test(uuid2)) return [];
|
|
40910
|
+
const ids = pks.filter((pk) => /^\d+$/.test(pk)).map((pk) => `x-coredata://${uuid2}/ICFolder/p${pk}`);
|
|
40911
|
+
cache = { dbPath: dbPath2, at: Date.now(), ids };
|
|
40912
|
+
return ids;
|
|
40913
|
+
} catch {
|
|
40914
|
+
return [];
|
|
40915
|
+
}
|
|
40916
|
+
}
|
|
40917
|
+
|
|
40857
40918
|
// src/utils/attachmentFs.ts
|
|
40858
40919
|
import {
|
|
40859
40920
|
closeSync,
|
|
40860
40921
|
constants,
|
|
40861
|
-
existsSync as
|
|
40922
|
+
existsSync as existsSync4,
|
|
40862
40923
|
fstatSync,
|
|
40863
40924
|
lstatSync,
|
|
40864
40925
|
mkdirSync,
|
|
@@ -40869,11 +40930,11 @@ import {
|
|
|
40869
40930
|
rmSync,
|
|
40870
40931
|
statSync
|
|
40871
40932
|
} from "fs";
|
|
40872
|
-
import { dirname, isAbsolute, join as
|
|
40873
|
-
import { homedir as
|
|
40933
|
+
import { dirname, isAbsolute, join as join6, relative, resolve, sep } from "path";
|
|
40934
|
+
import { homedir as homedir6, tmpdir } from "os";
|
|
40874
40935
|
function allowedSaveRoots() {
|
|
40875
40936
|
return [
|
|
40876
|
-
resolve(
|
|
40937
|
+
resolve(homedir6()),
|
|
40877
40938
|
resolve(tmpdir()),
|
|
40878
40939
|
"/Volumes",
|
|
40879
40940
|
"/private/var/folders",
|
|
@@ -40881,8 +40942,8 @@ function allowedSaveRoots() {
|
|
|
40881
40942
|
"/private/tmp"
|
|
40882
40943
|
];
|
|
40883
40944
|
}
|
|
40884
|
-
function canonicalize(
|
|
40885
|
-
return realpathSync.native(
|
|
40945
|
+
function canonicalize(path9) {
|
|
40946
|
+
return realpathSync.native(path9);
|
|
40886
40947
|
}
|
|
40887
40948
|
function isWithinRoots(candidate, roots) {
|
|
40888
40949
|
return roots.some((root) => {
|
|
@@ -40904,9 +40965,9 @@ function canonicalRoots(roots) {
|
|
|
40904
40965
|
}
|
|
40905
40966
|
return canonical;
|
|
40906
40967
|
}
|
|
40907
|
-
function entryExists(
|
|
40968
|
+
function entryExists(path9) {
|
|
40908
40969
|
try {
|
|
40909
|
-
lstatSync(
|
|
40970
|
+
lstatSync(path9);
|
|
40910
40971
|
return true;
|
|
40911
40972
|
} catch (e) {
|
|
40912
40973
|
const code = e.code;
|
|
@@ -40947,7 +41008,7 @@ function assertSafeSavePath(p, roots = allowedSaveRoots()) {
|
|
|
40947
41008
|
if (suffix.split(sep).includes("..")) {
|
|
40948
41009
|
throw new Error(`Refusing to write outside allowed locations (home, temp, /Volumes): "${abs}"`);
|
|
40949
41010
|
}
|
|
40950
|
-
const canonicalDest = suffix ?
|
|
41011
|
+
const canonicalDest = suffix ? join6(canonicalAncestor, suffix) : canonicalAncestor;
|
|
40951
41012
|
const allowed = canonicalRoots(roots);
|
|
40952
41013
|
if (!isWithinRoots(canonicalAncestor, allowed) || !isWithinRoots(canonicalDest, allowed)) {
|
|
40953
41014
|
throw new Error(
|
|
@@ -41031,13 +41092,13 @@ function readAllowedTextFile(p, maxBytes, roots = allowedSaveRoots()) {
|
|
|
41031
41092
|
}
|
|
41032
41093
|
function cleanupTempDir(dir) {
|
|
41033
41094
|
try {
|
|
41034
|
-
if (
|
|
41095
|
+
if (existsSync4(dir)) rmSync(dir, { recursive: true, force: true });
|
|
41035
41096
|
} catch {
|
|
41036
41097
|
}
|
|
41037
41098
|
}
|
|
41038
41099
|
|
|
41039
41100
|
// src/utils/paperAttachments.ts
|
|
41040
|
-
import { execFileSync as
|
|
41101
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
41041
41102
|
import {
|
|
41042
41103
|
closeSync as closeSync3,
|
|
41043
41104
|
constants as constants3,
|
|
@@ -41050,10 +41111,10 @@ import {
|
|
|
41050
41111
|
unlinkSync as unlinkSync2,
|
|
41051
41112
|
writeSync as writeSync2
|
|
41052
41113
|
} from "node:fs";
|
|
41053
|
-
import { basename as basename2, dirname as dirname3, extname as extname2, join as
|
|
41114
|
+
import { basename as basename2, dirname as dirname3, extname as extname2, join as join8 } from "node:path";
|
|
41054
41115
|
|
|
41055
41116
|
// src/utils/attachmentAssets.ts
|
|
41056
|
-
import { execFileSync as
|
|
41117
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
41057
41118
|
import {
|
|
41058
41119
|
closeSync as closeSync2,
|
|
41059
41120
|
constants as constants2,
|
|
@@ -41067,11 +41128,11 @@ import {
|
|
|
41067
41128
|
unlinkSync,
|
|
41068
41129
|
writeSync
|
|
41069
41130
|
} from "node:fs";
|
|
41070
|
-
import { homedir as
|
|
41071
|
-
import { basename, dirname as dirname2, extname, isAbsolute as isAbsolute2, join as
|
|
41131
|
+
import { homedir as homedir7 } from "node:os";
|
|
41132
|
+
import { basename, dirname as dirname2, extname, isAbsolute as isAbsolute2, join as join7, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
41072
41133
|
import { gunzipSync as gunzipSync5 } from "node:zlib";
|
|
41073
|
-
var NOTES_CONTAINER_DIR =
|
|
41074
|
-
|
|
41134
|
+
var NOTES_CONTAINER_DIR = join7(
|
|
41135
|
+
homedir7(),
|
|
41075
41136
|
"Library/Group Containers/group.com.apple.notes"
|
|
41076
41137
|
);
|
|
41077
41138
|
var AttachmentStoreError = class extends Error {
|
|
@@ -41176,7 +41237,7 @@ function buildAttachmentRowsSql(notePk, columns) {
|
|
|
41176
41237
|
}
|
|
41177
41238
|
function runSqlite3(dbPath2, sql) {
|
|
41178
41239
|
try {
|
|
41179
|
-
return
|
|
41240
|
+
return execFileSync7("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
|
|
41180
41241
|
encoding: "utf8",
|
|
41181
41242
|
timeout: 1e4,
|
|
41182
41243
|
maxBuffer: 128 * 1024 * 1024,
|
|
@@ -41244,7 +41305,7 @@ function attachmentOrderFromNoteData(hex3) {
|
|
|
41244
41305
|
return null;
|
|
41245
41306
|
}
|
|
41246
41307
|
}
|
|
41247
|
-
function readNoteAttachmentRows(noteId3, dbPath2 =
|
|
41308
|
+
function readNoteAttachmentRows(noteId3, dbPath2 = join7(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
|
|
41248
41309
|
const { pk } = parseNoteId2(noteId3);
|
|
41249
41310
|
const columnList = runSqlite3(
|
|
41250
41311
|
dbPath2,
|
|
@@ -41274,24 +41335,24 @@ function safeComponent(value) {
|
|
|
41274
41335
|
if (value === "." || value === ".." || value.includes("/") || value.includes("\0")) return null;
|
|
41275
41336
|
return value;
|
|
41276
41337
|
}
|
|
41277
|
-
function realInside(
|
|
41338
|
+
function realInside(path9, rootReal) {
|
|
41278
41339
|
try {
|
|
41279
|
-
const real = realpathSync2.native(
|
|
41340
|
+
const real = realpathSync2.native(path9);
|
|
41280
41341
|
return real === rootReal || real.startsWith(rootReal + sep2) ? real : null;
|
|
41281
41342
|
} catch {
|
|
41282
41343
|
return null;
|
|
41283
41344
|
}
|
|
41284
41345
|
}
|
|
41285
|
-
function isRegularFile(
|
|
41346
|
+
function isRegularFile(path9) {
|
|
41286
41347
|
try {
|
|
41287
|
-
return lstatSync2(
|
|
41348
|
+
return lstatSync2(path9).isFile();
|
|
41288
41349
|
} catch {
|
|
41289
41350
|
return false;
|
|
41290
41351
|
}
|
|
41291
41352
|
}
|
|
41292
|
-
function isDirectory(
|
|
41353
|
+
function isDirectory(path9) {
|
|
41293
41354
|
try {
|
|
41294
|
-
return lstatSync2(
|
|
41355
|
+
return lstatSync2(path9).isDirectory();
|
|
41295
41356
|
} catch {
|
|
41296
41357
|
return false;
|
|
41297
41358
|
}
|
|
@@ -41305,7 +41366,7 @@ function boundedEntries(dir, limit) {
|
|
|
41305
41366
|
}
|
|
41306
41367
|
}
|
|
41307
41368
|
function resolveAccountDir(containerDir, accountIdentifier) {
|
|
41308
|
-
const accountsDir =
|
|
41369
|
+
const accountsDir = join7(containerDir, "Accounts");
|
|
41309
41370
|
let accountsReal;
|
|
41310
41371
|
try {
|
|
41311
41372
|
accountsReal = realpathSync2.native(accountsDir);
|
|
@@ -41314,14 +41375,14 @@ function resolveAccountDir(containerDir, accountIdentifier) {
|
|
|
41314
41375
|
}
|
|
41315
41376
|
const account = safeComponent(accountIdentifier);
|
|
41316
41377
|
if (account) {
|
|
41317
|
-
const dir = realInside(
|
|
41378
|
+
const dir = realInside(join7(accountsReal, account), accountsReal);
|
|
41318
41379
|
if (dir && isDirectory(dir)) return dir;
|
|
41319
41380
|
}
|
|
41320
41381
|
const entries2 = (boundedEntries(accountsReal, 64) ?? []).filter(
|
|
41321
|
-
(e) => isDirectory(
|
|
41382
|
+
(e) => isDirectory(join7(accountsReal, e))
|
|
41322
41383
|
);
|
|
41323
41384
|
if (entries2.length !== 1) return null;
|
|
41324
|
-
return realInside(
|
|
41385
|
+
return realInside(join7(accountsReal, entries2[0]), accountsReal);
|
|
41325
41386
|
}
|
|
41326
41387
|
function generationRank(name) {
|
|
41327
41388
|
const m = /^(\d+)_/.exec(name);
|
|
@@ -41329,23 +41390,23 @@ function generationRank(name) {
|
|
|
41329
41390
|
}
|
|
41330
41391
|
function generationDirs(base, accountDir) {
|
|
41331
41392
|
const entries2 = boundedEntries(base, MAX_GENERATION_DIRS) ?? [];
|
|
41332
|
-
return entries2.map((e) => realInside(
|
|
41393
|
+
return entries2.map((e) => realInside(join7(base, e), accountDir)).filter((p) => p !== null && isDirectory(p)).sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
|
|
41333
41394
|
}
|
|
41334
41395
|
function fallbackFiles(accountDir, rootName, identifier, generation, names) {
|
|
41335
|
-
const base =
|
|
41396
|
+
const base = join7(accountDir, rootName, identifier);
|
|
41336
41397
|
const found = [];
|
|
41337
41398
|
const add = (candidate) => {
|
|
41338
41399
|
const real = realInside(candidate, accountDir);
|
|
41339
41400
|
if (real && isRegularFile(real) && !found.includes(real)) found.push(real);
|
|
41340
41401
|
};
|
|
41341
41402
|
const gen = safeComponent(generation);
|
|
41342
|
-
if (gen) for (const name of names) add(
|
|
41403
|
+
if (gen) for (const name of names) add(join7(base, gen, name));
|
|
41343
41404
|
if (isDirectory(base)) {
|
|
41344
41405
|
for (const dir of generationDirs(base, accountDir))
|
|
41345
|
-
for (const name of names) add(
|
|
41346
|
-
for (const name of names) add(
|
|
41406
|
+
for (const name of names) add(join7(dir, name));
|
|
41407
|
+
for (const name of names) add(join7(base, name));
|
|
41347
41408
|
}
|
|
41348
|
-
for (const name of names) add(
|
|
41409
|
+
for (const name of names) add(join7(accountDir, rootName, `${identifier}${extname(name)}`));
|
|
41349
41410
|
return found;
|
|
41350
41411
|
}
|
|
41351
41412
|
function previewPixelArea(name) {
|
|
@@ -41357,7 +41418,7 @@ function previewFileInBundle(bundle, accountDir) {
|
|
|
41357
41418
|
const direct = [];
|
|
41358
41419
|
const nested = [];
|
|
41359
41420
|
for (const entry of boundedEntries(bundle, MAX_BUNDLE_ENTRIES) ?? []) {
|
|
41360
|
-
const child = realInside(
|
|
41421
|
+
const child = realInside(join7(bundle, entry), accountDir);
|
|
41361
41422
|
if (!child) continue;
|
|
41362
41423
|
if (isRegularFile(child)) direct.push(child);
|
|
41363
41424
|
else if (isDirectory(child)) nested.push(child);
|
|
@@ -41365,14 +41426,14 @@ function previewFileInBundle(bundle, accountDir) {
|
|
|
41365
41426
|
nested.sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
|
|
41366
41427
|
const pick2 = (files) => files.find((f) => basename(f) === "Preview.png") ?? files.find((f) => PREVIEW_IMAGE_SUFFIXES.has(extname(f).toLowerCase())) ?? null;
|
|
41367
41428
|
for (const dir of nested) {
|
|
41368
|
-
const files = (boundedEntries(dir, MAX_BUNDLE_ENTRIES) ?? []).map((e) => realInside(
|
|
41429
|
+
const files = (boundedEntries(dir, MAX_BUNDLE_ENTRIES) ?? []).map((e) => realInside(join7(dir, e), accountDir)).filter((p) => p !== null && isRegularFile(p));
|
|
41369
41430
|
const chosen = pick2(files);
|
|
41370
41431
|
if (chosen) return chosen;
|
|
41371
41432
|
}
|
|
41372
41433
|
return pick2(direct);
|
|
41373
41434
|
}
|
|
41374
41435
|
function listPreviewEntries(accountDir) {
|
|
41375
|
-
return boundedEntries(
|
|
41436
|
+
return boundedEntries(join7(accountDir, "Previews"), MAX_PREVIEW_DIR_ENTRIES) ?? [];
|
|
41376
41437
|
}
|
|
41377
41438
|
function previewPaths(accountDir, identifier, entries2) {
|
|
41378
41439
|
const id2 = safeComponent(identifier);
|
|
@@ -41384,7 +41445,7 @@ function previewPaths(accountDir, identifier, entries2) {
|
|
|
41384
41445
|
}).sort((a, b) => previewPixelArea(b) - previewPixelArea(a) || a.localeCompare(b));
|
|
41385
41446
|
const files = [];
|
|
41386
41447
|
for (const name of candidates) {
|
|
41387
|
-
const real = realInside(
|
|
41448
|
+
const real = realInside(join7(accountDir, "Previews", name), accountDir);
|
|
41388
41449
|
if (!real) continue;
|
|
41389
41450
|
const file = isRegularFile(real) ? real : isDirectory(real) ? previewFileInBundle(real, accountDir) : null;
|
|
41390
41451
|
if (file && !files.includes(file)) files.push(file);
|
|
@@ -41401,13 +41462,13 @@ function assetPathsFor(accountDir, row) {
|
|
|
41401
41462
|
const mediaName = safeComponent(row.mediaFilename);
|
|
41402
41463
|
const mediaGen = safeComponent(row.mediaGeneration);
|
|
41403
41464
|
if (mediaId && mediaName) {
|
|
41404
|
-
if (mediaGen) add(
|
|
41405
|
-
add(
|
|
41465
|
+
if (mediaGen) add(join7(accountDir, "Media", mediaId, mediaGen, mediaName));
|
|
41466
|
+
add(join7(accountDir, "Media", mediaId, mediaName));
|
|
41406
41467
|
}
|
|
41407
41468
|
const id2 = safeComponent(row.identifier);
|
|
41408
41469
|
if (!id2) return found;
|
|
41409
41470
|
const ownName = safeComponent(row.filename);
|
|
41410
|
-
if (ownName) add(
|
|
41471
|
+
if (ownName) add(join7(accountDir, "Media", id2, ownName));
|
|
41411
41472
|
for (const file of fallbackFiles(accountDir, "FallbackImages", id2, row.fallbackImageGeneration, [
|
|
41412
41473
|
"FallbackImage.png",
|
|
41413
41474
|
"FallbackImage.jpg"
|
|
@@ -41498,10 +41559,10 @@ function canonicalTail(p) {
|
|
|
41498
41559
|
const tail = [];
|
|
41499
41560
|
for (; ; ) {
|
|
41500
41561
|
try {
|
|
41501
|
-
return
|
|
41562
|
+
return join7(realpathSync2.native(current), ...tail);
|
|
41502
41563
|
} catch {
|
|
41503
41564
|
const parent = dirname2(current);
|
|
41504
|
-
if (parent === current) return
|
|
41565
|
+
if (parent === current) return join7(current, ...tail);
|
|
41505
41566
|
tail.unshift(basename(current));
|
|
41506
41567
|
current = parent;
|
|
41507
41568
|
}
|
|
@@ -41583,7 +41644,7 @@ function exportOneAttachment(record2, dir, source) {
|
|
|
41583
41644
|
if (!source) return base;
|
|
41584
41645
|
const name = exportFileName(record2, source.path, source.kind);
|
|
41585
41646
|
for (let attempt = 1; attempt <= MAX_COLLISION_SUFFIX; attempt++) {
|
|
41586
|
-
const dest =
|
|
41647
|
+
const dest = join7(dir, collisionName(name, attempt));
|
|
41587
41648
|
try {
|
|
41588
41649
|
assertSafeSavePath(dest);
|
|
41589
41650
|
copyFileExclusive(source.path, dest);
|
|
@@ -41671,10 +41732,10 @@ function readImageInfoFd(fd) {
|
|
|
41671
41732
|
const read = readSync2(fd, buf, 0, buf.length, 0);
|
|
41672
41733
|
return parseImageHeader(buf.subarray(0, read));
|
|
41673
41734
|
}
|
|
41674
|
-
function readImageInfo(
|
|
41735
|
+
function readImageInfo(path9) {
|
|
41675
41736
|
let fd;
|
|
41676
41737
|
try {
|
|
41677
|
-
fd = openSync3(
|
|
41738
|
+
fd = openSync3(path9, constants3.O_RDONLY | constants3.O_NOFOLLOW);
|
|
41678
41739
|
} catch {
|
|
41679
41740
|
return null;
|
|
41680
41741
|
}
|
|
@@ -41710,7 +41771,7 @@ function buildDrawingRowsSql(notePk, columns) {
|
|
|
41710
41771
|
}
|
|
41711
41772
|
function runSqlite4(dbPath2, sql) {
|
|
41712
41773
|
try {
|
|
41713
|
-
return
|
|
41774
|
+
return execFileSync8("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
|
|
41714
41775
|
encoding: "utf8",
|
|
41715
41776
|
timeout: 1e4,
|
|
41716
41777
|
maxBuffer: 16 * 1024 * 1024,
|
|
@@ -41750,7 +41811,7 @@ function parseDrawingRows(json2) {
|
|
|
41750
41811
|
}
|
|
41751
41812
|
return rows.sort((a, b) => a.pk - b.pk);
|
|
41752
41813
|
}
|
|
41753
|
-
function readDrawingRows(noteId3, dbPath2 =
|
|
41814
|
+
function readDrawingRows(noteId3, dbPath2 = join8(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
|
|
41754
41815
|
const { pk } = parseNoteId2(noteId3);
|
|
41755
41816
|
const columns = new Set(
|
|
41756
41817
|
runSqlite4(
|
|
@@ -41774,9 +41835,9 @@ function readDrawingRows(noteId3, dbPath2 = join7(NOTES_CONTAINER_DIR, "NoteStor
|
|
|
41774
41835
|
throw new AttachmentStoreError("Drawing rows could not be parsed", "query_error");
|
|
41775
41836
|
}
|
|
41776
41837
|
}
|
|
41777
|
-
function kindOf(
|
|
41838
|
+
function kindOf(path9) {
|
|
41778
41839
|
try {
|
|
41779
|
-
const st = lstatSync3(
|
|
41840
|
+
const st = lstatSync3(path9);
|
|
41780
41841
|
return st.isFile() ? "file" : st.isDirectory() ? "dir" : null;
|
|
41781
41842
|
} catch {
|
|
41782
41843
|
return null;
|
|
@@ -41794,16 +41855,16 @@ var byGenerationDesc = (a, b) => generationRank(basename2(b)) - generationRank(b
|
|
|
41794
41855
|
function findFallbackImage(accountDir, identifier, generation) {
|
|
41795
41856
|
const id2 = safeComponent(identifier);
|
|
41796
41857
|
if (!id2) return null;
|
|
41797
|
-
const base =
|
|
41858
|
+
const base = join8(accountDir, "FallbackImages", id2);
|
|
41798
41859
|
const names = ["FallbackImage.png", "FallbackImage.jpg"];
|
|
41799
41860
|
const candidates = [];
|
|
41800
41861
|
const gen = safeComponent(generation);
|
|
41801
|
-
if (gen) for (const n of names) candidates.push(
|
|
41802
|
-
const gens = entries(base, MAX_DIR_ENTRIES).map((e) =>
|
|
41803
|
-
for (const g of gens) for (const n of names) candidates.push(
|
|
41804
|
-
for (const n of names) candidates.push(
|
|
41805
|
-
candidates.push(
|
|
41806
|
-
candidates.push(
|
|
41862
|
+
if (gen) for (const n of names) candidates.push(join8(base, gen, n));
|
|
41863
|
+
const gens = entries(base, MAX_DIR_ENTRIES).map((e) => join8(base, e)).sort(byGenerationDesc);
|
|
41864
|
+
for (const g of gens) for (const n of names) candidates.push(join8(g, n));
|
|
41865
|
+
for (const n of names) candidates.push(join8(base, n));
|
|
41866
|
+
candidates.push(join8(accountDir, "FallbackImages", `${id2}.png`));
|
|
41867
|
+
candidates.push(join8(accountDir, "FallbackImages", `${id2}.jpg`));
|
|
41807
41868
|
for (const c of candidates) {
|
|
41808
41869
|
const real = realInside(c, accountDir);
|
|
41809
41870
|
if (real && kindOf(real) === "file") return real;
|
|
@@ -41813,21 +41874,21 @@ function findFallbackImage(accountDir, identifier, generation) {
|
|
|
41813
41874
|
function findLargestPreview(accountDir, identifier) {
|
|
41814
41875
|
const id2 = safeComponent(identifier);
|
|
41815
41876
|
if (!id2) return null;
|
|
41816
|
-
const dir =
|
|
41877
|
+
const dir = join8(accountDir, "Previews");
|
|
41817
41878
|
const prefix = `${id2}-`.toLowerCase();
|
|
41818
41879
|
const names = entries(dir, MAX_PREVIEW_DIR_ENTRIES2).filter((n) => n.toLowerCase().startsWith(prefix)).filter((n) => {
|
|
41819
41880
|
const ext = extname2(n).toLowerCase();
|
|
41820
41881
|
return !ext || IMAGE_SUFFIXES.has(ext) || /^\.\d+$/.test(ext);
|
|
41821
41882
|
}).sort((a, b) => previewPixelArea(b) - previewPixelArea(a) || a.localeCompare(b));
|
|
41822
41883
|
for (const name of names) {
|
|
41823
|
-
const real = realInside(
|
|
41884
|
+
const real = realInside(join8(dir, name), accountDir);
|
|
41824
41885
|
if (!real) continue;
|
|
41825
41886
|
const kind = kindOf(real);
|
|
41826
41887
|
if (kind === "file") return real;
|
|
41827
41888
|
if (kind !== "dir") continue;
|
|
41828
|
-
const gens = entries(real, MAX_DIR_ENTRIES).map((e) => realInside(
|
|
41889
|
+
const gens = entries(real, MAX_DIR_ENTRIES).map((e) => realInside(join8(real, e), accountDir)).filter((p) => p !== null && kindOf(p) === "dir").sort(byGenerationDesc);
|
|
41829
41890
|
for (const g of [...gens, real]) {
|
|
41830
|
-
const file = realInside(
|
|
41891
|
+
const file = realInside(join8(g, "Preview.png"), accountDir);
|
|
41831
41892
|
if (file && kindOf(file) === "file") return file;
|
|
41832
41893
|
}
|
|
41833
41894
|
}
|
|
@@ -41840,15 +41901,15 @@ function describeDrawings(rows, containerDir = NOTES_CONTAINER_DIR) {
|
|
|
41840
41901
|
const id2 = safeComponent(row.identifier);
|
|
41841
41902
|
const fallbackImagePath = accountDir ? findFallbackImage(accountDir, row.identifier, row.fallbackImageGeneration) : null;
|
|
41842
41903
|
const previewPath = accountDir ? findLargestPreview(accountDir, row.identifier) : null;
|
|
41843
|
-
const bundle = accountDir && id2 && kind === "paper" ? realInside(
|
|
41904
|
+
const bundle = accountDir && id2 && kind === "paper" ? realInside(join8(accountDir, "Paper", "Bundles", `${id2}.bundle`), accountDir) : null;
|
|
41844
41905
|
let raster = null;
|
|
41845
|
-
for (const [
|
|
41906
|
+
for (const [path9, source] of [
|
|
41846
41907
|
[fallbackImagePath, "fallback"],
|
|
41847
41908
|
[previewPath, "preview"]
|
|
41848
41909
|
]) {
|
|
41849
|
-
const info =
|
|
41850
|
-
if (
|
|
41851
|
-
raster = { path:
|
|
41910
|
+
const info = path9 ? readImageInfo(path9) : null;
|
|
41911
|
+
if (path9 && info) {
|
|
41912
|
+
raster = { path: path9, source, ...info };
|
|
41852
41913
|
break;
|
|
41853
41914
|
}
|
|
41854
41915
|
}
|
|
@@ -41865,10 +41926,10 @@ function describeDrawings(rows, containerDir = NOTES_CONTAINER_DIR) {
|
|
|
41865
41926
|
};
|
|
41866
41927
|
});
|
|
41867
41928
|
}
|
|
41868
|
-
function verifyWrittenImage(fd,
|
|
41929
|
+
function verifyWrittenImage(fd, path9, expected) {
|
|
41869
41930
|
const check2 = readImageInfoFd(fd);
|
|
41870
41931
|
if (!check2 || check2.format !== expected.format || check2.width !== expected.width || check2.height !== expected.height) {
|
|
41871
|
-
unlinkSync2(
|
|
41932
|
+
unlinkSync2(path9);
|
|
41872
41933
|
throw new Error("The exported image failed validation and was removed.");
|
|
41873
41934
|
}
|
|
41874
41935
|
return check2;
|
|
@@ -41966,13 +42027,34 @@ function exportDrawingRaster(drawing, savePath, containerDir = NOTES_CONTAINER_D
|
|
|
41966
42027
|
|
|
41967
42028
|
// src/services/appleNotesManager.ts
|
|
41968
42029
|
var import_turndown = __toESM(require_turndown_cjs(), 1);
|
|
41969
|
-
import { existsSync as
|
|
41970
|
-
import { homedir as
|
|
41971
|
-
import { join as
|
|
42030
|
+
import { existsSync as existsSync5 } from "fs";
|
|
42031
|
+
import { homedir as homedir8 } from "os";
|
|
42032
|
+
import { join as join9 } from "path";
|
|
41972
42033
|
var FIELD_SEP = "";
|
|
41973
42034
|
var RECORD_SEP = "";
|
|
41974
42035
|
var AS_FIELD_SEP = "(character id 31)";
|
|
41975
42036
|
var AS_RECORD_SEP = "(character id 30)";
|
|
42037
|
+
var GROUP_SEP = "";
|
|
42038
|
+
var AS_GROUP_SEP = "(character id 29)";
|
|
42039
|
+
var FOLDER_ID_PATTERN = /^x-coredata:\/\/[0-9A-F-]+\/ICFolder\/p\d+$/i;
|
|
42040
|
+
function trashFolderIdList() {
|
|
42041
|
+
const ids = readTrashFolderIds().filter((id2) => FOLDER_ID_PATTERN.test(id2));
|
|
42042
|
+
return `{${ids.map((id2) => `"${id2}"`).join(", ")}}`;
|
|
42043
|
+
}
|
|
42044
|
+
function buildTrashNoteIdsCollector() {
|
|
42045
|
+
return `
|
|
42046
|
+
set __trashNoteIds to {}
|
|
42047
|
+
repeat with __trashFolderId in ${trashFolderIdList()}
|
|
42048
|
+
try
|
|
42049
|
+
set __trashNoteIds to __trashNoteIds & (id of notes of folder id (contents of __trashFolderId))
|
|
42050
|
+
end try
|
|
42051
|
+
end repeat
|
|
42052
|
+
try
|
|
42053
|
+
repeat with __trashFolder in (every folder whose name is "${RECENTLY_DELETED_FOLDER_NAME}")
|
|
42054
|
+
set __trashNoteIds to __trashNoteIds & (id of notes of __trashFolder)
|
|
42055
|
+
end repeat
|
|
42056
|
+
end try`;
|
|
42057
|
+
}
|
|
41976
42058
|
var DEFAULT_EXPORT_PAGE_SIZE = 50;
|
|
41977
42059
|
var DEFAULT_EXPORT_MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
41978
42060
|
function exportMaxResponseBytes(env = process.env) {
|
|
@@ -42196,8 +42278,8 @@ function getNoteLinkFromDB(coreDataId2) {
|
|
|
42196
42278
|
const match = coreDataId2.match(/\/p(\d+)$/);
|
|
42197
42279
|
if (!match) return null;
|
|
42198
42280
|
const pk = parseInt(match[1], 10);
|
|
42199
|
-
const dbPath2 =
|
|
42200
|
-
if (!
|
|
42281
|
+
const dbPath2 = join9(homedir8(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
|
|
42282
|
+
if (!existsSync5(dbPath2)) return null;
|
|
42201
42283
|
try {
|
|
42202
42284
|
const { DatabaseSync } = __require("node:sqlite");
|
|
42203
42285
|
const db = new DatabaseSync(dbPath2, { readOnly: true });
|
|
@@ -42402,9 +42484,9 @@ var AppleNotesManager = class {
|
|
|
42402
42484
|
* const topResults = manager.searchNotes("project", false, undefined, undefined, undefined, 10);
|
|
42403
42485
|
* ```
|
|
42404
42486
|
*/
|
|
42405
|
-
searchNotes(
|
|
42487
|
+
searchNotes(query2, searchContent = false, account, folder, modifiedSince, limit) {
|
|
42406
42488
|
const targetAccount = this.resolveAccount(account);
|
|
42407
|
-
const safeQuery = escapePlainStringForAppleScript(
|
|
42489
|
+
const safeQuery = escapePlainStringForAppleScript(query2);
|
|
42408
42490
|
const safeLimit = limit !== void 0 && limit > 0 ? Math.floor(limit) : void 0;
|
|
42409
42491
|
const whereParts = [];
|
|
42410
42492
|
if (searchContent) {
|
|
@@ -42462,7 +42544,7 @@ var AppleNotesManager = class {
|
|
|
42462
42544
|
const script = buildAccountScopedScript({ account: targetAccount }, searchCommand);
|
|
42463
42545
|
const result = executeAppleScript(script);
|
|
42464
42546
|
if (!result.success) {
|
|
42465
|
-
throw new Error(`Failed to search notes for "${
|
|
42547
|
+
throw new Error(`Failed to search notes for "${query2}": ${result.error ?? "unknown error"}`);
|
|
42466
42548
|
}
|
|
42467
42549
|
if (!result.output.trim()) {
|
|
42468
42550
|
return [];
|
|
@@ -42762,6 +42844,16 @@ var AppleNotesManager = class {
|
|
|
42762
42844
|
try
|
|
42763
42845
|
set originalFolder to container of noteRef
|
|
42764
42846
|
end try
|
|
42847
|
+
if originalFolder is not missing value then
|
|
42848
|
+
set __inTrash to false
|
|
42849
|
+
try
|
|
42850
|
+
if ${trashFolderIdList()} contains (id of originalFolder) then set __inTrash to true
|
|
42851
|
+
end try
|
|
42852
|
+
try
|
|
42853
|
+
if (name of originalFolder) is "${RECENTLY_DELETED_FOLDER_NAME}" then set __inTrash to true
|
|
42854
|
+
end try
|
|
42855
|
+
if __inTrash then return "SAFETY_IN_RECENTLY_DELETED"
|
|
42856
|
+
end if
|
|
42765
42857
|
set currentBody to body of noteRef
|
|
42766
42858
|
considering case
|
|
42767
42859
|
if currentBody is not "${safeExpectedBody}" and currentBody is not "${safeExpectedBody}" & linefeed then return "SAFETY_CONFLICT"
|
|
@@ -42781,6 +42873,7 @@ var AppleNotesManager = class {
|
|
|
42781
42873
|
}
|
|
42782
42874
|
const status = result.output.trim();
|
|
42783
42875
|
if (status === "SAFETY_CONFLICT") return { status: "conflict" };
|
|
42876
|
+
if (status === "SAFETY_IN_RECENTLY_DELETED") return { status: "in-recently-deleted" };
|
|
42784
42877
|
if (status === "SAFETY_NOT_DELETED") return { status: "not-deleted" };
|
|
42785
42878
|
return status === "SAFETY_DELETED" ? { status: "deleted" } : { status: "failed" };
|
|
42786
42879
|
}
|
|
@@ -42806,14 +42899,20 @@ var AppleNotesManager = class {
|
|
|
42806
42899
|
* dateSetup: a date filter must scan every note's date). The script then
|
|
42807
42900
|
* returns the total note count as a leading record so the caller can
|
|
42808
42901
|
* detect a dedup shortfall and fall back to a full fetch.
|
|
42902
|
+
* @param trashCheck - Also return the ids of notes in Recently Deleted,
|
|
42903
|
+
* after a group separator, so the caller can exclude or flag them (#207).
|
|
42904
|
+
* Only valid inside an account-scoped script.
|
|
42809
42905
|
*/
|
|
42810
42906
|
buildBulkListCommand(opts) {
|
|
42811
|
-
const { folderRef, dateSetup, sliceLimit } = opts;
|
|
42907
|
+
const { folderRef, dateSetup, sliceLimit, trashCheck } = opts;
|
|
42812
42908
|
const fullSource = folderRef ? `notes of ${folderRef}` : "notes";
|
|
42909
|
+
const trashSetup = trashCheck ? `${buildTrashNoteIdsCollector()}
|
|
42910
|
+
` : "";
|
|
42911
|
+
const trashSuffix = trashCheck ? ` & ${AS_GROUP_SEP} & (__trashNoteIds as text)` : "";
|
|
42813
42912
|
const countGuard = (listVar) => `if (count of ${listVar}) is not (count of noteNames) then error "${BULK_LIST_MUTATION_ERROR}"`;
|
|
42814
42913
|
if (sliceLimit !== void 0) {
|
|
42815
42914
|
const slicedSource = folderRef ? `(notes 1 thru fetchCount of ${folderRef})` : `(notes 1 thru fetchCount)`;
|
|
42816
|
-
return
|
|
42915
|
+
return `${trashSetup}
|
|
42817
42916
|
set totalCount to count of ${fullSource}
|
|
42818
42917
|
set fetchCount to ${sliceLimit}
|
|
42819
42918
|
if fetchCount > totalCount then set fetchCount to totalCount
|
|
@@ -42840,7 +42939,7 @@ var AppleNotesManager = class {
|
|
|
42840
42939
|
end repeat
|
|
42841
42940
|
end if
|
|
42842
42941
|
set AppleScript's text item delimiters to ${AS_RECORD_SEP}
|
|
42843
|
-
return (totalCount as text) & ${AS_RECORD_SEP} & (resultList as text)
|
|
42942
|
+
return (totalCount as text) & ${AS_RECORD_SEP} & (resultList as text)${trashSuffix}
|
|
42844
42943
|
`;
|
|
42845
42944
|
}
|
|
42846
42945
|
const dateFetch = dateSetup ? `set noteDates to modification date of ${fullSource}
|
|
@@ -42851,7 +42950,7 @@ var AppleNotesManager = class {
|
|
|
42851
42950
|
` : "";
|
|
42852
42951
|
const dateGuardClose = dateSetup ? `
|
|
42853
42952
|
end if` : "";
|
|
42854
|
-
return
|
|
42953
|
+
return `${trashSetup}
|
|
42855
42954
|
${dateSetup ?? ""}set noteNames to name of ${fullSource}
|
|
42856
42955
|
set noteIds to id of ${fullSource}
|
|
42857
42956
|
${dateFetch}${countGuard("noteIds")}
|
|
@@ -42860,7 +42959,7 @@ var AppleNotesManager = class {
|
|
|
42860
42959
|
${dateGuardOpen}set end of resultList to (item i of noteNames) & ${AS_FIELD_SEP} & (item i of noteIds)${dateGuardClose}
|
|
42861
42960
|
end repeat
|
|
42862
42961
|
set AppleScript's text item delimiters to ${AS_RECORD_SEP}
|
|
42863
|
-
return resultList as text
|
|
42962
|
+
return (resultList as text)${trashSuffix}
|
|
42864
42963
|
`;
|
|
42865
42964
|
}
|
|
42866
42965
|
/**
|
|
@@ -42922,19 +43021,49 @@ var AppleNotesManager = class {
|
|
|
42922
43021
|
* after dedup so duplicates never count against it.
|
|
42923
43022
|
*/
|
|
42924
43023
|
parseBulkListOutput(output, safeLimit) {
|
|
42925
|
-
|
|
42926
|
-
|
|
43024
|
+
return this.parseBulkListRecords(output, safeLimit).refs;
|
|
43025
|
+
}
|
|
43026
|
+
/**
|
|
43027
|
+
* Splits a trash-checked listing (see buildBulkListCommand's trashCheck)
|
|
43028
|
+
* into the listing records and the set of note ids in Recently Deleted.
|
|
43029
|
+
* Output without the group separator carries no trash information (null).
|
|
43030
|
+
*/
|
|
43031
|
+
splitTrashIds(output) {
|
|
43032
|
+
const idx = output.lastIndexOf(GROUP_SEP);
|
|
43033
|
+
if (idx === -1) return { records: output, trashIds: null };
|
|
43034
|
+
const trashIds = new Set(
|
|
43035
|
+
output.slice(idx + 1).split(RECORD_SEP).map((id2) => id2.trim()).filter(Boolean)
|
|
43036
|
+
);
|
|
43037
|
+
return { records: output.slice(0, idx), trashIds };
|
|
43038
|
+
}
|
|
43039
|
+
/**
|
|
43040
|
+
* Parses bulk listing records, deduplicated by id. Notes whose id is in
|
|
43041
|
+
* `trash.ids` are dropped (and counted) unless `trash.include`, in which
|
|
43042
|
+
* case they are kept and flagged `inRecentlyDeleted`. The limit applies
|
|
43043
|
+
* after both dedup and trash filtering.
|
|
43044
|
+
*/
|
|
43045
|
+
parseBulkListRecords(output, safeLimit, trash) {
|
|
42927
43046
|
const refs = [];
|
|
43047
|
+
let excludedRecentlyDeleted = 0;
|
|
43048
|
+
if (!output.trim()) return { refs, excludedRecentlyDeleted };
|
|
43049
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
42928
43050
|
for (const item of output.split(RECORD_SEP)) {
|
|
42929
43051
|
const [title, id2] = item.split(FIELD_SEP);
|
|
42930
43052
|
if (!title?.trim()) continue;
|
|
42931
43053
|
const noteId3 = id2?.trim() || generateFallbackId();
|
|
42932
43054
|
if (seenIds.has(noteId3)) continue;
|
|
42933
43055
|
seenIds.add(noteId3);
|
|
42934
|
-
|
|
43056
|
+
const trashed = trash?.ids?.has(noteId3) ?? false;
|
|
43057
|
+
if (trashed && !trash?.include) {
|
|
43058
|
+
excludedRecentlyDeleted++;
|
|
43059
|
+
continue;
|
|
43060
|
+
}
|
|
43061
|
+
refs.push(
|
|
43062
|
+
trashed ? { title: title.trim(), id: noteId3, inRecentlyDeleted: true } : { title: title.trim(), id: noteId3 }
|
|
43063
|
+
);
|
|
42935
43064
|
if (safeLimit !== void 0 && refs.length >= safeLimit) break;
|
|
42936
43065
|
}
|
|
42937
|
-
return refs;
|
|
43066
|
+
return { refs, excludedRecentlyDeleted };
|
|
42938
43067
|
}
|
|
42939
43068
|
/**
|
|
42940
43069
|
* Core listing path shared by `listNotes()` and `listNoteRefs()`: lists
|
|
@@ -42942,7 +43071,7 @@ var AppleNotesManager = class {
|
|
|
42942
43071
|
* returning (title, id) pairs. Kept private because both public callers
|
|
42943
43072
|
* need to apply their own return shape.
|
|
42944
43073
|
*/
|
|
42945
|
-
listNotesCore(account, folder, modifiedSince, limit) {
|
|
43074
|
+
listNotesCore(account, folder, modifiedSince, limit, includeRecentlyDeleted = false) {
|
|
42946
43075
|
const targetAccount = this.resolveAccount(account);
|
|
42947
43076
|
const safeLimit = limit !== void 0 && limit > 0 ? Math.floor(limit) : void 0;
|
|
42948
43077
|
const folderRef = folder ? buildFolderReference(folder) : void 0;
|
|
@@ -42956,30 +43085,38 @@ var AppleNotesManager = class {
|
|
|
42956
43085
|
if (safeLimit !== void 0 && !dateSetup) {
|
|
42957
43086
|
const script2 = buildAccountScopedScript(
|
|
42958
43087
|
{ account: targetAccount },
|
|
42959
|
-
this.buildBulkListCommand({ folderRef, sliceLimit: safeLimit })
|
|
43088
|
+
this.buildBulkListCommand({ folderRef, sliceLimit: safeLimit, trashCheck: true })
|
|
42960
43089
|
);
|
|
42961
43090
|
const result2 = executeAppleScript(script2);
|
|
42962
43091
|
if (!result2.success) {
|
|
42963
43092
|
throw new Error(`Failed to list notes: ${result2.error ?? "unknown error"}`);
|
|
42964
43093
|
}
|
|
42965
|
-
const
|
|
42966
|
-
const
|
|
43094
|
+
const { records: body, trashIds: trashIds2 } = this.splitTrashIds(result2.output);
|
|
43095
|
+
const sepIdx = body.indexOf(RECORD_SEP);
|
|
43096
|
+
const header = sepIdx === -1 ? body : body.slice(0, sepIdx);
|
|
42967
43097
|
const totalCount = Number.parseInt(header.trim(), 10);
|
|
42968
|
-
const
|
|
42969
|
-
const
|
|
42970
|
-
|
|
42971
|
-
|
|
43098
|
+
const records2 = sepIdx === -1 ? "" : body.slice(sepIdx + 1);
|
|
43099
|
+
const parsed = this.parseBulkListRecords(records2, safeLimit, {
|
|
43100
|
+
ids: trashIds2,
|
|
43101
|
+
include: includeRecentlyDeleted
|
|
43102
|
+
});
|
|
43103
|
+
if (!Number.isNaN(totalCount) && (parsed.refs.length >= safeLimit || totalCount <= safeLimit)) {
|
|
43104
|
+
return parsed;
|
|
42972
43105
|
}
|
|
42973
43106
|
}
|
|
42974
43107
|
const script = buildAccountScopedScript(
|
|
42975
43108
|
{ account: targetAccount },
|
|
42976
|
-
this.buildBulkListCommand({ folderRef, dateSetup })
|
|
43109
|
+
this.buildBulkListCommand({ folderRef, dateSetup, trashCheck: true })
|
|
42977
43110
|
);
|
|
42978
43111
|
const result = executeAppleScript(script);
|
|
42979
43112
|
if (!result.success) {
|
|
42980
43113
|
throw new Error(`Failed to list notes: ${result.error ?? "unknown error"}`);
|
|
42981
43114
|
}
|
|
42982
|
-
|
|
43115
|
+
const { records, trashIds } = this.splitTrashIds(result.output);
|
|
43116
|
+
return this.parseBulkListRecords(records, safeLimit, {
|
|
43117
|
+
ids: trashIds,
|
|
43118
|
+
include: includeRecentlyDeleted
|
|
43119
|
+
});
|
|
42983
43120
|
}
|
|
42984
43121
|
/**
|
|
42985
43122
|
* Lists all notes in an account, optionally filtered by folder, date, and limit.
|
|
@@ -42991,7 +43128,7 @@ var AppleNotesManager = class {
|
|
|
42991
43128
|
* @returns Array of note titles
|
|
42992
43129
|
*/
|
|
42993
43130
|
listNotes(account, folder, modifiedSince, limit) {
|
|
42994
|
-
return this.listNotesCore(account, folder, modifiedSince, limit).map((ref) => ref.title);
|
|
43131
|
+
return this.listNotesCore(account, folder, modifiedSince, limit).refs.map((ref) => ref.title);
|
|
42995
43132
|
}
|
|
42996
43133
|
/**
|
|
42997
43134
|
* Lists all notes in an account, optionally filtered by folder, date, and
|
|
@@ -43002,10 +43139,23 @@ var AppleNotesManager = class {
|
|
|
43002
43139
|
* specifier resolves ambiguously to the same one note every time — see
|
|
43003
43140
|
* the fix in exportNotesAsJson for the failure mode this avoids).
|
|
43004
43141
|
*
|
|
43142
|
+
* Notes in Recently Deleted are excluded (#207); use
|
|
43143
|
+
* `listNoteRefsDetailed()` to include and flag them.
|
|
43144
|
+
*
|
|
43005
43145
|
* @returns Array of { title, id } pairs, deduplicated by id
|
|
43006
43146
|
*/
|
|
43007
43147
|
listNoteRefs(account, folder, modifiedSince, limit) {
|
|
43008
|
-
return this.listNotesCore(account, folder, modifiedSince, limit);
|
|
43148
|
+
return this.listNotesCore(account, folder, modifiedSince, limit).refs;
|
|
43149
|
+
}
|
|
43150
|
+
/**
|
|
43151
|
+
* Same as `listNoteRefs()`, plus control over notes in Recently Deleted and
|
|
43152
|
+
* a count of how many were skipped. Notes.app's account-wide `notes`
|
|
43153
|
+
* includes Recently Deleted, so they are excluded unless
|
|
43154
|
+
* `includeRecentlyDeleted`, in which case each is flagged
|
|
43155
|
+
* `inRecentlyDeleted: true`. (#207)
|
|
43156
|
+
*/
|
|
43157
|
+
listNoteRefsDetailed(account, folder, modifiedSince, limit, includeRecentlyDeleted = false) {
|
|
43158
|
+
return this.listNotesCore(account, folder, modifiedSince, limit, includeRecentlyDeleted);
|
|
43009
43159
|
}
|
|
43010
43160
|
/**
|
|
43011
43161
|
* Lists all shared (collaborative) notes across all accounts.
|
|
@@ -44075,7 +44225,7 @@ var AppleNotesManager = class {
|
|
|
44075
44225
|
if (parts[0] !== "OK") {
|
|
44076
44226
|
return { success: false, error: parts[1]?.trim() || "attachment not found" };
|
|
44077
44227
|
}
|
|
44078
|
-
if (!
|
|
44228
|
+
if (!existsSync5(abs) || fileSize(abs) === 0) {
|
|
44079
44229
|
return { success: false, error: `Notes reported success but no file was written to ${abs}` };
|
|
44080
44230
|
}
|
|
44081
44231
|
return {
|
|
@@ -44580,15 +44730,15 @@ var AppleNotesManager = class {
|
|
|
44580
44730
|
};
|
|
44581
44731
|
|
|
44582
44732
|
// src/utils/syncDetection.ts
|
|
44583
|
-
import { execFileSync as
|
|
44584
|
-
import * as
|
|
44585
|
-
import * as
|
|
44586
|
-
import * as
|
|
44587
|
-
var
|
|
44588
|
-
|
|
44733
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
44734
|
+
import * as fs4 from "fs";
|
|
44735
|
+
import * as path4 from "path";
|
|
44736
|
+
import * as os4 from "os";
|
|
44737
|
+
var NOTES_DB_PATH5 = path4.join(
|
|
44738
|
+
os4.homedir(),
|
|
44589
44739
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
44590
44740
|
);
|
|
44591
|
-
var WAL_PATH = `${
|
|
44741
|
+
var WAL_PATH = `${NOTES_DB_PATH5}-wal`;
|
|
44592
44742
|
var RECENT_ACTIVITY_THRESHOLD_SECONDS = 5;
|
|
44593
44743
|
var SYNC_STATUS_CACHE_TTL_MS = 2e3;
|
|
44594
44744
|
var cachedSyncStatus = null;
|
|
@@ -44604,19 +44754,19 @@ function getSyncStatus(useCache = true) {
|
|
|
44604
44754
|
recentActivity: false
|
|
44605
44755
|
};
|
|
44606
44756
|
try {
|
|
44607
|
-
if (!
|
|
44757
|
+
if (!fs4.existsSync(NOTES_DB_PATH5)) {
|
|
44608
44758
|
status.error = "Notes database not found";
|
|
44609
44759
|
cachedSyncStatus = status;
|
|
44610
44760
|
cacheTimestamp = Date.now();
|
|
44611
44761
|
return status;
|
|
44612
44762
|
}
|
|
44613
|
-
if (
|
|
44614
|
-
const walStats =
|
|
44763
|
+
if (fs4.existsSync(WAL_PATH)) {
|
|
44764
|
+
const walStats = fs4.statSync(WAL_PATH);
|
|
44615
44765
|
const secondsAgo = (Date.now() - walStats.mtimeMs) / 1e3;
|
|
44616
44766
|
status.secondsSinceLastChange = Math.round(secondsAgo);
|
|
44617
44767
|
status.recentActivity = secondsAgo < RECENT_ACTIVITY_THRESHOLD_SECONDS;
|
|
44618
44768
|
}
|
|
44619
|
-
const
|
|
44769
|
+
const query2 = `
|
|
44620
44770
|
SELECT COUNT(*) FROM ZICCLOUDSTATE state
|
|
44621
44771
|
WHERE state.ZCURRENTLOCALVERSION > state.ZLATESTVERSIONSYNCEDTOCLOUD
|
|
44622
44772
|
AND state.ZLATESTVERSIONSYNCEDTOCLOUD IS NOT NULL
|
|
@@ -44625,9 +44775,9 @@ function getSyncStatus(useCache = true) {
|
|
|
44625
44775
|
WHERE object.ZCLOUDSTATE = state.Z_PK
|
|
44626
44776
|
);
|
|
44627
44777
|
`;
|
|
44628
|
-
const result =
|
|
44778
|
+
const result = execFileSync9(
|
|
44629
44779
|
"sqlite3",
|
|
44630
|
-
["-readonly",
|
|
44780
|
+
["-readonly", NOTES_DB_PATH5, query2.replace(/\n/g, " ")],
|
|
44631
44781
|
{
|
|
44632
44782
|
encoding: "utf8",
|
|
44633
44783
|
timeout: 5e3,
|
|
@@ -44684,12 +44834,12 @@ function withSyncAwarenessSync(operation, fn) {
|
|
|
44684
44834
|
}
|
|
44685
44835
|
|
|
44686
44836
|
// src/utils/noteMetadata.ts
|
|
44687
|
-
import { execFileSync as
|
|
44688
|
-
import * as
|
|
44689
|
-
import * as
|
|
44690
|
-
import * as
|
|
44691
|
-
var
|
|
44692
|
-
|
|
44837
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
44838
|
+
import * as fs5 from "fs";
|
|
44839
|
+
import * as path5 from "path";
|
|
44840
|
+
import * as os5 from "os";
|
|
44841
|
+
var NOTES_DB_PATH6 = path5.join(
|
|
44842
|
+
os5.homedir(),
|
|
44693
44843
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
44694
44844
|
);
|
|
44695
44845
|
var FDA_MESSAGE2 = `Full Disk Access is required to read note metadata. 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.`;
|
|
@@ -44704,8 +44854,8 @@ var COLUMN_MAP = [
|
|
|
44704
44854
|
{ key: "widgetSnippet", column: "ZWIDGETSNIPPET", type: "text" },
|
|
44705
44855
|
{ key: "smartFolderQuery", column: "ZSMARTFOLDERQUERYJSON", type: "text" }
|
|
44706
44856
|
];
|
|
44707
|
-
function runSqlite5(
|
|
44708
|
-
return
|
|
44857
|
+
function runSqlite5(query2) {
|
|
44858
|
+
return execFileSync10("sqlite3", ["-readonly", NOTES_DB_PATH6, query2], {
|
|
44709
44859
|
encoding: "utf8",
|
|
44710
44860
|
timeout: 5e3,
|
|
44711
44861
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -44730,7 +44880,7 @@ function getNoteMetadata(noteId3) {
|
|
|
44730
44880
|
};
|
|
44731
44881
|
}
|
|
44732
44882
|
const pk = pkMatch[1];
|
|
44733
|
-
if (!
|
|
44883
|
+
if (!fs5.existsSync(NOTES_DB_PATH6)) {
|
|
44734
44884
|
return { metadata: null, error: "no_fda", message: FDA_MESSAGE2 };
|
|
44735
44885
|
}
|
|
44736
44886
|
try {
|
|
@@ -44776,12 +44926,12 @@ function getNoteMetadata(noteId3) {
|
|
|
44776
44926
|
import { gunzipSync as gunzipSync6 } from "zlib";
|
|
44777
44927
|
|
|
44778
44928
|
// src/utils/noteStoreSql.ts
|
|
44779
|
-
import { execFileSync as
|
|
44780
|
-
import * as
|
|
44781
|
-
import * as
|
|
44782
|
-
import * as
|
|
44783
|
-
var
|
|
44784
|
-
|
|
44929
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
44930
|
+
import * as fs6 from "fs";
|
|
44931
|
+
import * as os6 from "os";
|
|
44932
|
+
import * as path6 from "path";
|
|
44933
|
+
var NOTES_DB_PATH7 = path6.join(
|
|
44934
|
+
os6.homedir(),
|
|
44785
44935
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
44786
44936
|
);
|
|
44787
44937
|
var CORE_DATA_EPOCH_MS = Date.UTC(2001, 0, 1);
|
|
@@ -44819,7 +44969,7 @@ function renderValue(value) {
|
|
|
44819
44969
|
return `ieee754_from_blob(x'${doubleToHex(double)}')`;
|
|
44820
44970
|
}
|
|
44821
44971
|
function runReadOnlySql(dbPath2, sql, params = {}) {
|
|
44822
|
-
if (!
|
|
44972
|
+
if (!fs6.existsSync(dbPath2)) throw new NoteStoreError(STORE_FDA_MESSAGE, "no_fda");
|
|
44823
44973
|
const args = ["-readonly"];
|
|
44824
44974
|
const names = Object.keys(params);
|
|
44825
44975
|
if (names.length) args.push("-cmd", ".parameter init");
|
|
@@ -44831,7 +44981,7 @@ function runReadOnlySql(dbPath2, sql, params = {}) {
|
|
|
44831
44981
|
}
|
|
44832
44982
|
args.push(dbPath2, sql);
|
|
44833
44983
|
try {
|
|
44834
|
-
return
|
|
44984
|
+
return execFileSync11("sqlite3", args, {
|
|
44835
44985
|
encoding: "utf8",
|
|
44836
44986
|
timeout: 3e4,
|
|
44837
44987
|
maxBuffer: 512 * 1024 * 1024,
|
|
@@ -45024,7 +45174,7 @@ function buildSpecialNotesSql(columns, kind, scoped) {
|
|
|
45024
45174
|
].join(" ");
|
|
45025
45175
|
}
|
|
45026
45176
|
function listSpecialNotes(options) {
|
|
45027
|
-
const dbPath2 = options.dbPath ??
|
|
45177
|
+
const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
|
|
45028
45178
|
const limit = Math.min(
|
|
45029
45179
|
Math.max(1, Math.trunc(options.limit ?? SPECIAL_LIMIT.DEFAULT)),
|
|
45030
45180
|
SPECIAL_LIMIT.MAX
|
|
@@ -45115,7 +45265,7 @@ function referencedObjects(hex3) {
|
|
|
45115
45265
|
}
|
|
45116
45266
|
}
|
|
45117
45267
|
function nativeTagInventory(options = {}) {
|
|
45118
|
-
const dbPath2 = options.dbPath ??
|
|
45268
|
+
const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
|
|
45119
45269
|
const columns = readColumns(dbPath2);
|
|
45120
45270
|
const context = readStoreContext(dbPath2, columns);
|
|
45121
45271
|
const scope2 = options.account ? resolveAccountName(context.accounts, options.account) : void 0;
|
|
@@ -45177,12 +45327,12 @@ function assembleInventory(rows, accounts, scope2) {
|
|
|
45177
45327
|
}
|
|
45178
45328
|
|
|
45179
45329
|
// src/utils/noteIdentifiers.ts
|
|
45180
|
-
import { execFileSync as
|
|
45181
|
-
import * as
|
|
45182
|
-
import * as
|
|
45183
|
-
import * as
|
|
45184
|
-
var
|
|
45185
|
-
|
|
45330
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
45331
|
+
import * as fs7 from "fs";
|
|
45332
|
+
import * as os7 from "os";
|
|
45333
|
+
import * as path7 from "path";
|
|
45334
|
+
var NOTES_DB_PATH8 = path7.join(
|
|
45335
|
+
os7.homedir(),
|
|
45186
45336
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
45187
45337
|
);
|
|
45188
45338
|
var UUID_PATTERN = /^[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$/;
|
|
@@ -45256,10 +45406,10 @@ function buildLookupSql(entity3, keys) {
|
|
|
45256
45406
|
return `SELECT json_object('store', (SELECT Z_UUID FROM Z_METADATA LIMIT 1), 'rows', (SELECT json_group_array(json_object(${fields})) FROM ZICCLOUDSYNCINGOBJECT o ${joins}WHERE o.Z_ENT = ${entityClause(entity3)} AND o.Z_PK IN (${inList})));`;
|
|
45257
45407
|
}
|
|
45258
45408
|
function runJsonQuery(sql, dbPath2) {
|
|
45259
|
-
if (!
|
|
45409
|
+
if (!fs7.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
|
|
45260
45410
|
let out;
|
|
45261
45411
|
try {
|
|
45262
|
-
out =
|
|
45412
|
+
out = execFileSync12("sqlite3", ["-readonly", dbPath2, sql], {
|
|
45263
45413
|
encoding: "utf8",
|
|
45264
45414
|
timeout: 5e3,
|
|
45265
45415
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -45279,7 +45429,7 @@ function runJsonQuery(sql, dbPath2) {
|
|
|
45279
45429
|
function coreDataId(store, entity3, pk) {
|
|
45280
45430
|
return `x-coredata://${store}/${entity3}/p${pk}`;
|
|
45281
45431
|
}
|
|
45282
|
-
function resolveIdentifiers(values, entity3, dbPath2 =
|
|
45432
|
+
function resolveIdentifiers(values, entity3, dbPath2 = NOTES_DB_PATH8) {
|
|
45283
45433
|
const resolved = /* @__PURE__ */ new Map();
|
|
45284
45434
|
const keys = [];
|
|
45285
45435
|
const uuids = [];
|
|
@@ -45325,7 +45475,7 @@ function resolveIdentifiers(values, entity3, dbPath2 = NOTES_DB_PATH7) {
|
|
|
45325
45475
|
return resolved;
|
|
45326
45476
|
}
|
|
45327
45477
|
var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
|
|
45328
|
-
function lookupStableIdentifiers(ids, entity3, dbPath2 =
|
|
45478
|
+
function lookupStableIdentifiers(ids, entity3, dbPath2 = NOTES_DB_PATH8) {
|
|
45329
45479
|
const result = /* @__PURE__ */ new Map();
|
|
45330
45480
|
const wanted = /* @__PURE__ */ new Map();
|
|
45331
45481
|
for (const id2 of ids) {
|
|
@@ -45337,14 +45487,14 @@ function lookupStableIdentifiers(ids, entity3, dbPath2 = NOTES_DB_PATH7) {
|
|
|
45337
45487
|
wanted.set(pk, list);
|
|
45338
45488
|
}
|
|
45339
45489
|
if (wanted.size === 0) return result;
|
|
45340
|
-
let
|
|
45490
|
+
let query2;
|
|
45341
45491
|
try {
|
|
45342
|
-
|
|
45492
|
+
query2 = runJsonQuery(buildLookupSql(entity3, [...wanted.keys()]), dbPath2);
|
|
45343
45493
|
} catch {
|
|
45344
45494
|
return result;
|
|
45345
45495
|
}
|
|
45346
|
-
const store =
|
|
45347
|
-
for (const row of
|
|
45496
|
+
const store = query2.store?.toUpperCase();
|
|
45497
|
+
for (const row of query2.rows) {
|
|
45348
45498
|
const fields = {};
|
|
45349
45499
|
for (const key of [
|
|
45350
45500
|
"identifier",
|
|
@@ -45361,7 +45511,7 @@ function lookupStableIdentifiers(ids, entity3, dbPath2 = NOTES_DB_PATH7) {
|
|
|
45361
45511
|
}
|
|
45362
45512
|
return result;
|
|
45363
45513
|
}
|
|
45364
|
-
function withStableIdentifiers(items, entity3, dbPath2 =
|
|
45514
|
+
function withStableIdentifiers(items, entity3, dbPath2 = NOTES_DB_PATH8) {
|
|
45365
45515
|
const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
|
|
45366
45516
|
if (ids.length === 0) return items;
|
|
45367
45517
|
const found = lookupStableIdentifiers(ids, entity3, dbPath2);
|
|
@@ -45949,13 +46099,13 @@ function positiveTextTerms(node, negated = false) {
|
|
|
45949
46099
|
}
|
|
45950
46100
|
|
|
45951
46101
|
// src/utils/noteQueryStore.ts
|
|
45952
|
-
import { execFileSync as
|
|
45953
|
-
import * as
|
|
45954
|
-
import * as
|
|
45955
|
-
import * as
|
|
46102
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
46103
|
+
import * as fs8 from "fs";
|
|
46104
|
+
import * as os8 from "os";
|
|
46105
|
+
import * as path8 from "path";
|
|
45956
46106
|
import { gunzipSync as gunzipSync7 } from "zlib";
|
|
45957
|
-
var
|
|
45958
|
-
|
|
46107
|
+
var NOTES_DB_PATH9 = path8.join(
|
|
46108
|
+
os8.homedir(),
|
|
45959
46109
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
45960
46110
|
);
|
|
45961
46111
|
var QUERY_SCAN = { DEFAULT: 500, MAX: 5e3 };
|
|
@@ -46089,8 +46239,8 @@ function buildScanSql(available, options) {
|
|
|
46089
46239
|
"COMMIT;"
|
|
46090
46240
|
].join(" ");
|
|
46091
46241
|
}
|
|
46092
|
-
function runSqlite6(dbPath2,
|
|
46093
|
-
return
|
|
46242
|
+
function runSqlite6(dbPath2, query2) {
|
|
46243
|
+
return execFileSync13("sqlite3", ["-readonly", dbPath2, query2], {
|
|
46094
46244
|
encoding: "utf8",
|
|
46095
46245
|
timeout: 3e4,
|
|
46096
46246
|
maxBuffer: 512 * 1024 * 1024,
|
|
@@ -46117,12 +46267,12 @@ function resolveFolders(rows) {
|
|
|
46117
46267
|
seen.add(pk);
|
|
46118
46268
|
const parent = row.parent !== null ? resolve5(row.parent, seen) : void 0;
|
|
46119
46269
|
const name = row.name ?? "";
|
|
46120
|
-
const
|
|
46270
|
+
const path9 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
|
|
46121
46271
|
const plainPath = parent ? `${parent.plainPath}/${name}` : name;
|
|
46122
46272
|
const info = {
|
|
46123
|
-
path:
|
|
46273
|
+
path: path9,
|
|
46124
46274
|
plainPath,
|
|
46125
|
-
keys: [...new Set([name, plainPath,
|
|
46275
|
+
keys: [...new Set([name, plainPath, path9].map(normalizeForMatch))],
|
|
46126
46276
|
accountPk: row.owner ?? parent?.accountPk ?? null,
|
|
46127
46277
|
shared: Boolean(row.shared) || Boolean(parent?.shared)
|
|
46128
46278
|
};
|
|
@@ -46154,8 +46304,8 @@ function queryNotes(expression, options = {}) {
|
|
|
46154
46304
|
const includeDeleted = options.includeDeleted ?? false;
|
|
46155
46305
|
const withBodies = needsContent(ast);
|
|
46156
46306
|
const withTags = needsTags(ast);
|
|
46157
|
-
const dbPath2 = options.dbPath ??
|
|
46158
|
-
if (!
|
|
46307
|
+
const dbPath2 = options.dbPath ?? NOTES_DB_PATH9;
|
|
46308
|
+
if (!fs8.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
|
|
46159
46309
|
let output;
|
|
46160
46310
|
try {
|
|
46161
46311
|
const available = presentColumns2(dbPath2);
|
|
@@ -46290,10 +46440,10 @@ function queryNotes(expression, options = {}) {
|
|
|
46290
46440
|
import { spawnSync } from "child_process";
|
|
46291
46441
|
|
|
46292
46442
|
// src/services/nativeTags.ts
|
|
46293
|
-
import { execFileSync as
|
|
46443
|
+
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
46294
46444
|
import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
|
|
46295
46445
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
46296
|
-
import { join as
|
|
46446
|
+
import { join as join15 } from "node:path";
|
|
46297
46447
|
|
|
46298
46448
|
// src/services/shortcutConsent.ts
|
|
46299
46449
|
function shortcutConsentHint(shortcut) {
|
|
@@ -46369,7 +46519,7 @@ function addNativeTags(request, deps) {
|
|
|
46369
46519
|
};
|
|
46370
46520
|
}
|
|
46371
46521
|
function listInstalledShortcuts() {
|
|
46372
|
-
return
|
|
46522
|
+
return execFileSync14("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
|
|
46373
46523
|
encoding: "utf8",
|
|
46374
46524
|
timeout: 15e3,
|
|
46375
46525
|
maxBuffer: 1024 * 1024,
|
|
@@ -46395,11 +46545,11 @@ function runNativeTagsShortcut(input) {
|
|
|
46395
46545
|
const status = nativeTagsStatus();
|
|
46396
46546
|
if (!status.installed)
|
|
46397
46547
|
throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
|
|
46398
|
-
const directory = mkdtempSync2(
|
|
46548
|
+
const directory = mkdtempSync2(join15(tmpdir2(), "apple-notes-native-tags-"));
|
|
46399
46549
|
try {
|
|
46400
|
-
const
|
|
46401
|
-
writeFileSync(
|
|
46402
|
-
|
|
46550
|
+
const path9 = join15(directory, "request.json");
|
|
46551
|
+
writeFileSync(path9, JSON.stringify(input), { mode: 384 });
|
|
46552
|
+
execFileSync14("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path9], {
|
|
46403
46553
|
encoding: "utf8",
|
|
46404
46554
|
timeout: 6e4,
|
|
46405
46555
|
maxBuffer: 1024 * 1024,
|
|
@@ -46418,10 +46568,10 @@ function runNativeTagsShortcut(input) {
|
|
|
46418
46568
|
}
|
|
46419
46569
|
|
|
46420
46570
|
// src/services/backgroundNotes.ts
|
|
46421
|
-
import { execFileSync as
|
|
46571
|
+
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
46422
46572
|
import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
|
|
46423
46573
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
46424
|
-
import { join as
|
|
46574
|
+
import { join as join16 } from "node:path";
|
|
46425
46575
|
|
|
46426
46576
|
// src/utils/appendMarkdown.ts
|
|
46427
46577
|
var escape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -46746,9 +46896,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
46746
46896
|
throw new Error(
|
|
46747
46897
|
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
46748
46898
|
);
|
|
46749
|
-
const directory = mkdtempSync3(
|
|
46899
|
+
const directory = mkdtempSync3(join16(tmpdir3(), "apple-notes-background-"));
|
|
46750
46900
|
try {
|
|
46751
|
-
const file =
|
|
46901
|
+
const file = join16(directory, "request.json");
|
|
46752
46902
|
writeFileSync2(
|
|
46753
46903
|
file,
|
|
46754
46904
|
JSON.stringify({
|
|
@@ -46768,7 +46918,7 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
46768
46918
|
{ mode: 384 }
|
|
46769
46919
|
);
|
|
46770
46920
|
try {
|
|
46771
|
-
|
|
46921
|
+
execFileSync15("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
|
|
46772
46922
|
encoding: "utf8",
|
|
46773
46923
|
timeout: callTimeoutMs() ?? 6e4,
|
|
46774
46924
|
maxBuffer: 1024 * 1024,
|
|
@@ -47003,7 +47153,7 @@ function createMarkdownNote(manager, request, run = runBackgroundShortcut) {
|
|
|
47003
47153
|
throw new Error(
|
|
47004
47154
|
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
47005
47155
|
);
|
|
47006
|
-
const segments = (
|
|
47156
|
+
const segments = (path9) => JSON.stringify(splitFolderPath(path9).map((part) => part.toLocaleLowerCase()));
|
|
47007
47157
|
if (request.folder) {
|
|
47008
47158
|
const wanted = segments(request.folder);
|
|
47009
47159
|
if (!manager.listAccounts().some(
|
|
@@ -47093,7 +47243,7 @@ ${request.content}`
|
|
|
47093
47243
|
}
|
|
47094
47244
|
|
|
47095
47245
|
// src/services/capabilityMatrix.ts
|
|
47096
|
-
import { execFileSync as
|
|
47246
|
+
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
47097
47247
|
import { release } from "node:os";
|
|
47098
47248
|
var MACOS_SHORTCUTS_CLI = "12.0";
|
|
47099
47249
|
var MACOS_MARKDOWN_IMPORT = "26.0";
|
|
@@ -47300,7 +47450,7 @@ function evaluateFeatures(env, features = FEATURES) {
|
|
|
47300
47450
|
function readMacOSVersion() {
|
|
47301
47451
|
if (process.platform !== "darwin") return null;
|
|
47302
47452
|
try {
|
|
47303
|
-
const out =
|
|
47453
|
+
const out = execFileSync16("/usr/bin/sw_vers", ["-productVersion"], {
|
|
47304
47454
|
encoding: "utf8",
|
|
47305
47455
|
timeout: 3e3,
|
|
47306
47456
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -47333,9 +47483,9 @@ function getCapabilityMatrix(probe = probeCapabilityEnvironment) {
|
|
|
47333
47483
|
return evaluateFeatures(probe());
|
|
47334
47484
|
}
|
|
47335
47485
|
function formatCapabilityMatrix(matrix) {
|
|
47336
|
-
const
|
|
47486
|
+
const os9 = matrix.runtimeOS;
|
|
47337
47487
|
const lines = [
|
|
47338
|
-
`Feature matrix (macOS ${
|
|
47488
|
+
`Feature matrix (macOS ${os9.macOSVersion ?? "unknown"}, Darwin ${os9.darwinRelease}):`
|
|
47339
47489
|
];
|
|
47340
47490
|
for (const [name, status] of Object.entries(matrix.features)) {
|
|
47341
47491
|
lines.push(
|
|
@@ -47453,19 +47603,19 @@ function formatDoctorReport(r) {
|
|
|
47453
47603
|
}
|
|
47454
47604
|
|
|
47455
47605
|
// src/services/fileConfig.ts
|
|
47456
|
-
import { existsSync as
|
|
47457
|
-
import { join as
|
|
47458
|
-
import { homedir as
|
|
47606
|
+
import { existsSync as existsSync11, readFileSync as readFileSync2 } from "fs";
|
|
47607
|
+
import { join as join17 } from "path";
|
|
47608
|
+
import { homedir as homedir14 } from "os";
|
|
47459
47609
|
function fileConfigPath(env = process.env) {
|
|
47460
47610
|
const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
|
|
47461
47611
|
if (override && override.trim()) return override.trim();
|
|
47462
|
-
return
|
|
47612
|
+
return join17(homedir14(), "Library", "Application Support", "apple-notes-mcp", "config.json");
|
|
47463
47613
|
}
|
|
47464
|
-
function loadFileConfig(env = process.env,
|
|
47614
|
+
function loadFileConfig(env = process.env, path9 = fileConfigPath(env)) {
|
|
47465
47615
|
const applied = [];
|
|
47466
47616
|
try {
|
|
47467
|
-
if (!
|
|
47468
|
-
const parsed = JSON.parse(readFileSync2(
|
|
47617
|
+
if (!existsSync11(path9)) return applied;
|
|
47618
|
+
const parsed = JSON.parse(readFileSync2(path9, "utf8"));
|
|
47469
47619
|
if (!parsed || typeof parsed !== "object") return applied;
|
|
47470
47620
|
for (const [k, v] of Object.entries(parsed)) {
|
|
47471
47621
|
if (typeof v !== "string") continue;
|
|
@@ -47475,7 +47625,7 @@ function loadFileConfig(env = process.env, path8 = fileConfigPath(env)) {
|
|
|
47475
47625
|
}
|
|
47476
47626
|
}
|
|
47477
47627
|
} catch (e) {
|
|
47478
|
-
console.error(`Failed to load apple-notes-mcp config file ${
|
|
47628
|
+
console.error(`Failed to load apple-notes-mcp config file ${path9}: ${String(e)}`);
|
|
47479
47629
|
}
|
|
47480
47630
|
return applied;
|
|
47481
47631
|
}
|
|
@@ -47762,10 +47912,10 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
|
|
|
47762
47912
|
}
|
|
47763
47913
|
|
|
47764
47914
|
// src/utils/noteBlocks.ts
|
|
47765
|
-
import { execFileSync as
|
|
47766
|
-
import { existsSync as
|
|
47767
|
-
import { homedir as
|
|
47768
|
-
import { join as
|
|
47915
|
+
import { execFileSync as execFileSync17 } from "node:child_process";
|
|
47916
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
47917
|
+
import { homedir as homedir15 } from "node:os";
|
|
47918
|
+
import { join as join18 } from "node:path";
|
|
47769
47919
|
import { gunzipSync as gunzipSync8 } from "node:zlib";
|
|
47770
47920
|
var NoteBlocksError = class extends Error {
|
|
47771
47921
|
code;
|
|
@@ -48108,23 +48258,23 @@ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResp
|
|
|
48108
48258
|
undecodedFields: doc.undecodedFields
|
|
48109
48259
|
};
|
|
48110
48260
|
}
|
|
48111
|
-
var
|
|
48112
|
-
|
|
48261
|
+
var NOTES_DB_PATH10 = join18(
|
|
48262
|
+
homedir15(),
|
|
48113
48263
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
48114
48264
|
);
|
|
48115
|
-
function readNoteBlocks(id2, { dbPath: dbPath2 =
|
|
48265
|
+
function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH10 } = {}) {
|
|
48116
48266
|
const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
|
|
48117
48267
|
if (!pk)
|
|
48118
48268
|
throw new NoteBlocksError(
|
|
48119
48269
|
"invalid-id",
|
|
48120
48270
|
`Invalid note ID: expected x-coredata://<store>/ICNote/p<number>`
|
|
48121
48271
|
);
|
|
48122
|
-
if (!
|
|
48272
|
+
if (!existsSync12(dbPath2))
|
|
48123
48273
|
throw new NoteBlocksError("no-full-disk-access", "The Notes database is not readable");
|
|
48124
48274
|
const sql = "SELECT json_object('exists', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = @pk AND Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICNote')), 'data', (SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = @pk), 'encrypted', (SELECT ZCRYPTOINITIALIZATIONVECTOR IS NOT NULL FROM ZICNOTEDATA WHERE ZNOTE = @pk));";
|
|
48125
48275
|
let output;
|
|
48126
48276
|
try {
|
|
48127
|
-
output =
|
|
48277
|
+
output = execFileSync17(
|
|
48128
48278
|
"/usr/bin/sqlite3",
|
|
48129
48279
|
["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
|
|
48130
48280
|
{
|
|
@@ -48274,7 +48424,7 @@ function insertLink(request, deps) {
|
|
|
48274
48424
|
|
|
48275
48425
|
// src/services/notesExport.ts
|
|
48276
48426
|
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
48277
|
-
import { basename as basename4, dirname as dirname4, extname as extname4, join as
|
|
48427
|
+
import { basename as basename4, dirname as dirname4, extname as extname4, join as join21 } from "node:path";
|
|
48278
48428
|
|
|
48279
48429
|
// src/utils/exportAssets.ts
|
|
48280
48430
|
import {
|
|
@@ -48289,9 +48439,9 @@ import {
|
|
|
48289
48439
|
statSync as statSync3,
|
|
48290
48440
|
writeSync as writeSync3
|
|
48291
48441
|
} from "node:fs";
|
|
48292
|
-
import { homedir as
|
|
48293
|
-
import { basename as basename3, extname as extname3, join as
|
|
48294
|
-
var NOTES_CONTAINER =
|
|
48442
|
+
import { homedir as homedir16 } from "node:os";
|
|
48443
|
+
import { basename as basename3, extname as extname3, join as join19, relative as relative3, resolve as resolve3, sep as sep3 } from "node:path";
|
|
48444
|
+
var NOTES_CONTAINER = join19(homedir16(), "Library/Group Containers/group.com.apple.notes");
|
|
48295
48445
|
var MAX_EMBED_BYTES = 10 * 1024 * 1024;
|
|
48296
48446
|
var MAX_EMBED_TOTAL_BYTES = 256 * 1024 * 1024;
|
|
48297
48447
|
var MAX_DIRECTORY_ENTRIES = 2e4;
|
|
@@ -48318,10 +48468,10 @@ function listDirectory(dir, limit) {
|
|
|
48318
48468
|
return [];
|
|
48319
48469
|
}
|
|
48320
48470
|
}
|
|
48321
|
-
function isFile(
|
|
48471
|
+
function isFile(path9) {
|
|
48322
48472
|
let fd;
|
|
48323
48473
|
try {
|
|
48324
|
-
fd = openSync4(
|
|
48474
|
+
fd = openSync4(path9, constants4.O_RDONLY | constants4.O_NOFOLLOW);
|
|
48325
48475
|
return fstatSync4(fd).isFile();
|
|
48326
48476
|
} catch {
|
|
48327
48477
|
return false;
|
|
@@ -48329,9 +48479,9 @@ function isFile(path8) {
|
|
|
48329
48479
|
if (fd !== void 0) closeSync4(fd);
|
|
48330
48480
|
}
|
|
48331
48481
|
}
|
|
48332
|
-
function isDirectory2(
|
|
48482
|
+
function isDirectory2(path9) {
|
|
48333
48483
|
try {
|
|
48334
|
-
return statSync3(
|
|
48484
|
+
return statSync3(path9).isDirectory();
|
|
48335
48485
|
} catch {
|
|
48336
48486
|
return false;
|
|
48337
48487
|
}
|
|
@@ -48347,11 +48497,11 @@ var AssetLocator = class {
|
|
|
48347
48497
|
constructor(container = NOTES_CONTAINER) {
|
|
48348
48498
|
let accountsRoot = "";
|
|
48349
48499
|
try {
|
|
48350
|
-
accountsRoot = realpathSync3.native(
|
|
48500
|
+
accountsRoot = realpathSync3.native(join19(container, "Accounts"));
|
|
48351
48501
|
} catch {
|
|
48352
48502
|
}
|
|
48353
48503
|
this.accounts = accountsRoot ? listDirectory(accountsRoot, 1e3).flatMap((name) => {
|
|
48354
|
-
const dir = safeComponent2(name) && confine(
|
|
48504
|
+
const dir = safeComponent2(name) && confine(join19(accountsRoot, name), accountsRoot);
|
|
48355
48505
|
return dir && isDirectory2(dir) ? [dir] : [];
|
|
48356
48506
|
}) : [];
|
|
48357
48507
|
}
|
|
@@ -48411,18 +48561,18 @@ var AssetLocator = class {
|
|
|
48411
48561
|
if (!mediaId || !filename) return void 0;
|
|
48412
48562
|
const generation = safeComponent2(source.mediaGeneration);
|
|
48413
48563
|
const candidates = [
|
|
48414
|
-
...generation ? [
|
|
48415
|
-
|
|
48564
|
+
...generation ? [join19(account, "Media", mediaId, generation, filename)] : [],
|
|
48565
|
+
join19(account, "Media", mediaId, filename)
|
|
48416
48566
|
];
|
|
48417
48567
|
for (const candidate of candidates) {
|
|
48418
|
-
const
|
|
48419
|
-
if (
|
|
48568
|
+
const path9 = confine(candidate, account);
|
|
48569
|
+
if (path9 && isFile(path9)) return { path: path9, name: filename, role: "original" };
|
|
48420
48570
|
}
|
|
48421
48571
|
return void 0;
|
|
48422
48572
|
}
|
|
48423
48573
|
/** <dir>/<id>/<generation>/<name>, <dir>/<id>/<name>, then any generation. */
|
|
48424
48574
|
fallback(account, dir, id2, generation, names) {
|
|
48425
|
-
const base =
|
|
48575
|
+
const base = join19(account, dir, id2);
|
|
48426
48576
|
const gen = safeComponent2(generation);
|
|
48427
48577
|
const generations = [
|
|
48428
48578
|
...gen ? [gen] : [],
|
|
@@ -48431,8 +48581,8 @@ var AssetLocator = class {
|
|
|
48431
48581
|
];
|
|
48432
48582
|
for (const g of generations)
|
|
48433
48583
|
for (const name of names) {
|
|
48434
|
-
const
|
|
48435
|
-
if (
|
|
48584
|
+
const path9 = confine(g ? join19(base, g, name) : join19(base, name), account);
|
|
48585
|
+
if (path9 && isFile(path9)) return path9;
|
|
48436
48586
|
}
|
|
48437
48587
|
return void 0;
|
|
48438
48588
|
}
|
|
@@ -48441,7 +48591,7 @@ var AssetLocator = class {
|
|
|
48441
48591
|
let index = this.previewIndex.get(account);
|
|
48442
48592
|
if (!index) {
|
|
48443
48593
|
index = /* @__PURE__ */ new Map();
|
|
48444
|
-
for (const name of listDirectory(
|
|
48594
|
+
for (const name of listDirectory(join19(account, "Previews"), MAX_DIRECTORY_ENTRIES)) {
|
|
48445
48595
|
const key = name.slice(0, 36).toUpperCase();
|
|
48446
48596
|
const entries3 = index.get(key) ?? [];
|
|
48447
48597
|
entries3.push(name);
|
|
@@ -48454,7 +48604,7 @@ var AssetLocator = class {
|
|
|
48454
48604
|
return !ext || IMAGE_EXTENSIONS.has(ext) || /^\.\d+$/.test(ext);
|
|
48455
48605
|
}).sort((a, b) => previewArea(b) - previewArea(a) || a.localeCompare(b));
|
|
48456
48606
|
for (const name of entries2) {
|
|
48457
|
-
const entry = confine(
|
|
48607
|
+
const entry = confine(join19(account, "Previews", name), account);
|
|
48458
48608
|
if (!entry) continue;
|
|
48459
48609
|
const file = isFile(entry) ? entry : this.bundleImage(entry, account);
|
|
48460
48610
|
if (file) return { path: file, name: "preview", role: "preview" };
|
|
@@ -48465,16 +48615,16 @@ var AssetLocator = class {
|
|
|
48465
48615
|
bundleImage(bundle, account) {
|
|
48466
48616
|
const found = [];
|
|
48467
48617
|
for (const child of listDirectory(bundle, MAX_BUNDLE_ENTRIES2)) {
|
|
48468
|
-
const
|
|
48469
|
-
if (!
|
|
48470
|
-
if (isFile(
|
|
48618
|
+
const path9 = confine(join19(bundle, child), account);
|
|
48619
|
+
if (!path9) continue;
|
|
48620
|
+
if (isFile(path9)) found.push(path9);
|
|
48471
48621
|
else
|
|
48472
|
-
for (const grandchild of listDirectory(
|
|
48473
|
-
const inner = confine(
|
|
48622
|
+
for (const grandchild of listDirectory(path9, MAX_BUNDLE_ENTRIES2)) {
|
|
48623
|
+
const inner = confine(join19(path9, grandchild), account);
|
|
48474
48624
|
if (inner && isFile(inner)) found.push(inner);
|
|
48475
48625
|
}
|
|
48476
48626
|
}
|
|
48477
|
-
return found.find((
|
|
48627
|
+
return found.find((path9) => basename3(path9) === "Preview.png") ?? found.find((path9) => IMAGE_EXTENSIONS.has(extname3(path9).toLowerCase()));
|
|
48478
48628
|
}
|
|
48479
48629
|
};
|
|
48480
48630
|
function sniffMime(head, name) {
|
|
@@ -48522,8 +48672,8 @@ function safeAssetName(name, mime) {
|
|
|
48522
48672
|
if (clean.length > 120) clean = clean.slice(0, 120 - ext.length) + ext;
|
|
48523
48673
|
return clean;
|
|
48524
48674
|
}
|
|
48525
|
-
function encodePathUrl(
|
|
48526
|
-
return
|
|
48675
|
+
function encodePathUrl(path9) {
|
|
48676
|
+
return path9.split("/").map(encodeURIComponent).join("/");
|
|
48527
48677
|
}
|
|
48528
48678
|
function canonicalForm(abs) {
|
|
48529
48679
|
let current = abs;
|
|
@@ -48531,18 +48681,18 @@ function canonicalForm(abs) {
|
|
|
48531
48681
|
for (; ; ) {
|
|
48532
48682
|
try {
|
|
48533
48683
|
const real = realpathSync3.native(current);
|
|
48534
|
-
return rest ?
|
|
48684
|
+
return rest ? join19(real, rest) : real;
|
|
48535
48685
|
} catch {
|
|
48536
48686
|
const parent = resolve3(current, "..");
|
|
48537
48687
|
if (parent === current) return abs;
|
|
48538
|
-
rest = rest ?
|
|
48688
|
+
rest = rest ? join19(basename3(current), rest) : basename3(current);
|
|
48539
48689
|
current = parent;
|
|
48540
48690
|
}
|
|
48541
48691
|
}
|
|
48542
48692
|
}
|
|
48543
48693
|
function assertExportPath(p, container = NOTES_CONTAINER) {
|
|
48544
48694
|
const abs = assertSafeSavePath(p);
|
|
48545
|
-
const within = (
|
|
48695
|
+
const within = (path9, root) => path9 === root || path9.startsWith(root + sep3);
|
|
48546
48696
|
const roots = [resolve3(container), canonicalForm(resolve3(container))];
|
|
48547
48697
|
const forms = [abs, canonicalForm(abs)];
|
|
48548
48698
|
if (forms.some((form) => roots.some((root) => within(form, root))))
|
|
@@ -48552,16 +48702,16 @@ function assertExportPath(p, container = NOTES_CONTAINER) {
|
|
|
48552
48702
|
var CREATE_FLAGS = constants4.O_WRONLY | constants4.O_CREAT | constants4.O_EXCL | constants4.O_NOFOLLOW;
|
|
48553
48703
|
var OutputExistsError = class extends Error {
|
|
48554
48704
|
code = "output_exists";
|
|
48555
|
-
constructor(
|
|
48556
|
-
super(`Output file already exists: ${
|
|
48705
|
+
constructor(path9) {
|
|
48706
|
+
super(`Output file already exists: ${path9}. Choose a new path or remove the old file first.`);
|
|
48557
48707
|
this.name = "OutputExistsError";
|
|
48558
48708
|
}
|
|
48559
48709
|
};
|
|
48560
|
-
function openCreateOnly(
|
|
48710
|
+
function openCreateOnly(path9) {
|
|
48561
48711
|
try {
|
|
48562
|
-
return openSync4(
|
|
48712
|
+
return openSync4(path9, CREATE_FLAGS, 420);
|
|
48563
48713
|
} catch (error2) {
|
|
48564
|
-
if (error2.code === "EEXIST") throw new OutputExistsError(
|
|
48714
|
+
if (error2.code === "EEXIST") throw new OutputExistsError(path9);
|
|
48565
48715
|
throw error2;
|
|
48566
48716
|
}
|
|
48567
48717
|
}
|
|
@@ -48575,8 +48725,8 @@ function writeAllAndClose(fd, content) {
|
|
|
48575
48725
|
closeSync4(fd);
|
|
48576
48726
|
}
|
|
48577
48727
|
}
|
|
48578
|
-
function openSource(
|
|
48579
|
-
const fd = openSync4(
|
|
48728
|
+
function openSource(path9) {
|
|
48729
|
+
const fd = openSync4(path9, constants4.O_RDONLY | constants4.O_NOFOLLOW);
|
|
48580
48730
|
const stat = fstatSync4(fd);
|
|
48581
48731
|
if (!stat.isFile()) {
|
|
48582
48732
|
closeSync4(fd);
|
|
@@ -48621,7 +48771,7 @@ var SidecarWriter = class {
|
|
|
48621
48771
|
let target;
|
|
48622
48772
|
let out;
|
|
48623
48773
|
for (let n = 1; n <= 1e3 && out === void 0; n++) {
|
|
48624
|
-
target =
|
|
48774
|
+
target = join19(this.dir, n === 1 ? name : `${stem}-${n}${ext}`);
|
|
48625
48775
|
try {
|
|
48626
48776
|
out = openSync4(target, CREATE_FLAGS, 420);
|
|
48627
48777
|
} catch (error2) {
|
|
@@ -49376,11 +49526,11 @@ function renderNotesMarkdown(notes, ctx, { wrap: wrap2 = 0 } = {}) {
|
|
|
49376
49526
|
}
|
|
49377
49527
|
|
|
49378
49528
|
// src/utils/noteExportData.ts
|
|
49379
|
-
import { execFileSync as
|
|
49380
|
-
import { homedir as
|
|
49381
|
-
import { join as
|
|
49382
|
-
var
|
|
49383
|
-
|
|
49529
|
+
import { execFileSync as execFileSync18 } from "node:child_process";
|
|
49530
|
+
import { homedir as homedir17 } from "node:os";
|
|
49531
|
+
import { join as join20 } from "node:path";
|
|
49532
|
+
var NOTES_DB_PATH11 = join20(
|
|
49533
|
+
homedir17(),
|
|
49384
49534
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
49385
49535
|
);
|
|
49386
49536
|
var IMAGE_UTIS2 = /* @__PURE__ */ new Set([
|
|
@@ -49444,7 +49594,7 @@ var ATTACHMENT_COLUMNS = [
|
|
|
49444
49594
|
var columnCache = /* @__PURE__ */ new Map();
|
|
49445
49595
|
function sqlite(dbPath2, args) {
|
|
49446
49596
|
try {
|
|
49447
|
-
return
|
|
49597
|
+
return execFileSync18(
|
|
49448
49598
|
"/usr/bin/sqlite3",
|
|
49449
49599
|
["-readonly", ...args.slice(0, -1), dbPath2, args.at(-1)],
|
|
49450
49600
|
{
|
|
@@ -49461,7 +49611,7 @@ function sqlite(dbPath2, args) {
|
|
|
49461
49611
|
throw new NoteBlocksError("query-failed", "Failed to query the Notes database");
|
|
49462
49612
|
}
|
|
49463
49613
|
}
|
|
49464
|
-
function objectColumns(dbPath2 =
|
|
49614
|
+
function objectColumns(dbPath2 = NOTES_DB_PATH11) {
|
|
49465
49615
|
let columns = columnCache.get(dbPath2);
|
|
49466
49616
|
if (!columns) {
|
|
49467
49617
|
columns = new Set(
|
|
@@ -49519,7 +49669,7 @@ function buildAttachments(rows) {
|
|
|
49519
49669
|
}
|
|
49520
49670
|
return { byId: new Map(all.map((a) => [a.id, a])), ordered };
|
|
49521
49671
|
}
|
|
49522
|
-
function readExportNote(id2, { dbPath: dbPath2 =
|
|
49672
|
+
function readExportNote(id2, { dbPath: dbPath2 = NOTES_DB_PATH11 } = {}) {
|
|
49523
49673
|
const doc = readNoteBlocks(id2, { dbPath: dbPath2 });
|
|
49524
49674
|
const pk = /\/ICNote\/p([0-9]{1,18})$/.exec(id2)[1];
|
|
49525
49675
|
const output = sqlite(dbPath2, [
|
|
@@ -49558,9 +49708,9 @@ var NotesExportError = class extends Error {
|
|
|
49558
49708
|
}
|
|
49559
49709
|
code;
|
|
49560
49710
|
};
|
|
49561
|
-
function validPath(
|
|
49711
|
+
function validPath(path9, what) {
|
|
49562
49712
|
try {
|
|
49563
|
-
return assertExportPath(
|
|
49713
|
+
return assertExportPath(path9);
|
|
49564
49714
|
} catch (error2) {
|
|
49565
49715
|
throw new NotesExportError(
|
|
49566
49716
|
"invalid-path",
|
|
@@ -49652,7 +49802,7 @@ function exportNotesMarkdown(request, deps) {
|
|
|
49652
49802
|
return { ...receipt, markdown };
|
|
49653
49803
|
}
|
|
49654
49804
|
function defaultSidecarDir(output) {
|
|
49655
|
-
return
|
|
49805
|
+
return join21(dirname4(output), `${basename4(output, extname4(output))}.assets`);
|
|
49656
49806
|
}
|
|
49657
49807
|
function exportNotesHtml(request, deps) {
|
|
49658
49808
|
if (!request.outputPath)
|
|
@@ -49706,7 +49856,7 @@ import {
|
|
|
49706
49856
|
writeFileSync as writeFileSync3
|
|
49707
49857
|
} from "node:fs";
|
|
49708
49858
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
49709
|
-
import { basename as basename5, extname as extname5, isAbsolute as isAbsolute3, join as
|
|
49859
|
+
import { basename as basename5, extname as extname5, isAbsolute as isAbsolute3, join as join22 } from "node:path";
|
|
49710
49860
|
var noteId = exactIdInput(
|
|
49711
49861
|
"ICNote",
|
|
49712
49862
|
/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
|
|
@@ -49749,9 +49899,9 @@ function assertExistingContentPreserved(before, after) {
|
|
|
49749
49899
|
throw new Error("Existing native object content or presentation changed");
|
|
49750
49900
|
}
|
|
49751
49901
|
}
|
|
49752
|
-
function localAttachment(
|
|
49753
|
-
if (!isAbsolute3(
|
|
49754
|
-
const descriptor = openSync5(
|
|
49902
|
+
function localAttachment(path9) {
|
|
49903
|
+
if (!isAbsolute3(path9)) throw new Error("An absolute local file path is required");
|
|
49904
|
+
const descriptor = openSync5(path9, constants5.O_RDONLY | constants5.O_NOFOLLOW);
|
|
49755
49905
|
try {
|
|
49756
49906
|
const stat = fstatSync5(descriptor);
|
|
49757
49907
|
if (!stat.isFile() || stat.size === 0 || stat.size > 64 * 1024 * 1024)
|
|
@@ -49873,8 +50023,8 @@ function registerDirectOperations(server2, manager) {
|
|
|
49873
50023
|
}
|
|
49874
50024
|
);
|
|
49875
50025
|
}
|
|
49876
|
-
function attachmentName(
|
|
49877
|
-
const source = basename5(
|
|
50026
|
+
function attachmentName(path9, filename) {
|
|
50027
|
+
const source = basename5(path9);
|
|
49878
50028
|
if (filename === void 0) return source;
|
|
49879
50029
|
if (Buffer.byteLength(filename, "utf8") > 255)
|
|
49880
50030
|
throw new Error("filename must be at most 255 bytes");
|
|
@@ -49889,14 +50039,14 @@ function attachmentName(path8, filename) {
|
|
|
49889
50039
|
return filename;
|
|
49890
50040
|
}
|
|
49891
50041
|
function attachFile(manager, args) {
|
|
49892
|
-
const { id: id2, expectedContentHash, path:
|
|
49893
|
-
const name = attachmentName(
|
|
50042
|
+
const { id: id2, expectedContentHash, path: path9 } = args;
|
|
50043
|
+
const name = attachmentName(path9, args.filename);
|
|
49894
50044
|
const before = readSnapshot(manager, id2);
|
|
49895
50045
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
49896
|
-
const bytes = localAttachment(
|
|
50046
|
+
const bytes = localAttachment(path9);
|
|
49897
50047
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
49898
|
-
const directory = mkdtempSync4(
|
|
49899
|
-
const temporaryFile =
|
|
50048
|
+
const directory = mkdtempSync4(join22(tmpdir4(), "notes-attachment-add-"));
|
|
50049
|
+
const temporaryFile = join22(directory, name);
|
|
49900
50050
|
try {
|
|
49901
50051
|
writeFileSync3(temporaryFile, bytes, { mode: 384 });
|
|
49902
50052
|
if (readSnapshot(manager, id2).hash !== before.hash) throw new Error("Note revision changed");
|
|
@@ -50454,7 +50604,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
50454
50604
|
|
|
50455
50605
|
// src/setupShortcuts.ts
|
|
50456
50606
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
50457
|
-
import { existsSync as
|
|
50607
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
50458
50608
|
import { release as release2 } from "node:os";
|
|
50459
50609
|
import { dirname as dirname5, resolve as resolve4 } from "node:path";
|
|
50460
50610
|
import { fileURLToPath } from "node:url";
|
|
@@ -50474,16 +50624,16 @@ var shortcutFiles = [
|
|
|
50474
50624
|
];
|
|
50475
50625
|
function setupShortcuts(checkOnly, dependencies = {}) {
|
|
50476
50626
|
const status = dependencies.status || nativeTagsStatus;
|
|
50477
|
-
const exists = dependencies.exists ||
|
|
50478
|
-
const open = dependencies.open || ((
|
|
50479
|
-
const result = spawnSync2("/usr/bin/open", [
|
|
50627
|
+
const exists = dependencies.exists || existsSync13;
|
|
50628
|
+
const open = dependencies.open || ((path9) => {
|
|
50629
|
+
const result = spawnSync2("/usr/bin/open", [path9], { encoding: "utf8" });
|
|
50480
50630
|
return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
|
|
50481
50631
|
});
|
|
50482
50632
|
const baseDirectory = dependencies.baseDirectory || resolve4(dirname5(fileURLToPath(import.meta.url)), "../shortcuts");
|
|
50483
50633
|
const osRelease = (dependencies.osRelease || release2)();
|
|
50484
50634
|
const darwinMajor = Number.parseInt(osRelease.split(".")[0], 10);
|
|
50485
50635
|
const items = shortcutFiles.map(({ name, file, optional: optional2 }) => {
|
|
50486
|
-
const
|
|
50636
|
+
const path9 = resolve4(baseDirectory, file);
|
|
50487
50637
|
let installed = false;
|
|
50488
50638
|
let identifier;
|
|
50489
50639
|
let error2;
|
|
@@ -50499,9 +50649,9 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
50499
50649
|
if (!installed && !checkOnly) {
|
|
50500
50650
|
if (optional2 && !(darwinMajor >= MARKDOWN_MIN_DARWIN_MAJOR))
|
|
50501
50651
|
skipped = `requires macOS 26 or later (this Mac reports Darwin ${osRelease})`;
|
|
50502
|
-
else if (!exists(
|
|
50652
|
+
else if (!exists(path9)) error2 = `Packaged Shortcut is missing: ${path9}`;
|
|
50503
50653
|
else {
|
|
50504
|
-
const result = open(
|
|
50654
|
+
const result = open(path9);
|
|
50505
50655
|
opened = result.ok;
|
|
50506
50656
|
if (!result.ok) error2 = result.error || `Could not open ${file}`;
|
|
50507
50657
|
}
|
|
@@ -50510,7 +50660,7 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
50510
50660
|
name,
|
|
50511
50661
|
installed,
|
|
50512
50662
|
identifier,
|
|
50513
|
-
file:
|
|
50663
|
+
file: path9,
|
|
50514
50664
|
opened,
|
|
50515
50665
|
...optional2 ? { optional: optional2 } : {},
|
|
50516
50666
|
...skipped ? { skipped } : {},
|
|
@@ -50640,6 +50790,9 @@ function readExactNoteSnapshot(id2) {
|
|
|
50640
50790
|
return { note, body, rich, contentHash: richContentHash(body, rich) };
|
|
50641
50791
|
}
|
|
50642
50792
|
var NOT_DELETED_MESSAGE = "Notes.app accepted the delete, but the note is still in its original folder, so it was not moved to Recently Deleted. Nothing was deleted; read the note again before retrying.";
|
|
50793
|
+
function inRecentlyDeletedMessage(title) {
|
|
50794
|
+
return `Note "${title}" is already in Recently Deleted, where deleting it would remove it permanently. Nothing was deleted. Remove it from Recently Deleted in Notes.app if that is intended.`;
|
|
50795
|
+
}
|
|
50643
50796
|
function revisionConflictMessage(title) {
|
|
50644
50797
|
return `Note "${title}" changed after it was read. Read it again and review the newer version before retrying.`;
|
|
50645
50798
|
}
|
|
@@ -50818,7 +50971,7 @@ registerTool(
|
|
|
50818
50971
|
count: external_exports.number().optional()
|
|
50819
50972
|
}
|
|
50820
50973
|
},
|
|
50821
|
-
withErrorHandling(({ query, searchContent = false, account, folder, modifiedSince, limit }) => {
|
|
50974
|
+
withErrorHandling(({ query: query2, searchContent = false, account, folder, modifiedSince, limit }) => {
|
|
50822
50975
|
const effectiveLimit = resolveSearchLimit(limit);
|
|
50823
50976
|
const limitWasDefault = limit === void 0;
|
|
50824
50977
|
const {
|
|
@@ -50827,7 +50980,7 @@ registerTool(
|
|
|
50827
50980
|
syncInterference
|
|
50828
50981
|
} = withSyncAwarenessSync(
|
|
50829
50982
|
"search-notes",
|
|
50830
|
-
() => notesManager.searchNotes(
|
|
50983
|
+
() => notesManager.searchNotes(query2, searchContent, account, folder, modifiedSince, effectiveLimit)
|
|
50831
50984
|
);
|
|
50832
50985
|
const searchType = searchContent ? "content" : "titles";
|
|
50833
50986
|
const folderInfo = folder ? ` in folder "${folder}"` : "";
|
|
@@ -50850,7 +51003,7 @@ ${syncWarnings.join(" ")}` : "";
|
|
|
50850
51003
|
if (notes.length === 0) {
|
|
50851
51004
|
const scopeHint = describeSearchScope(searchContent, notes.length);
|
|
50852
51005
|
return successResponse(
|
|
50853
|
-
`No notes found matching "${
|
|
51006
|
+
`No notes found matching "${query2}" in ${searchType}${folderInfo}${dateInfo}${scopeHint}${syncNote}`,
|
|
50854
51007
|
{ notes: [], count: 0 }
|
|
50855
51008
|
);
|
|
50856
51009
|
}
|
|
@@ -50902,10 +51055,10 @@ registerTool(
|
|
|
50902
51055
|
},
|
|
50903
51056
|
annotations: { readOnlyHint: true }
|
|
50904
51057
|
},
|
|
50905
|
-
withErrorHandling(({ query, limit, scanLimit, includeDeleted }) => {
|
|
51058
|
+
withErrorHandling(({ query: query2, limit, scanLimit, includeDeleted }) => {
|
|
50906
51059
|
let result;
|
|
50907
51060
|
try {
|
|
50908
|
-
result = queryNotes(
|
|
51061
|
+
result = queryNotes(query2, { limit, scanLimit, includeDeleted });
|
|
50909
51062
|
} catch (error2) {
|
|
50910
51063
|
if (error2 instanceof NoteQueryError || error2 instanceof NoteQueryStoreError) {
|
|
50911
51064
|
return errorResponse(
|
|
@@ -51816,7 +51969,7 @@ registerTool(
|
|
|
51816
51969
|
registerTool(
|
|
51817
51970
|
"delete-note",
|
|
51818
51971
|
{
|
|
51819
|
-
description: "Use when: moving one exact note to Recently Deleted after reading and reviewing it.\nReturns: confirmation with the exact id.\nDo not use when: you only have a title or the note changed since review.\nSafety: requires id and expectedContentHash from get-note-content. The body comparison and delete happen in one AppleScript, so a newer edit is preserved.",
|
|
51972
|
+
description: "Use when: moving one exact note to Recently Deleted after reading and reviewing it.\nReturns: confirmation with the exact id.\nDo not use when: you only have a title or the note changed since review.\nSafety: requires id and expectedContentHash from get-note-content. The body comparison and delete happen in one AppleScript, so a newer edit is preserved. Refuses a note already in Recently Deleted, where a delete would be permanent.",
|
|
51820
51973
|
inputSchema: {
|
|
51821
51974
|
id: noteIdInput,
|
|
51822
51975
|
expectedContentHash: expectedContentHashInput,
|
|
@@ -51840,6 +51993,9 @@ registerTool(
|
|
|
51840
51993
|
if (result.status === "conflict") {
|
|
51841
51994
|
return errorResponse(revisionConflictMessage(snapshot.note.title));
|
|
51842
51995
|
}
|
|
51996
|
+
if (result.status === "in-recently-deleted") {
|
|
51997
|
+
return errorResponse(inRecentlyDeletedMessage(snapshot.note.title));
|
|
51998
|
+
}
|
|
51843
51999
|
if (result.status === "not-deleted") {
|
|
51844
52000
|
return errorResponse(NOT_DELETED_MESSAGE);
|
|
51845
52001
|
}
|
|
@@ -51908,36 +52064,47 @@ registerTool(
|
|
|
51908
52064
|
registerTool(
|
|
51909
52065
|
"list-notes",
|
|
51910
52066
|
{
|
|
51911
|
-
description: "Use when: enumerating notes in an account or folder; supports modifiedSince and limit for large collections.\nReturns: each note's title and id (ids are safe to use for follow-up reads/edits even when titles are duplicated \u2014 see search-notes for keyword-based lookup instead).\nDo not use when: you need content (get-note-content).\nNote:
|
|
52067
|
+
description: "Use when: enumerating notes in an account or folder; supports modifiedSince and limit for large collections.\nReturns: each note's title and id (ids are safe to use for follow-up reads/edits even when titles are duplicated \u2014 see search-notes for keyword-based lookup instead).\nDo not use when: you need content (get-note-content).\nNote: excludes notes in Recently Deleted unless includeRecentlyDeleted is true, which flags them inRecentlyDeleted. Warns if iCloud sync is active and results may be partial.",
|
|
51912
52068
|
inputSchema: {
|
|
51913
52069
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account to list notes from"),
|
|
51914
52070
|
folder: external_exports.string().max(MAX.FOLDER).optional().describe("Filter to specific folder"),
|
|
51915
52071
|
modifiedSince: external_exports.string().max(64).optional().describe(
|
|
51916
52072
|
"ISO 8601 date string to filter notes modified on or after this date (e.g., '2025-01-01'). Useful for listing only recent notes in large collections."
|
|
51917
52073
|
),
|
|
51918
|
-
limit: external_exports.number().int().positive().optional().describe("Maximum number of notes to return")
|
|
52074
|
+
limit: external_exports.number().int().positive().optional().describe("Maximum number of notes to return"),
|
|
52075
|
+
includeRecentlyDeleted: external_exports.boolean().optional().describe(
|
|
52076
|
+
"Also list notes in Recently Deleted, each flagged inRecentlyDeleted (default false)"
|
|
52077
|
+
)
|
|
51919
52078
|
},
|
|
51920
52079
|
outputSchema: {
|
|
51921
52080
|
notes: external_exports.array(
|
|
51922
52081
|
external_exports.object({
|
|
51923
52082
|
title: external_exports.string(),
|
|
51924
52083
|
id: external_exports.string(),
|
|
52084
|
+
inRecentlyDeleted: external_exports.boolean().optional(),
|
|
51925
52085
|
identifier: external_exports.string().optional(),
|
|
51926
52086
|
folderIdentifier: external_exports.string().optional(),
|
|
51927
52087
|
accountIdentifier: external_exports.string().optional()
|
|
51928
52088
|
})
|
|
51929
52089
|
).optional(),
|
|
51930
|
-
count: external_exports.number().optional()
|
|
52090
|
+
count: external_exports.number().optional(),
|
|
52091
|
+
excludedRecentlyDeleted: external_exports.number().optional()
|
|
51931
52092
|
}
|
|
51932
52093
|
},
|
|
51933
|
-
withErrorHandling(({ account, folder, modifiedSince, limit }) => {
|
|
52094
|
+
withErrorHandling(({ account, folder, modifiedSince, limit, includeRecentlyDeleted }) => {
|
|
51934
52095
|
const {
|
|
51935
|
-
result: notes,
|
|
52096
|
+
result: { refs: notes, excludedRecentlyDeleted },
|
|
51936
52097
|
syncBefore,
|
|
51937
52098
|
syncInterference
|
|
51938
52099
|
} = withSyncAwarenessSync(
|
|
51939
52100
|
"list-notes",
|
|
51940
|
-
() => notesManager.
|
|
52101
|
+
() => notesManager.listNoteRefsDetailed(
|
|
52102
|
+
account,
|
|
52103
|
+
folder,
|
|
52104
|
+
modifiedSince,
|
|
52105
|
+
limit,
|
|
52106
|
+
includeRecentlyDeleted ?? false
|
|
52107
|
+
)
|
|
51941
52108
|
);
|
|
51942
52109
|
const location = folder ? ` in folder "${folder}"` : "";
|
|
51943
52110
|
const acct = account ? ` (${account})` : "";
|
|
@@ -51953,17 +52120,22 @@ registerTool(
|
|
|
51953
52120
|
const syncNote = syncWarnings.length > 0 ? `
|
|
51954
52121
|
|
|
51955
52122
|
${syncWarnings.join(" ")}` : "";
|
|
52123
|
+
const trashNote = excludedRecentlyDeleted > 0 ? `
|
|
52124
|
+
|
|
52125
|
+
Skipped ${excludedRecentlyDeleted} note(s) in Recently Deleted; pass includeRecentlyDeleted: true to list them.` : "";
|
|
52126
|
+
const trashData = excludedRecentlyDeleted > 0 ? { excludedRecentlyDeleted } : {};
|
|
51956
52127
|
if (notes.length === 0) {
|
|
51957
|
-
return successResponse(`No notes found${location}${acct}${dateInfo}${syncNote}`, {
|
|
52128
|
+
return successResponse(`No notes found${location}${acct}${dateInfo}${trashNote}${syncNote}`, {
|
|
51958
52129
|
notes: [],
|
|
51959
|
-
count: 0
|
|
52130
|
+
count: 0,
|
|
52131
|
+
...trashData
|
|
51960
52132
|
});
|
|
51961
52133
|
}
|
|
51962
|
-
const noteList = notes.map((n) => ` - ${n.title} [id: ${n.id}]`).join("\n");
|
|
52134
|
+
const noteList = notes.map((n) => ` - ${n.title}${n.inRecentlyDeleted ? " [RECENTLY DELETED]" : ""} [id: ${n.id}]`).join("\n");
|
|
51963
52135
|
return successResponse(
|
|
51964
52136
|
`Found ${notes.length} notes${location}${acct}${dateInfo}${limitInfo}:
|
|
51965
|
-
${noteList}${syncNote}`,
|
|
51966
|
-
{ notes: withStableIdentifiers(notes, "ICNote"), count: notes.length }
|
|
52137
|
+
${noteList}${trashNote}${syncNote}`,
|
|
52138
|
+
{ notes: withStableIdentifiers(notes, "ICNote"), count: notes.length, ...trashData }
|
|
51967
52139
|
);
|
|
51968
52140
|
}, "Error listing notes")
|
|
51969
52141
|
);
|
|
@@ -52469,7 +52641,7 @@ ${attachmentList}`,
|
|
|
52469
52641
|
registerTool(
|
|
52470
52642
|
"batch-delete-notes",
|
|
52471
52643
|
{
|
|
52472
|
-
description: "Use when: moving several reviewed notes to Recently Deleted.\nReturns: per-note success or conflict.\nDo not use when: deleting a single note.\nSafety: every entry requires an exact id and the content hash from get-note-content. Any note changed since review is preserved and reported as a conflict.",
|
|
52644
|
+
description: "Use when: moving several reviewed notes to Recently Deleted.\nReturns: per-note success or conflict.\nDo not use when: deleting a single note.\nSafety: every entry requires an exact id and the content hash from get-note-content. Any note changed since review is preserved and reported as a conflict. A note already in Recently Deleted is refused, since deleting it there would be permanent.",
|
|
52473
52645
|
inputSchema: {
|
|
52474
52646
|
notes: external_exports.array(
|
|
52475
52647
|
external_exports.object({
|
|
@@ -52500,6 +52672,9 @@ registerTool(
|
|
|
52500
52672
|
if (result.status === "conflict") {
|
|
52501
52673
|
return { id: id2, success: false, error: revisionConflictMessage(snapshot.note.title) };
|
|
52502
52674
|
}
|
|
52675
|
+
if (result.status === "in-recently-deleted") {
|
|
52676
|
+
return { id: id2, success: false, error: inRecentlyDeletedMessage(snapshot.note.title) };
|
|
52677
|
+
}
|
|
52503
52678
|
if (result.status === "not-deleted")
|
|
52504
52679
|
return { id: id2, success: false, error: NOT_DELETED_MESSAGE };
|
|
52505
52680
|
return { id: id2, success: false, error: "Delete result uncertain; inspect this exact ID" };
|