apple-notes-mcp 2.8.28 → 2.8.30
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 +57 -2
- package/build/index.js +758 -202
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -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 join16(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 = join16(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 join16(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);
|
|
@@ -24777,8 +24777,8 @@ var ZodError = class _ZodError extends Error {
|
|
|
24777
24777
|
constructor(issues) {
|
|
24778
24778
|
super();
|
|
24779
24779
|
this.issues = [];
|
|
24780
|
-
this.addIssue = (
|
|
24781
|
-
this.issues = [...this.issues,
|
|
24780
|
+
this.addIssue = (sub4) => {
|
|
24781
|
+
this.issues = [...this.issues, sub4];
|
|
24782
24782
|
};
|
|
24783
24783
|
this.addIssues = (subs = []) => {
|
|
24784
24784
|
this.issues = [...this.issues, ...subs];
|
|
@@ -24845,13 +24845,13 @@ var ZodError = class _ZodError extends Error {
|
|
|
24845
24845
|
flatten(mapper = (issue2) => issue2.message) {
|
|
24846
24846
|
const fieldErrors = {};
|
|
24847
24847
|
const formErrors = [];
|
|
24848
|
-
for (const
|
|
24849
|
-
if (
|
|
24850
|
-
const firstEl =
|
|
24848
|
+
for (const sub4 of this.issues) {
|
|
24849
|
+
if (sub4.path.length > 0) {
|
|
24850
|
+
const firstEl = sub4.path[0];
|
|
24851
24851
|
fieldErrors[firstEl] = fieldErrors[firstEl] || [];
|
|
24852
|
-
fieldErrors[firstEl].push(mapper(
|
|
24852
|
+
fieldErrors[firstEl].push(mapper(sub4));
|
|
24853
24853
|
} else {
|
|
24854
|
-
formErrors.push(mapper(
|
|
24854
|
+
formErrors.push(mapper(sub4));
|
|
24855
24855
|
}
|
|
24856
24856
|
}
|
|
24857
24857
|
return { formErrors, fieldErrors };
|
|
@@ -29150,12 +29150,12 @@ var $ZodRealError = $constructor("$ZodError", initializer, { Parent: Error });
|
|
|
29150
29150
|
function flattenError(error2, mapper = (issue2) => issue2.message) {
|
|
29151
29151
|
const fieldErrors = {};
|
|
29152
29152
|
const formErrors = [];
|
|
29153
|
-
for (const
|
|
29154
|
-
if (
|
|
29155
|
-
fieldErrors[
|
|
29156
|
-
fieldErrors[
|
|
29153
|
+
for (const sub4 of error2.issues) {
|
|
29154
|
+
if (sub4.path.length > 0) {
|
|
29155
|
+
fieldErrors[sub4.path[0]] = fieldErrors[sub4.path[0]] || [];
|
|
29156
|
+
fieldErrors[sub4.path[0]].push(mapper(sub4));
|
|
29157
29157
|
} else {
|
|
29158
|
-
formErrors.push(mapper(
|
|
29158
|
+
formErrors.push(mapper(sub4));
|
|
29159
29159
|
}
|
|
29160
29160
|
}
|
|
29161
29161
|
return { formErrors, fieldErrors };
|
|
@@ -39275,7 +39275,7 @@ function decodeVarint(buf, offset) {
|
|
|
39275
39275
|
}
|
|
39276
39276
|
throw new Error(`Unexpected end of buffer reading varint at offset ${offset}`);
|
|
39277
39277
|
}
|
|
39278
|
-
function decodeMessage(buf) {
|
|
39278
|
+
function decodeMessage(buf, options = {}) {
|
|
39279
39279
|
const fields = [];
|
|
39280
39280
|
let offset = 0;
|
|
39281
39281
|
while (offset < buf.length) {
|
|
@@ -39296,10 +39296,13 @@ function decodeMessage(buf) {
|
|
|
39296
39296
|
const value = buf.slice(offset, offset + length);
|
|
39297
39297
|
fields.push({ fieldNumber, wireType, value });
|
|
39298
39298
|
offset += length;
|
|
39299
|
-
} else if (wireType === 5) {
|
|
39300
|
-
|
|
39301
|
-
|
|
39302
|
-
|
|
39299
|
+
} else if (wireType === 5 || wireType === 1) {
|
|
39300
|
+
const width = wireType === 5 ? 4 : 8;
|
|
39301
|
+
if (offset + width > buf.length) break;
|
|
39302
|
+
if (options.keepFixed) {
|
|
39303
|
+
fields.push({ fieldNumber, wireType, value: buf.slice(offset, offset + width) });
|
|
39304
|
+
}
|
|
39305
|
+
offset += width;
|
|
39303
39306
|
} else {
|
|
39304
39307
|
break;
|
|
39305
39308
|
}
|
|
@@ -39330,6 +39333,11 @@ function embeddedMessage(field) {
|
|
|
39330
39333
|
if (!bytes) return void 0;
|
|
39331
39334
|
return decodeMessage(bytes);
|
|
39332
39335
|
}
|
|
39336
|
+
function fixed64Double(field) {
|
|
39337
|
+
if (!field || field.wireType !== 1 || !(field.value instanceof Uint8Array)) return void 0;
|
|
39338
|
+
if (field.value.length !== 8) return void 0;
|
|
39339
|
+
return new DataView(field.value.buffer, field.value.byteOffset, 8).getFloat64(0, true);
|
|
39340
|
+
}
|
|
39333
39341
|
var ProtobufDecodeError = class extends Error {
|
|
39334
39342
|
constructor(message) {
|
|
39335
39343
|
super(message);
|
|
@@ -39901,12 +39909,555 @@ function assertLinkedWrite(rich, content, format, allowLinkChanges = false) {
|
|
|
39901
39909
|
}
|
|
39902
39910
|
}
|
|
39903
39911
|
|
|
39912
|
+
// src/utils/audioTranscripts.ts
|
|
39913
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
39914
|
+
import { homedir as homedir3 } from "node:os";
|
|
39915
|
+
import { join as join3 } from "node:path";
|
|
39916
|
+
import { gunzipSync as gunzipSync3 } from "node:zlib";
|
|
39917
|
+
var NOTES_DB_PATH2 = join3(
|
|
39918
|
+
homedir3(),
|
|
39919
|
+
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
39920
|
+
);
|
|
39921
|
+
var DEFAULT_MAX_SEGMENTS = 2e3;
|
|
39922
|
+
var MAX_SEGMENTS_LIMIT = 2e4;
|
|
39923
|
+
var AudioTranscriptError = class extends Error {
|
|
39924
|
+
constructor(kind, message) {
|
|
39925
|
+
super(message);
|
|
39926
|
+
this.kind = kind;
|
|
39927
|
+
this.name = "AudioTranscriptError";
|
|
39928
|
+
}
|
|
39929
|
+
kind;
|
|
39930
|
+
};
|
|
39931
|
+
var decode = (bytes) => decodeMessage(bytes, { keepFixed: true });
|
|
39932
|
+
var sub = (f, n) => {
|
|
39933
|
+
const bytes = getField(f, n)?.value;
|
|
39934
|
+
return bytes instanceof Uint8Array ? decode(bytes) : void 0;
|
|
39935
|
+
};
|
|
39936
|
+
var need = (value, what) => {
|
|
39937
|
+
if (value === void 0) throw new Error(`Missing ${what}`);
|
|
39938
|
+
return value;
|
|
39939
|
+
};
|
|
39940
|
+
var many = (f, n) => getFields(f, n).map((field) => {
|
|
39941
|
+
if (!(field.value instanceof Uint8Array)) throw new Error("Invalid nested entry");
|
|
39942
|
+
return decode(field.value);
|
|
39943
|
+
});
|
|
39944
|
+
function parseAudioRecording(data) {
|
|
39945
|
+
const root = decode(data);
|
|
39946
|
+
const entries = many(root, 3);
|
|
39947
|
+
if (entries.length === 0) throw new Error("No mergeable objects");
|
|
39948
|
+
if (entries.length > 2e6) throw new Error("Recording too large");
|
|
39949
|
+
const keys = getFields(root, 4).map((f) => stringValue(f) ?? "");
|
|
39950
|
+
const types = getFields(root, 5).map((f) => stringValue(f) ?? "");
|
|
39951
|
+
const uuids = getFields(root, 6).map(
|
|
39952
|
+
(f) => f.value instanceof Uint8Array ? Buffer.from(f.value).toString("hex") : ""
|
|
39953
|
+
);
|
|
39954
|
+
const uuidSlot = /* @__PURE__ */ new Map();
|
|
39955
|
+
uuids.forEach((uuid2, i) => {
|
|
39956
|
+
if (uuid2 && !uuidSlot.has(uuid2)) uuidSlot.set(uuid2, i);
|
|
39957
|
+
});
|
|
39958
|
+
const entry = (index) => {
|
|
39959
|
+
const value = entries[index];
|
|
39960
|
+
if (!value) throw new Error("Invalid object reference");
|
|
39961
|
+
return value;
|
|
39962
|
+
};
|
|
39963
|
+
const customMap = (e) => {
|
|
39964
|
+
const map = sub(e, 13);
|
|
39965
|
+
if (!map) return void 0;
|
|
39966
|
+
const values = /* @__PURE__ */ new Map();
|
|
39967
|
+
for (const item of many(map, 3)) {
|
|
39968
|
+
const key = keys[need(varintValue(getField(item, 1)), "map key")];
|
|
39969
|
+
const value = sub(item, 2);
|
|
39970
|
+
if (key !== void 0 && value) values.set(key, value);
|
|
39971
|
+
}
|
|
39972
|
+
return { type: types[need(varintValue(getField(map, 1)), "map type")] ?? "", values };
|
|
39973
|
+
};
|
|
39974
|
+
const objectIndex = (id2) => id2 ? varintValue(getField(id2, 6)) : void 0;
|
|
39975
|
+
const registerTarget = (id2) => {
|
|
39976
|
+
const index = objectIndex(id2);
|
|
39977
|
+
if (index === void 0) return void 0;
|
|
39978
|
+
const register = sub(entry(index), 1);
|
|
39979
|
+
if (!register) return void 0;
|
|
39980
|
+
const target = objectIndex(sub(register, 2));
|
|
39981
|
+
return target === void 0 ? void 0 : entry(target);
|
|
39982
|
+
};
|
|
39983
|
+
const registerPrimitive = (id2) => {
|
|
39984
|
+
const target = registerTarget(id2);
|
|
39985
|
+
const map = target && customMap(target);
|
|
39986
|
+
if (!map) return void 0;
|
|
39987
|
+
const self = map.values.get("self");
|
|
39988
|
+
if (self) return stringValue(getField(self, 4));
|
|
39989
|
+
const double = map.values.get("doubleValue");
|
|
39990
|
+
if (double) return fixed64Double(getField(double, 3));
|
|
39991
|
+
const integer2 = map.values.get("integerValue");
|
|
39992
|
+
if (integer2) return varintValue(getField(integer2, 2));
|
|
39993
|
+
return void 0;
|
|
39994
|
+
};
|
|
39995
|
+
const registerNoteText = (id2) => {
|
|
39996
|
+
const target = registerTarget(id2);
|
|
39997
|
+
const note = target && sub(target, 10);
|
|
39998
|
+
const text = note && stringValue(getField(note, 2));
|
|
39999
|
+
return text?.replace(/\n+$/u, "") || void 0;
|
|
40000
|
+
};
|
|
40001
|
+
const recordings = entries.map((e) => customMap(e)).filter((m) => m?.type === "com.apple.notes.ICTTAudioRecording");
|
|
40002
|
+
if (recordings.length !== 1) throw new Error("Expected exactly one audio recording object");
|
|
40003
|
+
const recording = recordings[0];
|
|
40004
|
+
const fragments = [];
|
|
40005
|
+
const fragmentList = objectIndex(recording.values.get("fragments"));
|
|
40006
|
+
if (fragmentList !== void 0) {
|
|
40007
|
+
const list = need(sub(entry(fragmentList), 5), "fragment list");
|
|
40008
|
+
for (const item of many(list, 1)) {
|
|
40009
|
+
const fragment = customMap(entry(need(objectIndex(sub(item, 2)), "fragment reference")));
|
|
40010
|
+
if (fragment?.type !== "com.apple.notes.ICTTAudioRecording.Fragment")
|
|
40011
|
+
throw new Error("Unexpected fragment type");
|
|
40012
|
+
const identity = stringValue(getField(fragment.values.get("identity") ?? [], 4));
|
|
40013
|
+
const segments = [];
|
|
40014
|
+
const transcriptRef = objectIndex(fragment.values.get("transcript"));
|
|
40015
|
+
if (transcriptRef !== void 0) {
|
|
40016
|
+
const set = sub(entry(transcriptRef), 15);
|
|
40017
|
+
if (!set) throw new Error("Unsupported transcript container");
|
|
40018
|
+
const bySlot = /* @__PURE__ */ new Map();
|
|
40019
|
+
for (const element of many(sub(set, 2) ?? [], 1)) {
|
|
40020
|
+
const keyMap = customMap(entry(need(objectIndex(sub(element, 1)), "segment key")));
|
|
40021
|
+
const slot = varintValue(getField(keyMap?.values.get("UUIDIndex") ?? [], 2));
|
|
40022
|
+
const value = objectIndex(sub(element, 2));
|
|
40023
|
+
if (slot === void 0 || value === void 0) throw new Error("Invalid segment key");
|
|
40024
|
+
bySlot.set(slot, value);
|
|
40025
|
+
}
|
|
40026
|
+
const ordering = many(need(sub(set, 1), "transcript ordering"), 2).map((pair) => ({
|
|
40027
|
+
index: need(varintValue(getField(pair, 1)), "segment index"),
|
|
40028
|
+
uuid: getField(pair, 2)?.value
|
|
40029
|
+
})).sort((a, b) => a.index - b.index);
|
|
40030
|
+
for (const { uuid: uuid2 } of ordering) {
|
|
40031
|
+
if (!(uuid2 instanceof Uint8Array)) throw new Error("Invalid segment UUID");
|
|
40032
|
+
const slot = uuidSlot.get(Buffer.from(uuid2).toString("hex"));
|
|
40033
|
+
const segmentIndex = slot === void 0 ? void 0 : bySlot.get(slot);
|
|
40034
|
+
if (segmentIndex === void 0) throw new Error("Unresolved transcript segment");
|
|
40035
|
+
const segment = customMap(entry(segmentIndex));
|
|
40036
|
+
if (segment?.type !== "com.apple.notes.ICTTTranscriptSegment")
|
|
40037
|
+
throw new Error("Unexpected segment type");
|
|
40038
|
+
const text = registerPrimitive(segment.values.get("text"));
|
|
40039
|
+
if (typeof text !== "string") throw new Error("Segment without text");
|
|
40040
|
+
const start = registerPrimitive(segment.values.get("timestamp"));
|
|
40041
|
+
const duration3 = registerPrimitive(segment.values.get("duration"));
|
|
40042
|
+
const speaker = registerPrimitive(segment.values.get("speaker"));
|
|
40043
|
+
segments.push({
|
|
40044
|
+
text,
|
|
40045
|
+
...typeof start === "number" ? { start } : {},
|
|
40046
|
+
...typeof duration3 === "number" ? { duration: duration3 } : {},
|
|
40047
|
+
...typeof speaker === "string" && speaker ? { speaker } : {}
|
|
40048
|
+
});
|
|
40049
|
+
}
|
|
40050
|
+
}
|
|
40051
|
+
fragments.push({ ...identity ? { identity } : {}, segments });
|
|
40052
|
+
}
|
|
40053
|
+
}
|
|
40054
|
+
const summary = registerNoteText(recording.values.get("summary"));
|
|
40055
|
+
const topLineSummary = registerNoteText(recording.values.get("topLineSummary"));
|
|
40056
|
+
return {
|
|
40057
|
+
fragments,
|
|
40058
|
+
...summary ? { summary } : {},
|
|
40059
|
+
...topLineSummary ? { topLineSummary } : {}
|
|
40060
|
+
};
|
|
40061
|
+
}
|
|
40062
|
+
function joinSegments(segments) {
|
|
40063
|
+
let out = "";
|
|
40064
|
+
for (const { text } of segments) {
|
|
40065
|
+
if (!text) continue;
|
|
40066
|
+
const glue = !out || /\s$/u.test(out) || /^\s/u.test(text) || /^[.,!?;:%)\]}…'’]/u.test(text) ? "" : " ";
|
|
40067
|
+
out += glue + text;
|
|
40068
|
+
}
|
|
40069
|
+
return out.trim();
|
|
40070
|
+
}
|
|
40071
|
+
function parseNoteId(noteId3) {
|
|
40072
|
+
const match = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/ICNote\/p(\d+)$/.exec(noteId3);
|
|
40073
|
+
if (!match) {
|
|
40074
|
+
throw new AudioTranscriptError(
|
|
40075
|
+
"invalid_id",
|
|
40076
|
+
`Invalid note ID format: "${noteId3}". Expected format: x-coredata://UUID/ICNote/pNNN`
|
|
40077
|
+
);
|
|
40078
|
+
}
|
|
40079
|
+
return { store: match[1], pk: match[2] };
|
|
40080
|
+
}
|
|
40081
|
+
var AUDIO_UTI_SQL = "(a.ZTYPEUTI LIKE '%audio%' OR a.ZTYPEUTI IN ('public.mp3'))";
|
|
40082
|
+
function buildTranscriptSql(pk, available) {
|
|
40083
|
+
if (!/^\d+$/.test(pk)) throw new Error("Invalid primary key");
|
|
40084
|
+
const optional2 = (column, key, alias = "a") => available.has(column) ? `, '${key}', ${alias}.${column}` : "";
|
|
40085
|
+
const locked = available.has("ZISPASSWORDPROTECTED") ? "COALESCE(n.ZISPASSWORDPROTECTED, 0)" : "0";
|
|
40086
|
+
return [
|
|
40087
|
+
"BEGIN;",
|
|
40088
|
+
// Every statement yields exactly one row, so output lines stay positional.
|
|
40089
|
+
`SELECT json_object('found', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_PK = ${pk}), 'locked', (SELECT ${locked} FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_PK = ${pk}));`,
|
|
40090
|
+
`SELECT COALESCE((SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = ${pk} LIMIT 1), '');`,
|
|
40091
|
+
"SELECT json_group_array(json_object('pk', a.Z_PK, 'identifier', a.ZIDENTIFIER, 'uti', a.ZTYPEUTI, 'data', hex(a.ZMERGEABLEDATA1)" + optional2("ZDURATION", "duration") + optional2("ZNEEDSTRANSCRIPTION", "needsTranscription") + ", 'fragments', json((SELECT json_group_array(json_object('identifier', c.ZIDENTIFIER" + optional2("ZDURATION", "duration", "c") + `)) FROM ZICCLOUDSYNCINGOBJECT c WHERE c.ZPARENTATTACHMENT = a.Z_PK)))) FROM ZICCLOUDSYNCINGOBJECT a WHERE a.ZNOTE = ${pk} AND a.ZPARENTATTACHMENT IS NULL AND ${AUDIO_UTI_SQL};`,
|
|
40092
|
+
"COMMIT;"
|
|
40093
|
+
].join("\n");
|
|
40094
|
+
}
|
|
40095
|
+
var REQUIRED_COLUMNS = [
|
|
40096
|
+
"ZMERGEABLEDATA1",
|
|
40097
|
+
"ZPARENTATTACHMENT",
|
|
40098
|
+
"ZTYPEUTI",
|
|
40099
|
+
"ZNOTE",
|
|
40100
|
+
"ZIDENTIFIER"
|
|
40101
|
+
];
|
|
40102
|
+
function runSqlite(dbPath2, sql) {
|
|
40103
|
+
return execFileSync4("sqlite3", ["-readonly", dbPath2, sql], {
|
|
40104
|
+
encoding: "utf8",
|
|
40105
|
+
timeout: 1e4,
|
|
40106
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
40107
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
40108
|
+
});
|
|
40109
|
+
}
|
|
40110
|
+
function bodyAttachmentOrder(gzipped) {
|
|
40111
|
+
const doc = decodeMessage(gunzipSync3(gzipped, { maxOutputLength: 64 * 1024 * 1024 }));
|
|
40112
|
+
const body = embeddedMessage(getField(embeddedMessage(getField(doc, 2)) ?? [], 3));
|
|
40113
|
+
if (!body) throw new Error("Unsupported Notes document structure");
|
|
40114
|
+
const ids = [];
|
|
40115
|
+
for (const run of getFields(body, 5)) {
|
|
40116
|
+
const attachment = embeddedMessage(getField(embeddedMessage(run) ?? [], 12));
|
|
40117
|
+
const id2 = attachment && stringValue(getField(attachment, 1));
|
|
40118
|
+
if (id2 && !ids.includes(id2)) ids.push(id2);
|
|
40119
|
+
}
|
|
40120
|
+
return ids;
|
|
40121
|
+
}
|
|
40122
|
+
function readAudioTranscripts(noteId3, options = {}) {
|
|
40123
|
+
const { store, pk } = parseNoteId(noteId3);
|
|
40124
|
+
const dbPath2 = options.dbPath ?? NOTES_DB_PATH2;
|
|
40125
|
+
const maxSegments = Math.min(
|
|
40126
|
+
Math.max(1, Math.floor(options.maxSegments ?? DEFAULT_MAX_SEGMENTS)),
|
|
40127
|
+
MAX_SEGMENTS_LIMIT
|
|
40128
|
+
);
|
|
40129
|
+
let lines;
|
|
40130
|
+
try {
|
|
40131
|
+
const available = new Set(
|
|
40132
|
+
runSqlite(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").split("\n").map((line) => line.trim()).filter(Boolean)
|
|
40133
|
+
);
|
|
40134
|
+
const missing = REQUIRED_COLUMNS.filter((c) => !available.has(c));
|
|
40135
|
+
if (missing.length)
|
|
40136
|
+
throw new AudioTranscriptError(
|
|
40137
|
+
"query_error",
|
|
40138
|
+
`This macOS version's Notes database lacks ${missing.join(", ")}; stored transcripts cannot be read.`
|
|
40139
|
+
);
|
|
40140
|
+
lines = runSqlite(dbPath2, buildTranscriptSql(pk, available)).split("\n");
|
|
40141
|
+
} catch (error2) {
|
|
40142
|
+
if (error2 instanceof AudioTranscriptError) throw error2;
|
|
40143
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
40144
|
+
if (/authorization denied|unable to open database/i.test(message))
|
|
40145
|
+
throw new AudioTranscriptError(
|
|
40146
|
+
"no_fda",
|
|
40147
|
+
`Full Disk Access is required to read stored transcripts. Grant it to the app that launches this server, then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL}`
|
|
40148
|
+
);
|
|
40149
|
+
throw new AudioTranscriptError("query_error", "Failed to read the Notes database.");
|
|
40150
|
+
}
|
|
40151
|
+
const [noteLine = "{}", bodyLine = "", rowsLine = "[]"] = lines;
|
|
40152
|
+
const note = JSON.parse(noteLine || "{}");
|
|
40153
|
+
if (!note.found)
|
|
40154
|
+
throw new AudioTranscriptError(
|
|
40155
|
+
"not_found",
|
|
40156
|
+
`No note found in the database for ID "${noteId3}".`
|
|
40157
|
+
);
|
|
40158
|
+
if (note.locked)
|
|
40159
|
+
throw new AudioTranscriptError(
|
|
40160
|
+
"locked",
|
|
40161
|
+
"This note is password-protected; its transcripts are encrypted and cannot be read."
|
|
40162
|
+
);
|
|
40163
|
+
const rows = JSON.parse(rowsLine || "[]");
|
|
40164
|
+
let order;
|
|
40165
|
+
try {
|
|
40166
|
+
if (/^[0-9A-F]+$/i.test(bodyLine.trim()))
|
|
40167
|
+
order = bodyAttachmentOrder(Buffer.from(bodyLine.trim(), "hex"));
|
|
40168
|
+
} catch {
|
|
40169
|
+
order = void 0;
|
|
40170
|
+
}
|
|
40171
|
+
const ordered = order ? order.flatMap((id2) => rows.filter((row) => row.identifier === id2)) : [...rows].sort((a, b) => a.pk - b.pk);
|
|
40172
|
+
const attachments = ordered.map((row) => describeRow(row, store, options, maxSegments));
|
|
40173
|
+
return { id: noteId3, attachments, bodyOrder: Boolean(order), truncated: false };
|
|
40174
|
+
}
|
|
40175
|
+
function describeRow(row, store, options, maxSegments) {
|
|
40176
|
+
const childDurations = (row.fragments ?? []).map((f) => f.duration).filter((d) => typeof d === "number" && d > 0);
|
|
40177
|
+
const durationSeconds = typeof row.duration === "number" && row.duration > 0 ? row.duration : childDurations.length ? childDurations.reduce((a, b) => a + b, 0) : void 0;
|
|
40178
|
+
const base = {
|
|
40179
|
+
attachmentId: `x-coredata://${store}/ICAttachment/p${row.pk}`,
|
|
40180
|
+
identifier: row.identifier,
|
|
40181
|
+
typeUti: row.uti,
|
|
40182
|
+
status: "none",
|
|
40183
|
+
...durationSeconds !== void 0 ? { durationSeconds } : {},
|
|
40184
|
+
...typeof row.needsTranscription === "number" ? { needsTranscription: row.needsTranscription === 1 } : {}
|
|
40185
|
+
};
|
|
40186
|
+
if (!row.data) return base;
|
|
40187
|
+
let recording;
|
|
40188
|
+
try {
|
|
40189
|
+
recording = parseAudioRecording(Buffer.from(row.data, "hex"));
|
|
40190
|
+
} catch (error2) {
|
|
40191
|
+
return {
|
|
40192
|
+
...base,
|
|
40193
|
+
status: "undecodable",
|
|
40194
|
+
reason: error2 instanceof Error ? error2.message : String(error2)
|
|
40195
|
+
};
|
|
40196
|
+
}
|
|
40197
|
+
const multi = recording.fragments.length > 1;
|
|
40198
|
+
const all = recording.fragments.flatMap(
|
|
40199
|
+
(fragment, i) => fragment.segments.map((s) => multi ? { ...s, fragment: i } : s)
|
|
40200
|
+
);
|
|
40201
|
+
const text = recording.fragments.map((f) => joinSegments(f.segments)).filter(Boolean).join("\n\n");
|
|
40202
|
+
const speakers = [...new Set(all.flatMap((s) => s.speaker ? [s.speaker] : []))];
|
|
40203
|
+
return {
|
|
40204
|
+
...base,
|
|
40205
|
+
status: all.length ? "ok" : "none",
|
|
40206
|
+
fragmentCount: recording.fragments.length,
|
|
40207
|
+
wordCount: all.length,
|
|
40208
|
+
...text ? { text } : {},
|
|
40209
|
+
...speakers.length ? { speakers } : {},
|
|
40210
|
+
...recording.summary ? { summary: recording.summary } : {},
|
|
40211
|
+
...recording.topLineSummary ? { topLineSummary: recording.topLineSummary } : {},
|
|
40212
|
+
...options.includeSegments && all.length ? {
|
|
40213
|
+
segments: all.slice(0, maxSegments),
|
|
40214
|
+
...all.length > maxSegments ? { segmentsTruncated: true } : {}
|
|
40215
|
+
} : {}
|
|
40216
|
+
};
|
|
40217
|
+
}
|
|
40218
|
+
function fitTranscriptsToBudget(result, maxBytes, measure) {
|
|
40219
|
+
if (measure(result) <= maxBytes) return result;
|
|
40220
|
+
let next = {
|
|
40221
|
+
...result,
|
|
40222
|
+
truncated: true,
|
|
40223
|
+
attachments: result.attachments.map((a) => {
|
|
40224
|
+
if (!a.segments) return a;
|
|
40225
|
+
const rest = { ...a, segmentsTruncated: true };
|
|
40226
|
+
delete rest.segments;
|
|
40227
|
+
return rest;
|
|
40228
|
+
})
|
|
40229
|
+
};
|
|
40230
|
+
for (let share = 0.5; measure(next) > maxBytes && share > 1e-4; share /= 2) {
|
|
40231
|
+
next = {
|
|
40232
|
+
...next,
|
|
40233
|
+
attachments: next.attachments.map((a) => {
|
|
40234
|
+
const original = result.attachments.find((o) => o.attachmentId === a.attachmentId);
|
|
40235
|
+
if (!original?.text) return a;
|
|
40236
|
+
const keep = Math.floor(original.text.length * share);
|
|
40237
|
+
return { ...a, text: original.text.slice(0, keep), textTruncated: true };
|
|
40238
|
+
})
|
|
40239
|
+
};
|
|
40240
|
+
}
|
|
40241
|
+
return next;
|
|
40242
|
+
}
|
|
40243
|
+
var clock = (seconds) => {
|
|
40244
|
+
const total = Math.round(seconds);
|
|
40245
|
+
const h = Math.floor(total / 3600);
|
|
40246
|
+
const m = Math.floor(total % 3600 / 60);
|
|
40247
|
+
const s = total % 60;
|
|
40248
|
+
const mm = h ? String(m).padStart(2, "0") : String(m);
|
|
40249
|
+
return `${h ? `${h}:` : ""}${mm}:${String(s).padStart(2, "0")}`;
|
|
40250
|
+
};
|
|
40251
|
+
function formatTranscriptsText(result) {
|
|
40252
|
+
const count = result.attachments.length;
|
|
40253
|
+
if (count === 0) return `No audio attachments found in note ${result.id}.`;
|
|
40254
|
+
const lines = [`${count} audio attachment${count === 1 ? "" : "s"} in note ${result.id}:`];
|
|
40255
|
+
result.attachments.forEach((a, i) => {
|
|
40256
|
+
const facts = [
|
|
40257
|
+
`status ${a.status}`,
|
|
40258
|
+
...a.durationSeconds !== void 0 ? [clock(a.durationSeconds)] : [],
|
|
40259
|
+
...a.wordCount !== void 0 ? [`${a.wordCount} words`] : [],
|
|
40260
|
+
...a.fragmentCount && a.fragmentCount > 1 ? [`${a.fragmentCount} fragments`] : [],
|
|
40261
|
+
...a.speakers?.length ? [`${a.speakers.length} speakers`] : [],
|
|
40262
|
+
...a.reason ? [`reason: ${a.reason}`] : []
|
|
40263
|
+
];
|
|
40264
|
+
lines.push("", `[${i + 1}] ${a.attachmentId} (${facts.join(", ")})`);
|
|
40265
|
+
if (a.summary) lines.push(`Summary: ${a.summary}`);
|
|
40266
|
+
if (a.text) lines.push(a.textTruncated ? `${a.text} [truncated]` : a.text);
|
|
40267
|
+
});
|
|
40268
|
+
if (result.truncated)
|
|
40269
|
+
lines.push(
|
|
40270
|
+
"",
|
|
40271
|
+
"Some segments or text were left out to stay under the response size limit (APPLE_NOTES_MCP_EXPORT_MAX_BYTES)."
|
|
40272
|
+
);
|
|
40273
|
+
return lines.join("\n");
|
|
40274
|
+
}
|
|
40275
|
+
|
|
40276
|
+
// src/utils/noteTables.ts
|
|
40277
|
+
import { gunzipSync as gunzipSync4 } from "node:zlib";
|
|
40278
|
+
var sub2 = (f, n) => {
|
|
40279
|
+
const value = embeddedMessage(getField(f, n));
|
|
40280
|
+
if (!value) throw new Error(`Missing table field ${n}`);
|
|
40281
|
+
return value;
|
|
40282
|
+
};
|
|
40283
|
+
var num = (f, n) => {
|
|
40284
|
+
const v = varintValue(getField(f, n));
|
|
40285
|
+
if (v === void 0) throw new Error(`Missing table index ${n}`);
|
|
40286
|
+
return v;
|
|
40287
|
+
};
|
|
40288
|
+
var many2 = (f, n) => getFields(f, n).map((v) => {
|
|
40289
|
+
const m = embeddedMessage(v);
|
|
40290
|
+
if (!m) throw new Error("Invalid table entry");
|
|
40291
|
+
return m;
|
|
40292
|
+
});
|
|
40293
|
+
var hex = (f) => {
|
|
40294
|
+
if (!(f?.value instanceof Uint8Array)) throw new Error("Missing table UUID");
|
|
40295
|
+
return Buffer.from(f.value).toString("hex");
|
|
40296
|
+
};
|
|
40297
|
+
function parseNoteTable(compressed) {
|
|
40298
|
+
const table = decodeTable(compressed, true);
|
|
40299
|
+
return { rows: table.rows, rowIds: table.rowIds, columnIds: table.columnIds };
|
|
40300
|
+
}
|
|
40301
|
+
function parseNoteTableCells(compressed) {
|
|
40302
|
+
return decodeTable(compressed, false);
|
|
40303
|
+
}
|
|
40304
|
+
function decodeTable(compressed, strict) {
|
|
40305
|
+
const root = decodeMessage(gunzipSync4(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
|
|
40306
|
+
const data = sub2(sub2(root, 2), 3), entries = many2(data, 3);
|
|
40307
|
+
if (entries.length > 1e5) throw new Error("Table too large");
|
|
40308
|
+
const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
|
|
40309
|
+
const entry = (index) => {
|
|
40310
|
+
if (!entries[index]) throw new Error("Invalid table reference");
|
|
40311
|
+
return entries[index];
|
|
40312
|
+
};
|
|
40313
|
+
const uuidIndex = (index) => num(sub2(many2(sub2(entry(index), 13), 3)[0], 2), 2);
|
|
40314
|
+
const roots = entries.filter((e) => {
|
|
40315
|
+
const map = embeddedMessage(getField(e, 13));
|
|
40316
|
+
return map && types[num(map, 1)] === "com.apple.notes.ICTable";
|
|
40317
|
+
});
|
|
40318
|
+
if (roots.length !== 1) throw new Error("Ambiguous native table root");
|
|
40319
|
+
const refs = new Map(
|
|
40320
|
+
many2(sub2(roots[0], 13), 3).filter((m) => ["crRows", "crColumns", "cellColumns"].includes(keys[num(m, 1)] || "")).map((m) => [keys[num(m, 1)], num(sub2(m, 2), 6)])
|
|
40321
|
+
);
|
|
40322
|
+
const ordered = (key) => {
|
|
40323
|
+
const ref = refs.get(key);
|
|
40324
|
+
if (ref === void 0) throw new Error("Missing table dimension");
|
|
40325
|
+
const ordering = sub2(sub2(entry(ref), 16), 1), array2 = sub2(ordering, 1);
|
|
40326
|
+
const ids = many2(array2, 2).map((a) => hex(getField(a, 2)));
|
|
40327
|
+
const map = /* @__PURE__ */ new Map();
|
|
40328
|
+
ids.forEach((id2, i) => {
|
|
40329
|
+
const index = uuids.indexOf(id2);
|
|
40330
|
+
if (index < 0) throw new Error("Missing dimension UUID");
|
|
40331
|
+
map.set(index, i);
|
|
40332
|
+
});
|
|
40333
|
+
const aliases = many2(sub2(ordering, 2), 1).map((pair) => [
|
|
40334
|
+
uuidIndex(num(sub2(pair, 1), 6)),
|
|
40335
|
+
uuidIndex(num(sub2(pair, 2), 6))
|
|
40336
|
+
]);
|
|
40337
|
+
for (let pass = 0; pass < aliases.length + 1; pass++) {
|
|
40338
|
+
let changed = false;
|
|
40339
|
+
for (const [key2, value] of aliases)
|
|
40340
|
+
if (map.has(key2) && !map.has(value)) {
|
|
40341
|
+
map.set(value, map.get(key2));
|
|
40342
|
+
changed = true;
|
|
40343
|
+
}
|
|
40344
|
+
if (!changed) break;
|
|
40345
|
+
}
|
|
40346
|
+
return { ids, map };
|
|
40347
|
+
};
|
|
40348
|
+
const rows = ordered("crRows"), columns = ordered("crColumns");
|
|
40349
|
+
if (!rows.ids.length || !columns.ids.length || rows.ids.length * columns.ids.length > 1e5)
|
|
40350
|
+
throw new Error("Unsupported table size");
|
|
40351
|
+
const values = rows.ids.map(() => columns.ids.map(() => ""));
|
|
40352
|
+
const incomplete = [];
|
|
40353
|
+
const cellRef = refs.get("cellColumns");
|
|
40354
|
+
if (cellRef === void 0) throw new Error("Missing table cells");
|
|
40355
|
+
for (const column of many2(sub2(entry(cellRef), 6), 1)) {
|
|
40356
|
+
const ci = columns.map.get(uuidIndex(num(sub2(column, 1), 6)));
|
|
40357
|
+
const cells = entry(num(sub2(column, 2), 6));
|
|
40358
|
+
for (const row of many2(sub2(cells, 6), 1)) {
|
|
40359
|
+
const ri = rows.map.get(uuidIndex(num(sub2(row, 1), 6)));
|
|
40360
|
+
if (ri === void 0 || ci === void 0) continue;
|
|
40361
|
+
const note = sub2(entry(num(sub2(row, 2), 6)), 10);
|
|
40362
|
+
const text = stringValue(getField(note, 2));
|
|
40363
|
+
if (text === void 0 || text.includes("\uFFFC")) {
|
|
40364
|
+
if (strict) throw new Error("Embedded or unsupported table cell");
|
|
40365
|
+
values[ri][ci] = null;
|
|
40366
|
+
incomplete.push({
|
|
40367
|
+
ri,
|
|
40368
|
+
ci,
|
|
40369
|
+
reason: text === void 0 ? "Cell text is missing" : "Cell contains an embedded object"
|
|
40370
|
+
});
|
|
40371
|
+
continue;
|
|
40372
|
+
}
|
|
40373
|
+
values[ri][ci] = text.replace(/\n$/u, "");
|
|
40374
|
+
}
|
|
40375
|
+
}
|
|
40376
|
+
const rtl = entries.some((e) => {
|
|
40377
|
+
const map = embeddedMessage(getField(e, 13));
|
|
40378
|
+
return map && many2(map, 3).some(
|
|
40379
|
+
(m) => stringValue(getField(sub2(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
|
|
40380
|
+
);
|
|
40381
|
+
});
|
|
40382
|
+
const width = columns.ids.length;
|
|
40383
|
+
if (rtl) {
|
|
40384
|
+
for (const row of values) row.reverse();
|
|
40385
|
+
columns.ids.reverse();
|
|
40386
|
+
}
|
|
40387
|
+
const incompleteCells = incomplete.map(({ ri, ci, reason }) => ({ row: ri, column: rtl ? width - 1 - ci : ci, reason })).sort((a, b) => a.row - b.row || a.column - b.column);
|
|
40388
|
+
return { rows: values, rowIds: rows.ids, columnIds: columns.ids, incompleteCells };
|
|
40389
|
+
}
|
|
40390
|
+
|
|
40391
|
+
// src/utils/tableMarkdown.ts
|
|
40392
|
+
var UNDECODED_CELL_MARKER = "[undecoded cell]";
|
|
40393
|
+
function escapeMarkdownTableCell(text) {
|
|
40394
|
+
return text.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r\n|[\r\n\u2028\u2029]/g, "<br>");
|
|
40395
|
+
}
|
|
40396
|
+
function renderMarkdownTable(rows) {
|
|
40397
|
+
const width = rows.reduce((max, row) => Math.max(max, row.length), 0);
|
|
40398
|
+
if (rows.length === 0 || width === 0) return "";
|
|
40399
|
+
const line = (row) => "| " + Array.from({ length: width }, (_, i) => {
|
|
40400
|
+
const cell = row[i];
|
|
40401
|
+
return cell === null ? UNDECODED_CELL_MARKER : escapeMarkdownTableCell(cell ?? "");
|
|
40402
|
+
}).join(" | ") + " |";
|
|
40403
|
+
const [header, ...body] = rows;
|
|
40404
|
+
return [line(header), "| " + Array(width).fill("---").join(" | ") + " |", ...body.map(line)].join(
|
|
40405
|
+
"\n"
|
|
40406
|
+
);
|
|
40407
|
+
}
|
|
40408
|
+
function collectNoteTables(rich, noteId3) {
|
|
40409
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40410
|
+
const ordered = (rich.objects || []).filter((object3) => object3.type.includes("table")).sort((a, b) => a.start - b.start).filter((object3) => {
|
|
40411
|
+
if (seen.has(object3.id)) return false;
|
|
40412
|
+
seen.add(object3.id);
|
|
40413
|
+
return true;
|
|
40414
|
+
});
|
|
40415
|
+
const tables = ordered.map((object3, i) => {
|
|
40416
|
+
const index = i + 1;
|
|
40417
|
+
const data = (rich.objectData || []).find((row) => row.id === object3.id);
|
|
40418
|
+
if (!data || !data.mergeable) {
|
|
40419
|
+
return { index, id: object3.id, complete: false, reason: "Native table data is unavailable" };
|
|
40420
|
+
}
|
|
40421
|
+
const attachmentId = noteId3.replace(/ICNote\/p\d+$/, `ICAttachment/p${data.pk}`);
|
|
40422
|
+
try {
|
|
40423
|
+
const table = parseNoteTableCells(Buffer.from(data.mergeable, "hex"));
|
|
40424
|
+
const complete = table.incompleteCells.length === 0;
|
|
40425
|
+
return {
|
|
40426
|
+
index,
|
|
40427
|
+
id: object3.id,
|
|
40428
|
+
attachmentId,
|
|
40429
|
+
complete,
|
|
40430
|
+
...complete ? {} : { reason: `${table.incompleteCells.length} cell(s) could not be decoded` },
|
|
40431
|
+
rows: table.rows,
|
|
40432
|
+
rowIds: table.rowIds,
|
|
40433
|
+
columnIds: table.columnIds,
|
|
40434
|
+
rowCount: table.rowIds.length,
|
|
40435
|
+
columnCount: table.columnIds.length,
|
|
40436
|
+
incompleteCells: table.incompleteCells,
|
|
40437
|
+
markdown: renderMarkdownTable(table.rows)
|
|
40438
|
+
};
|
|
40439
|
+
} catch (error2) {
|
|
40440
|
+
return {
|
|
40441
|
+
index,
|
|
40442
|
+
id: object3.id,
|
|
40443
|
+
attachmentId,
|
|
40444
|
+
complete: false,
|
|
40445
|
+
reason: error2 instanceof Error ? error2.message : String(error2)
|
|
40446
|
+
};
|
|
40447
|
+
}
|
|
40448
|
+
});
|
|
40449
|
+
const markdown = tables.map(
|
|
40450
|
+
(table) => table.markdown !== void 0 ? table.markdown : `[table ${table.index} could not be decoded: ${table.reason}]`
|
|
40451
|
+
).join("\n\n");
|
|
40452
|
+
return { tables, tableCellsComplete: tables.every((table) => table.complete), markdown };
|
|
40453
|
+
}
|
|
40454
|
+
|
|
39904
40455
|
// src/utils/smartFolders.ts
|
|
39905
|
-
import { execFileSync as
|
|
40456
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
39906
40457
|
import * as fs2 from "fs";
|
|
39907
40458
|
import * as os2 from "os";
|
|
39908
40459
|
import * as path2 from "path";
|
|
39909
|
-
var
|
|
40460
|
+
var NOTES_DB_PATH3 = path2.join(
|
|
39910
40461
|
os2.homedir(),
|
|
39911
40462
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
39912
40463
|
);
|
|
@@ -40197,7 +40748,7 @@ WHERE Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICHashtag')
|
|
|
40197
40748
|
AND ZSTANDARDIZEDCONTENT IS NOT NULL
|
|
40198
40749
|
AND COALESCE(ZMARKEDFORDELETION, 0) = 0;
|
|
40199
40750
|
COMMIT;`;
|
|
40200
|
-
var
|
|
40751
|
+
var REQUIRED_COLUMNS2 = [
|
|
40201
40752
|
"ZIDENTIFIER",
|
|
40202
40753
|
"ZTITLE2",
|
|
40203
40754
|
"ZFOLDERTYPE",
|
|
@@ -40251,21 +40802,21 @@ function buildSmartFolders(output) {
|
|
|
40251
40802
|
(a, b) => (a.account ?? "").localeCompare(b.account ?? "") || (a.name ?? "").localeCompare(b.name ?? "") || a.id.localeCompare(b.id)
|
|
40252
40803
|
);
|
|
40253
40804
|
}
|
|
40254
|
-
function
|
|
40255
|
-
return
|
|
40805
|
+
function runSqlite2(dbPath2, sql) {
|
|
40806
|
+
return execFileSync5("sqlite3", ["-readonly", dbPath2, sql], {
|
|
40256
40807
|
encoding: "utf8",
|
|
40257
40808
|
timeout: 5e3,
|
|
40258
40809
|
maxBuffer: 16 * 1024 * 1024,
|
|
40259
40810
|
stdio: ["pipe", "pipe", "pipe"]
|
|
40260
40811
|
});
|
|
40261
40812
|
}
|
|
40262
|
-
function readSmartFolders(dbPath2 =
|
|
40813
|
+
function readSmartFolders(dbPath2 = NOTES_DB_PATH3) {
|
|
40263
40814
|
if (!fs2.existsSync(dbPath2)) return { folders: null, error: "no_fda", message: FDA_MESSAGE };
|
|
40264
40815
|
try {
|
|
40265
40816
|
const columns = new Set(
|
|
40266
|
-
|
|
40817
|
+
runSqlite2(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").trim().split("\n")
|
|
40267
40818
|
);
|
|
40268
|
-
const missing =
|
|
40819
|
+
const missing = REQUIRED_COLUMNS2.filter((column) => !columns.has(column));
|
|
40269
40820
|
if (missing.length) {
|
|
40270
40821
|
return {
|
|
40271
40822
|
folders: null,
|
|
@@ -40273,7 +40824,7 @@ function readSmartFolders(dbPath2 = NOTES_DB_PATH2) {
|
|
|
40273
40824
|
message: `This Notes database does not have the columns smart folders need (${missing.join(", ")}).`
|
|
40274
40825
|
};
|
|
40275
40826
|
}
|
|
40276
|
-
return { folders: buildSmartFolders(
|
|
40827
|
+
return { folders: buildSmartFolders(runSqlite2(dbPath2, SMART_FOLDERS_SQL)) };
|
|
40277
40828
|
} catch (error2) {
|
|
40278
40829
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
40279
40830
|
if (message.includes("authorization denied") || message.includes("unable to open database")) {
|
|
@@ -40295,11 +40846,11 @@ import {
|
|
|
40295
40846
|
rmSync,
|
|
40296
40847
|
statSync
|
|
40297
40848
|
} from "fs";
|
|
40298
|
-
import { dirname, isAbsolute, join as
|
|
40299
|
-
import { homedir as
|
|
40849
|
+
import { dirname, isAbsolute, join as join5, relative, resolve, sep } from "path";
|
|
40850
|
+
import { homedir as homedir5, tmpdir } from "os";
|
|
40300
40851
|
function allowedSaveRoots() {
|
|
40301
40852
|
return [
|
|
40302
|
-
resolve(
|
|
40853
|
+
resolve(homedir5()),
|
|
40303
40854
|
resolve(tmpdir()),
|
|
40304
40855
|
"/Volumes",
|
|
40305
40856
|
"/private/var/folders",
|
|
@@ -40373,7 +40924,7 @@ function assertSafeSavePath(p, roots = allowedSaveRoots()) {
|
|
|
40373
40924
|
if (suffix.split(sep).includes("..")) {
|
|
40374
40925
|
throw new Error(`Refusing to write outside allowed locations (home, temp, /Volumes): "${abs}"`);
|
|
40375
40926
|
}
|
|
40376
|
-
const canonicalDest = suffix ?
|
|
40927
|
+
const canonicalDest = suffix ? join5(canonicalAncestor, suffix) : canonicalAncestor;
|
|
40377
40928
|
const allowed = canonicalRoots(roots);
|
|
40378
40929
|
if (!isWithinRoots(canonicalAncestor, allowed) || !isWithinRoots(canonicalDest, allowed)) {
|
|
40379
40930
|
throw new Error(
|
|
@@ -40423,8 +40974,8 @@ function cleanupTempDir(dir) {
|
|
|
40423
40974
|
// src/services/appleNotesManager.ts
|
|
40424
40975
|
var import_turndown = __toESM(require_turndown_cjs(), 1);
|
|
40425
40976
|
import { existsSync as existsSync4 } from "fs";
|
|
40426
|
-
import { homedir as
|
|
40427
|
-
import { join as
|
|
40977
|
+
import { homedir as homedir6 } from "os";
|
|
40978
|
+
import { join as join6 } from "path";
|
|
40428
40979
|
var FIELD_SEP = "";
|
|
40429
40980
|
var RECORD_SEP = "";
|
|
40430
40981
|
var AS_FIELD_SEP = "(character id 31)";
|
|
@@ -40652,7 +41203,7 @@ function getNoteLinkFromDB(coreDataId2) {
|
|
|
40652
41203
|
const match = coreDataId2.match(/\/p(\d+)$/);
|
|
40653
41204
|
if (!match) return null;
|
|
40654
41205
|
const pk = parseInt(match[1], 10);
|
|
40655
|
-
const dbPath2 =
|
|
41206
|
+
const dbPath2 = join6(homedir6(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
|
|
40656
41207
|
if (!existsSync4(dbPath2)) return null;
|
|
40657
41208
|
try {
|
|
40658
41209
|
const { DatabaseSync } = __require("node:sqlite");
|
|
@@ -41094,6 +41645,21 @@ var AppleNotesManager = class {
|
|
|
41094
41645
|
passwordProtected: parsed.passwordProtected
|
|
41095
41646
|
};
|
|
41096
41647
|
}
|
|
41648
|
+
/**
|
|
41649
|
+
* Reads every native table in one note, in body order, as JSON rows and
|
|
41650
|
+
* GitHub-flavored Markdown.
|
|
41651
|
+
*
|
|
41652
|
+
* Reads the NoteStore database read-only (Full Disk Access required); no
|
|
41653
|
+
* AppleScript is involved. Cells that cannot be decoded are returned as null
|
|
41654
|
+
* and flagged, never guessed.
|
|
41655
|
+
*
|
|
41656
|
+
* @param id - CoreData URL identifier for the note
|
|
41657
|
+
* @returns Tables in body order with a combined Markdown rendering
|
|
41658
|
+
* @throws Error when the note's rich data cannot be read (e.g. no Full Disk Access)
|
|
41659
|
+
*/
|
|
41660
|
+
getNoteTablesById(id2) {
|
|
41661
|
+
return collectNoteTables(readRichNote(id2), id2);
|
|
41662
|
+
}
|
|
41097
41663
|
/**
|
|
41098
41664
|
* Retrieves detailed metadata for a note by title.
|
|
41099
41665
|
*
|
|
@@ -42946,18 +43512,31 @@ var AppleNotesManager = class {
|
|
|
42946
43512
|
}
|
|
42947
43513
|
return markdown;
|
|
42948
43514
|
}
|
|
43515
|
+
/**
|
|
43516
|
+
* Reads the transcripts Notes has stored for a note's top-level audio
|
|
43517
|
+
* recordings, one entry per attachment in body order. Read-only: queries the
|
|
43518
|
+
* NoteStore database with `sqlite3 -readonly` and needs Full Disk Access.
|
|
43519
|
+
*
|
|
43520
|
+
* @param id - CoreData URL identifier for the note
|
|
43521
|
+
* @param options - word-level segment inclusion and cap
|
|
43522
|
+
* @throws AudioTranscriptError for an invalid id, a missing or locked note,
|
|
43523
|
+
* missing Full Disk Access, or a database read failure
|
|
43524
|
+
*/
|
|
43525
|
+
getAudioTranscripts(id2, options = {}) {
|
|
43526
|
+
return readAudioTranscripts(id2, options);
|
|
43527
|
+
}
|
|
42949
43528
|
};
|
|
42950
43529
|
|
|
42951
43530
|
// src/utils/syncDetection.ts
|
|
42952
|
-
import { execFileSync as
|
|
43531
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
42953
43532
|
import * as fs3 from "fs";
|
|
42954
43533
|
import * as path3 from "path";
|
|
42955
43534
|
import * as os3 from "os";
|
|
42956
|
-
var
|
|
43535
|
+
var NOTES_DB_PATH4 = path3.join(
|
|
42957
43536
|
os3.homedir(),
|
|
42958
43537
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
42959
43538
|
);
|
|
42960
|
-
var WAL_PATH = `${
|
|
43539
|
+
var WAL_PATH = `${NOTES_DB_PATH4}-wal`;
|
|
42961
43540
|
var RECENT_ACTIVITY_THRESHOLD_SECONDS = 5;
|
|
42962
43541
|
var SYNC_STATUS_CACHE_TTL_MS = 2e3;
|
|
42963
43542
|
var cachedSyncStatus = null;
|
|
@@ -42973,7 +43552,7 @@ function getSyncStatus(useCache = true) {
|
|
|
42973
43552
|
recentActivity: false
|
|
42974
43553
|
};
|
|
42975
43554
|
try {
|
|
42976
|
-
if (!fs3.existsSync(
|
|
43555
|
+
if (!fs3.existsSync(NOTES_DB_PATH4)) {
|
|
42977
43556
|
status.error = "Notes database not found";
|
|
42978
43557
|
cachedSyncStatus = status;
|
|
42979
43558
|
cacheTimestamp = Date.now();
|
|
@@ -42994,9 +43573,9 @@ function getSyncStatus(useCache = true) {
|
|
|
42994
43573
|
WHERE object.ZCLOUDSTATE = state.Z_PK
|
|
42995
43574
|
);
|
|
42996
43575
|
`;
|
|
42997
|
-
const result =
|
|
43576
|
+
const result = execFileSync6(
|
|
42998
43577
|
"sqlite3",
|
|
42999
|
-
["-readonly",
|
|
43578
|
+
["-readonly", NOTES_DB_PATH4, query.replace(/\n/g, " ")],
|
|
43000
43579
|
{
|
|
43001
43580
|
encoding: "utf8",
|
|
43002
43581
|
timeout: 5e3,
|
|
@@ -43053,11 +43632,11 @@ function withSyncAwarenessSync(operation, fn) {
|
|
|
43053
43632
|
}
|
|
43054
43633
|
|
|
43055
43634
|
// src/utils/noteMetadata.ts
|
|
43056
|
-
import { execFileSync as
|
|
43635
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
43057
43636
|
import * as fs4 from "fs";
|
|
43058
43637
|
import * as path4 from "path";
|
|
43059
43638
|
import * as os4 from "os";
|
|
43060
|
-
var
|
|
43639
|
+
var NOTES_DB_PATH5 = path4.join(
|
|
43061
43640
|
os4.homedir(),
|
|
43062
43641
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
43063
43642
|
);
|
|
@@ -43073,15 +43652,15 @@ var COLUMN_MAP = [
|
|
|
43073
43652
|
{ key: "widgetSnippet", column: "ZWIDGETSNIPPET", type: "text" },
|
|
43074
43653
|
{ key: "smartFolderQuery", column: "ZSMARTFOLDERQUERYJSON", type: "text" }
|
|
43075
43654
|
];
|
|
43076
|
-
function
|
|
43077
|
-
return
|
|
43655
|
+
function runSqlite3(query) {
|
|
43656
|
+
return execFileSync7("sqlite3", ["-readonly", NOTES_DB_PATH5, query], {
|
|
43078
43657
|
encoding: "utf8",
|
|
43079
43658
|
timeout: 5e3,
|
|
43080
43659
|
stdio: ["pipe", "pipe", "pipe"]
|
|
43081
43660
|
}).trim();
|
|
43082
43661
|
}
|
|
43083
43662
|
function presentColumns() {
|
|
43084
|
-
const out =
|
|
43663
|
+
const out = runSqlite3("PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);");
|
|
43085
43664
|
const cols = /* @__PURE__ */ new Set();
|
|
43086
43665
|
for (const line of out.split("\n")) {
|
|
43087
43666
|
const name = line.split("|")[1];
|
|
@@ -43099,7 +43678,7 @@ function getNoteMetadata(noteId3) {
|
|
|
43099
43678
|
};
|
|
43100
43679
|
}
|
|
43101
43680
|
const pk = pkMatch[1];
|
|
43102
|
-
if (!fs4.existsSync(
|
|
43681
|
+
if (!fs4.existsSync(NOTES_DB_PATH5)) {
|
|
43103
43682
|
return { metadata: null, error: "no_fda", message: FDA_MESSAGE2 };
|
|
43104
43683
|
}
|
|
43105
43684
|
try {
|
|
@@ -43109,7 +43688,7 @@ function getNoteMetadata(noteId3) {
|
|
|
43109
43688
|
return { metadata: {} };
|
|
43110
43689
|
}
|
|
43111
43690
|
const pairs = selected.map((c) => `'${c.key}', ${c.column}`).join(", ");
|
|
43112
|
-
const row =
|
|
43691
|
+
const row = runSqlite3(
|
|
43113
43692
|
`SELECT json_object(${pairs}) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = ${pk};`
|
|
43114
43693
|
);
|
|
43115
43694
|
if (!row) {
|
|
@@ -43142,11 +43721,11 @@ function getNoteMetadata(noteId3) {
|
|
|
43142
43721
|
}
|
|
43143
43722
|
|
|
43144
43723
|
// src/utils/noteIdentifiers.ts
|
|
43145
|
-
import { execFileSync as
|
|
43724
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
43146
43725
|
import * as fs5 from "fs";
|
|
43147
43726
|
import * as os5 from "os";
|
|
43148
43727
|
import * as path5 from "path";
|
|
43149
|
-
var
|
|
43728
|
+
var NOTES_DB_PATH6 = path5.join(
|
|
43150
43729
|
os5.homedir(),
|
|
43151
43730
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
43152
43731
|
);
|
|
@@ -43224,7 +43803,7 @@ function runJsonQuery(sql, dbPath2) {
|
|
|
43224
43803
|
if (!fs5.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
|
|
43225
43804
|
let out;
|
|
43226
43805
|
try {
|
|
43227
|
-
out =
|
|
43806
|
+
out = execFileSync8("sqlite3", ["-readonly", dbPath2, sql], {
|
|
43228
43807
|
encoding: "utf8",
|
|
43229
43808
|
timeout: 5e3,
|
|
43230
43809
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -43244,7 +43823,7 @@ function runJsonQuery(sql, dbPath2) {
|
|
|
43244
43823
|
function coreDataId(store, entity2, pk) {
|
|
43245
43824
|
return `x-coredata://${store}/${entity2}/p${pk}`;
|
|
43246
43825
|
}
|
|
43247
|
-
function resolveIdentifiers(values, entity2, dbPath2 =
|
|
43826
|
+
function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH6) {
|
|
43248
43827
|
const resolved = /* @__PURE__ */ new Map();
|
|
43249
43828
|
const keys = [];
|
|
43250
43829
|
const uuids = [];
|
|
@@ -43290,7 +43869,7 @@ function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH5) {
|
|
|
43290
43869
|
return resolved;
|
|
43291
43870
|
}
|
|
43292
43871
|
var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
|
|
43293
|
-
function lookupStableIdentifiers(ids, entity2, dbPath2 =
|
|
43872
|
+
function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH6) {
|
|
43294
43873
|
const result = /* @__PURE__ */ new Map();
|
|
43295
43874
|
const wanted = /* @__PURE__ */ new Map();
|
|
43296
43875
|
for (const id2 of ids) {
|
|
@@ -43326,7 +43905,7 @@ function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH5) {
|
|
|
43326
43905
|
}
|
|
43327
43906
|
return result;
|
|
43328
43907
|
}
|
|
43329
|
-
function withStableIdentifiers(items, entity2, dbPath2 =
|
|
43908
|
+
function withStableIdentifiers(items, entity2, dbPath2 = NOTES_DB_PATH6) {
|
|
43330
43909
|
const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
|
|
43331
43910
|
if (ids.length === 0) return items;
|
|
43332
43911
|
const found = lookupStableIdentifiers(ids, entity2, dbPath2);
|
|
@@ -43914,12 +44493,12 @@ function positiveTextTerms(node, negated = false) {
|
|
|
43914
44493
|
}
|
|
43915
44494
|
|
|
43916
44495
|
// src/utils/noteQueryStore.ts
|
|
43917
|
-
import { execFileSync as
|
|
44496
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
43918
44497
|
import * as fs6 from "fs";
|
|
43919
44498
|
import * as os6 from "os";
|
|
43920
44499
|
import * as path6 from "path";
|
|
43921
|
-
import { gunzipSync as
|
|
43922
|
-
var
|
|
44500
|
+
import { gunzipSync as gunzipSync5 } from "zlib";
|
|
44501
|
+
var NOTES_DB_PATH7 = path6.join(
|
|
43923
44502
|
os6.homedir(),
|
|
43924
44503
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
43925
44504
|
);
|
|
@@ -44007,13 +44586,13 @@ function countWords(text) {
|
|
|
44007
44586
|
}
|
|
44008
44587
|
return count;
|
|
44009
44588
|
}
|
|
44010
|
-
var
|
|
44589
|
+
var REQUIRED_COLUMNS3 = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
|
|
44011
44590
|
function col(available, alias, name) {
|
|
44012
44591
|
return available.has(name) ? `${alias}.${name}` : "NULL";
|
|
44013
44592
|
}
|
|
44014
44593
|
var entity = (name) => `(SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME='${name}')`;
|
|
44015
44594
|
function buildScanSql(available, options) {
|
|
44016
|
-
const missing =
|
|
44595
|
+
const missing = REQUIRED_COLUMNS3.filter((c) => !available.has(c));
|
|
44017
44596
|
if (missing.length) {
|
|
44018
44597
|
throw new NoteQueryStoreError(
|
|
44019
44598
|
`This macOS version's Notes database lacks columns query-notes needs (${missing.join(", ")}).`,
|
|
@@ -44054,8 +44633,8 @@ function buildScanSql(available, options) {
|
|
|
44054
44633
|
"COMMIT;"
|
|
44055
44634
|
].join(" ");
|
|
44056
44635
|
}
|
|
44057
|
-
function
|
|
44058
|
-
return
|
|
44636
|
+
function runSqlite4(dbPath2, query) {
|
|
44637
|
+
return execFileSync9("sqlite3", ["-readonly", dbPath2, query], {
|
|
44059
44638
|
encoding: "utf8",
|
|
44060
44639
|
timeout: 3e4,
|
|
44061
44640
|
maxBuffer: 512 * 1024 * 1024,
|
|
@@ -44064,7 +44643,7 @@ function runSqlite3(dbPath2, query) {
|
|
|
44064
44643
|
}
|
|
44065
44644
|
function presentColumns2(dbPath2) {
|
|
44066
44645
|
const cols = /* @__PURE__ */ new Set();
|
|
44067
|
-
for (const line of
|
|
44646
|
+
for (const line of runSqlite4(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
|
|
44068
44647
|
const name = line.split("|")[1];
|
|
44069
44648
|
if (name) cols.add(name);
|
|
44070
44649
|
}
|
|
@@ -44119,12 +44698,12 @@ function queryNotes(expression, options = {}) {
|
|
|
44119
44698
|
const includeDeleted = options.includeDeleted ?? false;
|
|
44120
44699
|
const withBodies = needsContent(ast);
|
|
44121
44700
|
const withTags = needsTags(ast);
|
|
44122
|
-
const dbPath2 = options.dbPath ??
|
|
44701
|
+
const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
|
|
44123
44702
|
if (!fs6.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
|
|
44124
44703
|
let output;
|
|
44125
44704
|
try {
|
|
44126
44705
|
const available = presentColumns2(dbPath2);
|
|
44127
|
-
output =
|
|
44706
|
+
output = runSqlite4(
|
|
44128
44707
|
dbPath2,
|
|
44129
44708
|
buildScanSql(available, { scanLimit, includeDeleted, withBodies, withTags })
|
|
44130
44709
|
);
|
|
@@ -44169,7 +44748,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44169
44748
|
const locked = Boolean(row.locked);
|
|
44170
44749
|
let decoded;
|
|
44171
44750
|
let content;
|
|
44172
|
-
const
|
|
44751
|
+
const decode2 = () => {
|
|
44173
44752
|
if (decoded !== void 0) return decoded;
|
|
44174
44753
|
decoded = null;
|
|
44175
44754
|
if (locked) return decoded;
|
|
@@ -44180,7 +44759,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44180
44759
|
try {
|
|
44181
44760
|
decoded = decodeNoteBody(
|
|
44182
44761
|
new Uint8Array(
|
|
44183
|
-
|
|
44762
|
+
gunzipSync5(Buffer.from(row.data, "hex"), { maxOutputLength: 32 * 1024 * 1024 })
|
|
44184
44763
|
)
|
|
44185
44764
|
);
|
|
44186
44765
|
} catch {
|
|
@@ -44200,7 +44779,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44200
44779
|
modified: coreDataMs(row.modified),
|
|
44201
44780
|
content: () => {
|
|
44202
44781
|
if (content !== void 0) return content;
|
|
44203
|
-
const body2 =
|
|
44782
|
+
const body2 = decode2();
|
|
44204
44783
|
if (!body2) return content = null;
|
|
44205
44784
|
const text = body2.text;
|
|
44206
44785
|
const firstBreak = text.indexOf("\n");
|
|
@@ -44225,7 +44804,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44225
44804
|
if (!evaluateNoteQuery(ast, note)) continue;
|
|
44226
44805
|
matched++;
|
|
44227
44806
|
if (hits.length >= limit) continue;
|
|
44228
|
-
const body = locked ? null :
|
|
44807
|
+
const body = locked ? null : decode2();
|
|
44229
44808
|
hits.push({
|
|
44230
44809
|
id: `x-coredata://${uuid2}/ICNote/p${row.pk}`,
|
|
44231
44810
|
title: row.title ?? "",
|
|
@@ -44255,10 +44834,10 @@ function queryNotes(expression, options = {}) {
|
|
|
44255
44834
|
import { spawnSync } from "child_process";
|
|
44256
44835
|
|
|
44257
44836
|
// src/services/nativeTags.ts
|
|
44258
|
-
import { execFileSync as
|
|
44837
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
44259
44838
|
import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
|
|
44260
44839
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
44261
|
-
import { join as
|
|
44840
|
+
import { join as join11 } from "node:path";
|
|
44262
44841
|
|
|
44263
44842
|
// src/services/shortcutConsent.ts
|
|
44264
44843
|
function shortcutConsentHint(shortcut) {
|
|
@@ -44334,7 +44913,7 @@ function addNativeTags(request, deps) {
|
|
|
44334
44913
|
};
|
|
44335
44914
|
}
|
|
44336
44915
|
function listInstalledShortcuts() {
|
|
44337
|
-
return
|
|
44916
|
+
return execFileSync10("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
|
|
44338
44917
|
encoding: "utf8",
|
|
44339
44918
|
timeout: 15e3,
|
|
44340
44919
|
maxBuffer: 1024 * 1024,
|
|
@@ -44360,11 +44939,11 @@ function runNativeTagsShortcut(input) {
|
|
|
44360
44939
|
const status = nativeTagsStatus();
|
|
44361
44940
|
if (!status.installed)
|
|
44362
44941
|
throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
|
|
44363
|
-
const directory = mkdtempSync2(
|
|
44942
|
+
const directory = mkdtempSync2(join11(tmpdir2(), "apple-notes-native-tags-"));
|
|
44364
44943
|
try {
|
|
44365
|
-
const path7 =
|
|
44944
|
+
const path7 = join11(directory, "request.json");
|
|
44366
44945
|
writeFileSync(path7, JSON.stringify(input), { mode: 384 });
|
|
44367
|
-
|
|
44946
|
+
execFileSync10("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path7], {
|
|
44368
44947
|
encoding: "utf8",
|
|
44369
44948
|
timeout: 6e4,
|
|
44370
44949
|
maxBuffer: 1024 * 1024,
|
|
@@ -44383,10 +44962,10 @@ function runNativeTagsShortcut(input) {
|
|
|
44383
44962
|
}
|
|
44384
44963
|
|
|
44385
44964
|
// src/services/backgroundNotes.ts
|
|
44386
|
-
import { execFileSync as
|
|
44965
|
+
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
44387
44966
|
import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
|
|
44388
44967
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
44389
|
-
import { join as
|
|
44968
|
+
import { join as join12 } from "node:path";
|
|
44390
44969
|
|
|
44391
44970
|
// src/utils/appendMarkdown.ts
|
|
44392
44971
|
var escape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -44575,9 +45154,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
44575
45154
|
throw new Error(
|
|
44576
45155
|
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
44577
45156
|
);
|
|
44578
|
-
const directory = mkdtempSync3(
|
|
45157
|
+
const directory = mkdtempSync3(join12(tmpdir3(), "apple-notes-background-"));
|
|
44579
45158
|
try {
|
|
44580
|
-
const file =
|
|
45159
|
+
const file = join12(directory, "request.json");
|
|
44581
45160
|
writeFileSync2(
|
|
44582
45161
|
file,
|
|
44583
45162
|
JSON.stringify({
|
|
@@ -44597,7 +45176,7 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
44597
45176
|
{ mode: 384 }
|
|
44598
45177
|
);
|
|
44599
45178
|
try {
|
|
44600
|
-
|
|
45179
|
+
execFileSync11("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
|
|
44601
45180
|
encoding: "utf8",
|
|
44602
45181
|
timeout: 6e4,
|
|
44603
45182
|
maxBuffer: 1024 * 1024,
|
|
@@ -44889,7 +45468,7 @@ ${request.content}`
|
|
|
44889
45468
|
}
|
|
44890
45469
|
|
|
44891
45470
|
// src/services/capabilityMatrix.ts
|
|
44892
|
-
import { execFileSync as
|
|
45471
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
44893
45472
|
import { release } from "node:os";
|
|
44894
45473
|
var MACOS_SHORTCUTS_CLI = "12.0";
|
|
44895
45474
|
var MACOS_MARKDOWN_IMPORT = "26.0";
|
|
@@ -45096,7 +45675,7 @@ function evaluateFeatures(env, features = FEATURES) {
|
|
|
45096
45675
|
function readMacOSVersion() {
|
|
45097
45676
|
if (process.platform !== "darwin") return null;
|
|
45098
45677
|
try {
|
|
45099
|
-
const out =
|
|
45678
|
+
const out = execFileSync12("/usr/bin/sw_vers", ["-productVersion"], {
|
|
45100
45679
|
encoding: "utf8",
|
|
45101
45680
|
timeout: 3e3,
|
|
45102
45681
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -45250,12 +45829,12 @@ function formatDoctorReport(r) {
|
|
|
45250
45829
|
|
|
45251
45830
|
// src/services/fileConfig.ts
|
|
45252
45831
|
import { existsSync as existsSync9, readFileSync as readFileSync2 } from "fs";
|
|
45253
|
-
import { join as
|
|
45254
|
-
import { homedir as
|
|
45832
|
+
import { join as join13 } from "path";
|
|
45833
|
+
import { homedir as homedir11 } from "os";
|
|
45255
45834
|
function fileConfigPath(env = process.env) {
|
|
45256
45835
|
const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
|
|
45257
45836
|
if (override && override.trim()) return override.trim();
|
|
45258
|
-
return
|
|
45837
|
+
return join13(homedir11(), "Library", "Application Support", "apple-notes-mcp", "config.json");
|
|
45259
45838
|
}
|
|
45260
45839
|
function loadFileConfig(env = process.env, path7 = fileConfigPath(env)) {
|
|
45261
45840
|
const applied = [];
|
|
@@ -45557,109 +46136,12 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
|
|
|
45557
46136
|
};
|
|
45558
46137
|
}
|
|
45559
46138
|
|
|
45560
|
-
// src/utils/noteTables.ts
|
|
45561
|
-
import { gunzipSync as gunzipSync4 } from "node:zlib";
|
|
45562
|
-
var sub = (f, n) => {
|
|
45563
|
-
const value = embeddedMessage(getField(f, n));
|
|
45564
|
-
if (!value) throw new Error(`Missing table field ${n}`);
|
|
45565
|
-
return value;
|
|
45566
|
-
};
|
|
45567
|
-
var num = (f, n) => {
|
|
45568
|
-
const v = varintValue(getField(f, n));
|
|
45569
|
-
if (v === void 0) throw new Error(`Missing table index ${n}`);
|
|
45570
|
-
return v;
|
|
45571
|
-
};
|
|
45572
|
-
var many = (f, n) => getFields(f, n).map((v) => {
|
|
45573
|
-
const m = embeddedMessage(v);
|
|
45574
|
-
if (!m) throw new Error("Invalid table entry");
|
|
45575
|
-
return m;
|
|
45576
|
-
});
|
|
45577
|
-
var hex = (f) => {
|
|
45578
|
-
if (!(f?.value instanceof Uint8Array)) throw new Error("Missing table UUID");
|
|
45579
|
-
return Buffer.from(f.value).toString("hex");
|
|
45580
|
-
};
|
|
45581
|
-
function parseNoteTable(compressed) {
|
|
45582
|
-
const root = decodeMessage(gunzipSync4(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
|
|
45583
|
-
const data = sub(sub(root, 2), 3), entries = many(data, 3);
|
|
45584
|
-
if (entries.length > 1e5) throw new Error("Table too large");
|
|
45585
|
-
const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
|
|
45586
|
-
const entry = (index) => {
|
|
45587
|
-
if (!entries[index]) throw new Error("Invalid table reference");
|
|
45588
|
-
return entries[index];
|
|
45589
|
-
};
|
|
45590
|
-
const uuidIndex = (index) => num(sub(many(sub(entry(index), 13), 3)[0], 2), 2);
|
|
45591
|
-
const roots = entries.filter((e) => {
|
|
45592
|
-
const map = embeddedMessage(getField(e, 13));
|
|
45593
|
-
return map && types[num(map, 1)] === "com.apple.notes.ICTable";
|
|
45594
|
-
});
|
|
45595
|
-
if (roots.length !== 1) throw new Error("Ambiguous native table root");
|
|
45596
|
-
const refs = new Map(
|
|
45597
|
-
many(sub(roots[0], 13), 3).filter((m) => ["crRows", "crColumns", "cellColumns"].includes(keys[num(m, 1)] || "")).map((m) => [keys[num(m, 1)], num(sub(m, 2), 6)])
|
|
45598
|
-
);
|
|
45599
|
-
const ordered = (key) => {
|
|
45600
|
-
const ref = refs.get(key);
|
|
45601
|
-
if (ref === void 0) throw new Error("Missing table dimension");
|
|
45602
|
-
const ordering = sub(sub(entry(ref), 16), 1), array2 = sub(ordering, 1);
|
|
45603
|
-
const ids = many(array2, 2).map((a) => hex(getField(a, 2)));
|
|
45604
|
-
const map = /* @__PURE__ */ new Map();
|
|
45605
|
-
ids.forEach((id2, i) => {
|
|
45606
|
-
const index = uuids.indexOf(id2);
|
|
45607
|
-
if (index < 0) throw new Error("Missing dimension UUID");
|
|
45608
|
-
map.set(index, i);
|
|
45609
|
-
});
|
|
45610
|
-
const aliases = many(sub(ordering, 2), 1).map((pair) => [
|
|
45611
|
-
uuidIndex(num(sub(pair, 1), 6)),
|
|
45612
|
-
uuidIndex(num(sub(pair, 2), 6))
|
|
45613
|
-
]);
|
|
45614
|
-
for (let pass = 0; pass < aliases.length + 1; pass++) {
|
|
45615
|
-
let changed = false;
|
|
45616
|
-
for (const [key2, value] of aliases)
|
|
45617
|
-
if (map.has(key2) && !map.has(value)) {
|
|
45618
|
-
map.set(value, map.get(key2));
|
|
45619
|
-
changed = true;
|
|
45620
|
-
}
|
|
45621
|
-
if (!changed) break;
|
|
45622
|
-
}
|
|
45623
|
-
return { ids, map };
|
|
45624
|
-
};
|
|
45625
|
-
const rows = ordered("crRows"), columns = ordered("crColumns");
|
|
45626
|
-
if (!rows.ids.length || !columns.ids.length || rows.ids.length * columns.ids.length > 1e5)
|
|
45627
|
-
throw new Error("Unsupported table size");
|
|
45628
|
-
const values = rows.ids.map(() => columns.ids.map(() => ""));
|
|
45629
|
-
const cellRef = refs.get("cellColumns");
|
|
45630
|
-
if (cellRef === void 0) throw new Error("Missing table cells");
|
|
45631
|
-
for (const column of many(sub(entry(cellRef), 6), 1)) {
|
|
45632
|
-
const ci = columns.map.get(uuidIndex(num(sub(column, 1), 6)));
|
|
45633
|
-
const cells = entry(num(sub(column, 2), 6));
|
|
45634
|
-
for (const row of many(sub(cells, 6), 1)) {
|
|
45635
|
-
const ri = rows.map.get(uuidIndex(num(sub(row, 1), 6)));
|
|
45636
|
-
if (ri === void 0 || ci === void 0) continue;
|
|
45637
|
-
const note = sub(entry(num(sub(row, 2), 6)), 10);
|
|
45638
|
-
const text = stringValue(getField(note, 2));
|
|
45639
|
-
if (text === void 0 || text.includes("\uFFFC"))
|
|
45640
|
-
throw new Error("Embedded or unsupported table cell");
|
|
45641
|
-
values[ri][ci] = text.replace(/\n$/u, "");
|
|
45642
|
-
}
|
|
45643
|
-
}
|
|
45644
|
-
const rtl = entries.some((e) => {
|
|
45645
|
-
const map = embeddedMessage(getField(e, 13));
|
|
45646
|
-
return map && many(map, 3).some(
|
|
45647
|
-
(m) => stringValue(getField(sub(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
|
|
45648
|
-
);
|
|
45649
|
-
});
|
|
45650
|
-
if (rtl) {
|
|
45651
|
-
for (const row of values) row.reverse();
|
|
45652
|
-
columns.ids.reverse();
|
|
45653
|
-
}
|
|
45654
|
-
return { rows: values, rowIds: rows.ids, columnIds: columns.ids };
|
|
45655
|
-
}
|
|
45656
|
-
|
|
45657
46139
|
// src/utils/noteBlocks.ts
|
|
45658
|
-
import { execFileSync as
|
|
46140
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
45659
46141
|
import { existsSync as existsSync10 } from "node:fs";
|
|
45660
|
-
import { homedir as
|
|
45661
|
-
import { join as
|
|
45662
|
-
import { gunzipSync as
|
|
46142
|
+
import { homedir as homedir12 } from "node:os";
|
|
46143
|
+
import { join as join14 } from "node:path";
|
|
46144
|
+
import { gunzipSync as gunzipSync6 } from "node:zlib";
|
|
45663
46145
|
var NoteBlocksError = class extends Error {
|
|
45664
46146
|
code;
|
|
45665
46147
|
constructor(code, message) {
|
|
@@ -45708,7 +46190,7 @@ var stringOf = (fields, n) => {
|
|
|
45708
46190
|
const b = bytesOf(fields, n);
|
|
45709
46191
|
return b ? utf8.decode(b) : void 0;
|
|
45710
46192
|
};
|
|
45711
|
-
var
|
|
46193
|
+
var sub3 = (fields, n) => {
|
|
45712
46194
|
const b = bytesOf(fields, n);
|
|
45713
46195
|
return b ? decodeWireFields(b) : void 0;
|
|
45714
46196
|
};
|
|
@@ -45741,7 +46223,7 @@ function decodeParagraph(fields, tally) {
|
|
|
45741
46223
|
blockQuote: (varintOf(fields, 8) ?? 0) !== 0
|
|
45742
46224
|
};
|
|
45743
46225
|
if (attrs.styleType === -1) attrs.styleType = null;
|
|
45744
|
-
const checklist =
|
|
46226
|
+
const checklist = sub3(fields, 5);
|
|
45745
46227
|
const checklistId = checklist && bytesOf(checklist, 1);
|
|
45746
46228
|
if (attrs.styleType === 103 && checklistId)
|
|
45747
46229
|
attrs.checklist = { id: hex2(checklistId), done: varintOf(checklist, 2) === 1 };
|
|
@@ -45751,7 +46233,7 @@ function decodeParagraph(fields, tally) {
|
|
|
45751
46233
|
}
|
|
45752
46234
|
function decodeInline(fields) {
|
|
45753
46235
|
const inline = {};
|
|
45754
|
-
const font =
|
|
46236
|
+
const font = sub3(fields, 3);
|
|
45755
46237
|
const hints = font ? varintOf(font, 3) ?? 0 : 0;
|
|
45756
46238
|
const weight = varintOf(fields, 5) ?? 0;
|
|
45757
46239
|
if (weight === 1 || weight === 3 || hints & 1) inline.bold = true;
|
|
@@ -45761,7 +46243,7 @@ function decodeInline(fields) {
|
|
|
45761
46243
|
const baseline = varintOf(fields, 8) ?? 0;
|
|
45762
46244
|
if (baseline > 0) inline.superscript = true;
|
|
45763
46245
|
if (baseline < 0) inline.subscript = true;
|
|
45764
|
-
const color =
|
|
46246
|
+
const color = sub3(fields, 10);
|
|
45765
46247
|
const colorValue = color && colorOf(color);
|
|
45766
46248
|
if (colorValue) inline.color = colorValue;
|
|
45767
46249
|
const emphasis = varintOf(fields, 14);
|
|
@@ -45783,7 +46265,7 @@ function decodeInline(fields) {
|
|
|
45783
46265
|
...size !== void 0 && Number.isFinite(size) ? { size } : {}
|
|
45784
46266
|
};
|
|
45785
46267
|
}
|
|
45786
|
-
const attachment =
|
|
46268
|
+
const attachment = sub3(fields, 12);
|
|
45787
46269
|
const attachmentId = attachment && stringOf(attachment, 1);
|
|
45788
46270
|
if (attachmentId)
|
|
45789
46271
|
inline.attachment = { id: attachmentId, uti: stringOf(attachment, 2) || "unknown" };
|
|
@@ -45798,8 +46280,8 @@ function wrap(error2) {
|
|
|
45798
46280
|
function decodeNoteBlocks(data) {
|
|
45799
46281
|
let note;
|
|
45800
46282
|
try {
|
|
45801
|
-
const document =
|
|
45802
|
-
note = document &&
|
|
46283
|
+
const document = sub3(decodeWireFields(data), 2);
|
|
46284
|
+
note = document && sub3(document, 3);
|
|
45803
46285
|
} catch (error2) {
|
|
45804
46286
|
wrap(error2);
|
|
45805
46287
|
}
|
|
@@ -45824,7 +46306,7 @@ function decodeNoteBlocks(data) {
|
|
|
45824
46306
|
runs.push({
|
|
45825
46307
|
start: position,
|
|
45826
46308
|
length,
|
|
45827
|
-
paragraph: decodeParagraph(
|
|
46309
|
+
paragraph: decodeParagraph(sub3(fields, 2), paragraphTally),
|
|
45828
46310
|
inline: decodeInline(fields)
|
|
45829
46311
|
});
|
|
45830
46312
|
position += length;
|
|
@@ -45947,7 +46429,7 @@ function summarize(blocks, attachments) {
|
|
|
45947
46429
|
function decodeCompressedNoteBlocks(compressed) {
|
|
45948
46430
|
let data;
|
|
45949
46431
|
try {
|
|
45950
|
-
data =
|
|
46432
|
+
data = gunzipSync6(compressed, { maxOutputLength: 32 * 1024 * 1024 });
|
|
45951
46433
|
} catch (error2) {
|
|
45952
46434
|
throw new NoteBlocksError(
|
|
45953
46435
|
"decompress-failed",
|
|
@@ -46001,11 +46483,11 @@ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResp
|
|
|
46001
46483
|
undecodedFields: doc.undecodedFields
|
|
46002
46484
|
};
|
|
46003
46485
|
}
|
|
46004
|
-
var
|
|
46005
|
-
|
|
46486
|
+
var NOTES_DB_PATH8 = join14(
|
|
46487
|
+
homedir12(),
|
|
46006
46488
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
46007
46489
|
);
|
|
46008
|
-
function readNoteBlocks(id2, { dbPath: dbPath2 =
|
|
46490
|
+
function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH8 } = {}) {
|
|
46009
46491
|
const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
|
|
46010
46492
|
if (!pk)
|
|
46011
46493
|
throw new NoteBlocksError(
|
|
@@ -46017,7 +46499,7 @@ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
|
|
|
46017
46499
|
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));";
|
|
46018
46500
|
let output;
|
|
46019
46501
|
try {
|
|
46020
|
-
output =
|
|
46502
|
+
output = execFileSync13(
|
|
46021
46503
|
"/usr/bin/sqlite3",
|
|
46022
46504
|
["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
|
|
46023
46505
|
{
|
|
@@ -46178,7 +46660,7 @@ import {
|
|
|
46178
46660
|
writeFileSync as writeFileSync3
|
|
46179
46661
|
} from "node:fs";
|
|
46180
46662
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
46181
|
-
import { basename, isAbsolute as isAbsolute2, join as
|
|
46663
|
+
import { basename, isAbsolute as isAbsolute2, join as join15 } from "node:path";
|
|
46182
46664
|
var noteId = exactIdInput(
|
|
46183
46665
|
"ICNote",
|
|
46184
46666
|
/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
|
|
@@ -46292,8 +46774,8 @@ function registerDirectOperations(server2, manager) {
|
|
|
46292
46774
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
46293
46775
|
const bytes = localAttachment(path7);
|
|
46294
46776
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
46295
|
-
const directory = mkdtempSync4(
|
|
46296
|
-
const temporaryFile =
|
|
46777
|
+
const directory = mkdtempSync4(join15(tmpdir4(), "notes-attachment-add-"));
|
|
46778
|
+
const temporaryFile = join15(directory, basename(path7));
|
|
46297
46779
|
try {
|
|
46298
46780
|
writeFileSync3(temporaryFile, bytes, { mode: 384 });
|
|
46299
46781
|
if (readSnapshot(manager, id2).hash !== before.hash)
|
|
@@ -47675,6 +48157,42 @@ registerTool(
|
|
|
47675
48157
|
});
|
|
47676
48158
|
}, "Error reading native objects")
|
|
47677
48159
|
);
|
|
48160
|
+
registerTool(
|
|
48161
|
+
"get-note-tables",
|
|
48162
|
+
{
|
|
48163
|
+
description: "Use when: reading the native tables in one exact note as data or Markdown.\nReturns: every table in body order as GitHub-flavored Markdown (first row as header) plus JSON rows with stable row/column ids, and tableCellsComplete.\nDo not use when: you need the whole note (get-note-markdown / get-note-content) or native object ranges and checklist ids (get-native-objects).\nSafety: read-only; reads the NoteStore database and requires Full Disk Access. A cell that cannot be decoded is null in rows, listed in incompleteCells, and marked [undecoded cell] in Markdown; it is never guessed. Cell text only: links and styling inside cells are not rendered.",
|
|
48164
|
+
inputSchema: { id: noteIdInput },
|
|
48165
|
+
outputSchema: {
|
|
48166
|
+
id: external_exports.string().optional(),
|
|
48167
|
+
tables: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
|
|
48168
|
+
tableCount: external_exports.number().optional(),
|
|
48169
|
+
tableCellsComplete: external_exports.boolean().optional(),
|
|
48170
|
+
markdown: external_exports.string().optional()
|
|
48171
|
+
},
|
|
48172
|
+
annotations: { readOnlyHint: true }
|
|
48173
|
+
},
|
|
48174
|
+
withErrorHandling(({ id: id2 }) => {
|
|
48175
|
+
const { metadata, message } = getNoteMetadata(id2);
|
|
48176
|
+
if (!metadata) return errorResponse(message || `Failed to read note "${id2}"`);
|
|
48177
|
+
if (metadata.passwordProtected) {
|
|
48178
|
+
return errorResponse(
|
|
48179
|
+
`Note "${id2}" is password-protected; its tables are encrypted. Unlock it in Notes.app first.`
|
|
48180
|
+
);
|
|
48181
|
+
}
|
|
48182
|
+
const result = notesManager.getNoteTablesById(id2);
|
|
48183
|
+
const count = result.tables.length;
|
|
48184
|
+
const summary = count === 0 ? "This note has no native tables." : `${count} table(s)${result.tableCellsComplete ? "" : " (some content could not be decoded; see tables[].reason)"}:
|
|
48185
|
+
|
|
48186
|
+
${result.markdown}`;
|
|
48187
|
+
return successResponse(summary, {
|
|
48188
|
+
id: id2,
|
|
48189
|
+
tables: result.tables,
|
|
48190
|
+
tableCount: count,
|
|
48191
|
+
tableCellsComplete: result.tableCellsComplete,
|
|
48192
|
+
markdown: result.markdown
|
|
48193
|
+
});
|
|
48194
|
+
}, "Error reading note tables")
|
|
48195
|
+
);
|
|
47678
48196
|
registerTool(
|
|
47679
48197
|
"get-note-blocks",
|
|
47680
48198
|
{
|
|
@@ -48965,6 +49483,44 @@ ${summary}`,
|
|
|
48965
49483
|
);
|
|
48966
49484
|
}, "Error reading checklist state")
|
|
48967
49485
|
);
|
|
49486
|
+
registerTool(
|
|
49487
|
+
"get-audio-transcripts",
|
|
49488
|
+
{
|
|
49489
|
+
description: "Use when: reading the transcript (and summary, if any) that Notes already computed for the audio recordings in one note, by id.\nReturns: one entry per top-level audio attachment in body order, with attachmentId, durationSeconds, status (ok, none when no transcript is stored, or undecodable with a reason), joined transcript text, wordCount, speakers and summary when stored, and optional word-level segments.\nDo not use when: you need the audio file itself (save-attachment) or the note body (get-note-content). This tool does not transcribe; it only reads what Notes stored.\nSafety: read-only; requires Full Disk Access and reads the NoteStore database. Password-protected notes are refused. Large responses drop segments first, then shorten text, to stay under APPLE_NOTES_MCP_EXPORT_MAX_BYTES.",
|
|
49490
|
+
inputSchema: {
|
|
49491
|
+
id: noteIdInput,
|
|
49492
|
+
includeSegments: external_exports.boolean().optional().describe(
|
|
49493
|
+
"Also return word-level segments (text, start and duration in seconds, speaker). Default false."
|
|
49494
|
+
),
|
|
49495
|
+
maxSegments: external_exports.number().int().min(1).max(MAX_SEGMENTS_LIMIT).optional().describe(
|
|
49496
|
+
`Cap on segments returned per attachment when includeSegments is true (default ${DEFAULT_MAX_SEGMENTS}).`
|
|
49497
|
+
)
|
|
49498
|
+
},
|
|
49499
|
+
outputSchema: {
|
|
49500
|
+
id: external_exports.string().optional(),
|
|
49501
|
+
attachments: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
49502
|
+
bodyOrder: external_exports.boolean().optional(),
|
|
49503
|
+
truncated: external_exports.boolean().optional()
|
|
49504
|
+
},
|
|
49505
|
+
annotations: { readOnlyHint: true }
|
|
49506
|
+
},
|
|
49507
|
+
withErrorHandling(({ id: id2, includeSegments, maxSegments }) => {
|
|
49508
|
+
let result;
|
|
49509
|
+
try {
|
|
49510
|
+
result = notesManager.getAudioTranscripts(id2, { includeSegments, maxSegments });
|
|
49511
|
+
} catch (error2) {
|
|
49512
|
+
if (error2 instanceof AudioTranscriptError) return errorResponse(error2.message);
|
|
49513
|
+
throw error2;
|
|
49514
|
+
}
|
|
49515
|
+
const toResponse = (r) => successResponse(formatTranscriptsText(r), { ...r });
|
|
49516
|
+
const fitted = fitTranscriptsToBudget(
|
|
49517
|
+
result,
|
|
49518
|
+
exportMaxResponseBytes(),
|
|
49519
|
+
(r) => Buffer.byteLength(JSON.stringify(toResponse(r)))
|
|
49520
|
+
);
|
|
49521
|
+
return toResponse(fitted);
|
|
49522
|
+
}, "Error reading audio transcripts")
|
|
49523
|
+
);
|
|
48968
49524
|
registerTool(
|
|
48969
49525
|
"get-note-metadata",
|
|
48970
49526
|
{
|