apple-notes-mcp 2.9.4 → 2.9.6
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 +85 -2
- package/build/index.js +964 -71
- package/native/public-helper/apple-notes-public-helper.swift +257 -0
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -2991,7 +2991,7 @@ var require_compile = __commonJS({
|
|
|
2991
2991
|
const schOrFunc = root.refs[ref];
|
|
2992
2992
|
if (schOrFunc)
|
|
2993
2993
|
return schOrFunc;
|
|
2994
|
-
let _sch =
|
|
2994
|
+
let _sch = resolve7.call(this, root, ref);
|
|
2995
2995
|
if (_sch === void 0) {
|
|
2996
2996
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2997
2997
|
const { schemaId } = this.opts;
|
|
@@ -3018,7 +3018,7 @@ var require_compile = __commonJS({
|
|
|
3018
3018
|
function sameSchemaEnv(s1, s2) {
|
|
3019
3019
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3020
3020
|
}
|
|
3021
|
-
function
|
|
3021
|
+
function resolve7(root, ref) {
|
|
3022
3022
|
let sch;
|
|
3023
3023
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3024
3024
|
ref = sch;
|
|
@@ -3843,7 +3843,7 @@ var require_fast_uri = __commonJS({
|
|
|
3843
3843
|
}
|
|
3844
3844
|
return uri;
|
|
3845
3845
|
}
|
|
3846
|
-
function
|
|
3846
|
+
function resolve7(baseURI, relativeURI, options) {
|
|
3847
3847
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3848
3848
|
const {
|
|
3849
3849
|
parsed: baseParsed,
|
|
@@ -4205,7 +4205,7 @@ var require_fast_uri = __commonJS({
|
|
|
4205
4205
|
var fastUri = {
|
|
4206
4206
|
SCHEMES,
|
|
4207
4207
|
normalize,
|
|
4208
|
-
resolve:
|
|
4208
|
+
resolve: resolve7,
|
|
4209
4209
|
resolveComponent,
|
|
4210
4210
|
equal,
|
|
4211
4211
|
serialize,
|
|
@@ -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 join27(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 = join27(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 join27(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);
|
|
@@ -36608,7 +36608,7 @@ var Protocol = class {
|
|
|
36608
36608
|
return;
|
|
36609
36609
|
}
|
|
36610
36610
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
36611
|
-
await new Promise((
|
|
36611
|
+
await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
|
|
36612
36612
|
options?.signal?.throwIfAborted();
|
|
36613
36613
|
}
|
|
36614
36614
|
} catch (error2) {
|
|
@@ -36625,7 +36625,7 @@ var Protocol = class {
|
|
|
36625
36625
|
*/
|
|
36626
36626
|
request(request, resultSchema, options) {
|
|
36627
36627
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
36628
|
-
return new Promise((
|
|
36628
|
+
return new Promise((resolve7, reject) => {
|
|
36629
36629
|
const earlyReject = (error2) => {
|
|
36630
36630
|
reject(error2);
|
|
36631
36631
|
};
|
|
@@ -36703,7 +36703,7 @@ var Protocol = class {
|
|
|
36703
36703
|
if (!parseResult.success) {
|
|
36704
36704
|
reject(parseResult.error);
|
|
36705
36705
|
} else {
|
|
36706
|
-
|
|
36706
|
+
resolve7(parseResult.data);
|
|
36707
36707
|
}
|
|
36708
36708
|
} catch (error2) {
|
|
36709
36709
|
reject(error2);
|
|
@@ -36964,12 +36964,12 @@ var Protocol = class {
|
|
|
36964
36964
|
}
|
|
36965
36965
|
} catch {
|
|
36966
36966
|
}
|
|
36967
|
-
return new Promise((
|
|
36967
|
+
return new Promise((resolve7, reject) => {
|
|
36968
36968
|
if (signal.aborted) {
|
|
36969
36969
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
36970
36970
|
return;
|
|
36971
36971
|
}
|
|
36972
|
-
const timeoutId = setTimeout(
|
|
36972
|
+
const timeoutId = setTimeout(resolve7, interval);
|
|
36973
36973
|
signal.addEventListener("abort", () => {
|
|
36974
36974
|
clearTimeout(timeoutId);
|
|
36975
36975
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -38282,7 +38282,7 @@ var McpServer = class {
|
|
|
38282
38282
|
let task = createTaskResult.task;
|
|
38283
38283
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
38284
38284
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
38285
|
-
await new Promise((
|
|
38285
|
+
await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
|
|
38286
38286
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
38287
38287
|
if (!updatedTask) {
|
|
38288
38288
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -38970,12 +38970,12 @@ var StdioServerTransport = class {
|
|
|
38970
38970
|
this.onclose?.();
|
|
38971
38971
|
}
|
|
38972
38972
|
send(message) {
|
|
38973
|
-
return new Promise((
|
|
38973
|
+
return new Promise((resolve7) => {
|
|
38974
38974
|
const json2 = serializeMessage(message);
|
|
38975
38975
|
if (this._stdout.write(json2)) {
|
|
38976
|
-
|
|
38976
|
+
resolve7();
|
|
38977
38977
|
} else {
|
|
38978
|
-
this._stdout.once("drain",
|
|
38978
|
+
this._stdout.once("drain", resolve7);
|
|
38979
38979
|
}
|
|
38980
38980
|
});
|
|
38981
38981
|
}
|
|
@@ -47328,13 +47328,13 @@ var escapeSegment = escapeFolderName;
|
|
|
47328
47328
|
function resolveFolders(rows) {
|
|
47329
47329
|
const byPk = new Map(rows.map((row) => [row.pk, row]));
|
|
47330
47330
|
const resolved = /* @__PURE__ */ new Map();
|
|
47331
|
-
const
|
|
47331
|
+
const resolve7 = (pk, seen) => {
|
|
47332
47332
|
const cached2 = resolved.get(pk);
|
|
47333
47333
|
if (cached2) return cached2;
|
|
47334
47334
|
const row = byPk.get(pk);
|
|
47335
47335
|
if (!row || seen.has(pk)) return void 0;
|
|
47336
47336
|
seen.add(pk);
|
|
47337
|
-
const parent = row.parent !== null ?
|
|
47337
|
+
const parent = row.parent !== null ? resolve7(row.parent, seen) : void 0;
|
|
47338
47338
|
const name = row.name ?? "";
|
|
47339
47339
|
const path10 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
|
|
47340
47340
|
const plainPath = parent ? `${parent.plainPath}/${name}` : name;
|
|
@@ -47348,7 +47348,7 @@ function resolveFolders(rows) {
|
|
|
47348
47348
|
resolved.set(pk, info);
|
|
47349
47349
|
return info;
|
|
47350
47350
|
};
|
|
47351
|
-
for (const row of rows)
|
|
47351
|
+
for (const row of rows) resolve7(row.pk, /* @__PURE__ */ new Set());
|
|
47352
47352
|
return resolved;
|
|
47353
47353
|
}
|
|
47354
47354
|
var coreDataMs = (seconds) => seconds === null || !Number.isFinite(seconds) ? void 0 : CORE_DATA_EPOCH_MS2 + seconds * 1e3;
|
|
@@ -49582,6 +49582,212 @@ function readNoteStructure(id2, { dbPath: dbPath2 = NOTES_DB_PATH7, includeText
|
|
|
49582
49582
|
};
|
|
49583
49583
|
}
|
|
49584
49584
|
|
|
49585
|
+
// src/utils/noteLinkInventory.ts
|
|
49586
|
+
import { dirname as dirname5 } from "node:path";
|
|
49587
|
+
var BODY_BATCH = 100;
|
|
49588
|
+
function matchFolder(folders, wanted, account) {
|
|
49589
|
+
const paths = folderPaths(folders);
|
|
49590
|
+
const segments = splitFolderPath(wanted);
|
|
49591
|
+
const key = (parts) => parts.join("\0");
|
|
49592
|
+
const pool = folders.filter(
|
|
49593
|
+
(f) => !f.tombstoned && !f.trash && (account === void 0 || f.account === account)
|
|
49594
|
+
);
|
|
49595
|
+
const byPath = segments.length === 1 ? pool.filter((f) => f.name === segments[0]) : pool.filter((f) => key(splitFolderPath(paths.get(f.pk))) === key(segments));
|
|
49596
|
+
const matches = byPath.length ? byPath : pool.filter((f) => f.name === wanted);
|
|
49597
|
+
if (!matches.length) throw new NoteStoreError(`No folder matches "${wanted}".`, "invalid_input");
|
|
49598
|
+
if (matches.length > 1)
|
|
49599
|
+
throw new NoteStoreError(
|
|
49600
|
+
`Folder "${wanted}" is ambiguous; use one of these paths: ${matches.map((f) => paths.get(f.pk)).join(", ")}.`,
|
|
49601
|
+
"invalid_input"
|
|
49602
|
+
);
|
|
49603
|
+
return { folder: matches[0], path: paths.get(matches[0].pk) };
|
|
49604
|
+
}
|
|
49605
|
+
function inventorySql(columns) {
|
|
49606
|
+
requireColumns(columns, ["ZFOLDER"], "list-note-links");
|
|
49607
|
+
const c = (alias, name) => col(columns, alias, name);
|
|
49608
|
+
const subtree = columns.has("ZPARENT") ? `WITH RECURSIVE sub(pk) AS (SELECT @folder UNION
|
|
49609
|
+
SELECT x.Z_PK FROM ZICCLOUDSYNCINGOBJECT x JOIN sub ON x.ZPARENT = sub.pk
|
|
49610
|
+
WHERE @subfolders <> 0 AND x.Z_ENT = ${entity("ICFolder")})
|
|
49611
|
+
SELECT pk FROM sub` : "SELECT @folder";
|
|
49612
|
+
const scopeWhere = `n.Z_ENT = ${entity("ICNote")} AND (
|
|
49613
|
+
(@note <> 0 AND n.Z_PK = @note) OR
|
|
49614
|
+
(@note = 0 AND ${activeNoteSql(columns, "n", "f")}
|
|
49615
|
+
AND (@account = 0 OR a.Z_PK = @account)
|
|
49616
|
+
AND (@folder = 0 OR n.ZFOLDER IN (${subtree}))))`;
|
|
49617
|
+
const scope2 = `SELECT n.Z_PK FROM ZICCLOUDSYNCINGOBJECT n ${folderAccountJoins(columns)} WHERE ${scopeWhere}`;
|
|
49618
|
+
return {
|
|
49619
|
+
rows: [
|
|
49620
|
+
"BEGIN;",
|
|
49621
|
+
`SELECT json_object('k', 'note', 'pk', n.Z_PK, 'identifier', ${c("n", "ZIDENTIFIER")},
|
|
49622
|
+
'title', ${c("n", "ZTITLE1")}, 'folder', n.ZFOLDER, 'account', a.Z_PK,
|
|
49623
|
+
'modified', ${c("n", "ZMODIFICATIONDATE1")})
|
|
49624
|
+
FROM ZICCLOUDSYNCINGOBJECT n ${folderAccountJoins(columns)} WHERE ${scopeWhere};`,
|
|
49625
|
+
`SELECT json_object('k', 'card', 'pk', att.Z_PK, 'note', ${c("att", "ZNOTE")},
|
|
49626
|
+
'identifier', ${c("att", "ZIDENTIFIER")}, 'url', ${c("att", "ZURLSTRING")},
|
|
49627
|
+
'title', ${c("att", "ZTITLE")})
|
|
49628
|
+
FROM ZICCLOUDSYNCINGOBJECT att
|
|
49629
|
+
WHERE att.Z_ENT = ${entity("ICAttachment")} AND ${notTombstonedSql(columns, "att")}
|
|
49630
|
+
AND ${c("att", "ZTYPEUTI")} LIKE 'public.url%' AND ${c("att", "ZURLSTRING")} IS NOT NULL
|
|
49631
|
+
AND ${c("att", "ZNOTE")} IN (${scope2});`,
|
|
49632
|
+
`SELECT json_object('k', 'chip', 'note', ${c("i", "ZNOTE1")},
|
|
49633
|
+
'identifier', ${c("i", "ZIDENTIFIER")}, 'token', ${c("i", "ZTOKENCONTENTIDENTIFIER")},
|
|
49634
|
+
'alt', ${c("i", "ZALTTEXT")})
|
|
49635
|
+
FROM ZICCLOUDSYNCINGOBJECT i
|
|
49636
|
+
WHERE i.Z_ENT = ${entity("ICInlineAttachment")} AND ${notTombstonedSql(columns, "i")}
|
|
49637
|
+
AND ${c("i", "ZTYPEUTI1")} = '${NOTE_LINK_UTI}' AND ${c("i", "ZNOTE1")} IN (${scope2});`,
|
|
49638
|
+
"COMMIT;"
|
|
49639
|
+
].join("\n"),
|
|
49640
|
+
bodies: `SELECT json_object('note', d.ZNOTE, 'data', hex(d.ZDATA),
|
|
49641
|
+
'encrypted', d.ZCRYPTOINITIALIZATIONVECTOR IS NOT NULL)
|
|
49642
|
+
FROM ZICNOTEDATA d WHERE d.ZNOTE IN (${scope2}) AND d.ZNOTE > @after
|
|
49643
|
+
ORDER BY d.ZNOTE LIMIT ${BODY_BATCH};`
|
|
49644
|
+
};
|
|
49645
|
+
}
|
|
49646
|
+
function decodeBodies(dbPath2, sql, params) {
|
|
49647
|
+
const docs = /* @__PURE__ */ new Map();
|
|
49648
|
+
let after = 0;
|
|
49649
|
+
for (; ; ) {
|
|
49650
|
+
const rows = parseJsonLines(
|
|
49651
|
+
runReadOnlySql(dbPath2, sql, { ...params, after: { int: after } })
|
|
49652
|
+
);
|
|
49653
|
+
for (const row of rows) {
|
|
49654
|
+
after = Math.max(after, row.note);
|
|
49655
|
+
if (row.encrypted || !row.data) continue;
|
|
49656
|
+
try {
|
|
49657
|
+
docs.set(row.note, decodeCompressedNoteBlocks(Buffer.from(row.data, "hex")));
|
|
49658
|
+
} catch {
|
|
49659
|
+
}
|
|
49660
|
+
}
|
|
49661
|
+
if (rows.length < BODY_BATCH) return docs;
|
|
49662
|
+
}
|
|
49663
|
+
}
|
|
49664
|
+
var KIND_ORDER2 = { inline: 0, card: 1, note: 2, section: 3 };
|
|
49665
|
+
function listNoteLinks(options = {}) {
|
|
49666
|
+
const { dbPath: dbPath2 = NOTES_DB_PATH7, offset = 0, limit = 200, maxBytes = 4 * 1024 * 1024 } = options;
|
|
49667
|
+
if (options.id && (options.account || options.folder))
|
|
49668
|
+
throw new NoteStoreError("Pass either id or account/folder, not both.", "invalid_input");
|
|
49669
|
+
const notePk = options.id ? parseNoteId2(options.id).pk : 0;
|
|
49670
|
+
const columns = readColumns(dbPath2);
|
|
49671
|
+
const sql = inventorySql(columns);
|
|
49672
|
+
const context = readStoreContext(dbPath2, columns);
|
|
49673
|
+
const paths = folderPaths(context.folders);
|
|
49674
|
+
const scope2 = {};
|
|
49675
|
+
let accountPk = 0;
|
|
49676
|
+
let folderPk = 0;
|
|
49677
|
+
const includeSubfolders = options.includeSubfolders ?? true;
|
|
49678
|
+
if (options.account) {
|
|
49679
|
+
const account = resolveAccountName(context.accounts, options.account);
|
|
49680
|
+
accountPk = account.pk;
|
|
49681
|
+
scope2.account = account.name;
|
|
49682
|
+
scope2.accountIdentifier = account.identifier;
|
|
49683
|
+
}
|
|
49684
|
+
if (options.folder) {
|
|
49685
|
+
const { folder, path: path10 } = matchFolder(context.folders, options.folder, accountPk || void 0);
|
|
49686
|
+
folderPk = folder.pk;
|
|
49687
|
+
scope2.folder = folder.name;
|
|
49688
|
+
scope2.folderPath = path10;
|
|
49689
|
+
scope2.includeSubfolders = includeSubfolders;
|
|
49690
|
+
}
|
|
49691
|
+
if (options.id) scope2.note = options.id;
|
|
49692
|
+
const params = {
|
|
49693
|
+
note: { int: notePk },
|
|
49694
|
+
account: { int: accountPk },
|
|
49695
|
+
folder: { int: folderPk },
|
|
49696
|
+
subfolders: { int: includeSubfolders ? 1 : 0 }
|
|
49697
|
+
};
|
|
49698
|
+
const rows = parseJsonLines(runReadOnlySql(dbPath2, sql.rows, params));
|
|
49699
|
+
const notes = rows.filter((row) => row.k === "note");
|
|
49700
|
+
if (options.id && !notes.length)
|
|
49701
|
+
throw new NoteStoreError(`No note found for ID "${options.id}".`, "invalid_input");
|
|
49702
|
+
const includeInline = options.includeInline ?? Boolean(options.id);
|
|
49703
|
+
const docs = includeInline ? decodeBodies(dbPath2, sql.bodies, params) : /* @__PURE__ */ new Map();
|
|
49704
|
+
const noteById = new Map(notes.map((note) => [note.pk, note]));
|
|
49705
|
+
const accountById = new Map(context.accounts.map((account) => [account.pk, account]));
|
|
49706
|
+
const folderById = new Map(context.folders.map((folder) => [folder.pk, folder]));
|
|
49707
|
+
const source = (pk) => {
|
|
49708
|
+
const note = noteById.get(pk);
|
|
49709
|
+
const account = accountById.get(note.account ?? -1);
|
|
49710
|
+
const folder = folderById.get(note.folder ?? -1);
|
|
49711
|
+
return {
|
|
49712
|
+
noteId: noteIdFor(context.uuid, pk),
|
|
49713
|
+
noteIdentifier: note.identifier,
|
|
49714
|
+
noteTitle: note.title,
|
|
49715
|
+
noteModified: coreDataToIso(note.modified),
|
|
49716
|
+
folder: folder?.name ?? null,
|
|
49717
|
+
folderPath: folder ? paths.get(folder.pk) : null,
|
|
49718
|
+
account: account?.name ?? null,
|
|
49719
|
+
accountIdentifier: account?.identifier ?? null
|
|
49720
|
+
};
|
|
49721
|
+
};
|
|
49722
|
+
const containerDir = dirname5(dbPath2);
|
|
49723
|
+
const previewEntries = /* @__PURE__ */ new Map();
|
|
49724
|
+
const previewFor = (identifier, account) => {
|
|
49725
|
+
const accountDir = identifier ? resolveAccountDir(containerDir, account) : null;
|
|
49726
|
+
if (!accountDir || !identifier) return null;
|
|
49727
|
+
if (!previewEntries.has(accountDir))
|
|
49728
|
+
previewEntries.set(accountDir, listPreviewEntries(accountDir));
|
|
49729
|
+
return previewPaths(accountDir, identifier, previewEntries.get(accountDir))[0] ?? null;
|
|
49730
|
+
};
|
|
49731
|
+
const all = [];
|
|
49732
|
+
const add = (pk, link) => all.push({ pk, link: { ...link, ...source(pk) } });
|
|
49733
|
+
for (const [pk, doc] of docs) for (const link of inlineLinks(doc)) add(pk, link);
|
|
49734
|
+
for (const row of rows) {
|
|
49735
|
+
if (row.k === "card") {
|
|
49736
|
+
const noteId3 = noteIdFor(context.uuid, row.note);
|
|
49737
|
+
const link = cardLink(
|
|
49738
|
+
{ pk: row.pk, identifier: row.identifier ?? "", url: row.url, title: row.title },
|
|
49739
|
+
{
|
|
49740
|
+
doc: docs.get(row.note),
|
|
49741
|
+
noteId: noteId3,
|
|
49742
|
+
previewPath: previewFor(row.identifier, source(row.note).accountIdentifier)
|
|
49743
|
+
}
|
|
49744
|
+
);
|
|
49745
|
+
if (link) add(row.note, link);
|
|
49746
|
+
} else if (row.k === "chip") {
|
|
49747
|
+
const link = nativeLink(
|
|
49748
|
+
{ identifier: row.identifier ?? "", token: row.token, alt: row.alt },
|
|
49749
|
+
docs.get(row.note)
|
|
49750
|
+
);
|
|
49751
|
+
if (link) add(row.note, link);
|
|
49752
|
+
}
|
|
49753
|
+
}
|
|
49754
|
+
const wanted = options.kinds?.length ? new Set(options.kinds) : void 0;
|
|
49755
|
+
const links = all.filter(({ link }) => !wanted || wanted.has(link.kind)).sort(
|
|
49756
|
+
(a, b) => (b.link.noteModified ?? "").localeCompare(a.link.noteModified ?? "") || a.pk - b.pk || (a.link.start ?? Number.MAX_SAFE_INTEGER) - (b.link.start ?? Number.MAX_SAFE_INTEGER) || KIND_ORDER2[a.link.kind] - KIND_ORDER2[b.link.kind]
|
|
49757
|
+
).map(({ link }) => link);
|
|
49758
|
+
const counts = { inline: 0, card: 0, note: 0, section: 0 };
|
|
49759
|
+
for (const link of links) counts[link.kind]++;
|
|
49760
|
+
const start = Math.min(Math.max(0, offset), links.length);
|
|
49761
|
+
const page = [];
|
|
49762
|
+
let bytes = 0;
|
|
49763
|
+
for (let i = start; i < links.length && page.length < limit; i++) {
|
|
49764
|
+
const size = Buffer.byteLength(JSON.stringify(links[i]));
|
|
49765
|
+
if (page.length && bytes + size > maxBytes) break;
|
|
49766
|
+
page.push(links[i]);
|
|
49767
|
+
bytes += size;
|
|
49768
|
+
}
|
|
49769
|
+
const next = start + page.length;
|
|
49770
|
+
return {
|
|
49771
|
+
scope: scope2,
|
|
49772
|
+
inlineIncluded: includeInline,
|
|
49773
|
+
notesInScope: notes.length,
|
|
49774
|
+
notesWithoutBody: includeInline ? notes.length - docs.size : 0,
|
|
49775
|
+
counts,
|
|
49776
|
+
links: page,
|
|
49777
|
+
page: {
|
|
49778
|
+
offset: start,
|
|
49779
|
+
returned: page.length,
|
|
49780
|
+
total: links.length,
|
|
49781
|
+
hasMore: next < links.length,
|
|
49782
|
+
...next < links.length ? { nextOffset: next } : {}
|
|
49783
|
+
}
|
|
49784
|
+
};
|
|
49785
|
+
}
|
|
49786
|
+
function describeLinkInventory(result) {
|
|
49787
|
+
const { counts, page } = result;
|
|
49788
|
+
return `Found ${page.total} links in ${result.notesInScope} notes (inline ${counts.inline}${result.inlineIncluded ? "" : " not scanned"}, card ${counts.card}, note ${counts.note}, section ${counts.section}); returned ${page.returned} from offset ${page.offset}` + (page.hasMore ? `; more at offset ${page.nextOffset}` : "") + ".";
|
|
49789
|
+
}
|
|
49790
|
+
|
|
49585
49791
|
// src/utils/linkInsert.ts
|
|
49586
49792
|
var MAX_LINK_URL_LENGTH = 4096;
|
|
49587
49793
|
var MAX_LINK_LABEL_LENGTH = 2e3;
|
|
@@ -49699,7 +49905,7 @@ function insertLink(request, deps) {
|
|
|
49699
49905
|
|
|
49700
49906
|
// src/services/notesExport.ts
|
|
49701
49907
|
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
49702
|
-
import { basename as basename4, dirname as
|
|
49908
|
+
import { basename as basename4, dirname as dirname6, extname as extname4, join as join21 } from "node:path";
|
|
49703
49909
|
|
|
49704
49910
|
// src/utils/exportAssets.ts
|
|
49705
49911
|
import {
|
|
@@ -51022,7 +51228,7 @@ function loadNotes(ids, single, read) {
|
|
|
51022
51228
|
return { notes, skipped };
|
|
51023
51229
|
}
|
|
51024
51230
|
function openOutput(output) {
|
|
51025
|
-
mkdirSync5(
|
|
51231
|
+
mkdirSync5(dirname6(output), { recursive: true });
|
|
51026
51232
|
try {
|
|
51027
51233
|
return openCreateOnly(output);
|
|
51028
51234
|
} catch (error2) {
|
|
@@ -51047,7 +51253,7 @@ function exportNotesMarkdown(request, deps) {
|
|
|
51047
51253
|
const ids = selectNotes(request, deps);
|
|
51048
51254
|
const { notes, skipped } = loadNotes(ids, !!request.id, deps.readNote);
|
|
51049
51255
|
const fd = output ? openOutput(output) : void 0;
|
|
51050
|
-
const writer = assetsDir ? new SidecarWriter(assetsDir, output ?
|
|
51256
|
+
const writer = assetsDir ? new SidecarWriter(assetsDir, output ? dirname6(output) : void 0) : void 0;
|
|
51051
51257
|
const ctx = {
|
|
51052
51258
|
stats: emptyStats(),
|
|
51053
51259
|
...writer ? { writer, locator: deps.locator ?? new AssetLocator() } : {}
|
|
@@ -51077,7 +51283,7 @@ function exportNotesMarkdown(request, deps) {
|
|
|
51077
51283
|
return { ...receipt, markdown };
|
|
51078
51284
|
}
|
|
51079
51285
|
function defaultSidecarDir(output) {
|
|
51080
|
-
return join21(
|
|
51286
|
+
return join21(dirname6(output), `${basename4(output, extname4(output))}.assets`);
|
|
51081
51287
|
}
|
|
51082
51288
|
function exportNotesHtml(request, deps) {
|
|
51083
51289
|
if (!request.outputPath)
|
|
@@ -51098,7 +51304,7 @@ function exportNotesHtml(request, deps) {
|
|
|
51098
51304
|
const ids = selectNotes(request, deps);
|
|
51099
51305
|
const { notes, skipped } = loadNotes(ids, !!request.id, deps.readNote);
|
|
51100
51306
|
const fd = openOutput(output);
|
|
51101
|
-
const writer = assetsDir ? new SidecarWriter(assetsDir,
|
|
51307
|
+
const writer = assetsDir ? new SidecarWriter(assetsDir, dirname6(output)) : new DataUrlWriter();
|
|
51102
51308
|
const ctx = {
|
|
51103
51309
|
stats: emptyStats(),
|
|
51104
51310
|
writer,
|
|
@@ -52204,7 +52410,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
52204
52410
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
52205
52411
|
import { existsSync as existsSync13 } from "node:fs";
|
|
52206
52412
|
import { release as release2 } from "node:os";
|
|
52207
|
-
import { dirname as
|
|
52413
|
+
import { dirname as dirname7, resolve as resolve4 } from "node:path";
|
|
52208
52414
|
import { fileURLToPath } from "node:url";
|
|
52209
52415
|
var OPTIONAL_BRIDGE_NOTE = "(optional \u2014 needed only for create-note format: markdown, macOS 26+)";
|
|
52210
52416
|
var MARKDOWN_MIN_DARWIN_MAJOR = 25;
|
|
@@ -52227,7 +52433,7 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
52227
52433
|
const result = spawnSync2("/usr/bin/open", [path10], { encoding: "utf8" });
|
|
52228
52434
|
return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
|
|
52229
52435
|
});
|
|
52230
|
-
const baseDirectory = dependencies.baseDirectory || resolve4(
|
|
52436
|
+
const baseDirectory = dependencies.baseDirectory || resolve4(dirname7(fileURLToPath(import.meta.url)), "../shortcuts");
|
|
52231
52437
|
const osRelease = (dependencies.osRelease || release2)();
|
|
52232
52438
|
const darwinMajor = Number.parseInt(osRelease.split(".")[0], 10);
|
|
52233
52439
|
const items = shortcutFiles.map(({ name, file, optional: optional2 }) => {
|
|
@@ -52289,27 +52495,627 @@ function formatShortcutSetup(report) {
|
|
|
52289
52495
|
return lines.join("\n");
|
|
52290
52496
|
}
|
|
52291
52497
|
|
|
52292
|
-
// src/services/
|
|
52293
|
-
import { spawnSync as
|
|
52498
|
+
// src/services/publicHelper.ts
|
|
52499
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
52500
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
52294
52501
|
import {
|
|
52295
52502
|
chmodSync,
|
|
52296
|
-
existsSync as
|
|
52503
|
+
existsSync as existsSync14,
|
|
52297
52504
|
mkdirSync as mkdirSync6,
|
|
52298
52505
|
mkdtempSync as mkdtempSync5,
|
|
52506
|
+
readFileSync as readFileSync4,
|
|
52299
52507
|
renameSync,
|
|
52300
52508
|
rmSync as rmSync5,
|
|
52301
52509
|
writeFileSync as writeFileSync4
|
|
52302
52510
|
} from "node:fs";
|
|
52303
|
-
import { release as release3 } from "node:os";
|
|
52304
|
-
import { join as
|
|
52511
|
+
import { homedir as homedir19, release as release3 } from "node:os";
|
|
52512
|
+
import { dirname as dirname8, join as join24, resolve as resolve5 } from "node:path";
|
|
52513
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
52514
|
+
var PUBLIC_HELPER_PROTOCOL = 1;
|
|
52515
|
+
var PUBLIC_HELPER_DIR_ENV = "APPLE_NOTES_MCP_PUBLIC_HELPER_DIR";
|
|
52516
|
+
var PUBLIC_HELPER_TIMEOUT_ENV = "APPLE_NOTES_MCP_PUBLIC_HELPER_TIMEOUT_MS";
|
|
52517
|
+
var PUBLIC_HELPER_BINARY = "apple-notes-public-helper";
|
|
52518
|
+
var PUBLIC_HELPER_SOURCE = "native/public-helper/apple-notes-public-helper.swift";
|
|
52519
|
+
var PUBLIC_HELPER_MANIFEST = "manifest.json";
|
|
52520
|
+
var PUBLIC_HELPER_SETUP_COMMAND = "apple-notes-mcp setup --public-helper";
|
|
52521
|
+
var PUBLIC_HELPER_ACTIONS = /* @__PURE__ */ new Set(["hello", "decode_drawing"]);
|
|
52522
|
+
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
52523
|
+
var MAX_OUTPUT_BYTES = 256 * 1024 * 1024;
|
|
52524
|
+
var publicManifestSchema = external_exports.object({
|
|
52525
|
+
schemaVersion: external_exports.literal(1),
|
|
52526
|
+
protocolVersion: external_exports.number().int(),
|
|
52527
|
+
sourceSha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
52528
|
+
binarySha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
52529
|
+
builtAt: external_exports.string(),
|
|
52530
|
+
osVersion: external_exports.string(),
|
|
52531
|
+
compiler: external_exports.string()
|
|
52532
|
+
});
|
|
52533
|
+
function packageRoot(fromDir = dirname8(fileURLToPath2(import.meta.url))) {
|
|
52534
|
+
let dir = fromDir;
|
|
52535
|
+
for (; ; ) {
|
|
52536
|
+
const candidate = join24(dir, "package.json");
|
|
52537
|
+
if (existsSync14(candidate)) {
|
|
52538
|
+
try {
|
|
52539
|
+
if (JSON.parse(readFileSync4(candidate, "utf8")).name === "apple-notes-mcp")
|
|
52540
|
+
return dir;
|
|
52541
|
+
} catch {
|
|
52542
|
+
}
|
|
52543
|
+
}
|
|
52544
|
+
const parent = dirname8(dir);
|
|
52545
|
+
if (parent === dir) return resolve5(fromDir, "..");
|
|
52546
|
+
dir = parent;
|
|
52547
|
+
}
|
|
52548
|
+
}
|
|
52549
|
+
function defaultPublicHelperDeps(overrides = {}) {
|
|
52550
|
+
return {
|
|
52551
|
+
env: process.env,
|
|
52552
|
+
platform: process.platform,
|
|
52553
|
+
sourcePath: join24(packageRoot(), PUBLIC_HELPER_SOURCE),
|
|
52554
|
+
exists: existsSync14,
|
|
52555
|
+
readFile: (path10) => readFileSync4(path10),
|
|
52556
|
+
spawn: spawnSync3,
|
|
52557
|
+
...overrides
|
|
52558
|
+
};
|
|
52559
|
+
}
|
|
52560
|
+
function publicHelperInstallDir(env = process.env) {
|
|
52561
|
+
const override = env[PUBLIC_HELPER_DIR_ENV]?.trim();
|
|
52562
|
+
if (override) return override;
|
|
52563
|
+
return join24(homedir19(), "Library", "Application Support", "apple-notes-mcp", "public-helper");
|
|
52564
|
+
}
|
|
52565
|
+
function sha256Hex(data) {
|
|
52566
|
+
return createHash4("sha256").update(data).digest("hex");
|
|
52567
|
+
}
|
|
52568
|
+
function inspectPublicHelper(deps = defaultPublicHelperDeps()) {
|
|
52569
|
+
const installDir = publicHelperInstallDir(deps.env);
|
|
52570
|
+
const binaryPath = join24(installDir, PUBLIC_HELPER_BINARY);
|
|
52571
|
+
const base = { installDir, binaryPath, sourcePath: deps.sourcePath, manifest: null };
|
|
52572
|
+
const fail = (reason, detail) => ({
|
|
52573
|
+
...base,
|
|
52574
|
+
ready: false,
|
|
52575
|
+
reason,
|
|
52576
|
+
detail
|
|
52577
|
+
});
|
|
52578
|
+
if (deps.platform !== "darwin") return fail("unsupported_platform", "macOS only");
|
|
52579
|
+
if (!deps.exists(deps.sourcePath))
|
|
52580
|
+
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
52581
|
+
const manifestPath = join24(installDir, PUBLIC_HELPER_MANIFEST);
|
|
52582
|
+
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
52583
|
+
return fail(
|
|
52584
|
+
"helper_not_installed",
|
|
52585
|
+
`The public native helper is not built. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\`.`
|
|
52586
|
+
);
|
|
52587
|
+
let manifest;
|
|
52588
|
+
try {
|
|
52589
|
+
manifest = publicManifestSchema.parse(JSON.parse(deps.readFile(manifestPath).toString("utf8")));
|
|
52590
|
+
} catch {
|
|
52591
|
+
return fail(
|
|
52592
|
+
"helper_manifest_invalid",
|
|
52593
|
+
`The helper manifest is unreadable. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\`.`
|
|
52594
|
+
);
|
|
52595
|
+
}
|
|
52596
|
+
if (manifest.sourceSha256 !== sha256Hex(deps.readFile(deps.sourcePath)) || manifest.protocolVersion !== PUBLIC_HELPER_PROTOCOL)
|
|
52597
|
+
return {
|
|
52598
|
+
...fail(
|
|
52599
|
+
"helper_stale",
|
|
52600
|
+
`The installed helper was built from a different source than this apple-notes-mcp version ships. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\` again.`
|
|
52601
|
+
),
|
|
52602
|
+
manifest
|
|
52603
|
+
};
|
|
52604
|
+
if (sha256Hex(deps.readFile(binaryPath)) !== manifest.binarySha256)
|
|
52605
|
+
return {
|
|
52606
|
+
...fail(
|
|
52607
|
+
"helper_modified",
|
|
52608
|
+
`The helper binary no longer matches the checksum recorded when it was built. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\` to rebuild it.`
|
|
52609
|
+
),
|
|
52610
|
+
manifest
|
|
52611
|
+
};
|
|
52612
|
+
return { ...base, manifest, ready: true, reason: null, detail: null };
|
|
52613
|
+
}
|
|
52614
|
+
var UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
|
|
52615
|
+
"unsupported_platform",
|
|
52616
|
+
"helper_not_installed",
|
|
52617
|
+
"helper_stale",
|
|
52618
|
+
"helper_modified",
|
|
52619
|
+
"helper_manifest_invalid"
|
|
52620
|
+
]);
|
|
52621
|
+
var PublicHelperError = class extends CodedError {
|
|
52622
|
+
constructor(code, message) {
|
|
52623
|
+
super(message, {
|
|
52624
|
+
code: UNAVAILABLE_CODES.has(code) ? "unsupported" : "operation_failed",
|
|
52625
|
+
helperCode: code
|
|
52626
|
+
});
|
|
52627
|
+
this.code = code;
|
|
52628
|
+
this.name = "PublicHelperError";
|
|
52629
|
+
}
|
|
52630
|
+
code;
|
|
52631
|
+
};
|
|
52632
|
+
var errorSchema = external_exports.object({ status: external_exports.literal("error"), code: external_exports.string(), message: external_exports.string() });
|
|
52633
|
+
var publicHelloSchema = external_exports.object({
|
|
52634
|
+
status: external_exports.literal("ok"),
|
|
52635
|
+
protocolVersion: external_exports.number().int(),
|
|
52636
|
+
sourceSha256: external_exports.string(),
|
|
52637
|
+
actions: external_exports.array(external_exports.string())
|
|
52638
|
+
}).passthrough();
|
|
52639
|
+
function callPublicHelper(action, fields = {}, deps = defaultPublicHelperDeps(), options = {}) {
|
|
52640
|
+
if (!PUBLIC_HELPER_ACTIONS.has(action))
|
|
52641
|
+
throw new PublicHelperError(
|
|
52642
|
+
"unknown_action",
|
|
52643
|
+
`"${action}" is not an action the server sends to the public helper.`
|
|
52644
|
+
);
|
|
52645
|
+
let binaryPath = options.binaryPath;
|
|
52646
|
+
if (!binaryPath) {
|
|
52647
|
+
const install = inspectPublicHelper(deps);
|
|
52648
|
+
if (!install.ready)
|
|
52649
|
+
throw new PublicHelperError(install.reason ?? "helper_not_installed", install.detail ?? "");
|
|
52650
|
+
binaryPath = install.binaryPath;
|
|
52651
|
+
}
|
|
52652
|
+
const timeout = Number.parseInt(deps.env[PUBLIC_HELPER_TIMEOUT_ENV] || "", 10) || options.timeoutMs || DEFAULT_TIMEOUT_MS2;
|
|
52653
|
+
const result = deps.spawn(binaryPath, [], {
|
|
52654
|
+
input: JSON.stringify({ protocol: PUBLIC_HELPER_PROTOCOL, action, ...fields }),
|
|
52655
|
+
encoding: "utf8",
|
|
52656
|
+
timeout,
|
|
52657
|
+
killSignal: "SIGKILL",
|
|
52658
|
+
maxBuffer: MAX_OUTPUT_BYTES
|
|
52659
|
+
});
|
|
52660
|
+
const errno = result.error?.code;
|
|
52661
|
+
if (errno === "ETIMEDOUT" || result.signal && result.status === null)
|
|
52662
|
+
throw new PublicHelperError("timeout", `The helper did not answer within ${timeout} ms.`);
|
|
52663
|
+
if (result.error)
|
|
52664
|
+
throw new PublicHelperError(
|
|
52665
|
+
"helper_unreachable",
|
|
52666
|
+
`Could not run the helper: ${result.error.message}`
|
|
52667
|
+
);
|
|
52668
|
+
let parsed;
|
|
52669
|
+
try {
|
|
52670
|
+
parsed = JSON.parse(String(result.stdout ?? "").trim());
|
|
52671
|
+
} catch {
|
|
52672
|
+
throw new PublicHelperError(
|
|
52673
|
+
"invalid_response",
|
|
52674
|
+
`The helper exited with status ${result.status} and no JSON response.`
|
|
52675
|
+
);
|
|
52676
|
+
}
|
|
52677
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
52678
|
+
throw new PublicHelperError("invalid_response", "The helper response is not a JSON object.");
|
|
52679
|
+
const object3 = parsed;
|
|
52680
|
+
if (result.status !== 0 || object3.status !== "ok") {
|
|
52681
|
+
const error2 = errorSchema.safeParse(object3);
|
|
52682
|
+
if (!error2.success)
|
|
52683
|
+
throw new PublicHelperError(
|
|
52684
|
+
"invalid_response",
|
|
52685
|
+
`The helper failed with an unrecognized response (exit ${result.status}).`
|
|
52686
|
+
);
|
|
52687
|
+
throw new PublicHelperError(error2.data.code, error2.data.message);
|
|
52688
|
+
}
|
|
52689
|
+
return object3;
|
|
52690
|
+
}
|
|
52691
|
+
function defaultPublicHelperBuildDeps() {
|
|
52692
|
+
return {
|
|
52693
|
+
...defaultPublicHelperDeps(),
|
|
52694
|
+
osVersion: () => {
|
|
52695
|
+
const r = spawnSync3("/usr/bin/sw_vers", ["-productVersion"], { encoding: "utf8" });
|
|
52696
|
+
return r.status === 0 ? r.stdout.trim() : `Darwin ${release3()}`;
|
|
52697
|
+
},
|
|
52698
|
+
now: () => /* @__PURE__ */ new Date()
|
|
52699
|
+
};
|
|
52700
|
+
}
|
|
52701
|
+
function sourceDigestSwift(sourceSha) {
|
|
52702
|
+
return `let helperSourceSHA256 = "${sourceSha}"
|
|
52703
|
+
`;
|
|
52704
|
+
}
|
|
52705
|
+
var PUBLIC_HELPER_BUNDLE_ID = "apple-notes-mcp.public-helper";
|
|
52706
|
+
function publicHelperInfoPlist() {
|
|
52707
|
+
return [
|
|
52708
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
52709
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
52710
|
+
'<plist version="1.0">',
|
|
52711
|
+
"<dict>",
|
|
52712
|
+
" <key>CFBundleIdentifier</key>",
|
|
52713
|
+
` <string>${PUBLIC_HELPER_BUNDLE_ID}</string>`,
|
|
52714
|
+
" <key>CFBundleName</key>",
|
|
52715
|
+
" <string>apple-notes-mcp public helper</string>",
|
|
52716
|
+
" <key>CFBundleInfoDictionaryVersion</key>",
|
|
52717
|
+
" <string>6.0</string>",
|
|
52718
|
+
"</dict>",
|
|
52719
|
+
"</plist>",
|
|
52720
|
+
""
|
|
52721
|
+
].join("\n");
|
|
52722
|
+
}
|
|
52723
|
+
function publicHelperCompileArguments(sourcePath, digestPath, plistPath, outputPath) {
|
|
52724
|
+
return [
|
|
52725
|
+
"swiftc",
|
|
52726
|
+
"-O",
|
|
52727
|
+
"-parse-as-library",
|
|
52728
|
+
"-framework",
|
|
52729
|
+
"AppKit",
|
|
52730
|
+
"-framework",
|
|
52731
|
+
"PencilKit",
|
|
52732
|
+
"-Xlinker",
|
|
52733
|
+
"-sectcreate",
|
|
52734
|
+
"-Xlinker",
|
|
52735
|
+
"__TEXT",
|
|
52736
|
+
"-Xlinker",
|
|
52737
|
+
"__info_plist",
|
|
52738
|
+
"-Xlinker",
|
|
52739
|
+
plistPath,
|
|
52740
|
+
sourcePath,
|
|
52741
|
+
digestPath,
|
|
52742
|
+
"-o",
|
|
52743
|
+
outputPath
|
|
52744
|
+
];
|
|
52745
|
+
}
|
|
52746
|
+
function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
52747
|
+
const steps = [];
|
|
52748
|
+
const done = (ok) => ({
|
|
52749
|
+
ok,
|
|
52750
|
+
checkOnly,
|
|
52751
|
+
steps,
|
|
52752
|
+
installation: inspectPublicHelper(deps)
|
|
52753
|
+
});
|
|
52754
|
+
if (checkOnly) {
|
|
52755
|
+
const installation2 = inspectPublicHelper(deps);
|
|
52756
|
+
steps.push({
|
|
52757
|
+
step: "inspect installed helper",
|
|
52758
|
+
ok: installation2.ready,
|
|
52759
|
+
detail: installation2.ready ? installation2.binaryPath : installation2.detail ?? void 0
|
|
52760
|
+
});
|
|
52761
|
+
return { ok: installation2.ready, checkOnly, steps, installation: installation2 };
|
|
52762
|
+
}
|
|
52763
|
+
if (deps.platform !== "darwin") {
|
|
52764
|
+
steps.push({ step: "platform", ok: false, detail: "macOS only" });
|
|
52765
|
+
return done(false);
|
|
52766
|
+
}
|
|
52767
|
+
if (!deps.exists(deps.sourcePath)) {
|
|
52768
|
+
steps.push({ step: "locate source", ok: false, detail: deps.sourcePath });
|
|
52769
|
+
return done(false);
|
|
52770
|
+
}
|
|
52771
|
+
const sourceSha = sha256Hex(deps.readFile(deps.sourcePath));
|
|
52772
|
+
steps.push({ step: "locate source", ok: true, detail: `sha256 ${sourceSha}` });
|
|
52773
|
+
const version3 = deps.spawn("/usr/bin/xcrun", ["swiftc", "--version"], { encoding: "utf8" });
|
|
52774
|
+
if (version3.status !== 0) {
|
|
52775
|
+
steps.push({
|
|
52776
|
+
step: "find compiler",
|
|
52777
|
+
ok: false,
|
|
52778
|
+
detail: "No Swift compiler found. Install the Command Line Tools with `xcode-select --install`."
|
|
52779
|
+
});
|
|
52780
|
+
return done(false);
|
|
52781
|
+
}
|
|
52782
|
+
const compiler = String(version3.stdout || version3.stderr || "").split("\n").find((line) => line.includes("Swift version"))?.trim() || "swiftc";
|
|
52783
|
+
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
52784
|
+
const installDir = publicHelperInstallDir(deps.env);
|
|
52785
|
+
mkdirSync6(installDir, { recursive: true, mode: 448 });
|
|
52786
|
+
const staging = mkdtempSync5(join24(installDir, ".staging-"));
|
|
52787
|
+
try {
|
|
52788
|
+
const stagedBinary = join24(staging, PUBLIC_HELPER_BINARY);
|
|
52789
|
+
const digestPath = join24(staging, "source-digest.swift");
|
|
52790
|
+
const plistPath = join24(staging, "Info.plist");
|
|
52791
|
+
writeFileSync4(digestPath, sourceDigestSwift(sourceSha), { mode: 384 });
|
|
52792
|
+
writeFileSync4(plistPath, publicHelperInfoPlist(), { mode: 384 });
|
|
52793
|
+
const compile = deps.spawn(
|
|
52794
|
+
"/usr/bin/xcrun",
|
|
52795
|
+
publicHelperCompileArguments(deps.sourcePath, digestPath, plistPath, stagedBinary),
|
|
52796
|
+
{ encoding: "utf8", timeout: 3e5 }
|
|
52797
|
+
);
|
|
52798
|
+
if (compile.status !== 0) {
|
|
52799
|
+
steps.push({
|
|
52800
|
+
step: "compile",
|
|
52801
|
+
ok: false,
|
|
52802
|
+
detail: String(compile.stderr || compile.error?.message || "swiftc failed").slice(0, 4e3)
|
|
52803
|
+
});
|
|
52804
|
+
return done(false);
|
|
52805
|
+
}
|
|
52806
|
+
steps.push({ step: "compile", ok: true });
|
|
52807
|
+
const sign = deps.spawn(
|
|
52808
|
+
"/usr/bin/codesign",
|
|
52809
|
+
["--force", "--sign", "-", "--identifier", PUBLIC_HELPER_BUNDLE_ID, stagedBinary],
|
|
52810
|
+
{ encoding: "utf8" }
|
|
52811
|
+
);
|
|
52812
|
+
if (sign.status !== 0) {
|
|
52813
|
+
steps.push({
|
|
52814
|
+
step: "ad-hoc sign",
|
|
52815
|
+
ok: false,
|
|
52816
|
+
detail: String(sign.stderr || "codesign failed")
|
|
52817
|
+
});
|
|
52818
|
+
return done(false);
|
|
52819
|
+
}
|
|
52820
|
+
steps.push({ step: "ad-hoc sign", ok: true });
|
|
52821
|
+
let hello;
|
|
52822
|
+
try {
|
|
52823
|
+
hello = publicHelloSchema.parse(
|
|
52824
|
+
callPublicHelper("hello", {}, deps, { binaryPath: stagedBinary })
|
|
52825
|
+
);
|
|
52826
|
+
} catch (error2) {
|
|
52827
|
+
steps.push({
|
|
52828
|
+
step: "handshake",
|
|
52829
|
+
ok: false,
|
|
52830
|
+
detail: error2 instanceof Error ? error2.message : String(error2)
|
|
52831
|
+
});
|
|
52832
|
+
return done(false);
|
|
52833
|
+
}
|
|
52834
|
+
if (hello.protocolVersion !== PUBLIC_HELPER_PROTOCOL || hello.sourceSha256 !== sourceSha) {
|
|
52835
|
+
steps.push({
|
|
52836
|
+
step: "handshake",
|
|
52837
|
+
ok: false,
|
|
52838
|
+
detail: `helper reported protocol ${hello.protocolVersion}, source ${hello.sourceSha256}`
|
|
52839
|
+
});
|
|
52840
|
+
return done(false);
|
|
52841
|
+
}
|
|
52842
|
+
steps.push({ step: "handshake", ok: true, detail: `actions: ${hello.actions.join(", ")}` });
|
|
52843
|
+
const manifest = {
|
|
52844
|
+
schemaVersion: 1,
|
|
52845
|
+
protocolVersion: hello.protocolVersion,
|
|
52846
|
+
sourceSha256: sourceSha,
|
|
52847
|
+
binarySha256: sha256Hex(deps.readFile(stagedBinary)),
|
|
52848
|
+
builtAt: deps.now().toISOString(),
|
|
52849
|
+
osVersion: deps.osVersion(),
|
|
52850
|
+
compiler
|
|
52851
|
+
};
|
|
52852
|
+
chmodSync(stagedBinary, 448);
|
|
52853
|
+
renameSync(stagedBinary, join24(installDir, PUBLIC_HELPER_BINARY));
|
|
52854
|
+
writeFileSync4(
|
|
52855
|
+
join24(installDir, PUBLIC_HELPER_MANIFEST),
|
|
52856
|
+
JSON.stringify(manifest, null, 2) + "\n",
|
|
52857
|
+
{ mode: 384 }
|
|
52858
|
+
);
|
|
52859
|
+
steps.push({ step: "install", ok: true, detail: installDir });
|
|
52860
|
+
} finally {
|
|
52861
|
+
rmSync5(staging, { recursive: true, force: true });
|
|
52862
|
+
}
|
|
52863
|
+
const installation = inspectPublicHelper(deps);
|
|
52864
|
+
steps.push({
|
|
52865
|
+
step: "verify installation",
|
|
52866
|
+
ok: installation.ready,
|
|
52867
|
+
detail: installation.ready ? void 0 : installation.detail ?? void 0
|
|
52868
|
+
});
|
|
52869
|
+
return { ok: installation.ready, checkOnly, steps, installation };
|
|
52870
|
+
}
|
|
52871
|
+
function formatPublicHelperBuild(report) {
|
|
52872
|
+
const lines = ["Apple Notes MCP public native helper", ""];
|
|
52873
|
+
for (const step of report.steps)
|
|
52874
|
+
lines.push(`${step.ok ? "\u2713" : "\u2717"} ${step.step}${step.detail ? `: ${step.detail}` : ""}`);
|
|
52875
|
+
lines.push("");
|
|
52876
|
+
if (report.ok) lines.push(`Installed at ${report.installation.binaryPath}.`);
|
|
52877
|
+
else if (report.checkOnly) lines.push(`Run \`${PUBLIC_HELPER_SETUP_COMMAND}\` to build it.`);
|
|
52878
|
+
else lines.push("The helper was not installed. Fix the failed step above and run setup again.");
|
|
52879
|
+
return lines.join("\n");
|
|
52880
|
+
}
|
|
52881
|
+
|
|
52882
|
+
// src/utils/noteStoreQuery.ts
|
|
52883
|
+
function parseNoteObjectId(noteId3) {
|
|
52884
|
+
const match = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/ICNote\/p(\d{1,15})$/.exec(noteId3);
|
|
52885
|
+
if (!match)
|
|
52886
|
+
throw new NoteStoreError(
|
|
52887
|
+
`Invalid note ID format: "${noteId3}". Expected format: x-coredata://UUID/ICNote/pNNN`,
|
|
52888
|
+
"invalid_input"
|
|
52889
|
+
);
|
|
52890
|
+
return { store: match[1], pk: Number(match[2]) };
|
|
52891
|
+
}
|
|
52892
|
+
function queryNoteScoped(sql, pk, dbPath2 = NOTES_DB_PATH7) {
|
|
52893
|
+
return runReadOnlySql(dbPath2, sql, { pk: { int: pk } }).split("\n");
|
|
52894
|
+
}
|
|
52895
|
+
var NOTE_STATE_SQL = `SELECT json_object('found', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_PK = @pk AND n.Z_ENT = ${entity("ICNote")}), 'locked', (SELECT COALESCE(n.ZISPASSWORDPROTECTED, 0) FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_PK = @pk));`;
|
|
52896
|
+
function assertNoteReadable(stateLine, noteId3) {
|
|
52897
|
+
let state;
|
|
52898
|
+
try {
|
|
52899
|
+
state = JSON.parse(stateLine || "{}");
|
|
52900
|
+
} catch {
|
|
52901
|
+
throw new NoteStoreError("Unexpected response from the Notes database.", "query_error");
|
|
52902
|
+
}
|
|
52903
|
+
if (!state.found)
|
|
52904
|
+
throw new CodedError(`No note found in the database for ID "${noteId3}".`, {
|
|
52905
|
+
code: "not_found"
|
|
52906
|
+
});
|
|
52907
|
+
if (state.locked)
|
|
52908
|
+
throw new CodedError(
|
|
52909
|
+
"This note is password-protected; its attachments are encrypted and cannot be read.",
|
|
52910
|
+
{ code: "unsupported" }
|
|
52911
|
+
);
|
|
52912
|
+
}
|
|
52913
|
+
|
|
52914
|
+
// src/utils/noteDrawings.ts
|
|
52915
|
+
function drawingRowsSql(columns, dataColumn) {
|
|
52916
|
+
return [
|
|
52917
|
+
NOTE_STATE_SQL,
|
|
52918
|
+
`SELECT json_group_array(json_object('pk', a.Z_PK, 'identifier', a.ZIDENTIFIER, 'uti', a.ZTYPEUTI, 'data', hex(a.${dataColumn}))) FROM (SELECT * FROM ZICCLOUDSYNCINGOBJECT a WHERE a.ZNOTE = @pk AND a.ZTYPEUTI IN ('com.apple.drawing.2', 'com.apple.drawing') AND ${notTombstonedSql(columns, "a")} ORDER BY a.Z_PK) a;`
|
|
52919
|
+
].join("\n");
|
|
52920
|
+
}
|
|
52921
|
+
function readDrawingRows2(noteId3, dbPath2 = NOTES_DB_PATH7) {
|
|
52922
|
+
const { store, pk } = parseNoteObjectId(noteId3);
|
|
52923
|
+
const columns = readColumns(dbPath2);
|
|
52924
|
+
const dataColumn = columns.has("ZMERGEABLEDATA1") ? "ZMERGEABLEDATA1" : columns.has("ZMERGEABLEDATA") ? "ZMERGEABLEDATA" : null;
|
|
52925
|
+
if (!dataColumn)
|
|
52926
|
+
throw new NoteStoreError(
|
|
52927
|
+
"This macOS version's Notes database has no drawing data column.",
|
|
52928
|
+
"schema"
|
|
52929
|
+
);
|
|
52930
|
+
const [stateLine, rowsLine] = queryNoteScoped(drawingRowsSql(columns, dataColumn), pk, dbPath2);
|
|
52931
|
+
assertNoteReadable(stateLine, noteId3);
|
|
52932
|
+
const rows = JSON.parse(rowsLine || "[]");
|
|
52933
|
+
return rows.map((row) => ({
|
|
52934
|
+
pk: row.pk,
|
|
52935
|
+
attachmentId: `x-coredata://${store}/ICAttachment/p${row.pk}`,
|
|
52936
|
+
identifier: row.identifier ?? "",
|
|
52937
|
+
typeUti: row.uti,
|
|
52938
|
+
data: row.data ? Buffer.from(row.data, "hex") : null
|
|
52939
|
+
}));
|
|
52940
|
+
}
|
|
52941
|
+
function svgNumber(value) {
|
|
52942
|
+
if (!Number.isFinite(value)) return "0";
|
|
52943
|
+
const rounded = Math.round(value * 100) / 100;
|
|
52944
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
52945
|
+
}
|
|
52946
|
+
function escapeAttribute2(value) {
|
|
52947
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
52948
|
+
}
|
|
52949
|
+
var clampChannel = (value) => Math.min(255, Math.max(0, Math.round(value || 0)));
|
|
52950
|
+
var clampUnit = (value) => Math.min(1, Math.max(0, Number.isFinite(value) ? value : 1));
|
|
52951
|
+
function drawingViewBox(strokes, fallback) {
|
|
52952
|
+
let minX = Infinity;
|
|
52953
|
+
let minY = Infinity;
|
|
52954
|
+
let maxX = -Infinity;
|
|
52955
|
+
let maxY = -Infinity;
|
|
52956
|
+
for (const stroke of strokes) {
|
|
52957
|
+
const pad = stroke.width / 2;
|
|
52958
|
+
const extend2 = (x, y, r) => {
|
|
52959
|
+
minX = Math.min(minX, x - r);
|
|
52960
|
+
minY = Math.min(minY, y - r);
|
|
52961
|
+
maxX = Math.max(maxX, x + r);
|
|
52962
|
+
maxY = Math.max(maxY, y + r);
|
|
52963
|
+
};
|
|
52964
|
+
if (stroke.points?.length) for (const p of stroke.points) extend2(p.x, p.y, pad);
|
|
52965
|
+
else {
|
|
52966
|
+
extend2(stroke.bounds.x, stroke.bounds.y, 0);
|
|
52967
|
+
extend2(stroke.bounds.x + stroke.bounds.width, stroke.bounds.y + stroke.bounds.height, 0);
|
|
52968
|
+
}
|
|
52969
|
+
}
|
|
52970
|
+
if (minX === Infinity) return fallback ?? { x: 0, y: 0, width: 1, height: 1 };
|
|
52971
|
+
return { x: minX, y: minY, width: Math.max(1, maxX - minX), height: Math.max(1, maxY - minY) };
|
|
52972
|
+
}
|
|
52973
|
+
function drawingToSvg(strokes, fallback) {
|
|
52974
|
+
const box = drawingViewBox(strokes, fallback);
|
|
52975
|
+
const parts = [
|
|
52976
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${svgNumber(box.x)} ${svgNumber(box.y)} ${svgNumber(box.width)} ${svgNumber(box.height)}" width="${svgNumber(box.width)}" height="${svgNumber(box.height)}">`
|
|
52977
|
+
];
|
|
52978
|
+
for (const stroke of strokes) {
|
|
52979
|
+
const points = stroke.points ?? [];
|
|
52980
|
+
if (points.length === 0) continue;
|
|
52981
|
+
const c = stroke.color;
|
|
52982
|
+
const color = `rgb(${clampChannel(c.red)},${clampChannel(c.green)},${clampChannel(c.blue)})`;
|
|
52983
|
+
const shared = `stroke="${color}" stroke-opacity="${svgNumber(clampUnit(c.alpha))}" data-ink="${escapeAttribute2(stroke.inkType)}"`;
|
|
52984
|
+
if (points.length === 1) {
|
|
52985
|
+
parts.push(
|
|
52986
|
+
`<circle cx="${svgNumber(points[0].x)}" cy="${svgNumber(points[0].y)}" r="${svgNumber(stroke.width / 2)}" fill="${color}" fill-opacity="${svgNumber(clampUnit(c.alpha))}" data-ink="${escapeAttribute2(stroke.inkType)}"/>`
|
|
52987
|
+
);
|
|
52988
|
+
continue;
|
|
52989
|
+
}
|
|
52990
|
+
const d = points.map((p, i) => `${i === 0 ? "M" : "L"}${svgNumber(p.x)} ${svgNumber(p.y)}`).join(" ");
|
|
52991
|
+
parts.push(
|
|
52992
|
+
`<path d="${d}" fill="none" ${shared} stroke-width="${svgNumber(stroke.width)}" stroke-linecap="round" stroke-linejoin="round"/>`
|
|
52993
|
+
);
|
|
52994
|
+
}
|
|
52995
|
+
parts.push("</svg>");
|
|
52996
|
+
return parts.join("\n");
|
|
52997
|
+
}
|
|
52998
|
+
|
|
52999
|
+
// src/services/noteDrawings.ts
|
|
53000
|
+
var bounds = external_exports.object({ x: external_exports.number(), y: external_exports.number(), width: external_exports.number(), height: external_exports.number() });
|
|
53001
|
+
var strokeSchema = external_exports.object({
|
|
53002
|
+
inkType: external_exports.string(),
|
|
53003
|
+
color: external_exports.object({
|
|
53004
|
+
red: external_exports.number(),
|
|
53005
|
+
green: external_exports.number(),
|
|
53006
|
+
blue: external_exports.number(),
|
|
53007
|
+
alpha: external_exports.number()
|
|
53008
|
+
}),
|
|
53009
|
+
width: external_exports.number(),
|
|
53010
|
+
pointCount: external_exports.number().int(),
|
|
53011
|
+
bounds,
|
|
53012
|
+
points: external_exports.array(
|
|
53013
|
+
external_exports.object({
|
|
53014
|
+
x: external_exports.number(),
|
|
53015
|
+
y: external_exports.number(),
|
|
53016
|
+
width: external_exports.number(),
|
|
53017
|
+
opacity: external_exports.number(),
|
|
53018
|
+
force: external_exports.number()
|
|
53019
|
+
})
|
|
53020
|
+
).optional(),
|
|
53021
|
+
transformApplied: external_exports.boolean().optional()
|
|
53022
|
+
}).strip();
|
|
53023
|
+
var decodedDrawingSchema = external_exports.object({
|
|
53024
|
+
status: external_exports.literal("ok"),
|
|
53025
|
+
strokeCount: external_exports.number().int(),
|
|
53026
|
+
strokes: external_exports.array(strokeSchema),
|
|
53027
|
+
truncated: external_exports.boolean(),
|
|
53028
|
+
bounds
|
|
53029
|
+
});
|
|
53030
|
+
function decodeRow(row, format, includePoints, deps) {
|
|
53031
|
+
const base = {
|
|
53032
|
+
attachmentId: row.attachmentId,
|
|
53033
|
+
identifier: row.identifier,
|
|
53034
|
+
typeUti: row.typeUti,
|
|
53035
|
+
status: "error"
|
|
53036
|
+
};
|
|
53037
|
+
if (!row.data || row.data.length === 0)
|
|
53038
|
+
return { ...base, code: "no_data", message: "This drawing has no stored PencilKit data." };
|
|
53039
|
+
let decoded;
|
|
53040
|
+
try {
|
|
53041
|
+
const needPoints = includePoints || format !== "json";
|
|
53042
|
+
const raw = callPublicHelper(
|
|
53043
|
+
"decode_drawing",
|
|
53044
|
+
{ dataBase64: row.data.toString("base64"), includePoints: needPoints },
|
|
53045
|
+
deps
|
|
53046
|
+
);
|
|
53047
|
+
const parsed = decodedDrawingSchema.safeParse(raw);
|
|
53048
|
+
if (!parsed.success)
|
|
53049
|
+
return { ...base, code: "invalid_response", message: "Unexpected helper response." };
|
|
53050
|
+
decoded = parsed.data;
|
|
53051
|
+
} catch (error2) {
|
|
53052
|
+
const code = error2 instanceof PublicHelperError ? error2.code : "internal_error";
|
|
53053
|
+
return { ...base, code, message: error2 instanceof Error ? error2.message : String(error2) };
|
|
53054
|
+
}
|
|
53055
|
+
const strokes = decoded.strokes;
|
|
53056
|
+
const result = {
|
|
53057
|
+
...base,
|
|
53058
|
+
status: "ok",
|
|
53059
|
+
strokeCount: decoded.strokeCount,
|
|
53060
|
+
bounds: decoded.bounds,
|
|
53061
|
+
truncated: decoded.truncated
|
|
53062
|
+
};
|
|
53063
|
+
if (format !== "json") result.svg = drawingToSvg(strokes, decoded.bounds);
|
|
53064
|
+
if (format !== "svg")
|
|
53065
|
+
result.strokes = includePoints ? strokes : strokes.map(({ points: _points, ...rest }) => rest);
|
|
53066
|
+
return result;
|
|
53067
|
+
}
|
|
53068
|
+
function getNoteDrawings(noteId3, options = {}) {
|
|
53069
|
+
const format = options.format ?? "json";
|
|
53070
|
+
const includePoints = options.includePoints ?? true;
|
|
53071
|
+
const deps = options.deps ?? defaultPublicHelperDeps();
|
|
53072
|
+
const rows = (options.readRows ?? readDrawingRows2)(noteId3, options.dbPath);
|
|
53073
|
+
if (rows.length === 0) return { id: noteId3, drawingCount: 0, status: "none", drawings: [] };
|
|
53074
|
+
const install = inspectPublicHelper(deps);
|
|
53075
|
+
if (!install.ready)
|
|
53076
|
+
throw new PublicHelperError(install.reason ?? "helper_not_installed", install.detail ?? "");
|
|
53077
|
+
const drawings = rows.map((row) => decodeRow(row, format, includePoints, deps));
|
|
53078
|
+
const ok = drawings.filter((d) => d.status === "ok").length;
|
|
53079
|
+
return {
|
|
53080
|
+
id: noteId3,
|
|
53081
|
+
drawingCount: drawings.length,
|
|
53082
|
+
status: ok === drawings.length ? "ok" : ok ? "partial" : "error",
|
|
53083
|
+
drawings
|
|
53084
|
+
};
|
|
53085
|
+
}
|
|
53086
|
+
function formatNoteDrawings(result) {
|
|
53087
|
+
if (result.drawingCount === 0) return `No classic PencilKit drawings in note ${result.id}.`;
|
|
53088
|
+
const lines = [
|
|
53089
|
+
`${result.drawingCount} classic drawing${result.drawingCount === 1 ? "" : "s"} in note ${result.id} (${result.status}):`
|
|
53090
|
+
];
|
|
53091
|
+
for (const d of result.drawings)
|
|
53092
|
+
lines.push(
|
|
53093
|
+
d.status === "ok" ? `- ${d.attachmentId}: ${d.strokeCount} stroke${d.strokeCount === 1 ? "" : "s"}${d.truncated ? " (truncated)" : ""}` : `- ${d.attachmentId}: error ${d.code}: ${d.message}`
|
|
53094
|
+
);
|
|
53095
|
+
return lines.join("\n");
|
|
53096
|
+
}
|
|
53097
|
+
|
|
53098
|
+
// src/services/privateHelperBuild.ts
|
|
53099
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
53100
|
+
import {
|
|
53101
|
+
chmodSync as chmodSync2,
|
|
53102
|
+
existsSync as existsSync16,
|
|
53103
|
+
mkdirSync as mkdirSync7,
|
|
53104
|
+
mkdtempSync as mkdtempSync6,
|
|
53105
|
+
renameSync as renameSync2,
|
|
53106
|
+
rmSync as rmSync6,
|
|
53107
|
+
writeFileSync as writeFileSync5
|
|
53108
|
+
} from "node:fs";
|
|
53109
|
+
import { release as release4 } from "node:os";
|
|
53110
|
+
import { join as join26 } from "node:path";
|
|
52305
53111
|
|
|
52306
53112
|
// src/services/privateHelper.ts
|
|
52307
|
-
import { spawnSync as
|
|
52308
|
-
import { createHash as
|
|
52309
|
-
import { existsSync as
|
|
52310
|
-
import { homedir as
|
|
52311
|
-
import { dirname as
|
|
52312
|
-
import { fileURLToPath as
|
|
53113
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
53114
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
53115
|
+
import { existsSync as existsSync15, readFileSync as readFileSync5 } from "node:fs";
|
|
53116
|
+
import { homedir as homedir20 } from "node:os";
|
|
53117
|
+
import { dirname as dirname9, join as join25, resolve as resolve6 } from "node:path";
|
|
53118
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
52313
53119
|
var PRIVATE_HELPER_PROTOCOL = 1;
|
|
52314
53120
|
var ENABLE_ENV = "APPLE_NOTES_MCP_ENABLE_PRIVATE";
|
|
52315
53121
|
var HELPER_DIR_ENV = "APPLE_NOTES_MCP_PRIVATE_HELPER_DIR";
|
|
@@ -52322,8 +53128,8 @@ var READ_ONLY_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
52322
53128
|
var HELPER_BINARY_NAME = "apple-notes-private-helper";
|
|
52323
53129
|
var HELPER_SOURCE_RELATIVE = "native/private-helper/apple-notes-private-helper.m";
|
|
52324
53130
|
var MANIFEST_NAME = "manifest.json";
|
|
52325
|
-
var
|
|
52326
|
-
var
|
|
53131
|
+
var DEFAULT_TIMEOUT_MS3 = 2e4;
|
|
53132
|
+
var MAX_OUTPUT_BYTES2 = 4 * 1024 * 1024;
|
|
52327
53133
|
var manifestSchema = external_exports.object({
|
|
52328
53134
|
schemaVersion: external_exports.literal(1),
|
|
52329
53135
|
protocolVersion: external_exports.number().int(),
|
|
@@ -52333,19 +53139,19 @@ var manifestSchema = external_exports.object({
|
|
|
52333
53139
|
osVersion: external_exports.string(),
|
|
52334
53140
|
compiler: external_exports.string()
|
|
52335
53141
|
});
|
|
52336
|
-
function
|
|
53142
|
+
function packageRoot2(fromDir = dirname9(fileURLToPath3(import.meta.url))) {
|
|
52337
53143
|
let dir = fromDir;
|
|
52338
53144
|
for (; ; ) {
|
|
52339
|
-
const candidate =
|
|
52340
|
-
if (
|
|
53145
|
+
const candidate = join25(dir, "package.json");
|
|
53146
|
+
if (existsSync15(candidate)) {
|
|
52341
53147
|
try {
|
|
52342
|
-
const pkg = JSON.parse(
|
|
53148
|
+
const pkg = JSON.parse(readFileSync5(candidate, "utf8"));
|
|
52343
53149
|
if (pkg.name === "apple-notes-mcp") return dir;
|
|
52344
53150
|
} catch {
|
|
52345
53151
|
}
|
|
52346
53152
|
}
|
|
52347
|
-
const parent =
|
|
52348
|
-
if (parent === dir) return
|
|
53153
|
+
const parent = dirname9(dir);
|
|
53154
|
+
if (parent === dir) return resolve6(fromDir, "..");
|
|
52349
53155
|
dir = parent;
|
|
52350
53156
|
}
|
|
52351
53157
|
}
|
|
@@ -52353,10 +53159,10 @@ function defaultDeps2(overrides = {}) {
|
|
|
52353
53159
|
return {
|
|
52354
53160
|
env: process.env,
|
|
52355
53161
|
platform: process.platform,
|
|
52356
|
-
sourcePath:
|
|
52357
|
-
exists:
|
|
52358
|
-
readFile: (path10) =>
|
|
52359
|
-
spawn:
|
|
53162
|
+
sourcePath: join25(packageRoot2(), HELPER_SOURCE_RELATIVE),
|
|
53163
|
+
exists: existsSync15,
|
|
53164
|
+
readFile: (path10) => readFileSync5(path10),
|
|
53165
|
+
spawn: spawnSync4,
|
|
52360
53166
|
...overrides
|
|
52361
53167
|
};
|
|
52362
53168
|
}
|
|
@@ -52366,14 +53172,14 @@ function privateHelperEnabled(env = process.env) {
|
|
|
52366
53172
|
function helperInstallDir(env = process.env) {
|
|
52367
53173
|
const override = env[HELPER_DIR_ENV]?.trim();
|
|
52368
53174
|
if (override) return override;
|
|
52369
|
-
return
|
|
53175
|
+
return join25(homedir20(), "Library", "Application Support", "apple-notes-mcp", "private-helper");
|
|
52370
53176
|
}
|
|
52371
|
-
function
|
|
52372
|
-
return
|
|
53177
|
+
function sha256Hex2(data) {
|
|
53178
|
+
return createHash5("sha256").update(data).digest("hex");
|
|
52373
53179
|
}
|
|
52374
53180
|
function inspectInstallation(deps = defaultDeps2()) {
|
|
52375
53181
|
const installDir = helperInstallDir(deps.env);
|
|
52376
|
-
const binaryPath =
|
|
53182
|
+
const binaryPath = join25(installDir, HELPER_BINARY_NAME);
|
|
52377
53183
|
const base = {
|
|
52378
53184
|
installDir,
|
|
52379
53185
|
binaryPath,
|
|
@@ -52390,8 +53196,8 @@ function inspectInstallation(deps = defaultDeps2()) {
|
|
|
52390
53196
|
if (deps.platform !== "darwin") return fail("unsupported_platform", "macOS only");
|
|
52391
53197
|
if (!deps.exists(deps.sourcePath))
|
|
52392
53198
|
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
52393
|
-
base.expectedSourceSha256 =
|
|
52394
|
-
const manifestPath =
|
|
53199
|
+
base.expectedSourceSha256 = sha256Hex2(deps.readFile(deps.sourcePath));
|
|
53200
|
+
const manifestPath = join25(installDir, MANIFEST_NAME);
|
|
52395
53201
|
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
52396
53202
|
return fail(
|
|
52397
53203
|
"helper_not_installed",
|
|
@@ -52412,7 +53218,7 @@ function inspectInstallation(deps = defaultDeps2()) {
|
|
|
52412
53218
|
"helper_stale",
|
|
52413
53219
|
"The installed helper was built from a different helper source or protocol than this apple-notes-mcp version ships. Run `apple-notes-mcp setup --native-helper` again."
|
|
52414
53220
|
);
|
|
52415
|
-
if (
|
|
53221
|
+
if (sha256Hex2(deps.readFile(binaryPath)) !== manifest.binarySha256)
|
|
52416
53222
|
return fail(
|
|
52417
53223
|
"helper_modified",
|
|
52418
53224
|
"The helper binary does not match the checksum recorded when it was built. Run `apple-notes-mcp setup --native-helper` to rebuild it."
|
|
@@ -52429,7 +53235,7 @@ var PrivateHelperError = class extends Error {
|
|
|
52429
53235
|
code;
|
|
52430
53236
|
details;
|
|
52431
53237
|
};
|
|
52432
|
-
var
|
|
53238
|
+
var errorSchema2 = external_exports.object({
|
|
52433
53239
|
status: external_exports.literal("error"),
|
|
52434
53240
|
code: external_exports.string(),
|
|
52435
53241
|
message: external_exports.string()
|
|
@@ -52501,13 +53307,13 @@ function callPrivateHelper(action, fields = {}, deps = defaultDeps2(), options =
|
|
|
52501
53307
|
throw new PrivateHelperError(install.reason || "helper_not_installed", install.detail || "");
|
|
52502
53308
|
binaryPath = install.binaryPath;
|
|
52503
53309
|
}
|
|
52504
|
-
const timeout = Number.parseInt(deps.env[TIMEOUT_ENV] || "", 10) ||
|
|
53310
|
+
const timeout = Number.parseInt(deps.env[TIMEOUT_ENV] || "", 10) || DEFAULT_TIMEOUT_MS3;
|
|
52505
53311
|
const result = deps.spawn(binaryPath, [], {
|
|
52506
53312
|
input: JSON.stringify({ protocol: PRIVATE_HELPER_PROTOCOL, action, ...fields }),
|
|
52507
53313
|
encoding: "utf8",
|
|
52508
53314
|
timeout,
|
|
52509
53315
|
killSignal: "SIGKILL",
|
|
52510
|
-
maxBuffer:
|
|
53316
|
+
maxBuffer: MAX_OUTPUT_BYTES2,
|
|
52511
53317
|
env: deps.env
|
|
52512
53318
|
});
|
|
52513
53319
|
const errno = result.error?.code;
|
|
@@ -52532,7 +53338,7 @@ function callPrivateHelper(action, fields = {}, deps = defaultDeps2(), options =
|
|
|
52532
53338
|
throw new PrivateHelperError("invalid_response", "The helper response is not a JSON object");
|
|
52533
53339
|
const object3 = parsed;
|
|
52534
53340
|
if (result.status !== 0 || object3.status === "error") {
|
|
52535
|
-
const error2 =
|
|
53341
|
+
const error2 = errorSchema2.safeParse(object3);
|
|
52536
53342
|
if (!error2.success)
|
|
52537
53343
|
throw new PrivateHelperError(
|
|
52538
53344
|
"invalid_response",
|
|
@@ -52635,8 +53441,8 @@ function defaultBuildDeps() {
|
|
|
52635
53441
|
return {
|
|
52636
53442
|
...defaultDeps2(),
|
|
52637
53443
|
osVersion: () => {
|
|
52638
|
-
const r =
|
|
52639
|
-
return r.status === 0 ? r.stdout.trim() : `Darwin ${
|
|
53444
|
+
const r = spawnSync5("/usr/bin/sw_vers", ["-productVersion"], { encoding: "utf8" });
|
|
53445
|
+
return r.status === 0 ? r.stdout.trim() : `Darwin ${release4()}`;
|
|
52640
53446
|
},
|
|
52641
53447
|
now: () => /* @__PURE__ */ new Date()
|
|
52642
53448
|
};
|
|
@@ -52686,7 +53492,7 @@ function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
|
52686
53492
|
return done(false);
|
|
52687
53493
|
}
|
|
52688
53494
|
const source = deps.readFile(deps.sourcePath);
|
|
52689
|
-
const sourceSha =
|
|
53495
|
+
const sourceSha = sha256Hex2(source);
|
|
52690
53496
|
steps.push({
|
|
52691
53497
|
step: "locate source",
|
|
52692
53498
|
ok: true,
|
|
@@ -52705,10 +53511,10 @@ function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
|
52705
53511
|
const compiler = String(clangVersion.stdout || "").split("\n")[0] || "clang";
|
|
52706
53512
|
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
52707
53513
|
const installDir = helperInstallDir(deps.env);
|
|
52708
|
-
|
|
52709
|
-
const staging =
|
|
53514
|
+
mkdirSync7(installDir, { recursive: true, mode: 448 });
|
|
53515
|
+
const staging = mkdtempSync6(join26(installDir, ".staging-"));
|
|
52710
53516
|
try {
|
|
52711
|
-
const stagedBinary =
|
|
53517
|
+
const stagedBinary = join26(staging, HELPER_BINARY_NAME);
|
|
52712
53518
|
const compile = deps.spawn(
|
|
52713
53519
|
"/usr/bin/xcrun",
|
|
52714
53520
|
compileArguments(deps.sourcePath, stagedBinary, sourceSha),
|
|
@@ -52779,19 +53585,19 @@ function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
|
52779
53585
|
schemaVersion: 1,
|
|
52780
53586
|
protocolVersion: hello.protocolVersion,
|
|
52781
53587
|
sourceSha256: sourceSha,
|
|
52782
|
-
binarySha256:
|
|
53588
|
+
binarySha256: sha256Hex2(deps.readFile(stagedBinary)),
|
|
52783
53589
|
builtAt: deps.now().toISOString(),
|
|
52784
53590
|
osVersion: deps.osVersion(),
|
|
52785
53591
|
compiler
|
|
52786
53592
|
};
|
|
52787
|
-
|
|
52788
|
-
|
|
52789
|
-
|
|
53593
|
+
chmodSync2(stagedBinary, 448);
|
|
53594
|
+
renameSync2(stagedBinary, join26(installDir, HELPER_BINARY_NAME));
|
|
53595
|
+
writeFileSync5(join26(installDir, MANIFEST_NAME), JSON.stringify(manifest, null, 2) + "\n", {
|
|
52790
53596
|
mode: 384
|
|
52791
53597
|
});
|
|
52792
53598
|
steps.push({ step: "install", ok: true, detail: installDir });
|
|
52793
53599
|
} finally {
|
|
52794
|
-
if (
|
|
53600
|
+
if (existsSync16(staging)) rmSync6(staging, { recursive: true, force: true });
|
|
52795
53601
|
}
|
|
52796
53602
|
const installation = inspectInstallation(deps);
|
|
52797
53603
|
steps.push({
|
|
@@ -52931,6 +53737,11 @@ function registerPrivateHelperTools(server2, manager, depsFactory = () => defaul
|
|
|
52931
53737
|
loadFileConfig();
|
|
52932
53738
|
var require2 = createRequire(import.meta.url);
|
|
52933
53739
|
var { version: version2 } = require2("../package.json");
|
|
53740
|
+
if (process.argv[2] === "setup" && process.argv.slice(3).includes("--public-helper")) {
|
|
53741
|
+
const report = buildPublicHelper(process.argv.slice(3).includes("--check"));
|
|
53742
|
+
process.stdout.write(formatPublicHelperBuild(report) + "\n");
|
|
53743
|
+
process.exit(report.ok ? 0 : 1);
|
|
53744
|
+
}
|
|
52934
53745
|
if (process.argv[2] === "setup" && process.argv.slice(3).includes("--native-helper")) {
|
|
52935
53746
|
const report = buildPrivateHelper(process.argv.slice(3).includes("--check"));
|
|
52936
53747
|
process.stdout.write(formatHelperBuild(report) + "\n");
|
|
@@ -54089,6 +54900,53 @@ registerTool(
|
|
|
54089
54900
|
return successResponse(describeNoteStructure(structure), { ...structure });
|
|
54090
54901
|
}, "Error reading note structure")
|
|
54091
54902
|
);
|
|
54903
|
+
registerTool(
|
|
54904
|
+
"list-note-links",
|
|
54905
|
+
{
|
|
54906
|
+
description: "Use when: you need the links in one note (by exact id) or across a folder (with its subfolders by default), an account, or the whole library, with each link's kind: inline (a hyperlink on text), card (a rich link preview), note (a native link chip to another note) or section (a native link chip to a heading or paragraph).\nReturns: one page of links, newest-modified note first, each with its URL, label, linkSafe, target note and paragraph UUIDs for Notes deep links, card previewPath, and its source noteId, note title, folder path (as list-folders prints it) and account; plus per-kind counts and page info (call again with offset set to page.nextOffset while page.hasMore is true).\nDo not use when: you want one note's full structure (get-note-structure) or its formatting (get-note-blocks).\nSafety: read-only; reads the NoteStore database directly and requires Full Disk Access. Inline links need every body in scope decoded, so they are included only with includeInline (default true for id, false for a folder, account or library scan). Recently Deleted is skipped unless the note is requested by id.",
|
|
54907
|
+
inputSchema: {
|
|
54908
|
+
id: noteIdInput.optional().describe("One exact note ID (do not combine with account/folder)"),
|
|
54909
|
+
account: external_exports.string().min(1).max(MAX.ACCOUNT).optional().describe("Account name (exact or unique-prefix match)"),
|
|
54910
|
+
folder: external_exports.string().min(1).max(MAX.FOLDER).optional().describe(
|
|
54911
|
+
"Folder name or path as list-folders prints it, such as Work/Clients (escape a literal slash as \\/)"
|
|
54912
|
+
),
|
|
54913
|
+
includeSubfolders: external_exports.boolean().optional().describe("With folder, also list notes in its subfolders (default true)"),
|
|
54914
|
+
includeInline: external_exports.boolean().optional().describe(
|
|
54915
|
+
"Also decode note bodies for inline hyperlinks (slower). Default true for id, false otherwise"
|
|
54916
|
+
),
|
|
54917
|
+
kinds: external_exports.array(external_exports.enum(["inline", "card", "note", "section"])).max(4).optional().describe("Only these link kinds"),
|
|
54918
|
+
offset: external_exports.number().int().min(0).optional().describe("Index of the first link to return (default 0); use page.nextOffset"),
|
|
54919
|
+
limit: external_exports.number().int().min(1).max(2e3).optional().describe("Maximum links to return (default 200, max 2000)")
|
|
54920
|
+
},
|
|
54921
|
+
outputSchema: {
|
|
54922
|
+
scope: external_exports.record(external_exports.unknown()).optional(),
|
|
54923
|
+
inlineIncluded: external_exports.boolean().optional(),
|
|
54924
|
+
notesInScope: external_exports.number().optional(),
|
|
54925
|
+
notesWithoutBody: external_exports.number().optional(),
|
|
54926
|
+
counts: external_exports.record(external_exports.unknown()).optional(),
|
|
54927
|
+
links: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
|
|
54928
|
+
page: external_exports.record(external_exports.unknown()).optional()
|
|
54929
|
+
},
|
|
54930
|
+
annotations: { readOnlyHint: true }
|
|
54931
|
+
},
|
|
54932
|
+
withErrorHandling(
|
|
54933
|
+
({ id: id2, account, folder, includeSubfolders, includeInline, kinds, offset, limit }) => {
|
|
54934
|
+
const result = listNoteLinks({
|
|
54935
|
+
id: id2,
|
|
54936
|
+
account,
|
|
54937
|
+
folder,
|
|
54938
|
+
includeSubfolders,
|
|
54939
|
+
includeInline,
|
|
54940
|
+
kinds,
|
|
54941
|
+
offset,
|
|
54942
|
+
limit,
|
|
54943
|
+
maxBytes: blocksMaxResponseBytes()
|
|
54944
|
+
});
|
|
54945
|
+
return successResponse(describeLinkInventory(result), { ...result });
|
|
54946
|
+
},
|
|
54947
|
+
"Error listing note links"
|
|
54948
|
+
)
|
|
54949
|
+
);
|
|
54092
54950
|
registerTool(
|
|
54093
54951
|
"list-native-tags",
|
|
54094
54952
|
{
|
|
@@ -55913,6 +56771,41 @@ ${lines.join("\n")}${more}`,
|
|
|
55913
56771
|
);
|
|
55914
56772
|
}, "Error listing notes")
|
|
55915
56773
|
);
|
|
56774
|
+
registerTool(
|
|
56775
|
+
"get-note-drawings",
|
|
56776
|
+
{
|
|
56777
|
+
description: "Use when: reading the strokes of a note's classic PencilKit drawings (com.apple.drawing / com.apple.drawing.2 attachments), by note id, as JSON or SVG.\nReturns: per drawing its attachment id, status (ok/error with a code), stroke count, bounds, and strokes (ink type, sRGB color, width, points) and/or a standalone SVG document; overall status ok/partial/error/none.\nDo not use when: the drawing is a modern Paper sketch (com.apple.paper is not decoded here) or you want the attachment file itself (save-attachment).\nNote: read-only. Needs Full Disk Access and the public native helper built once with `apple-notes-mcp setup --public-helper` (compiled locally with PencilKit; no Notes writes).",
|
|
56778
|
+
inputSchema: {
|
|
56779
|
+
id: noteIdInput,
|
|
56780
|
+
format: external_exports.enum(["json", "svg", "both"]).optional().describe(
|
|
56781
|
+
'"json" (default) returns strokes, "svg" returns SVG documents, "both" returns both'
|
|
56782
|
+
),
|
|
56783
|
+
includePoints: external_exports.boolean().optional().describe(
|
|
56784
|
+
"Include per-point x/y/width/opacity/force in JSON strokes (default true). SVG output always uses the points."
|
|
56785
|
+
)
|
|
56786
|
+
},
|
|
56787
|
+
outputSchema: {
|
|
56788
|
+
id: external_exports.string().optional(),
|
|
56789
|
+
drawingCount: external_exports.number().optional(),
|
|
56790
|
+
status: external_exports.string().optional(),
|
|
56791
|
+
drawings: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
56792
|
+
pointsOmitted: external_exports.boolean().optional()
|
|
56793
|
+
}
|
|
56794
|
+
},
|
|
56795
|
+
withErrorHandling(({ id: id2, format, includePoints }) => {
|
|
56796
|
+
let result = getNoteDrawings(id2, { format, includePoints });
|
|
56797
|
+
let pointsOmitted = false;
|
|
56798
|
+
if (Buffer.byteLength(JSON.stringify(result)) > exportMaxResponseBytes() && includePoints !== false && format !== "svg") {
|
|
56799
|
+
result = getNoteDrawings(id2, { format, includePoints: false });
|
|
56800
|
+
pointsOmitted = true;
|
|
56801
|
+
}
|
|
56802
|
+
const text2 = formatNoteDrawings(result) + (pointsOmitted ? "\nStroke points were omitted to stay under the response size limit (APPLE_NOTES_MCP_EXPORT_MAX_BYTES)." : "");
|
|
56803
|
+
return successResponse(text2, {
|
|
56804
|
+
...result,
|
|
56805
|
+
...pointsOmitted ? { pointsOmitted } : {}
|
|
56806
|
+
});
|
|
56807
|
+
}, "Error reading drawings")
|
|
56808
|
+
);
|
|
55916
56809
|
registerTool(
|
|
55917
56810
|
"list-recent-notes",
|
|
55918
56811
|
{
|