apple-notes-mcp 2.9.24 → 2.9.25

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.
Files changed (3) hide show
  1. package/README.md +26 -16
  2. package/build/index.js +749 -549
  3. package/package.json +1 -1
package/build/index.js CHANGED
@@ -39088,6 +39088,10 @@ function isPermissionDenied(error2) {
39088
39088
  if (!error2) return false;
39089
39089
  return PERMISSION_DENIED_PATTERN.test(error2) || error2.includes(PERMISSION_DENIED_MESSAGE);
39090
39090
  }
39091
+ var QUOTED = /"[^"\n]*"|“[^”\n]*”/g;
39092
+ function stripQuoted(text2) {
39093
+ return text2.replace(QUOTED, '""');
39094
+ }
39091
39095
  var ERROR_MAPPINGS = [
39092
39096
  // Permission errors
39093
39097
  {
@@ -39158,12 +39162,13 @@ function parseErrorMessage(errorOutput) {
39158
39162
  if (executionError) {
39159
39163
  coreError = executionError[1].trim();
39160
39164
  }
39161
- if (isPermissionDenied(errorOutput)) {
39165
+ if (isPermissionDenied(stripQuoted(errorOutput))) {
39162
39166
  return PERMISSION_DENIED_MESSAGE;
39163
39167
  }
39168
+ const unquoted = stripQuoted(coreError);
39164
39169
  for (const { pattern: pattern2, message } of ERROR_MAPPINGS) {
39165
39170
  const match = coreError.match(pattern2);
39166
- if (match) {
39171
+ if (match && (match.length > 1 || pattern2.test(unquoted))) {
39167
39172
  let result = message;
39168
39173
  for (let i = 1; i < match.length; i++) {
39169
39174
  result = result.replace(`$${i}`, match[i] || "");
@@ -39179,7 +39184,10 @@ function parseErrorMessage(errorOutput) {
39179
39184
  }
39180
39185
  function executeAppleScript(script, options = {}) {
39181
39186
  const timeoutMs = options.timeoutMs ?? callTimeoutMs() ?? envPositiveNumber("APPLE_NOTES_MCP_TIMEOUT_MS") ?? DEFAULT_TIMEOUT_MS;
39182
- const maxRetries = options.maxRetries ?? envPositiveNumber("APPLE_NOTES_MCP_MAX_RETRIES") ?? DEFAULT_MAX_RETRIES;
39187
+ const maxRetries = Math.max(
39188
+ 1,
39189
+ options.maxRetries ?? envPositiveNumber("APPLE_NOTES_MCP_MAX_RETRIES") ?? DEFAULT_MAX_RETRIES
39190
+ );
39183
39191
  const retryDelayMs = options.retryDelayMs ?? envPositiveNumber("APPLE_NOTES_MCP_RETRY_DELAY_MS") ?? DEFAULT_RETRY_DELAY_MS;
39184
39192
  const maxBufferBytes = Math.min(options.maxBufferBytes ?? getMaxBuffer(), MAX_OUTPUT_BYTES);
39185
39193
  if (!script || !script.trim()) {
@@ -39588,6 +39596,252 @@ function getChecklistItems(noteId3) {
39588
39596
  return { items };
39589
39597
  }
39590
39598
 
39599
+ // src/utils/noteIdentifiers.ts
39600
+ import { execFileSync as execFileSync3 } from "child_process";
39601
+ import * as fs2 from "fs";
39602
+ import * as os2 from "os";
39603
+ import * as path2 from "path";
39604
+ var NOTES_DB_PATH2 = path2.join(
39605
+ os2.homedir(),
39606
+ "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
39607
+ );
39608
+ var UUID_PATTERN = /^[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$/;
39609
+ var NUMERIC_KEY_PATTERN = /^\d{1,18}$/;
39610
+ var NOTE_ID_MESSAGE = "A canonical Apple Note ID is required (x-coredata://.../ICNote/p...), or the note's Notes UUID or numeric key";
39611
+ function identifierForm(value) {
39612
+ if (UUID_PATTERN.test(value)) return "uuid";
39613
+ if (NUMERIC_KEY_PATTERN.test(value)) return "key";
39614
+ return "other";
39615
+ }
39616
+ function isAlternateIdentifier(value) {
39617
+ return identifierForm(value) !== "other";
39618
+ }
39619
+ var IdentifierResolutionError = class extends Error {
39620
+ constructor(code, message) {
39621
+ super(message);
39622
+ this.code = code;
39623
+ this.name = "IdentifierResolutionError";
39624
+ }
39625
+ code;
39626
+ };
39627
+ var ENTITY_LABEL = {
39628
+ ICNote: "note",
39629
+ ICFolder: "folder",
39630
+ ICAccount: "account"
39631
+ };
39632
+ var NO_FDA_MESSAGE = `Resolving a Notes UUID or numeric key reads the Notes database, which needs Full Disk Access for the Node binary running this server, or the terminal that launches it (System Settings > Privacy & Security > Full Disk Access, then fully quit and relaunch it). x-coredata ids from search-notes, list-notes, or list-folders work without it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL}`;
39633
+ function identifierFailureIn(message) {
39634
+ if (message.includes(NO_FDA_MESSAGE)) return "no_fda";
39635
+ if (/\bNo (?:note|folder|account) found for (?:numeric key|identifier) /.test(message))
39636
+ return "not_found";
39637
+ if (message.includes("Failed to read the Notes database.") || message.includes("The Notes database has no store UUID"))
39638
+ return "query_error";
39639
+ return null;
39640
+ }
39641
+ function canonicalKey(value) {
39642
+ return BigInt(value).toString();
39643
+ }
39644
+ function assertAll(values, pattern2, label) {
39645
+ for (const value of values) {
39646
+ if (!pattern2.test(value)) throw new Error(`Refusing to query with an invalid ${label}`);
39647
+ }
39648
+ }
39649
+ function entityClause(entity3) {
39650
+ return `(SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = '${entity3}')`;
39651
+ }
39652
+ function buildResolveSql(entity3, keys, uuids) {
39653
+ assertAll(keys, NUMERIC_KEY_PATTERN, "numeric key");
39654
+ assertAll(uuids, UUID_PATTERN, "UUID");
39655
+ const matches = [];
39656
+ if (keys.length > 0) {
39657
+ matches.push(`o.Z_PK IN (${[...new Set(keys.map(canonicalKey))].join(", ")})`);
39658
+ }
39659
+ if (uuids.length > 0) {
39660
+ const variants = /* @__PURE__ */ new Set();
39661
+ for (const uuid2 of uuids) {
39662
+ variants.add(uuid2);
39663
+ variants.add(uuid2.toUpperCase());
39664
+ variants.add(uuid2.toLowerCase());
39665
+ }
39666
+ matches.push(`o.ZIDENTIFIER IN (${[...variants].map((v) => `'${v}'`).join(", ")})`);
39667
+ }
39668
+ const where = matches.length > 0 ? matches.join(" OR ") : "0";
39669
+ return `SELECT json_object('store', (SELECT Z_UUID FROM Z_METADATA LIMIT 1), 'rows', (SELECT json_group_array(json_object('pk', o.Z_PK, 'identifier', o.ZIDENTIFIER)) FROM ZICCLOUDSYNCINGOBJECT o WHERE o.Z_ENT = ${entityClause(entity3)} AND (${where})));`;
39670
+ }
39671
+ function buildLookupSql(entity3, keys) {
39672
+ assertAll(keys, NUMERIC_KEY_PATTERN, "numeric key");
39673
+ const pks = [...new Set(keys.map(canonicalKey))];
39674
+ const inList = pks.length > 0 ? pks.join(", ") : "NULL";
39675
+ let fields = "'pk', o.Z_PK, 'identifier', o.ZIDENTIFIER";
39676
+ let joins = "";
39677
+ if (entity3 === "ICNote") {
39678
+ fields += ", 'folderIdentifier', f.ZIDENTIFIER, 'accountIdentifier', a.ZIDENTIFIER";
39679
+ joins = "LEFT JOIN ZICCLOUDSYNCINGOBJECT f ON f.Z_PK = o.ZFOLDER LEFT JOIN ZICCLOUDSYNCINGOBJECT a ON a.Z_PK = f.ZOWNER ";
39680
+ } else if (entity3 === "ICFolder") {
39681
+ fields += ", 'parentIdentifier', p.ZIDENTIFIER, 'accountIdentifier', a.ZIDENTIFIER";
39682
+ joins = "LEFT JOIN ZICCLOUDSYNCINGOBJECT p ON p.Z_PK = o.ZPARENT LEFT JOIN ZICCLOUDSYNCINGOBJECT a ON a.Z_PK = o.ZOWNER ";
39683
+ }
39684
+ return `SELECT json_object('store', (SELECT Z_UUID FROM Z_METADATA LIMIT 1), 'rows', (SELECT json_group_array(json_object(${fields})) FROM ZICCLOUDSYNCINGOBJECT o ${joins}WHERE o.Z_ENT = ${entityClause(entity3)} AND o.Z_PK IN (${inList})));`;
39685
+ }
39686
+ function runJsonQuery(sql, dbPath2) {
39687
+ if (!fs2.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
39688
+ let out;
39689
+ try {
39690
+ out = execFileSync3("sqlite3", ["-readonly", dbPath2, sql], {
39691
+ encoding: "utf8",
39692
+ timeout: 5e3,
39693
+ stdio: ["pipe", "pipe", "pipe"]
39694
+ }).trim();
39695
+ } catch (error2) {
39696
+ const detail = error2 instanceof Error ? `${error2.message} ${String(error2.stderr ?? "")}` : String(error2);
39697
+ if (detail.includes("authorization denied") || detail.includes("unable to open database")) {
39698
+ throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
39699
+ }
39700
+ console.error(`Identifier query failed: ${detail}`);
39701
+ throw new IdentifierResolutionError("query_error", "Failed to read the Notes database.");
39702
+ }
39703
+ const parsed = JSON.parse(out || "{}");
39704
+ const rows = typeof parsed.rows === "string" ? JSON.parse(parsed.rows) : parsed.rows;
39705
+ return { store: parsed.store ?? null, rows: rows ?? [] };
39706
+ }
39707
+ function coreDataId(store, entity3, pk) {
39708
+ return `x-coredata://${store}/${entity3}/p${pk}`;
39709
+ }
39710
+ function resolveIdentifiers(values, entity3, dbPath2 = NOTES_DB_PATH2) {
39711
+ const resolved = /* @__PURE__ */ new Map();
39712
+ const keys = [];
39713
+ const uuids = [];
39714
+ for (const value of values) {
39715
+ const form = identifierForm(value);
39716
+ if (form === "key") keys.push(value);
39717
+ else if (form === "uuid") uuids.push(value);
39718
+ else resolved.set(value, value);
39719
+ }
39720
+ if (keys.length === 0 && uuids.length === 0) return resolved;
39721
+ const { store, rows } = runJsonQuery(
39722
+ buildResolveSql(entity3, keys, uuids),
39723
+ dbPath2
39724
+ );
39725
+ if (!store) {
39726
+ throw new IdentifierResolutionError(
39727
+ "query_error",
39728
+ "The Notes database has no store UUID, so an x-coredata id cannot be built."
39729
+ );
39730
+ }
39731
+ const byKey = new Map(rows.map((row) => [String(row.pk), row]));
39732
+ const byUuid = new Map(
39733
+ rows.filter((row) => row.identifier).map((row) => [String(row.identifier).toUpperCase(), row])
39734
+ );
39735
+ const label = ENTITY_LABEL[entity3];
39736
+ const missing = [];
39737
+ for (const key of keys) {
39738
+ const row = byKey.get(canonicalKey(key));
39739
+ if (row) resolved.set(key, coreDataId(store, entity3, row.pk));
39740
+ else missing.push(`numeric key ${key}`);
39741
+ }
39742
+ for (const uuid2 of uuids) {
39743
+ const row = byUuid.get(uuid2.toUpperCase());
39744
+ if (row) resolved.set(uuid2, coreDataId(store, entity3, row.pk));
39745
+ else missing.push(`identifier ${uuid2}`);
39746
+ }
39747
+ if (missing.length > 0) {
39748
+ throw new IdentifierResolutionError(
39749
+ "not_found",
39750
+ `No ${label} found for ${missing.join(", ")}. A numeric key must belong to a ${label}, not another object type.`
39751
+ );
39752
+ }
39753
+ return resolved;
39754
+ }
39755
+ var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
39756
+ function lookupStableIdentifiers(ids, entity3, dbPath2 = NOTES_DB_PATH2) {
39757
+ const result = /* @__PURE__ */ new Map();
39758
+ const wanted = /* @__PURE__ */ new Map();
39759
+ for (const id2 of ids) {
39760
+ const match = COREDATA_PARTS.exec(id2);
39761
+ if (!match || match[2] !== entity3) continue;
39762
+ const pk = canonicalKey(match[3]);
39763
+ const list = wanted.get(pk) ?? [];
39764
+ list.push({ id: id2, store: match[1] });
39765
+ wanted.set(pk, list);
39766
+ }
39767
+ if (wanted.size === 0) return result;
39768
+ let query2;
39769
+ try {
39770
+ query2 = runJsonQuery(buildLookupSql(entity3, [...wanted.keys()]), dbPath2);
39771
+ } catch {
39772
+ return result;
39773
+ }
39774
+ const store = query2.store?.toUpperCase();
39775
+ for (const row of query2.rows) {
39776
+ const fields = {};
39777
+ for (const key of [
39778
+ "identifier",
39779
+ "folderIdentifier",
39780
+ "parentIdentifier",
39781
+ "accountIdentifier"
39782
+ ]) {
39783
+ const value = row[key];
39784
+ if (typeof value === "string" && value) fields[key] = value;
39785
+ }
39786
+ for (const target of wanted.get(String(row.pk)) ?? []) {
39787
+ if (target.store.toUpperCase() === store) result.set(target.id, fields);
39788
+ }
39789
+ }
39790
+ return result;
39791
+ }
39792
+ function withStableIdentifiers(items, entity3, dbPath2 = NOTES_DB_PATH2) {
39793
+ const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
39794
+ if (ids.length === 0) return items;
39795
+ const found = lookupStableIdentifiers(ids, entity3, dbPath2);
39796
+ if (found.size === 0) return items;
39797
+ return items.map(
39798
+ (item) => item.id && found.has(item.id) ? { ...item, ...found.get(item.id) } : item
39799
+ );
39800
+ }
39801
+ var defaultResolver = (values, entity3) => resolveIdentifiers(values, entity3);
39802
+ var DEFAULT_MAX_ID_LENGTH = 2e3;
39803
+ function acceptAlternateForms(pattern2) {
39804
+ return new RegExp(
39805
+ `${pattern2.source}|${UUID_PATTERN.source}|${NUMERIC_KEY_PATTERN.source}`,
39806
+ pattern2.flags
39807
+ );
39808
+ }
39809
+ function resolveInSchema(values, entity3, ctx, resolver) {
39810
+ if (!values.some(isAlternateIdentifier)) return new Map(values.map((v) => [v, v]));
39811
+ try {
39812
+ return resolver(values, entity3);
39813
+ } catch (error2) {
39814
+ ctx.addIssue({
39815
+ code: external_exports.ZodIssueCode.custom,
39816
+ message: error2 instanceof Error ? error2.message : String(error2)
39817
+ });
39818
+ return null;
39819
+ }
39820
+ }
39821
+ function exactIdInput(entity3, pattern2, message, options = {}) {
39822
+ const resolver = options.resolver ?? defaultResolver;
39823
+ return external_exports.string().max(options.maxLength ?? DEFAULT_MAX_ID_LENGTH).regex(acceptAlternateForms(pattern2), message).transform((value, ctx) => {
39824
+ const map = resolveInSchema([value], entity3, ctx, resolver);
39825
+ return map ? map.get(value) ?? value : external_exports.NEVER;
39826
+ });
39827
+ }
39828
+ function exactIdArrayInput(entity3, pattern2, message, options = {}) {
39829
+ const resolver = options.resolver ?? defaultResolver;
39830
+ const item = external_exports.string().max(options.maxLength ?? DEFAULT_MAX_ID_LENGTH).regex(acceptAlternateForms(pattern2), message);
39831
+ const array2 = options.maxItems === void 0 ? external_exports.array(item) : external_exports.array(item).max(options.maxItems);
39832
+ return array2.transform((values, ctx) => {
39833
+ const map = resolveInSchema(values, entity3, ctx, resolver);
39834
+ return map ? values.map((value) => map.get(value) ?? value) : external_exports.NEVER;
39835
+ });
39836
+ }
39837
+ function looseIdTransform(entity3, resolver = defaultResolver) {
39838
+ return (value, ctx) => {
39839
+ if (!isAlternateIdentifier(value)) return value;
39840
+ const map = resolveInSchema([value], entity3, ctx, resolver);
39841
+ return map ? map.get(value) ?? value : external_exports.NEVER;
39842
+ };
39843
+ }
39844
+
39591
39845
  // src/utils/errorCodes.ts
39592
39846
  var CodedError = class extends Error {
39593
39847
  envelope;
@@ -39634,8 +39888,10 @@ var NOTHING_WRITTEN = /nothing (?:was )?(?:changed|created|written)|no replaceme
39634
39888
  var WRITE_ACCEPTED = /\baccepted (?:the|an) \w+, but/i;
39635
39889
  function classifyError(message, cause) {
39636
39890
  if (cause instanceof CodedError) return { ...cause.envelope };
39637
- const text2 = cause instanceof Error && cause.message && !message.includes(cause.message) ? `${message}
39638
- ${cause.message}` : message;
39891
+ const text2 = stripQuoted(
39892
+ cause instanceof Error && cause.message && !message.includes(cause.message) ? `${message}
39893
+ ${cause.message}` : message
39894
+ );
39639
39895
  const timeoutCause = typeof cause === "object" && cause !== null && cause.code === "ETIMEDOUT";
39640
39896
  let code = "operation_failed";
39641
39897
  if (timeoutCause) code = "timeout_indeterminate";
@@ -39658,6 +39914,24 @@ function errorResult(message, cause) {
39658
39914
  isError: true
39659
39915
  };
39660
39916
  }
39917
+ function unavailableWriteEnvelope(envelope) {
39918
+ if (envelope.code !== "notes_unavailable" || envelope.indeterminate !== void 0 || envelope.committed !== void 0)
39919
+ return envelope;
39920
+ return { ...envelope, indeterminate: true };
39921
+ }
39922
+ var INPUT_VALIDATION = /^(?:MCP error -?\d+: )?Input validation error:/;
39923
+ function inputValidationEnvelope(message) {
39924
+ const failure2 = identifierFailureIn(message);
39925
+ const code = failure2 === "no_fda" ? "full_disk_access_missing" : failure2 === "not_found" ? "not_found" : failure2 === "query_error" ? "operation_failed" : "validation_error";
39926
+ return { code, committed: false, indeterminate: false };
39927
+ }
39928
+ function sdkToolError(message) {
39929
+ return INPUT_VALIDATION.test(message) ? errorResult(message, new CodedError(message, inputValidationEnvelope(message))) : errorResult(message);
39930
+ }
39931
+ function installSdkErrorCodes(server2) {
39932
+ const target = server2;
39933
+ if (typeof target.createToolError === "function") target.createToolError = sdkToolError;
39934
+ }
39661
39935
 
39662
39936
  // src/utils/inlineImages.ts
39663
39937
  var DEFAULT_MAX_INLINE_IMAGE_BYTES = 256 * 1024;
@@ -39700,10 +39974,10 @@ function strippedImagesWarning(stripped) {
39700
39974
  }
39701
39975
 
39702
39976
  // src/utils/noteRichText.ts
39703
- import { execFileSync as execFileSync3 } from "node:child_process";
39977
+ import { execFileSync as execFileSync4 } from "node:child_process";
39704
39978
  import { createHash } from "node:crypto";
39705
- import { homedir as homedir2 } from "node:os";
39706
- import { join as join2 } from "node:path";
39979
+ import { homedir as homedir3 } from "node:os";
39980
+ import { join as join3 } from "node:path";
39707
39981
  import { gunzipSync as gunzipSync2 } from "node:zlib";
39708
39982
 
39709
39983
  // src/utils/uniqueById.ts
@@ -39727,7 +40001,7 @@ var HTML_LOSSY_ORDER = [
39727
40001
  "alignment",
39728
40002
  "highlight"
39729
40003
  ];
39730
- var dbPath = join2(homedir2(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
40004
+ var dbPath = join3(homedir3(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
39731
40005
  var safeUrl = (url) => /^(?:https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url) && !Array.from(url).some((char) => char.charCodeAt(0) < 32);
39732
40006
  var escapeAttribute = (text2) => text2.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
39733
40007
  var normalized = (text2) => text2.replace(/[\s\ufffc]/gu, "");
@@ -39859,7 +40133,7 @@ function readRichNote(id2) {
39859
40133
  const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]+)$/i.exec(id2)?.[1];
39860
40134
  if (!pk) throw new Error("Invalid exact note ID");
39861
40135
  const sql = `BEGIN; SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE=${pk}; SELECT json_group_object(ZIDENTIFIER,ZALTTEXT) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} AND ZTYPEUTI1='com.apple.notes.inlinetextattachment.hashtag'; SELECT json_group_array(json_object('id',ZIDENTIFIER,'pk',Z_PK,'type',COALESCE(ZTYPEUTI1,ZTYPEUTI),'mergeable',hex(COALESCE(ZMERGEABLEDATA1,ZMERGEABLEDATA)),'view',ZATTACHMENTVIEWTYPE)) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} OR ZNOTE=${pk}; COMMIT;`;
39862
- const rows = execFileSync3("/usr/bin/sqlite3", ["-readonly", dbPath, sql], {
40136
+ const rows = execFileSync4("/usr/bin/sqlite3", ["-readonly", dbPath, sql], {
39863
40137
  encoding: "utf8",
39864
40138
  timeout: 5e3,
39865
40139
  maxBuffer: 64 * 1024 * 1024,
@@ -40055,12 +40329,12 @@ function assertLinkedWrite(rich, content, format, allowLinkChanges = false) {
40055
40329
  }
40056
40330
 
40057
40331
  // src/utils/audioTranscripts.ts
40058
- import { execFileSync as execFileSync4 } from "node:child_process";
40059
- import { homedir as homedir3 } from "node:os";
40060
- import { join as join3 } from "node:path";
40332
+ import { execFileSync as execFileSync5 } from "node:child_process";
40333
+ import { homedir as homedir4 } from "node:os";
40334
+ import { join as join4 } from "node:path";
40061
40335
  import { gunzipSync as gunzipSync3 } from "node:zlib";
40062
- var NOTES_DB_PATH2 = join3(
40063
- homedir3(),
40336
+ var NOTES_DB_PATH3 = join4(
40337
+ homedir4(),
40064
40338
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
40065
40339
  );
40066
40340
  var DEFAULT_MAX_SEGMENTS = 2e3;
@@ -40228,10 +40502,11 @@ function buildTranscriptSql(pk, available) {
40228
40502
  if (!/^\d+$/.test(pk)) throw new Error("Invalid primary key");
40229
40503
  const optional2 = (column, key, alias = "a") => available.has(column) ? `, '${key}', ${alias}.${column}` : "";
40230
40504
  const locked = available.has("ZISPASSWORDPROTECTED") ? "COALESCE(n.ZISPASSWORDPROTECTED, 0)" : "0";
40505
+ const isNote = `n.Z_PK = ${pk} AND n.Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICNote')`;
40231
40506
  return [
40232
40507
  "BEGIN;",
40233
40508
  // Every statement yields exactly one row, so output lines stay positional.
40234
- `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}));`,
40509
+ `SELECT json_object('found', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT n WHERE ${isNote}), 'locked', (SELECT ${locked} FROM ZICCLOUDSYNCINGOBJECT n WHERE ${isNote}));`,
40235
40510
  `SELECT COALESCE((SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = ${pk} LIMIT 1), '');`,
40236
40511
  "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};`,
40237
40512
  "COMMIT;"
@@ -40245,7 +40520,7 @@ var REQUIRED_COLUMNS = [
40245
40520
  "ZIDENTIFIER"
40246
40521
  ];
40247
40522
  function runSqlite(dbPath2, sql) {
40248
- return execFileSync4("sqlite3", ["-readonly", dbPath2, sql], {
40523
+ return execFileSync5("sqlite3", ["-readonly", dbPath2, sql], {
40249
40524
  encoding: "utf8",
40250
40525
  timeout: 1e4,
40251
40526
  maxBuffer: 256 * 1024 * 1024,
@@ -40266,7 +40541,7 @@ function bodyAttachmentOrder(gzipped) {
40266
40541
  }
40267
40542
  function readAudioTranscripts(noteId3, options = {}) {
40268
40543
  const { store, pk } = parseNoteId(noteId3);
40269
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH2;
40544
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH3;
40270
40545
  const maxSegments = Math.min(
40271
40546
  Math.max(1, Math.floor(options.maxSegments ?? DEFAULT_MAX_SEGMENTS)),
40272
40547
  MAX_SEGMENTS_LIMIT
@@ -40598,17 +40873,17 @@ function collectNoteTables(rich, noteId3) {
40598
40873
  }
40599
40874
 
40600
40875
  // src/utils/smartFolders.ts
40601
- import { execFileSync as execFileSync5 } from "child_process";
40602
- import * as fs2 from "fs";
40603
- import * as os2 from "os";
40604
- import * as path2 from "path";
40605
- var NOTES_DB_PATH3 = path2.join(
40606
- os2.homedir(),
40607
- "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
40608
- );
40609
- var FDA_MESSAGE = `Full Disk Access is required to read smart folders. In System Settings > Privacy & Security > Full Disk Access, grant access to the Node binary running this server (required under Claude Desktop) or the terminal that launches it, then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL} \u2014 run the doctor tool to verify.`;
40610
- var COCOA_EPOCH_OFFSET = 978307200;
40611
- var MAX_DEPTH = 32;
40876
+ import { execFileSync as execFileSync6 } from "child_process";
40877
+ import * as fs3 from "fs";
40878
+ import * as os3 from "os";
40879
+ import * as path3 from "path";
40880
+ var NOTES_DB_PATH4 = path3.join(
40881
+ os3.homedir(),
40882
+ "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
40883
+ );
40884
+ var FDA_MESSAGE = `Full Disk Access is required to read smart folders. In System Settings > Privacy & Security > Full Disk Access, grant access to the Node binary running this server (required under Claude Desktop) or the terminal that launches it, then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL} \u2014 run the doctor tool to verify.`;
40885
+ var COCOA_EPOCH_OFFSET = 978307200;
40886
+ var MAX_DEPTH = 32;
40612
40887
  var RELATIVE_RANGES = {
40613
40888
  0: "today",
40614
40889
  1: "yesterday",
@@ -40948,15 +41223,15 @@ function buildSmartFolders(output) {
40948
41223
  );
40949
41224
  }
40950
41225
  function runSqlite2(dbPath2, sql) {
40951
- return execFileSync5("sqlite3", ["-readonly", dbPath2, sql], {
41226
+ return execFileSync6("sqlite3", ["-readonly", dbPath2, sql], {
40952
41227
  encoding: "utf8",
40953
41228
  timeout: 5e3,
40954
41229
  maxBuffer: 16 * 1024 * 1024,
40955
41230
  stdio: ["pipe", "pipe", "pipe"]
40956
41231
  });
40957
41232
  }
40958
- function readSmartFolders(dbPath2 = NOTES_DB_PATH3) {
40959
- if (!fs2.existsSync(dbPath2)) return { folders: null, error: "no_fda", message: FDA_MESSAGE };
41233
+ function readSmartFolders(dbPath2 = NOTES_DB_PATH4) {
41234
+ if (!fs3.existsSync(dbPath2)) return { folders: null, error: "no_fda", message: FDA_MESSAGE };
40960
41235
  try {
40961
41236
  const columns = new Set(
40962
41237
  runSqlite2(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").trim().split("\n")
@@ -40981,30 +41256,30 @@ function readSmartFolders(dbPath2 = NOTES_DB_PATH3) {
40981
41256
  }
40982
41257
 
40983
41258
  // src/utils/trashFolders.ts
40984
- import { execFileSync as execFileSync6 } from "child_process";
40985
- import * as fs3 from "fs";
40986
- import * as os3 from "os";
40987
- import * as path3 from "path";
40988
- var NOTES_DB_PATH4 = path3.join(
40989
- os3.homedir(),
41259
+ import { execFileSync as execFileSync7 } from "child_process";
41260
+ import * as fs4 from "fs";
41261
+ import * as os4 from "os";
41262
+ import * as path4 from "path";
41263
+ var NOTES_DB_PATH5 = path4.join(
41264
+ os4.homedir(),
40990
41265
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
40991
41266
  );
40992
41267
  var RECENTLY_DELETED_FOLDER_NAME = "Recently Deleted";
40993
41268
  var CACHE_TTL_MS = 5 * 60 * 1e3;
40994
41269
  var cache = null;
40995
41270
  function query(dbPath2, sql) {
40996
- return execFileSync6("sqlite3", ["-readonly", dbPath2, sql], {
41271
+ return execFileSync7("sqlite3", ["-readonly", dbPath2, sql], {
40997
41272
  encoding: "utf8",
40998
41273
  timeout: 5e3,
40999
41274
  stdio: ["pipe", "pipe", "pipe"]
41000
41275
  });
41001
41276
  }
41002
- function readTrashFolderIds(dbPath2 = NOTES_DB_PATH4) {
41277
+ function readTrashFolderIds(dbPath2 = NOTES_DB_PATH5) {
41003
41278
  if (cache && cache.dbPath === dbPath2 && Date.now() - cache.at < CACHE_TTL_MS) {
41004
41279
  return cache.ids;
41005
41280
  }
41006
41281
  try {
41007
- if (!fs3.existsSync(dbPath2)) return [];
41282
+ if (!fs4.existsSync(dbPath2)) return [];
41008
41283
  const columns = new Set(
41009
41284
  query(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").split("\n").map((line) => line.trim()).filter(Boolean)
41010
41285
  );
@@ -41028,7 +41303,7 @@ function readTrashFolderIds(dbPath2 = NOTES_DB_PATH4) {
41028
41303
  import {
41029
41304
  closeSync,
41030
41305
  constants,
41031
- existsSync as existsSync4,
41306
+ existsSync as existsSync5,
41032
41307
  fstatSync,
41033
41308
  lstatSync,
41034
41309
  mkdirSync,
@@ -41039,11 +41314,11 @@ import {
41039
41314
  rmSync,
41040
41315
  statSync
41041
41316
  } from "fs";
41042
- import { dirname, isAbsolute, join as join6, relative, resolve, sep } from "path";
41043
- import { homedir as homedir6, tmpdir } from "os";
41317
+ import { dirname, isAbsolute, join as join7, relative, resolve, sep } from "path";
41318
+ import { homedir as homedir7, tmpdir } from "os";
41044
41319
  function allowedSaveRoots() {
41045
41320
  return [
41046
- resolve(homedir6()),
41321
+ resolve(homedir7()),
41047
41322
  resolve(tmpdir()),
41048
41323
  "/Volumes",
41049
41324
  "/private/var/folders",
@@ -41052,7 +41327,7 @@ function allowedSaveRoots() {
41052
41327
  ];
41053
41328
  }
41054
41329
  function deniedSaveRoots() {
41055
- return [join6(homedir6(), "Library/Group Containers/group.com.apple.notes")];
41330
+ return [join7(homedir7(), "Library/Group Containers/group.com.apple.notes")];
41056
41331
  }
41057
41332
  function canonicalize(path10) {
41058
41333
  return realpathSync.native(path10);
@@ -41120,7 +41395,7 @@ function assertSafeSavePath(p, roots = allowedSaveRoots(), denied = deniedSaveRo
41120
41395
  if (suffix.split(sep).includes("..")) {
41121
41396
  throw new Error(`Refusing to write outside allowed locations (home, temp, /Volumes): "${abs}"`);
41122
41397
  }
41123
- const canonicalDest = suffix ? join6(canonicalAncestor, suffix) : canonicalAncestor;
41398
+ const canonicalDest = suffix ? join7(canonicalAncestor, suffix) : canonicalAncestor;
41124
41399
  const allowed = canonicalRoots(roots);
41125
41400
  if (!isWithinRoots(canonicalAncestor, allowed) || !isWithinRoots(canonicalDest, allowed)) {
41126
41401
  throw new Error(
@@ -41189,13 +41464,13 @@ function privateContentReason(p, roots) {
41189
41464
  const containing = roots.map((r) => r.endsWith(sep) ? r.slice(0, -1) : r).filter((r) => isWithinRoots(p, [r])).sort((a, b) => b.length - a.length)[0];
41190
41465
  const below = containing === void 0 ? p : relative(containing, p);
41191
41466
  if (below.split(sep).some((part) => part.startsWith("."))) return "a hidden file or directory";
41192
- const home = resolve(homedir6());
41467
+ const home = resolve(homedir7());
41193
41468
  const libraries = [
41194
- join6(home, "Library"),
41195
- ...canonicalRoots([home]).map((h) => join6(h, "Library"))
41469
+ join7(home, "Library"),
41470
+ ...canonicalRoots([home]).map((h) => join7(h, "Library"))
41196
41471
  ];
41197
41472
  if (!isWithinRoots(p, libraries)) return null;
41198
- const cloudDocuments = libraries.flatMap((l) => CLOUD_DOCUMENT_DIRS.map((d) => join6(l, d)));
41473
+ const cloudDocuments = libraries.flatMap((l) => CLOUD_DOCUMENT_DIRS.map((d) => join7(l, d)));
41199
41474
  return isWithinRoots(p, cloudDocuments) ? null : "~/Library";
41200
41475
  }
41201
41476
  var CLOUD_DOCUMENT_DIRS = ["Mobile Documents", "CloudStorage"];
@@ -41253,13 +41528,13 @@ function readAllowedTextFile(p, maxBytes, roots = allowedSaveRoots(), allowPriva
41253
41528
  }
41254
41529
  function cleanupTempDir(dir) {
41255
41530
  try {
41256
- if (existsSync4(dir)) rmSync(dir, { recursive: true, force: true });
41531
+ if (existsSync5(dir)) rmSync(dir, { recursive: true, force: true });
41257
41532
  } catch {
41258
41533
  }
41259
41534
  }
41260
41535
 
41261
41536
  // src/utils/paperAttachments.ts
41262
- import { execFileSync as execFileSync8 } from "node:child_process";
41537
+ import { execFileSync as execFileSync9 } from "node:child_process";
41263
41538
  import {
41264
41539
  closeSync as closeSync3,
41265
41540
  constants as constants3,
@@ -41272,10 +41547,10 @@ import {
41272
41547
  unlinkSync as unlinkSync2,
41273
41548
  writeSync as writeSync2
41274
41549
  } from "node:fs";
41275
- import { basename as basename2, dirname as dirname3, extname as extname2, join as join8 } from "node:path";
41550
+ import { basename as basename2, dirname as dirname3, extname as extname2, join as join9 } from "node:path";
41276
41551
 
41277
41552
  // src/utils/attachmentAssets.ts
41278
- import { execFileSync as execFileSync7 } from "node:child_process";
41553
+ import { execFileSync as execFileSync8 } from "node:child_process";
41279
41554
  import {
41280
41555
  closeSync as closeSync2,
41281
41556
  constants as constants2,
@@ -41289,11 +41564,11 @@ import {
41289
41564
  unlinkSync,
41290
41565
  writeSync
41291
41566
  } from "node:fs";
41292
- import { homedir as homedir7 } from "node:os";
41293
- import { basename, dirname as dirname2, extname, isAbsolute as isAbsolute2, join as join7, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
41567
+ import { homedir as homedir8 } from "node:os";
41568
+ import { basename, dirname as dirname2, extname, isAbsolute as isAbsolute2, join as join8, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
41294
41569
  import { gunzipSync as gunzipSync5 } from "node:zlib";
41295
- var NOTES_CONTAINER_DIR = join7(
41296
- homedir7(),
41570
+ var NOTES_CONTAINER_DIR = join8(
41571
+ homedir8(),
41297
41572
  "Library/Group Containers/group.com.apple.notes"
41298
41573
  );
41299
41574
  var AttachmentStoreError = class extends Error {
@@ -41398,7 +41673,7 @@ function buildAttachmentRowsSql(notePk, columns) {
41398
41673
  }
41399
41674
  function runSqlite3(dbPath2, sql) {
41400
41675
  try {
41401
- return execFileSync7("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
41676
+ return execFileSync8("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
41402
41677
  encoding: "utf8",
41403
41678
  timeout: 1e4,
41404
41679
  maxBuffer: 128 * 1024 * 1024,
@@ -41466,7 +41741,7 @@ function attachmentOrderFromNoteData(hex3) {
41466
41741
  return null;
41467
41742
  }
41468
41743
  }
41469
- function readNoteAttachmentRows(noteId3, dbPath2 = join7(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
41744
+ function readNoteAttachmentRows(noteId3, dbPath2 = join8(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
41470
41745
  const { pk } = parseNoteId2(noteId3);
41471
41746
  const columnList = runSqlite3(
41472
41747
  dbPath2,
@@ -41527,7 +41802,7 @@ function boundedEntries(dir, limit) {
41527
41802
  }
41528
41803
  }
41529
41804
  function resolveAccountDir(containerDir, accountIdentifier) {
41530
- const accountsDir = join7(containerDir, "Accounts");
41805
+ const accountsDir = join8(containerDir, "Accounts");
41531
41806
  let accountsReal;
41532
41807
  try {
41533
41808
  accountsReal = realpathSync2.native(accountsDir);
@@ -41536,14 +41811,14 @@ function resolveAccountDir(containerDir, accountIdentifier) {
41536
41811
  }
41537
41812
  const account = safeComponent(accountIdentifier);
41538
41813
  if (account) {
41539
- const dir = realInside(join7(accountsReal, account), accountsReal);
41814
+ const dir = realInside(join8(accountsReal, account), accountsReal);
41540
41815
  if (dir && isDirectory(dir)) return dir;
41541
41816
  }
41542
41817
  const entries2 = (boundedEntries(accountsReal, 64) ?? []).filter(
41543
- (e) => isDirectory(join7(accountsReal, e))
41818
+ (e) => isDirectory(join8(accountsReal, e))
41544
41819
  );
41545
41820
  if (entries2.length !== 1) return null;
41546
- return realInside(join7(accountsReal, entries2[0]), accountsReal);
41821
+ return realInside(join8(accountsReal, entries2[0]), accountsReal);
41547
41822
  }
41548
41823
  function generationRank(name) {
41549
41824
  const m = /^(\d+)_/.exec(name);
@@ -41551,23 +41826,23 @@ function generationRank(name) {
41551
41826
  }
41552
41827
  function generationDirs(base, accountDir) {
41553
41828
  const entries2 = boundedEntries(base, MAX_GENERATION_DIRS) ?? [];
41554
- return entries2.map((e) => realInside(join7(base, e), accountDir)).filter((p) => p !== null && isDirectory(p)).sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
41829
+ return entries2.map((e) => realInside(join8(base, e), accountDir)).filter((p) => p !== null && isDirectory(p)).sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
41555
41830
  }
41556
41831
  function fallbackFiles(accountDir, rootName, identifier, generation, names) {
41557
- const base = join7(accountDir, rootName, identifier);
41832
+ const base = join8(accountDir, rootName, identifier);
41558
41833
  const found = [];
41559
41834
  const add = (candidate) => {
41560
41835
  const real = realInside(candidate, accountDir);
41561
41836
  if (real && isRegularFile(real) && !found.includes(real)) found.push(real);
41562
41837
  };
41563
41838
  const gen = safeComponent(generation);
41564
- if (gen) for (const name of names) add(join7(base, gen, name));
41839
+ if (gen) for (const name of names) add(join8(base, gen, name));
41565
41840
  if (isDirectory(base)) {
41566
41841
  for (const dir of generationDirs(base, accountDir))
41567
- for (const name of names) add(join7(dir, name));
41568
- for (const name of names) add(join7(base, name));
41842
+ for (const name of names) add(join8(dir, name));
41843
+ for (const name of names) add(join8(base, name));
41569
41844
  }
41570
- for (const name of names) add(join7(accountDir, rootName, `${identifier}${extname(name)}`));
41845
+ for (const name of names) add(join8(accountDir, rootName, `${identifier}${extname(name)}`));
41571
41846
  return found;
41572
41847
  }
41573
41848
  function previewPixelArea(name) {
@@ -41579,7 +41854,7 @@ function previewFileInBundle(bundle, accountDir) {
41579
41854
  const direct = [];
41580
41855
  const nested = [];
41581
41856
  for (const entry of boundedEntries(bundle, MAX_BUNDLE_ENTRIES) ?? []) {
41582
- const child2 = realInside(join7(bundle, entry), accountDir);
41857
+ const child2 = realInside(join8(bundle, entry), accountDir);
41583
41858
  if (!child2) continue;
41584
41859
  if (isRegularFile(child2)) direct.push(child2);
41585
41860
  else if (isDirectory(child2)) nested.push(child2);
@@ -41587,14 +41862,14 @@ function previewFileInBundle(bundle, accountDir) {
41587
41862
  nested.sort((a, b) => generationRank(basename(b)) - generationRank(basename(a)));
41588
41863
  const pick2 = (files) => files.find((f) => basename(f) === "Preview.png") ?? files.find((f) => PREVIEW_IMAGE_SUFFIXES.has(extname(f).toLowerCase())) ?? null;
41589
41864
  for (const dir of nested) {
41590
- const files = (boundedEntries(dir, MAX_BUNDLE_ENTRIES) ?? []).map((e) => realInside(join7(dir, e), accountDir)).filter((p) => p !== null && isRegularFile(p));
41865
+ const files = (boundedEntries(dir, MAX_BUNDLE_ENTRIES) ?? []).map((e) => realInside(join8(dir, e), accountDir)).filter((p) => p !== null && isRegularFile(p));
41591
41866
  const chosen = pick2(files);
41592
41867
  if (chosen) return chosen;
41593
41868
  }
41594
41869
  return pick2(direct);
41595
41870
  }
41596
41871
  function listPreviewEntries(accountDir) {
41597
- return boundedEntries(join7(accountDir, "Previews"), MAX_PREVIEW_DIR_ENTRIES) ?? [];
41872
+ return boundedEntries(join8(accountDir, "Previews"), MAX_PREVIEW_DIR_ENTRIES) ?? [];
41598
41873
  }
41599
41874
  function previewPaths(accountDir, identifier, entries2) {
41600
41875
  const id2 = safeComponent(identifier);
@@ -41606,7 +41881,7 @@ function previewPaths(accountDir, identifier, entries2) {
41606
41881
  }).sort((a, b) => previewPixelArea(b) - previewPixelArea(a) || a.localeCompare(b));
41607
41882
  const files = [];
41608
41883
  for (const name of candidates) {
41609
- const real = realInside(join7(accountDir, "Previews", name), accountDir);
41884
+ const real = realInside(join8(accountDir, "Previews", name), accountDir);
41610
41885
  if (!real) continue;
41611
41886
  const file = isRegularFile(real) ? real : isDirectory(real) ? previewFileInBundle(real, accountDir) : null;
41612
41887
  if (file && !files.includes(file)) files.push(file);
@@ -41623,13 +41898,13 @@ function assetPathsFor(accountDir, row) {
41623
41898
  const mediaName = safeComponent(row.mediaFilename);
41624
41899
  const mediaGen = safeComponent(row.mediaGeneration);
41625
41900
  if (mediaId && mediaName) {
41626
- if (mediaGen) add(join7(accountDir, "Media", mediaId, mediaGen, mediaName));
41627
- add(join7(accountDir, "Media", mediaId, mediaName));
41901
+ if (mediaGen) add(join8(accountDir, "Media", mediaId, mediaGen, mediaName));
41902
+ add(join8(accountDir, "Media", mediaId, mediaName));
41628
41903
  }
41629
41904
  const id2 = safeComponent(row.identifier);
41630
41905
  if (!id2) return found;
41631
41906
  const ownName = safeComponent(row.filename);
41632
- if (ownName) add(join7(accountDir, "Media", id2, ownName));
41907
+ if (ownName) add(join8(accountDir, "Media", id2, ownName));
41633
41908
  for (const file of fallbackFiles(accountDir, "FallbackImages", id2, row.fallbackImageGeneration, [
41634
41909
  "FallbackImage.png",
41635
41910
  "FallbackImage.jpg"
@@ -41720,10 +41995,10 @@ function canonicalTail(p) {
41720
41995
  const tail = [];
41721
41996
  for (; ; ) {
41722
41997
  try {
41723
- return join7(realpathSync2.native(current), ...tail);
41998
+ return join8(realpathSync2.native(current), ...tail);
41724
41999
  } catch {
41725
42000
  const parent = dirname2(current);
41726
- if (parent === current) return join7(current, ...tail);
42001
+ if (parent === current) return join8(current, ...tail);
41727
42002
  tail.unshift(basename(current));
41728
42003
  current = parent;
41729
42004
  }
@@ -41806,7 +42081,7 @@ function exportOneAttachment(record2, dir, source) {
41806
42081
  if (!source) return base;
41807
42082
  const name = exportFileName(record2, source.path, source.kind);
41808
42083
  for (let attempt = 1; attempt <= MAX_COLLISION_SUFFIX; attempt++) {
41809
- const dest = join7(dir, collisionName(name, attempt));
42084
+ const dest = join8(dir, collisionName(name, attempt));
41810
42085
  try {
41811
42086
  if (dirname2(dest) !== resolve2(dir))
41812
42087
  throw new Error(`Refusing to write outside the export directory: "${dest}"`);
@@ -41935,7 +42210,7 @@ function buildDrawingRowsSql(notePk, columns) {
41935
42210
  }
41936
42211
  function runSqlite4(dbPath2, sql) {
41937
42212
  try {
41938
- return execFileSync8("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
42213
+ return execFileSync9("/usr/bin/sqlite3", ["-readonly", dbPath2, sql], {
41939
42214
  encoding: "utf8",
41940
42215
  timeout: 1e4,
41941
42216
  maxBuffer: 16 * 1024 * 1024,
@@ -41975,7 +42250,7 @@ function parseDrawingRows(json2) {
41975
42250
  }
41976
42251
  return rows.sort((a, b) => a.pk - b.pk);
41977
42252
  }
41978
- function readDrawingRows(noteId3, dbPath2 = join8(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
42253
+ function readDrawingRows(noteId3, dbPath2 = join9(NOTES_CONTAINER_DIR, "NoteStore.sqlite")) {
41979
42254
  const { pk } = parseNoteId2(noteId3);
41980
42255
  const columns = new Set(
41981
42256
  runSqlite4(
@@ -42019,16 +42294,16 @@ var byGenerationDesc = (a, b) => generationRank(basename2(b)) - generationRank(b
42019
42294
  function findFallbackImage(accountDir, identifier, generation) {
42020
42295
  const id2 = safeComponent(identifier);
42021
42296
  if (!id2) return null;
42022
- const base = join8(accountDir, "FallbackImages", id2);
42297
+ const base = join9(accountDir, "FallbackImages", id2);
42023
42298
  const names = ["FallbackImage.png", "FallbackImage.jpg"];
42024
42299
  const candidates = [];
42025
42300
  const gen = safeComponent(generation);
42026
- if (gen) for (const n of names) candidates.push(join8(base, gen, n));
42027
- const gens = entries(base, MAX_DIR_ENTRIES).map((e) => join8(base, e)).sort(byGenerationDesc);
42028
- for (const g of gens) for (const n of names) candidates.push(join8(g, n));
42029
- for (const n of names) candidates.push(join8(base, n));
42030
- candidates.push(join8(accountDir, "FallbackImages", `${id2}.png`));
42031
- candidates.push(join8(accountDir, "FallbackImages", `${id2}.jpg`));
42301
+ if (gen) for (const n of names) candidates.push(join9(base, gen, n));
42302
+ const gens = entries(base, MAX_DIR_ENTRIES).map((e) => join9(base, e)).sort(byGenerationDesc);
42303
+ for (const g of gens) for (const n of names) candidates.push(join9(g, n));
42304
+ for (const n of names) candidates.push(join9(base, n));
42305
+ candidates.push(join9(accountDir, "FallbackImages", `${id2}.png`));
42306
+ candidates.push(join9(accountDir, "FallbackImages", `${id2}.jpg`));
42032
42307
  for (const c of candidates) {
42033
42308
  const real = realInside(c, accountDir);
42034
42309
  if (real && kindOf(real) === "file") return real;
@@ -42038,21 +42313,21 @@ function findFallbackImage(accountDir, identifier, generation) {
42038
42313
  function findLargestPreview(accountDir, identifier) {
42039
42314
  const id2 = safeComponent(identifier);
42040
42315
  if (!id2) return null;
42041
- const dir = join8(accountDir, "Previews");
42316
+ const dir = join9(accountDir, "Previews");
42042
42317
  const prefix = `${id2}-`.toLowerCase();
42043
42318
  const names = entries(dir, MAX_PREVIEW_DIR_ENTRIES2).filter((n) => n.toLowerCase().startsWith(prefix)).filter((n) => {
42044
42319
  const ext = extname2(n).toLowerCase();
42045
42320
  return !ext || IMAGE_SUFFIXES.has(ext) || /^\.\d+$/.test(ext);
42046
42321
  }).sort((a, b) => previewPixelArea(b) - previewPixelArea(a) || a.localeCompare(b));
42047
42322
  for (const name of names) {
42048
- const real = realInside(join8(dir, name), accountDir);
42323
+ const real = realInside(join9(dir, name), accountDir);
42049
42324
  if (!real) continue;
42050
42325
  const kind = kindOf(real);
42051
42326
  if (kind === "file") return real;
42052
42327
  if (kind !== "dir") continue;
42053
- const gens = entries(real, MAX_DIR_ENTRIES).map((e) => realInside(join8(real, e), accountDir)).filter((p) => p !== null && kindOf(p) === "dir").sort(byGenerationDesc);
42328
+ const gens = entries(real, MAX_DIR_ENTRIES).map((e) => realInside(join9(real, e), accountDir)).filter((p) => p !== null && kindOf(p) === "dir").sort(byGenerationDesc);
42054
42329
  for (const g of [...gens, real]) {
42055
- const file = realInside(join8(g, "Preview.png"), accountDir);
42330
+ const file = realInside(join9(g, "Preview.png"), accountDir);
42056
42331
  if (file && kindOf(file) === "file") return file;
42057
42332
  }
42058
42333
  }
@@ -42065,7 +42340,7 @@ function describeDrawings(rows, containerDir = NOTES_CONTAINER_DIR) {
42065
42340
  const id2 = safeComponent(row.identifier);
42066
42341
  const fallbackImagePath = accountDir ? findFallbackImage(accountDir, row.identifier, row.fallbackImageGeneration) : null;
42067
42342
  const previewPath = accountDir ? findLargestPreview(accountDir, row.identifier) : null;
42068
- const bundle = accountDir && id2 && kind === "paper" ? realInside(join8(accountDir, "Paper", "Bundles", `${id2}.bundle`), accountDir) : null;
42343
+ const bundle = accountDir && id2 && kind === "paper" ? realInside(join9(accountDir, "Paper", "Bundles", `${id2}.bundle`), accountDir) : null;
42069
42344
  let raster = null;
42070
42345
  for (const [path10, source] of [
42071
42346
  [fallbackImagePath, "fallback"],
@@ -42262,9 +42537,9 @@ function scopeConflictMessage(reason) {
42262
42537
 
42263
42538
  // src/services/appleNotesManager.ts
42264
42539
  var import_turndown = __toESM(require_turndown_cjs(), 1);
42265
- import { existsSync as existsSync5, mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync } from "fs";
42266
- import { homedir as homedir8, tmpdir as tmpdir2 } from "os";
42267
- import { join as join9 } from "path";
42540
+ import { existsSync as existsSync6, mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync } from "fs";
42541
+ import { homedir as homedir9, tmpdir as tmpdir2 } from "os";
42542
+ import { join as join10 } from "path";
42268
42543
  var FIELD_SEP = "";
42269
42544
  var RECORD_SEP = "";
42270
42545
  var AS_FIELD_SEP = "(character id 31)";
@@ -42486,6 +42761,11 @@ function smartFolderDestinationError(folderPath) {
42486
42761
  }
42487
42762
  );
42488
42763
  }
42764
+ function smartFoldersReadError(kind, message) {
42765
+ return new CodedError(message, {
42766
+ code: kind === "unsupported_schema" ? "unsupported" : kind === "no_fda" ? "full_disk_access_missing" : "operation_failed"
42767
+ });
42768
+ }
42489
42769
  function throwIfSmartFolderDestination(error2, folderPath) {
42490
42770
  if (error2?.includes(SMART_FOLDER_DESTINATION)) throw smartFolderDestinationError(folderPath);
42491
42771
  }
@@ -42621,8 +42901,8 @@ var ExpectedBodies = class {
42621
42901
  if (body.length <= INLINE_EXPECTED_BODY_LIMIT) {
42622
42902
  return { setup: "", operand: `"${escapeHtmlForAppleScript(body)}"` };
42623
42903
  }
42624
- this.directory ??= mkdtempSync2(join9(tmpdir2(), "apple-notes-expected-body-"));
42625
- const file = join9(this.directory, `body-${this.files++}.html`);
42904
+ this.directory ??= mkdtempSync2(join10(tmpdir2(), "apple-notes-expected-body-"));
42905
+ const file = join10(this.directory, `body-${this.files++}.html`);
42626
42906
  writeFileSync(file, body, { encoding: "utf8", mode: 384 });
42627
42907
  return {
42628
42908
  setup: `
@@ -42668,8 +42948,8 @@ function getNoteLinkFromDB(coreDataId3) {
42668
42948
  const match = coreDataId3.match(/\/p(\d+)$/);
42669
42949
  if (!match) return null;
42670
42950
  const pk = parseInt(match[1], 10);
42671
- const dbPath2 = join9(homedir8(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
42672
- if (!existsSync5(dbPath2)) return null;
42951
+ const dbPath2 = join10(homedir9(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
42952
+ if (!existsSync6(dbPath2)) return null;
42673
42953
  try {
42674
42954
  const { DatabaseSync } = __require("node:sqlite");
42675
42955
  const db = new DatabaseSync(dbPath2, { readOnly: true });
@@ -43303,9 +43583,12 @@ var AppleNotesManager = class {
43303
43583
  if currentBody is not ${expected.operand} and currentBody is not ${expected.operand} & linefeed then return "SAFETY_CONFLICT"
43304
43584
  delete noteRef
43305
43585
  end considering
43586
+ set __stillThere to missing value
43306
43587
  try
43307
- if (id of notes of originalFolder) contains "${safeId}" then return "SAFETY_NOT_DELETED"
43588
+ set __stillThere to (id of notes of originalFolder) contains "${safeId}"
43308
43589
  end try
43590
+ if __stillThere is missing value then return "SAFETY_DELETE_UNVERIFIED"
43591
+ if __stillThere then return "SAFETY_NOT_DELETED"
43309
43592
  return "SAFETY_DELETED"
43310
43593
  `);
43311
43594
  result = executeMutationAppleScript(script);
@@ -43328,6 +43611,7 @@ var AppleNotesManager = class {
43328
43611
  if (status === "SAFETY_IN_RECENTLY_DELETED") return { status: "in-recently-deleted" };
43329
43612
  if (status === "SAFETY_CONTAINER_UNKNOWN") return { status: "container-unknown" };
43330
43613
  if (status === "SAFETY_NOT_DELETED") return { status: "not-deleted" };
43614
+ if (status === "SAFETY_DELETE_UNVERIFIED") return { status: "unverified" };
43331
43615
  return status === "SAFETY_DELETED" ? { status: "deleted" } : { status: "failed" };
43332
43616
  }
43333
43617
  /**
@@ -43475,7 +43759,8 @@ var AppleNotesManager = class {
43475
43759
  */
43476
43760
  listSmartFolders(options = {}) {
43477
43761
  const result = readSmartFolders();
43478
- if (!result.folders) throw new Error(result.message || "Failed to read smart folders.");
43762
+ if (!result.folders)
43763
+ throw smartFoldersReadError(result.error, result.message || "Failed to read smart folders.");
43479
43764
  if (!options.includeMatchingNotes) return result.folders;
43480
43765
  const limit = options.limit ?? 50;
43481
43766
  return result.folders.map((folder) => {
@@ -44863,7 +45148,7 @@ var AppleNotesManager = class {
44863
45148
  if (parts[0] !== "OK") {
44864
45149
  return { success: false, error: parts[1]?.trim() || "attachment not found" };
44865
45150
  }
44866
- if (!existsSync5(abs) || fileSize(abs) === 0) {
45151
+ if (!existsSync6(abs) || fileSize(abs) === 0) {
44867
45152
  return { success: false, error: `Notes reported success but no file was written to ${abs}` };
44868
45153
  }
44869
45154
  return {
@@ -45371,15 +45656,15 @@ var AppleNotesManager = class {
45371
45656
  };
45372
45657
 
45373
45658
  // src/utils/syncDetection.ts
45374
- import { execFileSync as execFileSync9 } from "child_process";
45375
- import * as fs4 from "fs";
45376
- import * as path4 from "path";
45377
- import * as os4 from "os";
45378
- var NOTES_DB_PATH5 = path4.join(
45379
- os4.homedir(),
45659
+ import { execFileSync as execFileSync10 } from "child_process";
45660
+ import * as fs5 from "fs";
45661
+ import * as path5 from "path";
45662
+ import * as os5 from "os";
45663
+ var NOTES_DB_PATH6 = path5.join(
45664
+ os5.homedir(),
45380
45665
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
45381
45666
  );
45382
- var WAL_PATH = `${NOTES_DB_PATH5}-wal`;
45667
+ var WAL_PATH = `${NOTES_DB_PATH6}-wal`;
45383
45668
  var RECENT_ACTIVITY_THRESHOLD_SECONDS = 5;
45384
45669
  var SYNC_STATUS_CACHE_TTL_MS = 2e3;
45385
45670
  var cachedSyncStatus = null;
@@ -45395,14 +45680,14 @@ function getSyncStatus(useCache = true) {
45395
45680
  recentActivity: false
45396
45681
  };
45397
45682
  try {
45398
- if (!fs4.existsSync(NOTES_DB_PATH5)) {
45683
+ if (!fs5.existsSync(NOTES_DB_PATH6)) {
45399
45684
  status.error = "Notes database not found";
45400
45685
  cachedSyncStatus = status;
45401
45686
  cacheTimestamp = Date.now();
45402
45687
  return status;
45403
45688
  }
45404
- if (fs4.existsSync(WAL_PATH)) {
45405
- const walStats = fs4.statSync(WAL_PATH);
45689
+ if (fs5.existsSync(WAL_PATH)) {
45690
+ const walStats = fs5.statSync(WAL_PATH);
45406
45691
  const secondsAgo = (Date.now() - walStats.mtimeMs) / 1e3;
45407
45692
  status.secondsSinceLastChange = Math.round(secondsAgo);
45408
45693
  status.recentActivity = secondsAgo < RECENT_ACTIVITY_THRESHOLD_SECONDS;
@@ -45416,9 +45701,9 @@ function getSyncStatus(useCache = true) {
45416
45701
  WHERE object.ZCLOUDSTATE = state.Z_PK
45417
45702
  );
45418
45703
  `;
45419
- const result = execFileSync9(
45704
+ const result = execFileSync10(
45420
45705
  "sqlite3",
45421
- ["-readonly", NOTES_DB_PATH5, query2.replace(/\n/g, " ")],
45706
+ ["-readonly", NOTES_DB_PATH6, query2.replace(/\n/g, " ")],
45422
45707
  {
45423
45708
  encoding: "utf8",
45424
45709
  timeout: 5e3,
@@ -45475,12 +45760,12 @@ function withSyncAwarenessSync(operation, fn) {
45475
45760
  }
45476
45761
 
45477
45762
  // src/utils/noteMetadata.ts
45478
- import { execFileSync as execFileSync10 } from "child_process";
45479
- import * as fs5 from "fs";
45480
- import * as path5 from "path";
45481
- import * as os5 from "os";
45482
- var NOTES_DB_PATH6 = path5.join(
45483
- os5.homedir(),
45763
+ import { execFileSync as execFileSync11 } from "child_process";
45764
+ import * as fs6 from "fs";
45765
+ import * as path6 from "path";
45766
+ import * as os6 from "os";
45767
+ var NOTES_DB_PATH7 = path6.join(
45768
+ os6.homedir(),
45484
45769
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
45485
45770
  );
45486
45771
  var FDA_MESSAGE2 = `Full Disk Access is required to read note metadata. In System Settings > Privacy & Security > Full Disk Access, grant access to the Node binary running this server (required under Claude Desktop) or the terminal that launches it, then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL} \u2014 run the doctor tool to verify.`;
@@ -45496,7 +45781,7 @@ var COLUMN_MAP = [
45496
45781
  { key: "smartFolderQuery", column: "ZSMARTFOLDERQUERYJSON", type: "text" }
45497
45782
  ];
45498
45783
  function runSqlite5(query2) {
45499
- return execFileSync10("sqlite3", ["-readonly", NOTES_DB_PATH6, query2], {
45784
+ return execFileSync11("sqlite3", ["-readonly", NOTES_DB_PATH7, query2], {
45500
45785
  encoding: "utf8",
45501
45786
  timeout: 5e3,
45502
45787
  stdio: ["pipe", "pipe", "pipe"]
@@ -45521,7 +45806,7 @@ function getNoteMetadata(noteId3) {
45521
45806
  };
45522
45807
  }
45523
45808
  const pk = pkMatch[1];
45524
- if (!fs5.existsSync(NOTES_DB_PATH6)) {
45809
+ if (!fs6.existsSync(NOTES_DB_PATH7)) {
45525
45810
  return { metadata: null, error: "no_fda", message: FDA_MESSAGE2 };
45526
45811
  }
45527
45812
  try {
@@ -45567,12 +45852,12 @@ function getNoteMetadata(noteId3) {
45567
45852
  import { gunzipSync as gunzipSync6 } from "zlib";
45568
45853
 
45569
45854
  // src/utils/noteStoreSql.ts
45570
- import { execFileSync as execFileSync11 } from "child_process";
45571
- import * as fs6 from "fs";
45572
- import * as os6 from "os";
45573
- import * as path6 from "path";
45574
- var NOTES_DB_PATH7 = path6.join(
45575
- os6.homedir(),
45855
+ import { execFileSync as execFileSync12 } from "child_process";
45856
+ import * as fs7 from "fs";
45857
+ import * as os7 from "os";
45858
+ import * as path7 from "path";
45859
+ var NOTES_DB_PATH8 = path7.join(
45860
+ os7.homedir(),
45576
45861
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
45577
45862
  );
45578
45863
  var CORE_DATA_EPOCH_MS = Date.UTC(2001, 0, 1);
@@ -45610,7 +45895,7 @@ function renderValue(value) {
45610
45895
  return `ieee754_from_blob(x'${doubleToHex(double)}')`;
45611
45896
  }
45612
45897
  function runReadOnlySql(dbPath2, sql, params = {}) {
45613
- if (!fs6.existsSync(dbPath2)) throw new NoteStoreError(STORE_FDA_MESSAGE, "no_fda");
45898
+ if (!fs7.existsSync(dbPath2)) throw new NoteStoreError(STORE_FDA_MESSAGE, "no_fda");
45614
45899
  const args = ["-readonly"];
45615
45900
  const names = Object.keys(params);
45616
45901
  if (names.length) args.push("-cmd", ".parameter init");
@@ -45622,7 +45907,7 @@ function runReadOnlySql(dbPath2, sql, params = {}) {
45622
45907
  }
45623
45908
  args.push(dbPath2, sql);
45624
45909
  try {
45625
- return execFileSync11("sqlite3", args, {
45910
+ return execFileSync12("sqlite3", args, {
45626
45911
  encoding: "utf8",
45627
45912
  timeout: 3e4,
45628
45913
  maxBuffer: 512 * 1024 * 1024,
@@ -45815,7 +46100,7 @@ function buildSpecialNotesSql(columns, kind, scoped) {
45815
46100
  ].join(" ");
45816
46101
  }
45817
46102
  function listSpecialNotes(options) {
45818
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
46103
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH8;
45819
46104
  const limit = Math.min(
45820
46105
  Math.max(1, Math.trunc(options.limit ?? SPECIAL_LIMIT.DEFAULT)),
45821
46106
  SPECIAL_LIMIT.MAX
@@ -45871,7 +46156,7 @@ function toSpecialRow(row, uuid2, paths, accountNames, kind) {
45871
46156
  return note;
45872
46157
  }
45873
46158
  var EXACT_NOTE_ID = /^x-coredata:\/\/([0-9A-F-]+)\/ICNote\/p(\d+)$/i;
45874
- function quickNoteFlag(noteId3, dbPath2 = NOTES_DB_PATH7) {
46159
+ function quickNoteFlag(noteId3, dbPath2 = NOTES_DB_PATH8) {
45875
46160
  const match = EXACT_NOTE_ID.exec(noteId3);
45876
46161
  if (!match) throw new NoteStoreError(`Not an exact note id: ${noteId3}`, "invalid_input");
45877
46162
  const columns = readColumns(dbPath2);
@@ -45923,7 +46208,7 @@ function referencedObjects(hex3) {
45923
46208
  }
45924
46209
  }
45925
46210
  function nativeTagInventory(options = {}) {
45926
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
46211
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH8;
45927
46212
  const columns = readColumns(dbPath2);
45928
46213
  const context = readStoreContext(dbPath2, columns);
45929
46214
  const scope2 = options.account ? resolveAccountName(context.accounts, options.account) : void 0;
@@ -45985,15 +46270,27 @@ function assembleInventory(rows, accounts, scope2) {
45985
46270
  }
45986
46271
 
45987
46272
  // src/utils/noteBlocks.ts
45988
- import { execFileSync as execFileSync12 } from "node:child_process";
45989
- import { existsSync as existsSync9 } from "node:fs";
45990
- import { homedir as homedir12 } from "node:os";
45991
- import { join as join13 } from "node:path";
46273
+ import { execFileSync as execFileSync13 } from "node:child_process";
46274
+ import { existsSync as existsSync10 } from "node:fs";
46275
+ import { homedir as homedir13 } from "node:os";
46276
+ import { join as join14 } from "node:path";
45992
46277
  import { gunzipSync as gunzipSync7 } from "node:zlib";
45993
- var NoteBlocksError = class extends Error {
46278
+ var NOTE_BLOCKS_ERROR_CODES = {
46279
+ "invalid-id": "validation_error",
46280
+ "no-full-disk-access": "full_disk_access_missing",
46281
+ "not-found": "not_found",
46282
+ "no-body": "operation_failed",
46283
+ encrypted: "unsupported",
46284
+ "decompress-failed": "unsupported",
46285
+ "malformed-protobuf": "unsupported",
46286
+ "unsupported-structure": "unsupported",
46287
+ "invalid-runs": "unsupported",
46288
+ "query-failed": "operation_failed"
46289
+ };
46290
+ var NoteBlocksError = class extends CodedError {
45994
46291
  code;
45995
46292
  constructor(code, message) {
45996
- super(message);
46293
+ super(message, { code: NOTE_BLOCKS_ERROR_CODES[code] });
45997
46294
  this.name = "NoteBlocksError";
45998
46295
  this.code = code;
45999
46296
  }
@@ -46331,23 +46628,23 @@ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResp
46331
46628
  undecodedFields: doc.undecodedFields
46332
46629
  };
46333
46630
  }
46334
- var NOTES_DB_PATH8 = join13(
46335
- homedir12(),
46631
+ var NOTES_DB_PATH9 = join14(
46632
+ homedir13(),
46336
46633
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
46337
46634
  );
46338
- function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH8 } = {}) {
46635
+ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH9 } = {}) {
46339
46636
  const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
46340
46637
  if (!pk)
46341
46638
  throw new NoteBlocksError(
46342
46639
  "invalid-id",
46343
46640
  `Invalid note ID: expected x-coredata://<store>/ICNote/p<number>`
46344
46641
  );
46345
- if (!existsSync9(dbPath2))
46642
+ if (!existsSync10(dbPath2))
46346
46643
  throw new NoteBlocksError("no-full-disk-access", "The Notes database is not readable");
46347
46644
  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));";
46348
46645
  let output;
46349
46646
  try {
46350
- output = execFileSync12(
46647
+ output = execFileSync13(
46351
46648
  "/usr/bin/sqlite3",
46352
46649
  ["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
46353
46650
  {
@@ -46494,7 +46791,7 @@ function resolveFolderPath(folders, paths, input, accountPk) {
46494
46791
  );
46495
46792
  }
46496
46793
  function listRecentNotes(options = {}) {
46497
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
46794
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH8;
46498
46795
  const limit = Math.min(
46499
46796
  Math.max(1, Math.trunc(options.limit ?? RECENT_LIMIT.DEFAULT)),
46500
46797
  RECENT_LIMIT.MAX
@@ -46549,346 +46846,108 @@ function toRecentRow(row, context, paths, accountNames, options) {
46549
46846
  account: row.account !== null ? accountNames.get(row.account) ?? null : null,
46550
46847
  created: coreDataToIso(row.created),
46551
46848
  modified: coreDataToIso(row.modified),
46552
- modifiedCheckpoint: checkpointFromBits(row.bits, row.pk),
46553
- pinned: Boolean(row.pinned),
46554
- locked: Boolean(row.locked),
46555
- inRecentlyDeleted: Boolean(row.trash),
46556
- markedForDeletion: Boolean(row.tombstoned)
46557
- };
46558
- const text2 = row.locked || row.cloud ? null : decodeBodyText(row.data);
46559
- if (options.wordCounts) {
46560
- const stats = text2 === null ? null : textStats(text2);
46561
- note.wordCount = stats?.wordCount ?? null;
46562
- note.charCount = stats?.charCount ?? null;
46563
- }
46564
- if (options.bodyPreview) {
46565
- note.textDecoded = text2 !== null;
46566
- note.bodyPreview = text2 !== null ? previewText(text2) : row.snippet ? previewText(row.snippet) : null;
46567
- }
46568
- return note;
46569
- }
46570
- function buildFolderCountsSql(columns) {
46571
- requireColumns(columns, ["Z_PK", "Z_ENT", "ZFOLDER"], "list-folder-tree");
46572
- return `SELECT json_object('folder', n.ZFOLDER, 'n', COUNT(*)) FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_ENT = ${entity("ICNote")} AND n.ZFOLDER IS NOT NULL AND ${notTombstonedSql(columns, "n")} GROUP BY n.ZFOLDER;`;
46573
- }
46574
- var KIND_ORDER = { folder: 0, smart: 1, trash: 2 };
46575
- function compareNodes(a, b) {
46576
- return KIND_ORDER[a.kind] - KIND_ORDER[b.kind] || a.name.localeCompare(b.name);
46577
- }
46578
- function folderTree(options = {}) {
46579
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
46580
- const columns = readColumns(dbPath2);
46581
- const context = readStoreContext(dbPath2, columns);
46582
- const scope2 = options.account ? resolveAccountName(context.accounts, options.account) : void 0;
46583
- const counts = new Map(
46584
- parseJsonLines(
46585
- runReadOnlySql(dbPath2, buildFolderCountsSql(columns))
46586
- ).map((row) => [row.folder, row.n])
46587
- );
46588
- return assembleFolderTree(context, counts, Boolean(options.includeDeleted), scope2);
46589
- }
46590
- function assembleFolderTree(context, counts, includeDeleted, scope2) {
46591
- const paths = folderPaths(context.folders);
46592
- const visible2 = context.folders.filter(
46593
- (folder) => (includeDeleted || !folder.tombstoned) && (!scope2 || folder.account === scope2.pk)
46594
- );
46595
- const byPk = new Map(visible2.map((folder) => [folder.pk, folder]));
46596
- const nodes = /* @__PURE__ */ new Map();
46597
- for (const folder of visible2) {
46598
- const node = {
46599
- id: `x-coredata://${context.uuid}/ICFolder/p${folder.pk}`,
46600
- identifier: folder.identifier,
46601
- name: folder.name ?? "",
46602
- path: paths.get(folder.pk) ?? "",
46603
- kind: folder.trash ? "trash" : folder.folderType === 2 ? "smart" : "folder",
46604
- noteCount: counts.get(folder.pk) ?? 0,
46605
- totalNoteCount: 0,
46606
- children: []
46607
- };
46608
- if (includeDeleted) node.markedForDeletion = Boolean(folder.tombstoned);
46609
- nodes.set(folder.pk, node);
46610
- }
46611
- const parentOf = (folder) => {
46612
- if (folder.parent === null || !byPk.has(folder.parent)) return void 0;
46613
- const seen = /* @__PURE__ */ new Set([folder.pk]);
46614
- for (let at = byPk.get(folder.parent); at; at = at.parent !== null ? byPk.get(at.parent) : void 0) {
46615
- if (seen.has(at.pk)) return void 0;
46616
- seen.add(at.pk);
46617
- }
46618
- return folder.parent;
46619
- };
46620
- const roots = /* @__PURE__ */ new Map();
46621
- for (const folder of visible2) {
46622
- const node = nodes.get(folder.pk);
46623
- const parent = parentOf(folder);
46624
- if (parent !== void 0) nodes.get(parent).children.push(node);
46625
- else {
46626
- const key = folder.account !== null && context.accounts.some((a) => a.pk === folder.account) ? folder.account : null;
46627
- roots.set(key, [...roots.get(key) ?? [], node]);
46628
- }
46629
- }
46630
- const finish = (node) => {
46631
- node.children.sort(compareNodes);
46632
- node.totalNoteCount = node.noteCount + node.children.reduce((sum, c) => sum + finish(c), 0);
46633
- return node.totalNoteCount;
46634
- };
46635
- const accounts = [];
46636
- const addAccount = (name, identifier, folders) => {
46637
- folders.sort(compareNodes);
46638
- let noteCount = 0;
46639
- for (const node of folders) {
46640
- const total = finish(node);
46641
- if (node.kind === "folder") noteCount += total;
46642
- }
46643
- accounts.push({ account: name, identifier, noteCount, folders });
46644
- };
46645
- for (const account of scope2 ? [scope2] : context.accounts) {
46646
- addAccount(account.name, account.identifier, roots.get(account.pk) ?? []);
46647
- }
46648
- const orphans = roots.get(null);
46649
- if (orphans && includeDeleted) addAccount("unknown", null, orphans);
46650
- return { accounts, folderCount: accounts.reduce((sum, a) => sum + countNodes(a.folders), 0) };
46651
- }
46652
- function countNodes(nodes) {
46653
- return nodes.reduce((sum, node) => sum + 1 + countNodes(node.children), 0);
46654
- }
46655
-
46656
- // src/utils/noteIdentifiers.ts
46657
- import { execFileSync as execFileSync13 } from "child_process";
46658
- import * as fs7 from "fs";
46659
- import * as os7 from "os";
46660
- import * as path7 from "path";
46661
- var NOTES_DB_PATH9 = path7.join(
46662
- os7.homedir(),
46663
- "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
46664
- );
46665
- var UUID_PATTERN = /^[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$/;
46666
- var NUMERIC_KEY_PATTERN = /^\d{1,18}$/;
46667
- var NOTE_ID_MESSAGE = "A canonical Apple Note ID is required (x-coredata://.../ICNote/p...), or the note's Notes UUID or numeric key";
46668
- function identifierForm(value) {
46669
- if (UUID_PATTERN.test(value)) return "uuid";
46670
- if (NUMERIC_KEY_PATTERN.test(value)) return "key";
46671
- return "other";
46672
- }
46673
- function isAlternateIdentifier(value) {
46674
- return identifierForm(value) !== "other";
46675
- }
46676
- var IdentifierResolutionError = class extends Error {
46677
- constructor(code, message) {
46678
- super(message);
46679
- this.code = code;
46680
- this.name = "IdentifierResolutionError";
46681
- }
46682
- code;
46683
- };
46684
- var ENTITY_LABEL = {
46685
- ICNote: "note",
46686
- ICFolder: "folder",
46687
- ICAccount: "account"
46688
- };
46689
- var NO_FDA_MESSAGE = `Resolving a Notes UUID or numeric key reads the Notes database, which needs Full Disk Access for the Node binary running this server, or the terminal that launches it (System Settings > Privacy & Security > Full Disk Access, then fully quit and relaunch it). x-coredata ids from search-notes, list-notes, or list-folders work without it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL}`;
46690
- function canonicalKey(value) {
46691
- return BigInt(value).toString();
46692
- }
46693
- function assertAll(values, pattern2, label) {
46694
- for (const value of values) {
46695
- if (!pattern2.test(value)) throw new Error(`Refusing to query with an invalid ${label}`);
46696
- }
46697
- }
46698
- function entityClause(entity3) {
46699
- return `(SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = '${entity3}')`;
46700
- }
46701
- function buildResolveSql(entity3, keys, uuids) {
46702
- assertAll(keys, NUMERIC_KEY_PATTERN, "numeric key");
46703
- assertAll(uuids, UUID_PATTERN, "UUID");
46704
- const matches = [];
46705
- if (keys.length > 0) {
46706
- matches.push(`o.Z_PK IN (${[...new Set(keys.map(canonicalKey))].join(", ")})`);
46707
- }
46708
- if (uuids.length > 0) {
46709
- const variants = /* @__PURE__ */ new Set();
46710
- for (const uuid2 of uuids) {
46711
- variants.add(uuid2);
46712
- variants.add(uuid2.toUpperCase());
46713
- variants.add(uuid2.toLowerCase());
46714
- }
46715
- matches.push(`o.ZIDENTIFIER IN (${[...variants].map((v) => `'${v}'`).join(", ")})`);
46716
- }
46717
- const where = matches.length > 0 ? matches.join(" OR ") : "0";
46718
- return `SELECT json_object('store', (SELECT Z_UUID FROM Z_METADATA LIMIT 1), 'rows', (SELECT json_group_array(json_object('pk', o.Z_PK, 'identifier', o.ZIDENTIFIER)) FROM ZICCLOUDSYNCINGOBJECT o WHERE o.Z_ENT = ${entityClause(entity3)} AND (${where})));`;
46719
- }
46720
- function buildLookupSql(entity3, keys) {
46721
- assertAll(keys, NUMERIC_KEY_PATTERN, "numeric key");
46722
- const pks = [...new Set(keys.map(canonicalKey))];
46723
- const inList = pks.length > 0 ? pks.join(", ") : "NULL";
46724
- let fields = "'pk', o.Z_PK, 'identifier', o.ZIDENTIFIER";
46725
- let joins = "";
46726
- if (entity3 === "ICNote") {
46727
- fields += ", 'folderIdentifier', f.ZIDENTIFIER, 'accountIdentifier', a.ZIDENTIFIER";
46728
- joins = "LEFT JOIN ZICCLOUDSYNCINGOBJECT f ON f.Z_PK = o.ZFOLDER LEFT JOIN ZICCLOUDSYNCINGOBJECT a ON a.Z_PK = f.ZOWNER ";
46729
- } else if (entity3 === "ICFolder") {
46730
- fields += ", 'parentIdentifier', p.ZIDENTIFIER, 'accountIdentifier', a.ZIDENTIFIER";
46731
- joins = "LEFT JOIN ZICCLOUDSYNCINGOBJECT p ON p.Z_PK = o.ZPARENT LEFT JOIN ZICCLOUDSYNCINGOBJECT a ON a.Z_PK = o.ZOWNER ";
46732
- }
46733
- return `SELECT json_object('store', (SELECT Z_UUID FROM Z_METADATA LIMIT 1), 'rows', (SELECT json_group_array(json_object(${fields})) FROM ZICCLOUDSYNCINGOBJECT o ${joins}WHERE o.Z_ENT = ${entityClause(entity3)} AND o.Z_PK IN (${inList})));`;
46734
- }
46735
- function runJsonQuery(sql, dbPath2) {
46736
- if (!fs7.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
46737
- let out;
46738
- try {
46739
- out = execFileSync13("sqlite3", ["-readonly", dbPath2, sql], {
46740
- encoding: "utf8",
46741
- timeout: 5e3,
46742
- stdio: ["pipe", "pipe", "pipe"]
46743
- }).trim();
46744
- } catch (error2) {
46745
- const detail = error2 instanceof Error ? `${error2.message} ${String(error2.stderr ?? "")}` : String(error2);
46746
- if (detail.includes("authorization denied") || detail.includes("unable to open database")) {
46747
- throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
46748
- }
46749
- console.error(`Identifier query failed: ${detail}`);
46750
- throw new IdentifierResolutionError("query_error", "Failed to read the Notes database.");
46751
- }
46752
- const parsed = JSON.parse(out || "{}");
46753
- const rows = typeof parsed.rows === "string" ? JSON.parse(parsed.rows) : parsed.rows;
46754
- return { store: parsed.store ?? null, rows: rows ?? [] };
46755
- }
46756
- function coreDataId(store, entity3, pk) {
46757
- return `x-coredata://${store}/${entity3}/p${pk}`;
46758
- }
46759
- function resolveIdentifiers(values, entity3, dbPath2 = NOTES_DB_PATH9) {
46760
- const resolved = /* @__PURE__ */ new Map();
46761
- const keys = [];
46762
- const uuids = [];
46763
- for (const value of values) {
46764
- const form = identifierForm(value);
46765
- if (form === "key") keys.push(value);
46766
- else if (form === "uuid") uuids.push(value);
46767
- else resolved.set(value, value);
46768
- }
46769
- if (keys.length === 0 && uuids.length === 0) return resolved;
46770
- const { store, rows } = runJsonQuery(
46771
- buildResolveSql(entity3, keys, uuids),
46772
- dbPath2
46773
- );
46774
- if (!store) {
46775
- throw new IdentifierResolutionError(
46776
- "query_error",
46777
- "The Notes database has no store UUID, so an x-coredata id cannot be built."
46778
- );
46779
- }
46780
- const byKey = new Map(rows.map((row) => [String(row.pk), row]));
46781
- const byUuid = new Map(
46782
- rows.filter((row) => row.identifier).map((row) => [String(row.identifier).toUpperCase(), row])
46783
- );
46784
- const label = ENTITY_LABEL[entity3];
46785
- const missing = [];
46786
- for (const key of keys) {
46787
- const row = byKey.get(canonicalKey(key));
46788
- if (row) resolved.set(key, coreDataId(store, entity3, row.pk));
46789
- else missing.push(`numeric key ${key}`);
46790
- }
46791
- for (const uuid2 of uuids) {
46792
- const row = byUuid.get(uuid2.toUpperCase());
46793
- if (row) resolved.set(uuid2, coreDataId(store, entity3, row.pk));
46794
- else missing.push(`identifier ${uuid2}`);
46849
+ modifiedCheckpoint: checkpointFromBits(row.bits, row.pk),
46850
+ pinned: Boolean(row.pinned),
46851
+ locked: Boolean(row.locked),
46852
+ inRecentlyDeleted: Boolean(row.trash),
46853
+ markedForDeletion: Boolean(row.tombstoned)
46854
+ };
46855
+ const text2 = row.locked || row.cloud ? null : decodeBodyText(row.data);
46856
+ if (options.wordCounts) {
46857
+ const stats = text2 === null ? null : textStats(text2);
46858
+ note.wordCount = stats?.wordCount ?? null;
46859
+ note.charCount = stats?.charCount ?? null;
46795
46860
  }
46796
- if (missing.length > 0) {
46797
- throw new IdentifierResolutionError(
46798
- "not_found",
46799
- `No ${label} found for ${missing.join(", ")}. A numeric key must belong to a ${label}, not another object type.`
46800
- );
46861
+ if (options.bodyPreview) {
46862
+ note.textDecoded = text2 !== null;
46863
+ note.bodyPreview = text2 !== null ? previewText(text2) : row.snippet ? previewText(row.snippet) : null;
46801
46864
  }
46802
- return resolved;
46865
+ return note;
46803
46866
  }
46804
- var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
46805
- function lookupStableIdentifiers(ids, entity3, dbPath2 = NOTES_DB_PATH9) {
46806
- const result = /* @__PURE__ */ new Map();
46807
- const wanted = /* @__PURE__ */ new Map();
46808
- for (const id2 of ids) {
46809
- const match = COREDATA_PARTS.exec(id2);
46810
- if (!match || match[2] !== entity3) continue;
46811
- const pk = canonicalKey(match[3]);
46812
- const list = wanted.get(pk) ?? [];
46813
- list.push({ id: id2, store: match[1] });
46814
- wanted.set(pk, list);
46815
- }
46816
- if (wanted.size === 0) return result;
46817
- let query2;
46818
- try {
46819
- query2 = runJsonQuery(buildLookupSql(entity3, [...wanted.keys()]), dbPath2);
46820
- } catch {
46821
- return result;
46822
- }
46823
- const store = query2.store?.toUpperCase();
46824
- for (const row of query2.rows) {
46825
- const fields = {};
46826
- for (const key of [
46827
- "identifier",
46828
- "folderIdentifier",
46829
- "parentIdentifier",
46830
- "accountIdentifier"
46831
- ]) {
46832
- const value = row[key];
46833
- if (typeof value === "string" && value) fields[key] = value;
46834
- }
46835
- for (const target of wanted.get(String(row.pk)) ?? []) {
46836
- if (target.store.toUpperCase() === store) result.set(target.id, fields);
46837
- }
46838
- }
46839
- return result;
46867
+ function buildFolderCountsSql(columns) {
46868
+ requireColumns(columns, ["Z_PK", "Z_ENT", "ZFOLDER"], "list-folder-tree");
46869
+ return `SELECT json_object('folder', n.ZFOLDER, 'n', COUNT(*)) FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_ENT = ${entity("ICNote")} AND n.ZFOLDER IS NOT NULL AND ${notTombstonedSql(columns, "n")} GROUP BY n.ZFOLDER;`;
46840
46870
  }
46841
- function withStableIdentifiers(items, entity3, dbPath2 = NOTES_DB_PATH9) {
46842
- const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
46843
- if (ids.length === 0) return items;
46844
- const found = lookupStableIdentifiers(ids, entity3, dbPath2);
46845
- if (found.size === 0) return items;
46846
- return items.map(
46847
- (item) => item.id && found.has(item.id) ? { ...item, ...found.get(item.id) } : item
46848
- );
46871
+ var KIND_ORDER = { folder: 0, smart: 1, trash: 2 };
46872
+ function compareNodes(a, b) {
46873
+ return KIND_ORDER[a.kind] - KIND_ORDER[b.kind] || a.name.localeCompare(b.name);
46849
46874
  }
46850
- var defaultResolver = (values, entity3) => resolveIdentifiers(values, entity3);
46851
- var DEFAULT_MAX_ID_LENGTH = 2e3;
46852
- function acceptAlternateForms(pattern2) {
46853
- return new RegExp(
46854
- `${pattern2.source}|${UUID_PATTERN.source}|${NUMERIC_KEY_PATTERN.source}`,
46855
- pattern2.flags
46875
+ function folderTree(options = {}) {
46876
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH8;
46877
+ const columns = readColumns(dbPath2);
46878
+ const context = readStoreContext(dbPath2, columns);
46879
+ const scope2 = options.account ? resolveAccountName(context.accounts, options.account) : void 0;
46880
+ const counts = new Map(
46881
+ parseJsonLines(
46882
+ runReadOnlySql(dbPath2, buildFolderCountsSql(columns))
46883
+ ).map((row) => [row.folder, row.n])
46856
46884
  );
46885
+ return assembleFolderTree(context, counts, Boolean(options.includeDeleted), scope2);
46857
46886
  }
46858
- function resolveInSchema(values, entity3, ctx, resolver) {
46859
- if (!values.some(isAlternateIdentifier)) return new Map(values.map((v) => [v, v]));
46860
- try {
46861
- return resolver(values, entity3);
46862
- } catch (error2) {
46863
- ctx.addIssue({
46864
- code: external_exports.ZodIssueCode.custom,
46865
- message: error2 instanceof Error ? error2.message : String(error2)
46866
- });
46867
- return null;
46887
+ function assembleFolderTree(context, counts, includeDeleted, scope2) {
46888
+ const paths = folderPaths(context.folders);
46889
+ const visible2 = context.folders.filter(
46890
+ (folder) => (includeDeleted || !folder.tombstoned) && (!scope2 || folder.account === scope2.pk)
46891
+ );
46892
+ const byPk = new Map(visible2.map((folder) => [folder.pk, folder]));
46893
+ const nodes = /* @__PURE__ */ new Map();
46894
+ for (const folder of visible2) {
46895
+ const node = {
46896
+ id: `x-coredata://${context.uuid}/ICFolder/p${folder.pk}`,
46897
+ identifier: folder.identifier,
46898
+ name: folder.name ?? "",
46899
+ path: paths.get(folder.pk) ?? "",
46900
+ kind: folder.trash ? "trash" : folder.folderType === 2 ? "smart" : "folder",
46901
+ noteCount: counts.get(folder.pk) ?? 0,
46902
+ totalNoteCount: 0,
46903
+ children: []
46904
+ };
46905
+ if (includeDeleted) node.markedForDeletion = Boolean(folder.tombstoned);
46906
+ nodes.set(folder.pk, node);
46868
46907
  }
46869
- }
46870
- function exactIdInput(entity3, pattern2, message, options = {}) {
46871
- const resolver = options.resolver ?? defaultResolver;
46872
- return external_exports.string().max(options.maxLength ?? DEFAULT_MAX_ID_LENGTH).regex(acceptAlternateForms(pattern2), message).transform((value, ctx) => {
46873
- const map = resolveInSchema([value], entity3, ctx, resolver);
46874
- return map ? map.get(value) ?? value : external_exports.NEVER;
46875
- });
46876
- }
46877
- function exactIdArrayInput(entity3, pattern2, message, options = {}) {
46878
- const resolver = options.resolver ?? defaultResolver;
46879
- const item = external_exports.string().max(options.maxLength ?? DEFAULT_MAX_ID_LENGTH).regex(acceptAlternateForms(pattern2), message);
46880
- const array2 = options.maxItems === void 0 ? external_exports.array(item) : external_exports.array(item).max(options.maxItems);
46881
- return array2.transform((values, ctx) => {
46882
- const map = resolveInSchema(values, entity3, ctx, resolver);
46883
- return map ? values.map((value) => map.get(value) ?? value) : external_exports.NEVER;
46884
- });
46885
- }
46886
- function looseIdTransform(entity3, resolver = defaultResolver) {
46887
- return (value, ctx) => {
46888
- if (!isAlternateIdentifier(value)) return value;
46889
- const map = resolveInSchema([value], entity3, ctx, resolver);
46890
- return map ? map.get(value) ?? value : external_exports.NEVER;
46908
+ const parentOf = (folder) => {
46909
+ if (folder.parent === null || !byPk.has(folder.parent)) return void 0;
46910
+ const seen = /* @__PURE__ */ new Set([folder.pk]);
46911
+ for (let at = byPk.get(folder.parent); at; at = at.parent !== null ? byPk.get(at.parent) : void 0) {
46912
+ if (seen.has(at.pk)) return void 0;
46913
+ seen.add(at.pk);
46914
+ }
46915
+ return folder.parent;
46916
+ };
46917
+ const roots = /* @__PURE__ */ new Map();
46918
+ for (const folder of visible2) {
46919
+ const node = nodes.get(folder.pk);
46920
+ const parent = parentOf(folder);
46921
+ if (parent !== void 0) nodes.get(parent).children.push(node);
46922
+ else {
46923
+ const key = folder.account !== null && context.accounts.some((a) => a.pk === folder.account) ? folder.account : null;
46924
+ roots.set(key, [...roots.get(key) ?? [], node]);
46925
+ }
46926
+ }
46927
+ const finish = (node) => {
46928
+ node.children.sort(compareNodes);
46929
+ node.totalNoteCount = node.noteCount + node.children.reduce((sum, c) => sum + finish(c), 0);
46930
+ return node.totalNoteCount;
46931
+ };
46932
+ const accounts = [];
46933
+ const addAccount = (name, identifier, folders) => {
46934
+ folders.sort(compareNodes);
46935
+ let noteCount = 0;
46936
+ for (const node of folders) {
46937
+ const total = finish(node);
46938
+ if (node.kind === "folder") noteCount += total;
46939
+ }
46940
+ accounts.push({ account: name, identifier, noteCount, folders });
46891
46941
  };
46942
+ for (const account of scope2 ? [scope2] : context.accounts) {
46943
+ addAccount(account.name, account.identifier, roots.get(account.pk) ?? []);
46944
+ }
46945
+ const orphans = roots.get(null);
46946
+ if (orphans && includeDeleted) addAccount("unknown", null, orphans);
46947
+ return { accounts, folderCount: accounts.reduce((sum, a) => sum + countNodes(a.folders), 0) };
46948
+ }
46949
+ function countNodes(nodes) {
46950
+ return nodes.reduce((sum, node) => sum + 1 + countNodes(node.children), 0);
46892
46951
  }
46893
46952
 
46894
46953
  // src/utils/contentWarnings.ts
@@ -48573,7 +48632,15 @@ function mutateBackground(request, operation, data, verify, deps) {
48573
48632
  transportError = error2 ?? {};
48574
48633
  transportMessage = describeTransportFailure(error2);
48575
48634
  }
48576
- const after = deps.read(request.id);
48635
+ let after;
48636
+ try {
48637
+ after = deps.read(request.id);
48638
+ } catch (error2) {
48639
+ throw new CodedError(
48640
+ `Operation outcome uncertain; read exact note before any retry: readback failed: ${error2 instanceof Error ? error2.message : String(error2)}${transportMessage ? "; " + transportMessage : ""}`,
48641
+ { code: "verification_failed", indeterminate: true }
48642
+ );
48643
+ }
48577
48644
  try {
48578
48645
  verify(before, after);
48579
48646
  } catch (error2) {
@@ -48839,14 +48906,27 @@ var FEATURES = [
48839
48906
  },
48840
48907
  {
48841
48908
  name: "fullDiskAccessReads",
48842
- description: "Read-only reads of the Notes database: query-notes, checklist state, note metadata, note links, native objects, native tags, and sync detail",
48909
+ description: "Read-only reads of the Notes database: queries, checklist state, metadata, links, note structure, tables, native objects, tags, smart folders, special and recent notes, folder tree, Paper and attachment exports, Markdown and HTML exports, and sync detail",
48843
48910
  tools: [
48844
48911
  "query-notes",
48845
48912
  "get-checklist-state",
48846
48913
  "get-note-metadata",
48847
48914
  "get-note-link",
48848
48915
  "get-native-objects",
48916
+ "get-note-blocks",
48917
+ "get-note-structure",
48918
+ "get-note-tables",
48919
+ "list-note-links",
48849
48920
  "list-native-tags",
48921
+ "list-smart-folders",
48922
+ "list-special-notes",
48923
+ "list-recent-notes",
48924
+ "list-folder-tree",
48925
+ "list-paper-attachments",
48926
+ "export-paper-image",
48927
+ "export-attachments",
48928
+ "export-notes-markdown",
48929
+ "export-notes-html",
48850
48930
  "get-note-markdown (checklist annotations)",
48851
48931
  "get-sync-status (pending uploads)"
48852
48932
  ],
@@ -48907,7 +48987,8 @@ var FEATURES = [
48907
48987
  // Placeholders for features that need a native WRITE helper, which this
48908
48988
  // server does not ship. The opt-in private helper is read-only (write
48909
48989
  // support was deliberately deferred), so enabling or installing it changes
48910
- // nothing here: these always report `not_implemented`.
48990
+ // nothing here: these always report `not_implemented`. Reading smart folders
48991
+ // is available through list-smart-folders (fullDiskAccessReads).
48911
48992
  {
48912
48993
  name: "checklistToggle",
48913
48994
  description: "Check or uncheck an existing checklist item in place",
@@ -48931,17 +49012,17 @@ var FEATURES = [
48931
49012
  },
48932
49013
  {
48933
49014
  name: "paragraphLinks",
48934
- description: "Link to a specific paragraph or heading inside a note",
48935
- tools: [],
49015
+ description: "List a note's paragraphs and return a link that opens Notes at one paragraph or heading, read from the Notes database",
49016
+ tools: ["list-note-paragraphs", "get-paragraph-link"],
48936
49017
  minimumMacOSVersion: null,
48937
- requirements: [{ kind: "native_write_helper" }]
49018
+ requirements: [{ kind: "full_disk_access" }]
48938
49019
  },
48939
49020
  {
48940
49021
  name: "audioTranscription",
48941
- description: "Transcribe audio recordings attached to a note",
48942
- tools: [],
49022
+ description: "Read the transcripts Notes stored for a note's audio recordings, or transcribe them on this Mac (transcribe-note-audio also needs the public helper built with setup --public-helper)",
49023
+ tools: ["get-audio-transcripts", "transcribe-note-audio"],
48943
49024
  minimumMacOSVersion: null,
48944
- requirements: [{ kind: "native_write_helper" }]
49025
+ requirements: [{ kind: "full_disk_access" }]
48945
49026
  },
48946
49027
  {
48947
49028
  name: "markdownTemplateLibrary",
@@ -49612,7 +49693,7 @@ function resolveNote(dbPath2, columns, selector) {
49612
49693
  function noteBodySql(columns) {
49613
49694
  return `SELECT json_object('isNote', n.Z_ENT = ${entity("ICNote")}, 'identifier', ${col(columns, "n", "ZIDENTIFIER")}, 'data', (SELECT hex(d.ZDATA) FROM ZICNOTEDATA d WHERE d.ZNOTE = n.Z_PK), 'encrypted', (SELECT d.ZCRYPTOINITIALIZATIONVECTOR IS NOT NULL FROM ZICNOTEDATA d WHERE d.ZNOTE = n.Z_PK), 'locked', ${col(columns, "n", "ZISPASSWORDPROTECTED")}) FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_PK = @pk;`;
49614
49695
  }
49615
- function readNoteParagraphs(selector, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
49696
+ function readNoteParagraphs(selector, { dbPath: dbPath2 = NOTES_DB_PATH8 } = {}) {
49616
49697
  checkNoteSelector(selector);
49617
49698
  const columns = readColumns(dbPath2);
49618
49699
  const { pk, id: id2 } = resolveNote(dbPath2, columns, selector);
@@ -49841,7 +49922,7 @@ function describeNoteStructure(s) {
49841
49922
  if (s.isShared) parts.push("shared");
49842
49923
  return `Note structure: ${parts.join("; ")}.`;
49843
49924
  }
49844
- function readNoteStructure(id2, { dbPath: dbPath2 = NOTES_DB_PATH7, includeText = true, maxTextBytes = 4 * 1024 * 1024 } = {}) {
49925
+ function readNoteStructure(id2, { dbPath: dbPath2 = NOTES_DB_PATH8, includeText = true, maxTextBytes = 4 * 1024 * 1024 } = {}) {
49845
49926
  const { pk } = parseNoteId2(id2);
49846
49927
  const columns = readColumns(dbPath2);
49847
49928
  const rows = parseJsonLines(
@@ -50087,7 +50168,7 @@ function decodeBodies(dbPath2, sql, params) {
50087
50168
  }
50088
50169
  var KIND_ORDER2 = { inline: 0, card: 1, note: 2, section: 3 };
50089
50170
  function listNoteLinks(options = {}) {
50090
- const { dbPath: dbPath2 = NOTES_DB_PATH7, offset = 0, limit = 200, maxBytes = 4 * 1024 * 1024 } = options;
50171
+ const { dbPath: dbPath2 = NOTES_DB_PATH8, offset = 0, limit = 200, maxBytes = 4 * 1024 * 1024 } = options;
50091
50172
  if (options.id && (options.account || options.folder))
50092
50173
  throw new NoteStoreError("Pass either id or account/folder, not both.", "invalid_input");
50093
50174
  const notePk = options.id ? parseNoteId2(options.id).pk : 0;
@@ -50309,8 +50390,9 @@ function insertLink(request, deps) {
50309
50390
  try {
50310
50391
  readback = verifyLinkReadback(before, deps.readLinks(request.id), insertion, request.url);
50311
50392
  } catch (error2) {
50312
- throw new Error(
50313
- `The text was written, but link verification failed: ${error2 instanceof Error ? error2.message : String(error2)}. Do not retry automatically.`
50393
+ throw new CodedError(
50394
+ `The text was written, but link verification failed: ${error2 instanceof Error ? error2.message : String(error2)}. Do not retry automatically.`,
50395
+ { code: "verification_failed", committed: true, indeterminate: true }
50314
50396
  );
50315
50397
  }
50316
50398
  return {
@@ -53554,7 +53636,7 @@ function registerDirectOperations(server2, manager) {
53554
53636
  );
53555
53637
  tool(
53556
53638
  "create-note-with-attachment",
53557
- "Use when: creating a new note that holds one local file, in one call.\nReturns: the new note id plus the add-attachment result (attachment id, bytes, name, content hash).\nDo not use when: the note already exists (add-attachment).\nSafety: checks the file and filename before creating anything, creates the note through Notes.app like create-note, then attaches with the same byte verification as add-attachment. If the attachment step fails after the note exists, the error names the new note's id; attach to it with add-attachment instead of creating another note.",
53639
+ "Use when: creating a new note that holds one local file, in one call.\nReturns: the new note id plus the add-attachment result (attachment id, bytes, name, content hash).\nDo not use when: the note already exists (add-attachment).\nSafety: checks the file and filename before creating anything, creates the note through Notes.app like create-note, then attaches with the same byte verification as add-attachment. If the attachment step fails before the file is inserted, the error names the new note's id; attach to it with add-attachment instead of creating another note. If insertion started but could not be verified, the outcome is uncertain: read the note with list-attachments before attaching again.",
53558
53640
  {
53559
53641
  title: external_exports.string().min(1).max(2e3).refine((s) => !/[\r\n\0]/u.test(s), "One-line title"),
53560
53642
  content: external_exports.string().min(1).max(1024 * 1024).optional().describe("Optional plain-text body placed above the attachment"),
@@ -53580,26 +53662,45 @@ function registerDirectOperations(server2, manager) {
53580
53662
  `Failed to create note "${args.title}". Check that the folder and account exist (list-folders, list-accounts); nothing was attached`
53581
53663
  );
53582
53664
  const handOff = `Note ${note.id} was created; attach to it with add-attachment instead of creating another note`;
53665
+ const createdError = (message, cause) => new CodedError(message, {
53666
+ code: classifyError(message, cause).code,
53667
+ committed: true,
53668
+ indeterminate: false
53669
+ });
53583
53670
  let snapshot;
53584
53671
  try {
53585
53672
  snapshot = readSnapshot(manager, note.id);
53586
53673
  } catch (error2) {
53587
- throw new Error(`${handOff}. The new note could not be read back: ${String(error2)}`);
53674
+ const message = `${handOff}. The new note could not be read back: ${String(error2)}`;
53675
+ throw createdError(message, error2);
53588
53676
  }
53677
+ const progress = { insertionStarted: false };
53589
53678
  try {
53590
53679
  return {
53591
- ...attachFile(manager, {
53592
- id: note.id,
53593
- expectedContentHash: snapshot.hash,
53594
- path: args.path,
53595
- filename: name
53596
- }),
53680
+ ...attachFile(
53681
+ manager,
53682
+ {
53683
+ id: note.id,
53684
+ expectedContentHash: snapshot.hash,
53685
+ path: args.path,
53686
+ filename: name
53687
+ },
53688
+ void 0,
53689
+ progress
53690
+ ),
53597
53691
  title: args.title,
53598
53692
  folder: args.folder,
53599
53693
  noteCreated: true
53600
53694
  };
53601
53695
  } catch (error2) {
53602
- throw new Error(`${handOff}. ${error2 instanceof Error ? error2.message : String(error2)}`);
53696
+ const detail = error2 instanceof Error ? error2.message : String(error2);
53697
+ if (!progress.insertionStarted) throw createdError(`${handOff}. ${detail}`, error2);
53698
+ const message = `Note ${note.id} was created, but the attachment outcome is uncertain: ${detail}. Read the note (list-attachments) before attaching again, and do not create another note`;
53699
+ throw new CodedError(message, {
53700
+ code: "verification_failed",
53701
+ committed: true,
53702
+ indeterminate: true
53703
+ });
53603
53704
  }
53604
53705
  }
53605
53706
  );
@@ -53706,7 +53807,7 @@ function storedInsertion(manager, id2, before, bytes, returnedId) {
53706
53807
  );
53707
53808
  return { attachmentId, name: row.filename };
53708
53809
  }
53709
- function attachFile(manager, args, checked) {
53810
+ function attachFile(manager, args, checked, progress = { insertionStarted: false }) {
53710
53811
  const { id: id2, expectedContentHash, path: path10 } = args;
53711
53812
  const name = attachmentName(path10, args.filename);
53712
53813
  const before = checked ?? readSnapshot(manager, id2);
@@ -53721,6 +53822,7 @@ function attachFile(manager, args, checked) {
53721
53822
  if (readSnapshot(manager, id2).hash !== before.hash) throw new Error("Note revision changed");
53722
53823
  let returnedId;
53723
53824
  let transportUncertain = false;
53825
+ progress.insertionStarted = true;
53724
53826
  try {
53725
53827
  returnedId = manager.addAttachmentById(id2, before.body, temporaryFile);
53726
53828
  } catch {
@@ -54048,15 +54150,27 @@ function runFolderDelete(manager, args, deps = defaultDeps) {
54048
54150
  throw uncertain(
54049
54151
  `The delete outcome is uncertain (${outcome.reason}); read folder ${args.id} before retrying`
54050
54152
  );
54051
- if (manager.folderExistsById(args.id))
54153
+ let stillExists;
54154
+ try {
54155
+ stillExists = manager.folderExistsById(args.id);
54156
+ } catch (error2) {
54157
+ throw uncertain(
54158
+ `The delete outcome is uncertain: Notes.app accepted the delete, but the readback failed (${error2 instanceof Error ? error2.message : String(error2)}); read folder ${args.id} before retrying`
54159
+ );
54160
+ }
54161
+ if (stillExists)
54052
54162
  throw uncertain(
54053
54163
  `The delete outcome is uncertain: Notes.app still resolves folder ${args.id}; read it before retrying`
54054
54164
  );
54055
54165
  let storeTombstoned = false;
54056
54166
  for (let attempt = 0; attempt < 5 && !storeTombstoned; attempt++) {
54057
54167
  if (attempt > 0) deps.sleep(200);
54058
- const after = deps.readStore(coreDataPk(args.id));
54059
- storeTombstoned = !after || after.markedForDeletion;
54168
+ try {
54169
+ const after = deps.readStore(coreDataPk(args.id));
54170
+ storeTombstoned = !after || after.markedForDeletion;
54171
+ } catch {
54172
+ break;
54173
+ }
54060
54174
  }
54061
54175
  return {
54062
54176
  ...base,
@@ -56808,20 +56922,26 @@ function appendChecklistItems(args, deps, readRich) {
56808
56922
  const landed = [];
56809
56923
  let contentHash = args.expectedContentHash;
56810
56924
  for (const [index, text2] of args.items.entries()) {
56811
- let wrote = false;
56925
+ let attempted = false;
56926
+ const tracked = {
56927
+ ...deps,
56928
+ run: (input) => {
56929
+ attempted = true;
56930
+ deps.run(input);
56931
+ }
56932
+ };
56812
56933
  try {
56813
56934
  const result = mutateBackground(
56814
56935
  { ...args, expectedContentHash: contentHash },
56815
56936
  "create-checklist-item",
56816
56937
  { text: text2 },
56817
56938
  (before, after) => {
56818
- wrote = true;
56819
56939
  assertPreserved(before, after, { append: true });
56820
56940
  const added = after.checklist.slice(before.checklist.length);
56821
56941
  if (added.length !== 1 || added[0].text !== text2 || added[0].done)
56822
56942
  throw new Error("Native checklist item not verified");
56823
56943
  },
56824
- deps
56944
+ tracked
56825
56945
  );
56826
56946
  const items = byPosition(readRich(args.id).checklistItems);
56827
56947
  const fresh = items.filter(
@@ -56837,20 +56957,33 @@ function appendChecklistItems(args, deps, readRich) {
56837
56957
  landed.push({ index, id: fresh[0].id, text: text2 });
56838
56958
  contentHash = result.contentHash;
56839
56959
  } catch (error2) {
56960
+ const message = error2 instanceof Error ? error2.message : String(error2);
56961
+ const uncertain2 = attempted && !(error2 instanceof CodedError && error2.envelope.committed === false);
56962
+ const classified = classifyError(message, error2).code;
56963
+ const envelope = uncertain2 ? {
56964
+ code: classified === "timeout_indeterminate" ? classified : "verification_failed",
56965
+ indeterminate: true
56966
+ } : {
56967
+ code: classified,
56968
+ indeterminate: false,
56969
+ // Earlier items did land, so only an empty batch wrote nothing.
56970
+ ...landed.length === 0 ? { committed: false } : {}
56971
+ };
56840
56972
  return {
56841
56973
  ok: false,
56974
+ ...envelope,
56842
56975
  id: args.id,
56843
56976
  landed,
56844
56977
  stoppedAt: {
56845
56978
  index,
56846
56979
  text: text2,
56847
- // A bridge refusal or failure with an unchanged readback is
56848
- // reported as not written even though verification ran (#248).
56849
- outcome: wrote && !(error2 instanceof CodedError && error2.envelope.committed === false) ? "uncertain" : "not-written",
56850
- error: error2 instanceof Error ? error2.message : String(error2)
56980
+ outcome: uncertain2 ? "uncertain" : "not-written",
56981
+ error: message
56851
56982
  },
56852
56983
  notAttempted: args.items.slice(index + 1),
56853
- contentHash,
56984
+ // After an uncertain run the note may have changed, so the last
56985
+ // verified revision would be stale; read the note for a fresh one.
56986
+ ...uncertain2 ? {} : { contentHash },
56854
56987
  message: `Stopped at item ${index + 1} of ${args.items.length}; ${landed.length} item(s) landed and were verified. Read the exact note before retrying, and retry only items that are not present.`
56855
56988
  };
56856
56989
  }
@@ -56860,15 +56993,24 @@ function appendChecklistItems(args, deps, readRich) {
56860
56993
  const orderVerified = tail.length === landed.length && tail.every((item, i) => item.id === landed[i].id && item.text === landed[i].text);
56861
56994
  return {
56862
56995
  ok: orderVerified,
56996
+ ...orderVerified ? {} : { code: "verification_failed", committed: true },
56863
56997
  id: args.id,
56864
56998
  contentHash,
56865
56999
  items: landed,
57000
+ landed,
56866
57001
  orderVerified,
56867
57002
  ...orderVerified ? {} : {
56868
57003
  message: "Every item landed and was verified, but they are not the note's last checklist items in the requested order. Read the note before editing it further."
56869
57004
  }
56870
57005
  };
56871
57006
  }
57007
+ function nativeToolResult(result) {
57008
+ return {
57009
+ content: [{ type: "text", text: JSON.stringify(result) }],
57010
+ structuredContent: result,
57011
+ ...result.ok === false && typeof result.code === "string" ? { isError: true } : {}
57012
+ };
57013
+ }
56872
57014
  function registerNativeOperations(server2, manager) {
56873
57015
  function tool(name, description, input, handler, readOnly = false) {
56874
57016
  server2.registerTool(
@@ -56881,11 +57023,7 @@ function registerNativeOperations(server2, manager) {
56881
57023
  },
56882
57024
  (async (args) => {
56883
57025
  try {
56884
- const result = handler(args);
56885
- return {
56886
- content: [{ type: "text", text: JSON.stringify(result) }],
56887
- structuredContent: result
56888
- };
57026
+ return nativeToolResult(handler(args));
56889
57027
  } catch (error2) {
56890
57028
  return errorResult(error2 instanceof Error ? error2.message : "Operation failed", error2);
56891
57029
  }
@@ -57733,7 +57871,7 @@ function parseNoteObjectId(noteId3) {
57733
57871
  );
57734
57872
  return { store: match[1], pk: Number(match[2]) };
57735
57873
  }
57736
- function queryNoteScoped(sql, pk, dbPath2 = NOTES_DB_PATH7) {
57874
+ function queryNoteScoped(sql, pk, dbPath2 = NOTES_DB_PATH8) {
57737
57875
  return runReadOnlySql(dbPath2, sql, { pk: { int: pk } }).split("\n");
57738
57876
  }
57739
57877
  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));`;
@@ -57762,7 +57900,7 @@ function drawingRowsSql(columns, dataColumn) {
57762
57900
  `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;`
57763
57901
  ].join("\n");
57764
57902
  }
57765
- function readDrawingRows2(noteId3, dbPath2 = NOTES_DB_PATH7) {
57903
+ function readDrawingRows2(noteId3, dbPath2 = NOTES_DB_PATH8) {
57766
57904
  const { store, pk } = parseNoteObjectId(noteId3);
57767
57905
  const columns = readColumns(dbPath2);
57768
57906
  const dataColumn = columns.has("ZMERGEABLEDATA1") ? "ZMERGEABLEDATA1" : columns.has("ZMERGEABLEDATA") ? "ZMERGEABLEDATA" : null;
@@ -57997,7 +58135,7 @@ function isFile2(path10) {
57997
58135
  var positive = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
57998
58136
  function readAudioAssets(noteId3, options = {}) {
57999
58137
  const { store, pk } = parseNoteId2(noteId3);
58000
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
58138
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH8;
58001
58139
  const columns = readColumns(dbPath2);
58002
58140
  const missing = ["ZMEDIA", "ZPARENTATTACHMENT", "ZFILENAME"].filter((c) => !columns.has(c));
58003
58141
  if (missing.length)
@@ -58376,6 +58514,10 @@ function inspectInstallation(deps = defaultDeps2()) {
58376
58514
  );
58377
58515
  return { ...base, ready: true, reason: null, detail: null };
58378
58516
  }
58517
+ function helperTimeoutMs(env) {
58518
+ const value = Number.parseInt(env[TIMEOUT_ENV] || "", 10);
58519
+ return Number.isFinite(value) && value > 0 ? value : DEFAULT_TIMEOUT_MS3;
58520
+ }
58379
58521
  var PrivateHelperError = class extends Error {
58380
58522
  constructor(code, message, details = {}) {
58381
58523
  super(message);
@@ -58458,7 +58600,7 @@ function callPrivateHelper(action, fields = {}, deps = defaultDeps2(), options =
58458
58600
  throw new PrivateHelperError(install.reason || "helper_not_installed", install.detail || "");
58459
58601
  binaryPath = install.binaryPath;
58460
58602
  }
58461
- const timeout = Number.parseInt(deps.env[TIMEOUT_ENV] || "", 10) || DEFAULT_TIMEOUT_MS3;
58603
+ const timeout = helperTimeoutMs(deps.env);
58462
58604
  const result = deps.spawn(binaryPath, [], {
58463
58605
  input: JSON.stringify({ protocol: PRIVATE_HELPER_PROTOCOL, action, ...fields }),
58464
58606
  encoding: "utf8",
@@ -58468,13 +58610,23 @@ function callPrivateHelper(action, fields = {}, deps = defaultDeps2(), options =
58468
58610
  env: deps.env
58469
58611
  });
58470
58612
  const errno = result.error?.code;
58471
- if (errno === "ETIMEDOUT" || result.signal && result.status === null)
58613
+ if (errno === "ETIMEDOUT")
58472
58614
  throw new PrivateHelperError("timeout", `The helper did not answer within ${timeout} ms.`);
58615
+ if (errno === "ENOBUFS")
58616
+ throw new PrivateHelperError(
58617
+ "invalid_response",
58618
+ `The helper's output exceeded ${MAX_OUTPUT_BYTES3} bytes and was cut off.`
58619
+ );
58473
58620
  if (result.error)
58474
58621
  throw new PrivateHelperError(
58475
58622
  "helper_unreachable",
58476
58623
  `Could not run the helper: ${result.error.message}`
58477
58624
  );
58625
+ if (result.signal)
58626
+ throw new PrivateHelperError(
58627
+ "helper_crashed",
58628
+ `The helper was terminated by ${result.signal} before it answered.`
58629
+ );
58478
58630
  const stdout = String(result.stdout ?? "").trim();
58479
58631
  let parsed;
58480
58632
  try {
@@ -58798,7 +58950,8 @@ function envelopeCode(helperCode, message) {
58798
58950
  case "not_found":
58799
58951
  return "not_found";
58800
58952
  case "timeout":
58801
- return "timeout_indeterminate";
58953
+ case "helper_crashed":
58954
+ return "operation_failed";
58802
58955
  case "invalid_request":
58803
58956
  case "invalid_json":
58804
58957
  case "input_too_large":
@@ -58908,6 +59061,7 @@ var server = new McpServer({
58908
59061
  version: version2,
58909
59062
  description: "MCP server for managing Apple Notes - create, search, update, and organize notes"
58910
59063
  });
59064
+ installSdkErrorCodes(server);
58911
59065
  var notesManager = new AppleNotesManager();
58912
59066
  registerDirectOperations(server, notesManager);
58913
59067
  registerFolderDelete(server, notesManager);
@@ -58924,6 +59078,9 @@ function successResponse(message, structured) {
58924
59078
  function errorResponse(message, cause) {
58925
59079
  return errorResult(message, cause);
58926
59080
  }
59081
+ function notFoundResponse(message) {
59082
+ return errorResult(message, new CodedError(message, { code: "not_found" }));
59083
+ }
58927
59084
  function withErrorHandling(handler, errorPrefix) {
58928
59085
  return async (params) => {
58929
59086
  try {
@@ -59051,6 +59208,9 @@ function bodyReadFailureMessage(id2, title, error2) {
59051
59208
  return describeBodyReadFailure(title, error2, attachments);
59052
59209
  }
59053
59210
  var NOT_DELETED_MESSAGE = "Notes.app accepted the delete, but the note is still in its original folder, so it was not moved to Recently Deleted. Nothing was deleted; read the note again before retrying.";
59211
+ function deleteUnverifiedMessage(title, id2) {
59212
+ return `Notes.app accepted the delete for note "${title}", but its original folder could not be re-read to confirm the note left it. The outcome is uncertain; read exact ID ${id2} before retrying.`;
59213
+ }
59054
59214
  function containerUnknownMessage(title) {
59055
59215
  return `Could not read which folder note "${title}" is in, so it may already be in Recently Deleted, where deleting it would remove it permanently. Nothing was deleted. Retry in a moment; if it keeps failing, quit and reopen Notes.app, then retry.`;
59056
59216
  }
@@ -59121,12 +59281,32 @@ var folderNameSchema = {
59121
59281
  };
59122
59282
  function registerTool(name, config2, cb) {
59123
59283
  const { outputSchema, ...rest } = config2;
59284
+ const callback = NOTES_WRITE_TOOLS.has(name) ? (async (...args) => writeToolResult(
59285
+ await cb(...args)
59286
+ )) : cb;
59124
59287
  return server.registerTool(
59125
59288
  name,
59126
59289
  outputSchema ? { ...rest, outputSchema: external_exports.object(outputSchema).passthrough() } : rest,
59127
- cb
59290
+ callback
59128
59291
  );
59129
59292
  }
59293
+ var NOTES_WRITE_TOOLS = /* @__PURE__ */ new Set([
59294
+ "create-note",
59295
+ "update-note",
59296
+ "append-to-note",
59297
+ "insert-link",
59298
+ "delete-note",
59299
+ "move-note",
59300
+ "create-folder",
59301
+ "delete-folder",
59302
+ "batch-delete-notes",
59303
+ "batch-move-notes"
59304
+ ]);
59305
+ function writeToolResult(result) {
59306
+ const envelope = result.structuredContent;
59307
+ if (!result.isError || !envelope) return result;
59308
+ return { ...result, structuredContent: unavailableWriteEnvelope(envelope) };
59309
+ }
59130
59310
  registerTool(
59131
59311
  "create-note",
59132
59312
  {
@@ -59519,7 +59699,7 @@ registerTool(
59519
59699
  if (id2) {
59520
59700
  const note2 = notesManager.getNoteById(id2);
59521
59701
  if (!note2) {
59522
- return errorResponse(`Note with ID "${id2}" not found`);
59702
+ return notFoundResponse(`Note with ID "${id2}" not found`);
59523
59703
  }
59524
59704
  if (note2.passwordProtected) {
59525
59705
  return errorResponse(
@@ -59561,7 +59741,7 @@ registerTool(
59561
59741
  }
59562
59742
  const note = notesManager.getNoteDetails(title, account);
59563
59743
  if (!note) {
59564
- return errorResponse(`Note "${title}" not found`);
59744
+ return notFoundResponse(`Note "${title}" not found`);
59565
59745
  }
59566
59746
  if (note.passwordProtected) {
59567
59747
  return errorResponse(
@@ -59619,7 +59799,7 @@ registerTool(
59619
59799
  if (id2) {
59620
59800
  const note2 = notesManager.getNoteById(id2);
59621
59801
  if (!note2) {
59622
- return errorResponse(`Note with ID "${id2}" not found`);
59802
+ return notFoundResponse(`Note with ID "${id2}" not found`);
59623
59803
  }
59624
59804
  if (note2.passwordProtected) {
59625
59805
  return errorResponse(
@@ -59637,7 +59817,7 @@ registerTool(
59637
59817
  }
59638
59818
  const note = notesManager.getNoteDetails(title, account);
59639
59819
  if (!note) {
59640
- return errorResponse(`Note "${title}" not found`);
59820
+ return notFoundResponse(`Note "${title}" not found`);
59641
59821
  }
59642
59822
  if (note.passwordProtected) {
59643
59823
  return errorResponse(
@@ -59673,7 +59853,7 @@ registerTool(
59673
59853
  withErrorHandling(({ id: id2 }) => {
59674
59854
  const note = notesManager.getNoteById(id2);
59675
59855
  if (!note) {
59676
- return errorResponse(`Note with ID "${id2}" not found`);
59856
+ return notFoundResponse(`Note with ID "${id2}" not found`);
59677
59857
  }
59678
59858
  const metadata = {
59679
59859
  id: note.id,
@@ -59706,7 +59886,7 @@ registerTool(
59706
59886
  withErrorHandling(({ title, account }) => {
59707
59887
  const note = notesManager.getNoteDetails(title, account);
59708
59888
  if (!note) {
59709
- return errorResponse(`Note "${title}" not found`);
59889
+ return notFoundResponse(`Note "${title}" not found`);
59710
59890
  }
59711
59891
  const metadata = {
59712
59892
  id: note.id,
@@ -59763,7 +59943,7 @@ registerTool(
59763
59943
  if (id2) {
59764
59944
  const note2 = notesManager.getNoteById(id2);
59765
59945
  if (!note2) {
59766
- return errorResponse(`Note with ID "${id2}" not found`);
59946
+ return notFoundResponse(`Note with ID "${id2}" not found`);
59767
59947
  }
59768
59948
  if (note2.passwordProtected) {
59769
59949
  return errorResponse(
@@ -59783,7 +59963,7 @@ registerTool(
59783
59963
  }
59784
59964
  const note = notesManager.getNoteDetails(title, account);
59785
59965
  if (!note) {
59786
- return errorResponse(
59966
+ return notFoundResponse(
59787
59967
  `Note "${title}" not found. Use search-notes to find notes, then use the note's ID for reliable operations.`
59788
59968
  );
59789
59969
  }
@@ -59861,7 +60041,7 @@ registerTool(
59861
60041
  },
59862
60042
  withErrorHandling(({ id: id2 }) => {
59863
60043
  const note = notesManager.getNoteById(id2);
59864
- if (!note) return errorResponse(`Note with ID "${id2}" not found`);
60044
+ if (!note) return notFoundResponse(`Note with ID "${id2}" not found`);
59865
60045
  const body = notesManager.getNoteContentById(id2);
59866
60046
  if (!body) return errorResponse(`Failed to read content of note "${note.title}"`);
59867
60047
  const rich = readRichNote(id2);
@@ -59967,7 +60147,10 @@ registerTool(
59967
60147
  } catch (error2) {
59968
60148
  if (!(error2 instanceof NoteBlocksError)) throw error2;
59969
60149
  const hint = error2.code === "no-full-disk-access" ? ` Grant Full Disk Access to the Node binary running this server (run the doctor tool for its path): ${FULL_DISK_ACCESS_GUIDE_URL}` : "";
59970
- return errorResponse(`Error reading note blocks [${error2.code}]: ${error2.message}${hint}`);
60150
+ return errorResponse(
60151
+ `Error reading note blocks [${error2.code}]: ${error2.message}${hint}`,
60152
+ error2
60153
+ );
59971
60154
  }
59972
60155
  const { summary } = page;
59973
60156
  const styles = Object.entries(summary.styles).map(([style, count]) => `${style} ${count}`).join(", ");
@@ -60528,7 +60711,10 @@ registerTool(
60528
60711
  },
60529
60712
  (r) => route = r
60530
60713
  );
60531
- if (response.isError) throw new Error(response.content[0].text);
60714
+ if (response.isError) {
60715
+ const envelope = response.structuredContent;
60716
+ throw envelope?.code ? new CodedError(response.content[0].text, envelope) : new Error(response.content[0].text);
60717
+ }
60532
60718
  return { route, contentHash: String(response.structuredContent?.contentHash ?? "") };
60533
60719
  }
60534
60720
  }
@@ -60620,6 +60806,13 @@ registerTool(
60620
60806
  if (result.status === "not-deleted") {
60621
60807
  return errorResponse(NOT_DELETED_MESSAGE);
60622
60808
  }
60809
+ if (result.status === "unverified") {
60810
+ const message = deleteUnverifiedMessage(snapshot.note.title, id2);
60811
+ return errorResponse(
60812
+ message,
60813
+ new CodedError(message, { code: "verification_failed", indeterminate: true })
60814
+ );
60815
+ }
60623
60816
  if (result.status !== "deleted") {
60624
60817
  return errorResponse(
60625
60818
  `The delete result for note "${snapshot.note.title}" is uncertain. Inspect exact ID ${id2} before retrying.`
@@ -60664,7 +60857,7 @@ registerTool(
60664
60857
  withErrorHandling(({ id: id2, folder, account, ...scopeArgs }) => {
60665
60858
  const note = notesManager.getNoteById(id2);
60666
60859
  if (!note) {
60667
- return errorResponse(`Note with ID "${id2}" not found`);
60860
+ return notFoundResponse(`Note with ID "${id2}" not found`);
60668
60861
  }
60669
60862
  const success = notesManager.moveNoteById(id2, folder, account, scopeFrom(scopeArgs));
60670
60863
  if (!success) {
@@ -61195,7 +61388,7 @@ registerTool(
61195
61388
  if (id2) {
61196
61389
  const note2 = notesManager.getNoteById(id2);
61197
61390
  if (!note2) {
61198
- return errorResponse(`Note with ID "${id2}" not found`);
61391
+ return notFoundResponse(`Note with ID "${id2}" not found`);
61199
61392
  }
61200
61393
  const attachments2 = notesManager.listAttachmentsById(id2);
61201
61394
  if (attachments2.length === 0) {
@@ -61248,7 +61441,7 @@ ${attachmentList2}`,
61248
61441
  }
61249
61442
  const note = notesManager.getNoteDetails(title, account);
61250
61443
  if (!note) {
61251
- return errorResponse(
61444
+ return notFoundResponse(
61252
61445
  `Note "${title}" not found. Use search-notes to find notes, then use the note's ID for reliable operations.`
61253
61446
  );
61254
61447
  }
@@ -61306,6 +61499,8 @@ registerTool(
61306
61499
  }
61307
61500
  if (result.status === "not-deleted")
61308
61501
  return { id: id2, success: false, error: NOT_DELETED_MESSAGE };
61502
+ if (result.status === "unverified")
61503
+ return { id: id2, success: false, error: deleteUnverifiedMessage(snapshot.note.title, id2) };
61309
61504
  return { id: id2, success: false, error: "Delete result uncertain; inspect this exact ID" };
61310
61505
  });
61311
61506
  const succeeded = results.filter((r) => r.success).length;
@@ -61533,10 +61728,12 @@ Safety: writes files; exportDir must be absolute and under the home directory, a
61533
61728
  );
61534
61729
  }
61535
61730
  }
61536
- return successResponse(
61537
- `Exported ${exported} file(s) to ${r.exportDir} (${previews} preview-only, ${skipped} with nothing on disk, ${failed} failed).`,
61538
- structured
61539
- );
61731
+ const summary = `Exported ${exported} file(s) to ${r.exportDir} (${previews} preview-only, ${skipped} with nothing on disk, ${failed} failed).`;
61732
+ if (exported === 0 && failed > 0) {
61733
+ const error2 = errorResponse(summary, new CodedError(summary, { code: "operation_failed" }));
61734
+ return { ...error2, structuredContent: { ...structured, ...error2.structuredContent } };
61735
+ }
61736
+ return successResponse(summary, structured);
61540
61737
  }, "Error exporting attachments")
61541
61738
  );
61542
61739
  registerTool(
@@ -61686,7 +61883,7 @@ registerTool(
61686
61883
  if (id2) {
61687
61884
  const markdown2 = notesManager.getNoteMarkdownById(id2);
61688
61885
  if (!markdown2) {
61689
- return errorResponse(`Note with ID "${id2}" not found or has no content`);
61886
+ return notFoundResponse(`Note with ID "${id2}" not found or has no content`);
61690
61887
  }
61691
61888
  return successResponse(markdown2, { markdown: markdown2 });
61692
61889
  }
@@ -61695,7 +61892,7 @@ registerTool(
61695
61892
  }
61696
61893
  const markdown = notesManager.getNoteMarkdown(title, account);
61697
61894
  if (!markdown) {
61698
- return errorResponse(
61895
+ return notFoundResponse(
61699
61896
  `Note "${title}" not found or has no content. Use search-notes to find notes, then use the note's ID for reliable operations.`
61700
61897
  );
61701
61898
  }
@@ -61770,7 +61967,10 @@ registerTool(
61770
61967
  `Error exporting Markdown [${error2.code}]: the template is invalid:
61771
61968
  ` + error2.details.map((detail) => `${detail.path}: ${detail.message}`).join("\n")
61772
61969
  );
61773
- return errorResponse(`Error exporting Markdown [${error2.code}]: ${error2.message}${hint}`);
61970
+ return errorResponse(
61971
+ `Error exporting Markdown [${error2.code}]: ${error2.message}${hint}`,
61972
+ error2
61973
+ );
61774
61974
  }
61775
61975
  const warned = receipt.warnings?.length ? `; ${receipt.warnings.length + (receipt.warningsOmitted ?? 0)} warning(s)` : "";
61776
61976
  const skipped = (receipt.skipped.length ? `; skipped ${receipt.skipped.length}` : "") + warned;
@@ -61823,7 +62023,7 @@ registerTool(
61823
62023
  } catch (error2) {
61824
62024
  if (!(error2 instanceof NotesExportError || error2 instanceof NoteBlocksError)) throw error2;
61825
62025
  const hint = error2.code === "no-full-disk-access" ? ` Grant Full Disk Access to the Node binary running this server (run the doctor tool for its path): ${FULL_DISK_ACCESS_GUIDE_URL}` : "";
61826
- return errorResponse(`Error exporting HTML [${error2.code}]: ${error2.message}${hint}`);
62026
+ return errorResponse(`Error exporting HTML [${error2.code}]: ${error2.message}${hint}`, error2);
61827
62027
  }
61828
62028
  const assets = receipt.assets ? `; copied ${receipt.assets.files} asset file(s) to ${receipt.assets.dir}` : `; embedded ${receipt.embedded ?? 0} asset(s)`;
61829
62029
  const skipped = receipt.skipped.length ? `; skipped ${receipt.skipped.length}` : "";
@@ -61851,7 +62051,7 @@ registerTool(
61851
62051
  withErrorHandling(({ id: id2 }) => {
61852
62052
  const note = notesManager.getNoteById(id2);
61853
62053
  if (!note) {
61854
- return errorResponse(`Note with ID "${id2}" not found`);
62054
+ return notFoundResponse(`Note with ID "${id2}" not found`);
61855
62055
  }
61856
62056
  if (note.passwordProtected) {
61857
62057
  return errorResponse(