apple-notes-mcp 2.8.27 → 2.8.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -1
- package/build/index.js +778 -151
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -513,6 +513,58 @@ Disk Access.
|
|
|
513
513
|
|
|
514
514
|
---
|
|
515
515
|
|
|
516
|
+
#### `get-note-tables`
|
|
517
|
+
|
|
518
|
+
Reads every native table in one exact note, in body order, from the NoteStore
|
|
519
|
+
database. Each table is returned as GitHub-flavored Markdown and as JSON `rows`
|
|
520
|
+
with stable `rowIds` and `columnIds`. Notes tables have no header row, so the
|
|
521
|
+
Markdown uses the first row as the header. Pipes are escaped as `\|`,
|
|
522
|
+
backslashes are doubled, and line breaks inside a cell become `<br>`.
|
|
523
|
+
|
|
524
|
+
| Parameter | Type | Required | Description |
|
|
525
|
+
|-----------|------|----------|-------------|
|
|
526
|
+
| `id` | string | Yes | Exact note ID (`x-coredata://…/ICNote/pNNN`) |
|
|
527
|
+
|
|
528
|
+
`tableCellsComplete` is false when any table or cell could not be decoded. A
|
|
529
|
+
cell holding an embedded object is `null` in `rows`, listed in
|
|
530
|
+
`incompleteCells`, and shown as `[undecoded cell]` in Markdown. A table whose
|
|
531
|
+
data cannot be decoded at all has `complete: false`, a `reason`, and no rows.
|
|
532
|
+
Cell text is never guessed. Links and styling inside cells are not rendered.
|
|
533
|
+
A note without tables returns an empty `tables` list. This tool is read-only,
|
|
534
|
+
requires Full Disk Access, and refuses password-protected notes.
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
#### `get-note-blocks`
|
|
539
|
+
|
|
540
|
+
Decodes one note's body into typed blocks, read-only, from the NoteStore
|
|
541
|
+
database. Each block is one paragraph with its `style` (`title`, `heading`,
|
|
542
|
+
`subheading`, `body`, `monospaced`, `bulleted`, `dashed`, `numbered`,
|
|
543
|
+
`checklist`, or `unknown` with the raw `styleType`), `indent`, `alignment`,
|
|
544
|
+
`blockQuote`, checklist `id`/`done`, and `paragraphUuid` when stored. Each
|
|
545
|
+
block lists its inline `runs` with `bold`, `italic`, `underline`,
|
|
546
|
+
`strikethrough`, `superscript`, `subscript`, `color`, `highlight`, `link`
|
|
547
|
+
(plus `linkSafe`), `font`, and `attachment`, and its attachment markers in body
|
|
548
|
+
order. Offsets and lengths count UTF-16 code units. `summary` counts styles and
|
|
549
|
+
inline attributes for the whole note. `undecodedFields` lists stored field
|
|
550
|
+
numbers the decoder deliberately does not interpret.
|
|
551
|
+
|
|
552
|
+
**Requires:** Full Disk Access. Password-protected notes are refused.
|
|
553
|
+
|
|
554
|
+
| Parameter | Type | Required | Description |
|
|
555
|
+
|-----------|------|----------|-------------|
|
|
556
|
+
| `id` | string | Yes | Exact note ID |
|
|
557
|
+
| `offset` | number | No | First block to return (default 0). Use `page.nextOffset` |
|
|
558
|
+
| `limit` | number | No | Maximum blocks per page (default 500, max 5000) |
|
|
559
|
+
|
|
560
|
+
A page also stops early to stay under `APPLE_NOTES_MCP_BLOCKS_MAX_BYTES`
|
|
561
|
+
(default 4 MB). `paragraphUuid` is not unique: Notes copies it when a paragraph
|
|
562
|
+
is split. Link URLs are returned as stored, and `linkSafe` is false for schemes
|
|
563
|
+
other than http(s), `notes:`, `applenotes:` and `mailto:`. Errors carry a
|
|
564
|
+
stable code in brackets, such as `[encrypted]` or `[no-full-disk-access]`.
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
516
568
|
#### `list-native-tags`
|
|
517
569
|
|
|
518
570
|
Lists actual native Notes tags used within one explicit `account` and `folder`,
|
|
@@ -1610,6 +1662,7 @@ All configuration is optional — the server works out of the box. Override beha
|
|
|
1610
1662
|
| `APPLE_NOTES_MCP_CONFIG_FILE` | `~/Library/Application Support/apple-notes-mcp/config.json` | Path to the JSON config file (see below). |
|
|
1611
1663
|
| `APPLE_NOTES_MCP_TIMEOUT_MS` | `30000` (30 s) | Total AppleScript operation timeout, including retry attempts and delays. Raise it if full-library operations (large searches, exports) time out on a big Notes library. Per-call `timeoutMs` options still win. |
|
|
1612
1664
|
| `APPLE_NOTES_MCP_EXPORT_MAX_BYTES` | `8388608` (8 MB) | Largest response `export-notes-json` sends; a page closes early to stay under it. The default sits below the 10 MB per-message limit of MCP SDK stdio clients, which drop the connection on anything larger. Raise it only if your MCP client accepts bigger messages. |
|
|
1665
|
+
| `APPLE_NOTES_MCP_BLOCKS_MAX_BYTES` | `4194304` (4 MB) | Largest block payload one [`get-note-blocks`](#get-note-blocks) page returns; the page closes early to stay under it, and a single oversized paragraph comes back with `textOmitted: true`. |
|
|
1613
1666
|
| `APPLE_NOTES_MCP_MAX_RETRIES` | `2` | Maximum attempts for a read-only AppleScript call that fails with a **transient** error (Notes.app busy / not responding / lost connection). `2` means one retry; set `1` to fail fast with no retries. Retries share the single `APPLE_NOTES_MCP_TIMEOUT_MS` budget rather than each getting a fresh one, and a retry is skipped when under a second of that budget remains — so this is a ceiling, not a guarantee. In particular a call that exhausts the budget with a **timeout** has no time left to retry by construction. Mutating operations run once because a timeout can occur after Notes.app applied the change. Non-transient errors (e.g. "note not found") never retry. |
|
|
1614
1667
|
| `APPLE_NOTES_MCP_RETRY_DELAY_MS` | `1000` (1 s) | Base delay before the first retry; subsequent retries back off exponentially (1s, 2s, 4s, ...). |
|
|
1615
1668
|
| `DEBUG` / `VERBOSE` | unset | Set either to enable verbose diagnostic logging to stderr. |
|
|
@@ -1638,7 +1691,7 @@ MCP stores no secrets, but as a general rule keep only non-secret config here.
|
|
|
1638
1691
|
|
|
1639
1692
|
## Full Disk Access
|
|
1640
1693
|
|
|
1641
|
-
Several tools read directly from the Apple Notes SQLite database, which lives in a macOS-protected directory. Those tools require **Full Disk Access** for the process running the MCP server: `get-checklist-state`, `get-note-metadata`, `get-note-link`, the checklist annotations in `get-note-markdown`, and the database half of `get-sync-status`.
|
|
1694
|
+
Several tools read directly from the Apple Notes SQLite database, which lives in a macOS-protected directory. Those tools require **Full Disk Access** for the process running the MCP server: `get-checklist-state`, `get-note-metadata`, `get-note-blocks`, `get-note-link`, the checklist annotations in `get-note-markdown`, and the database half of `get-sync-status`.
|
|
1642
1695
|
|
|
1643
1696
|
> 📘 **For the full why-and-how walkthrough (which app to grant, verifying with `doctor`, graceful degradation), see the [Full Disk Access Setup Guide](https://github.com/sweetrb/apple-notes-mcp/blob/main/docs/FULL-DISK-ACCESS.md).** The summary below is the quick version.
|
|
1644
1697
|
|
package/build/index.js
CHANGED
|
@@ -3358,9 +3358,9 @@ var require_utils = __commonJS({
|
|
|
3358
3358
|
let output = "";
|
|
3359
3359
|
for (let i = 0; i < input.length; i++) {
|
|
3360
3360
|
if (input[i] === "%" && i + 2 < input.length) {
|
|
3361
|
-
const
|
|
3362
|
-
if (isHexPair(
|
|
3363
|
-
const normalizedHex =
|
|
3361
|
+
const hex3 = input.slice(i + 1, i + 3);
|
|
3362
|
+
if (isHexPair(hex3)) {
|
|
3363
|
+
const normalizedHex = hex3.toUpperCase();
|
|
3364
3364
|
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3365
3365
|
if (decodeUnreserved && isUnreserved(decoded)) {
|
|
3366
3366
|
output += decoded;
|
|
@@ -3380,9 +3380,9 @@ var require_utils = __commonJS({
|
|
|
3380
3380
|
for (let i = 0; i < input.length; i++) {
|
|
3381
3381
|
const ch = input[i];
|
|
3382
3382
|
if (ch === "%" && i + 2 < input.length) {
|
|
3383
|
-
const
|
|
3384
|
-
if (isHexPair(
|
|
3385
|
-
const normalizedHex =
|
|
3383
|
+
const hex3 = input.slice(i + 1, i + 3);
|
|
3384
|
+
if (isHexPair(hex3)) {
|
|
3385
|
+
const normalizedHex = hex3.toUpperCase();
|
|
3386
3386
|
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3387
3387
|
if (decoded !== "." && isUnreserved(decoded)) {
|
|
3388
3388
|
output += decoded;
|
|
@@ -3422,9 +3422,9 @@ var require_utils = __commonJS({
|
|
|
3422
3422
|
for (let i = 0; i < input.length; i++) {
|
|
3423
3423
|
const ch = input[i];
|
|
3424
3424
|
if (ch === "%" && i + 2 < input.length) {
|
|
3425
|
-
const
|
|
3426
|
-
if (isHexPair(
|
|
3427
|
-
output += "%" +
|
|
3425
|
+
const hex3 = input.slice(i + 1, i + 3);
|
|
3426
|
+
if (isHexPair(hex3)) {
|
|
3427
|
+
output += "%" + hex3.toUpperCase();
|
|
3428
3428
|
i += 2;
|
|
3429
3429
|
continue;
|
|
3430
3430
|
}
|
|
@@ -3460,9 +3460,9 @@ var require_utils = __commonJS({
|
|
|
3460
3460
|
for (let i = 0; i < input.length; i++) {
|
|
3461
3461
|
const ch = input[i];
|
|
3462
3462
|
if (ch === "%" && i + 2 < input.length) {
|
|
3463
|
-
const
|
|
3464
|
-
if (isHexPair(
|
|
3465
|
-
output += "%" +
|
|
3463
|
+
const hex3 = input.slice(i + 1, i + 3);
|
|
3464
|
+
if (isHexPair(hex3)) {
|
|
3465
|
+
output += "%" + hex3.toUpperCase();
|
|
3466
3466
|
i += 2;
|
|
3467
3467
|
continue;
|
|
3468
3468
|
}
|
|
@@ -3507,9 +3507,9 @@ var require_utils = __commonJS({
|
|
|
3507
3507
|
for (let i = 0; i < input.length; i++) {
|
|
3508
3508
|
const ch = input[i];
|
|
3509
3509
|
if (ch === "%" && i + 2 < input.length) {
|
|
3510
|
-
const
|
|
3511
|
-
if (isHexPair(
|
|
3512
|
-
const normalizedHex =
|
|
3510
|
+
const hex3 = input.slice(i + 1, i + 3);
|
|
3511
|
+
if (isHexPair(hex3)) {
|
|
3512
|
+
const normalizedHex = hex3.toUpperCase();
|
|
3513
3513
|
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3514
3514
|
if (isUnreserved(decoded)) {
|
|
3515
3515
|
output += decoded;
|
|
@@ -3547,9 +3547,9 @@ var require_utils = __commonJS({
|
|
|
3547
3547
|
let output = "";
|
|
3548
3548
|
for (let i = 0; i < input.length; i++) {
|
|
3549
3549
|
if (input[i] === "%" && i + 2 < input.length) {
|
|
3550
|
-
const
|
|
3551
|
-
if (isHexPair(
|
|
3552
|
-
output += "%" +
|
|
3550
|
+
const hex3 = input.slice(i + 1, i + 3);
|
|
3551
|
+
if (isHexPair(hex3)) {
|
|
3552
|
+
output += "%" + hex3.toUpperCase();
|
|
3553
3553
|
i += 2;
|
|
3554
3554
|
continue;
|
|
3555
3555
|
}
|
|
@@ -8768,25 +8768,25 @@ var require_ContainerNode = __commonJS({
|
|
|
8768
8768
|
return this._firstChild;
|
|
8769
8769
|
} },
|
|
8770
8770
|
lastChild: { get: function() {
|
|
8771
|
-
var kids = this._childNodes,
|
|
8771
|
+
var kids = this._childNodes, first2;
|
|
8772
8772
|
if (kids) {
|
|
8773
8773
|
return kids.length === 0 ? null : kids[kids.length - 1];
|
|
8774
8774
|
}
|
|
8775
|
-
|
|
8776
|
-
if (
|
|
8775
|
+
first2 = this._firstChild;
|
|
8776
|
+
if (first2 === null) {
|
|
8777
8777
|
return null;
|
|
8778
8778
|
}
|
|
8779
|
-
return
|
|
8779
|
+
return first2._previousSibling;
|
|
8780
8780
|
} },
|
|
8781
8781
|
_ensureChildNodes: { value: function() {
|
|
8782
8782
|
if (this._childNodes) {
|
|
8783
8783
|
return;
|
|
8784
8784
|
}
|
|
8785
|
-
var
|
|
8786
|
-
if (
|
|
8785
|
+
var first2 = this._firstChild, kid = first2, childNodes = this._childNodes = new NodeList();
|
|
8786
|
+
if (first2) do {
|
|
8787
8787
|
childNodes.push(kid);
|
|
8788
8788
|
kid = kid._nextSibling;
|
|
8789
|
-
} while (kid !==
|
|
8789
|
+
} while (kid !== first2);
|
|
8790
8790
|
this._firstChild = null;
|
|
8791
8791
|
} },
|
|
8792
8792
|
// Remove all of this node's children. This is a minor
|
|
@@ -10257,22 +10257,22 @@ var require_Element = __commonJS({
|
|
|
10257
10257
|
}
|
|
10258
10258
|
},
|
|
10259
10259
|
_insertAdjacent: { value: function _insertAdjacent(position, node) {
|
|
10260
|
-
var
|
|
10260
|
+
var first2 = false;
|
|
10261
10261
|
switch (position) {
|
|
10262
10262
|
case "beforebegin":
|
|
10263
|
-
|
|
10263
|
+
first2 = true;
|
|
10264
10264
|
/* falls through */
|
|
10265
10265
|
case "afterend":
|
|
10266
10266
|
var parent = this.parentNode;
|
|
10267
10267
|
if (parent === null) {
|
|
10268
10268
|
return null;
|
|
10269
10269
|
}
|
|
10270
|
-
return parent.insertBefore(node,
|
|
10270
|
+
return parent.insertBefore(node, first2 ? this : this.nextSibling);
|
|
10271
10271
|
case "afterbegin":
|
|
10272
|
-
|
|
10272
|
+
first2 = true;
|
|
10273
10273
|
/* falls through */
|
|
10274
10274
|
case "beforeend":
|
|
10275
|
-
return this.insertBefore(node,
|
|
10275
|
+
return this.insertBefore(node, first2 ? this.firstChild : null);
|
|
10276
10276
|
default:
|
|
10277
10277
|
return utils.SyntaxError();
|
|
10278
10278
|
}
|
|
@@ -24465,14 +24465,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
24465
24465
|
} else if (node.nodeType === 1) {
|
|
24466
24466
|
replacement = replacementForNode.call(self, node);
|
|
24467
24467
|
}
|
|
24468
|
-
return
|
|
24468
|
+
return join15(output, replacement);
|
|
24469
24469
|
}, "");
|
|
24470
24470
|
}
|
|
24471
24471
|
function postProcess(output) {
|
|
24472
24472
|
var self = this;
|
|
24473
24473
|
this.rules.forEach(function(rule) {
|
|
24474
24474
|
if (typeof rule.append === "function") {
|
|
24475
|
-
output =
|
|
24475
|
+
output = join15(output, rule.append(self.options));
|
|
24476
24476
|
}
|
|
24477
24477
|
});
|
|
24478
24478
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -24484,7 +24484,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
24484
24484
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
24485
24485
|
return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
|
|
24486
24486
|
}
|
|
24487
|
-
function
|
|
24487
|
+
function join15(output, replacement) {
|
|
24488
24488
|
var s1 = trimTrailingNewlines(output);
|
|
24489
24489
|
var s2 = trimLeadingNewlines(replacement);
|
|
24490
24490
|
var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
|
@@ -24675,9 +24675,9 @@ var util;
|
|
|
24675
24675
|
})(util || (util = {}));
|
|
24676
24676
|
var objectUtil;
|
|
24677
24677
|
(function(objectUtil2) {
|
|
24678
|
-
objectUtil2.mergeShapes = (
|
|
24678
|
+
objectUtil2.mergeShapes = (first2, second) => {
|
|
24679
24679
|
return {
|
|
24680
|
-
...
|
|
24680
|
+
...first2,
|
|
24681
24681
|
...second
|
|
24682
24682
|
// second overwrites first
|
|
24683
24683
|
};
|
|
@@ -24777,8 +24777,8 @@ var ZodError = class _ZodError extends Error {
|
|
|
24777
24777
|
constructor(issues) {
|
|
24778
24778
|
super();
|
|
24779
24779
|
this.issues = [];
|
|
24780
|
-
this.addIssue = (
|
|
24781
|
-
this.issues = [...this.issues,
|
|
24780
|
+
this.addIssue = (sub3) => {
|
|
24781
|
+
this.issues = [...this.issues, sub3];
|
|
24782
24782
|
};
|
|
24783
24783
|
this.addIssues = (subs = []) => {
|
|
24784
24784
|
this.issues = [...this.issues, ...subs];
|
|
@@ -24845,13 +24845,13 @@ var ZodError = class _ZodError extends Error {
|
|
|
24845
24845
|
flatten(mapper = (issue2) => issue2.message) {
|
|
24846
24846
|
const fieldErrors = {};
|
|
24847
24847
|
const formErrors = [];
|
|
24848
|
-
for (const
|
|
24849
|
-
if (
|
|
24850
|
-
const firstEl =
|
|
24848
|
+
for (const sub3 of this.issues) {
|
|
24849
|
+
if (sub3.path.length > 0) {
|
|
24850
|
+
const firstEl = sub3.path[0];
|
|
24851
24851
|
fieldErrors[firstEl] = fieldErrors[firstEl] || [];
|
|
24852
|
-
fieldErrors[firstEl].push(mapper(
|
|
24852
|
+
fieldErrors[firstEl].push(mapper(sub3));
|
|
24853
24853
|
} else {
|
|
24854
|
-
formErrors.push(mapper(
|
|
24854
|
+
formErrors.push(mapper(sub3));
|
|
24855
24855
|
}
|
|
24856
24856
|
}
|
|
24857
24857
|
return { formErrors, fieldErrors };
|
|
@@ -27599,10 +27599,10 @@ var ZodRecord = class _ZodRecord extends ZodType {
|
|
|
27599
27599
|
get element() {
|
|
27600
27600
|
return this._def.valueType;
|
|
27601
27601
|
}
|
|
27602
|
-
static create(
|
|
27602
|
+
static create(first2, second, third) {
|
|
27603
27603
|
if (second instanceof ZodType) {
|
|
27604
27604
|
return new _ZodRecord({
|
|
27605
|
-
keyType:
|
|
27605
|
+
keyType: first2,
|
|
27606
27606
|
valueType: second,
|
|
27607
27607
|
typeName: ZodFirstPartyTypeKind.ZodRecord,
|
|
27608
27608
|
...processCreateParams(third)
|
|
@@ -27610,7 +27610,7 @@ var ZodRecord = class _ZodRecord extends ZodType {
|
|
|
27610
27610
|
}
|
|
27611
27611
|
return new _ZodRecord({
|
|
27612
27612
|
keyType: ZodString.create(),
|
|
27613
|
-
valueType:
|
|
27613
|
+
valueType: first2,
|
|
27614
27614
|
typeName: ZodFirstPartyTypeKind.ZodRecord,
|
|
27615
27615
|
...processCreateParams(second)
|
|
27616
27616
|
});
|
|
@@ -29150,12 +29150,12 @@ var $ZodRealError = $constructor("$ZodError", initializer, { Parent: Error });
|
|
|
29150
29150
|
function flattenError(error2, mapper = (issue2) => issue2.message) {
|
|
29151
29151
|
const fieldErrors = {};
|
|
29152
29152
|
const formErrors = [];
|
|
29153
|
-
for (const
|
|
29154
|
-
if (
|
|
29155
|
-
fieldErrors[
|
|
29156
|
-
fieldErrors[
|
|
29153
|
+
for (const sub3 of error2.issues) {
|
|
29154
|
+
if (sub3.path.length > 0) {
|
|
29155
|
+
fieldErrors[sub3.path[0]] = fieldErrors[sub3.path[0]] || [];
|
|
29156
|
+
fieldErrors[sub3.path[0]].push(mapper(sub3));
|
|
29157
29157
|
} else {
|
|
29158
|
-
formErrors.push(mapper(
|
|
29158
|
+
formErrors.push(mapper(sub3));
|
|
29159
29159
|
}
|
|
29160
29160
|
}
|
|
29161
29161
|
return { formErrors, fieldErrors };
|
|
@@ -39330,6 +39330,68 @@ function embeddedMessage(field) {
|
|
|
39330
39330
|
if (!bytes) return void 0;
|
|
39331
39331
|
return decodeMessage(bytes);
|
|
39332
39332
|
}
|
|
39333
|
+
var ProtobufDecodeError = class extends Error {
|
|
39334
|
+
constructor(message) {
|
|
39335
|
+
super(message);
|
|
39336
|
+
this.name = "ProtobufDecodeError";
|
|
39337
|
+
}
|
|
39338
|
+
};
|
|
39339
|
+
function decodeVarint64(buf, offset) {
|
|
39340
|
+
let result = 0n;
|
|
39341
|
+
let shift = 0n;
|
|
39342
|
+
let pos = offset;
|
|
39343
|
+
while (pos < buf.length) {
|
|
39344
|
+
const byte = buf[pos++];
|
|
39345
|
+
result |= BigInt(byte & 127) << shift;
|
|
39346
|
+
if ((byte & 128) === 0) return [BigInt.asUintN(64, result), pos];
|
|
39347
|
+
shift += 7n;
|
|
39348
|
+
if (shift >= 70n) throw new ProtobufDecodeError(`Varint too long at offset ${offset}`);
|
|
39349
|
+
}
|
|
39350
|
+
throw new ProtobufDecodeError(`Unexpected end of buffer reading varint at offset ${offset}`);
|
|
39351
|
+
}
|
|
39352
|
+
function decodeWireFields(buf) {
|
|
39353
|
+
const fields = [];
|
|
39354
|
+
let offset = 0;
|
|
39355
|
+
while (offset < buf.length) {
|
|
39356
|
+
const [tag, afterTag] = decodeVarint64(buf, offset);
|
|
39357
|
+
offset = afterTag;
|
|
39358
|
+
const fieldNumber = Number(tag >> 3n);
|
|
39359
|
+
const wireType = Number(tag & 7n);
|
|
39360
|
+
if (fieldNumber === 0 || fieldNumber > 536870911)
|
|
39361
|
+
throw new ProtobufDecodeError("Invalid field number");
|
|
39362
|
+
if (wireType === 0) {
|
|
39363
|
+
const [value, next] = decodeVarint64(buf, offset);
|
|
39364
|
+
offset = next;
|
|
39365
|
+
fields.push({ fieldNumber, wireType, varint: value });
|
|
39366
|
+
continue;
|
|
39367
|
+
}
|
|
39368
|
+
let length;
|
|
39369
|
+
if (wireType === 1) length = 8;
|
|
39370
|
+
else if (wireType === 5) length = 4;
|
|
39371
|
+
else if (wireType === 2) {
|
|
39372
|
+
const [value, next] = decodeVarint64(buf, offset);
|
|
39373
|
+
if (value > BigInt(buf.length)) throw new ProtobufDecodeError("Truncated field");
|
|
39374
|
+
length = Number(value);
|
|
39375
|
+
offset = next;
|
|
39376
|
+
} else throw new ProtobufDecodeError(`Unsupported wire type ${wireType}`);
|
|
39377
|
+
if (offset + length > buf.length) throw new ProtobufDecodeError("Truncated field");
|
|
39378
|
+
fields.push({
|
|
39379
|
+
fieldNumber,
|
|
39380
|
+
wireType,
|
|
39381
|
+
bytes: buf.subarray(offset, offset + length)
|
|
39382
|
+
});
|
|
39383
|
+
offset += length;
|
|
39384
|
+
}
|
|
39385
|
+
return fields;
|
|
39386
|
+
}
|
|
39387
|
+
function signedVarint(value) {
|
|
39388
|
+
return Number(BigInt.asIntN(64, value));
|
|
39389
|
+
}
|
|
39390
|
+
function fixed32Float(field) {
|
|
39391
|
+
if (!field || field.wireType !== 5 || !field.bytes || field.bytes.length !== 4) return void 0;
|
|
39392
|
+
const bytes = field.bytes;
|
|
39393
|
+
return new DataView(bytes.buffer, bytes.byteOffset, 4).getFloat32(0, true);
|
|
39394
|
+
}
|
|
39333
39395
|
|
|
39334
39396
|
// src/utils/checklistRuns.ts
|
|
39335
39397
|
function checklistRunLineStart(text, position, length) {
|
|
@@ -39373,9 +39435,9 @@ function queryNoteData(noteId3) {
|
|
|
39373
39435
|
timeout: 5e3,
|
|
39374
39436
|
stdio: ["pipe", "pipe", "pipe"]
|
|
39375
39437
|
});
|
|
39376
|
-
const
|
|
39377
|
-
if (!
|
|
39378
|
-
return { hex:
|
|
39438
|
+
const hex3 = result.trim();
|
|
39439
|
+
if (!hex3) return { hex: null };
|
|
39440
|
+
return { hex: hex3 };
|
|
39379
39441
|
} catch (error2) {
|
|
39380
39442
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
39381
39443
|
console.error(`Failed to query NoteStore database: ${message}`);
|
|
@@ -39385,10 +39447,10 @@ function queryNoteData(noteId3) {
|
|
|
39385
39447
|
return { hex: null };
|
|
39386
39448
|
}
|
|
39387
39449
|
}
|
|
39388
|
-
function hexToBytes(
|
|
39389
|
-
const bytes = new Uint8Array(
|
|
39390
|
-
for (let i = 0; i <
|
|
39391
|
-
bytes[i / 2] = parseInt(
|
|
39450
|
+
function hexToBytes(hex3) {
|
|
39451
|
+
const bytes = new Uint8Array(hex3.length / 2);
|
|
39452
|
+
for (let i = 0; i < hex3.length; i += 2) {
|
|
39453
|
+
bytes[i / 2] = parseInt(hex3.substring(i, i + 2), 16);
|
|
39392
39454
|
}
|
|
39393
39455
|
return bytes;
|
|
39394
39456
|
}
|
|
@@ -39839,6 +39901,185 @@ function assertLinkedWrite(rich, content, format, allowLinkChanges = false) {
|
|
|
39839
39901
|
}
|
|
39840
39902
|
}
|
|
39841
39903
|
|
|
39904
|
+
// src/utils/noteTables.ts
|
|
39905
|
+
import { gunzipSync as gunzipSync3 } from "node:zlib";
|
|
39906
|
+
var sub = (f, n) => {
|
|
39907
|
+
const value = embeddedMessage(getField(f, n));
|
|
39908
|
+
if (!value) throw new Error(`Missing table field ${n}`);
|
|
39909
|
+
return value;
|
|
39910
|
+
};
|
|
39911
|
+
var num = (f, n) => {
|
|
39912
|
+
const v = varintValue(getField(f, n));
|
|
39913
|
+
if (v === void 0) throw new Error(`Missing table index ${n}`);
|
|
39914
|
+
return v;
|
|
39915
|
+
};
|
|
39916
|
+
var many = (f, n) => getFields(f, n).map((v) => {
|
|
39917
|
+
const m = embeddedMessage(v);
|
|
39918
|
+
if (!m) throw new Error("Invalid table entry");
|
|
39919
|
+
return m;
|
|
39920
|
+
});
|
|
39921
|
+
var hex = (f) => {
|
|
39922
|
+
if (!(f?.value instanceof Uint8Array)) throw new Error("Missing table UUID");
|
|
39923
|
+
return Buffer.from(f.value).toString("hex");
|
|
39924
|
+
};
|
|
39925
|
+
function parseNoteTable(compressed) {
|
|
39926
|
+
const table = decodeTable(compressed, true);
|
|
39927
|
+
return { rows: table.rows, rowIds: table.rowIds, columnIds: table.columnIds };
|
|
39928
|
+
}
|
|
39929
|
+
function parseNoteTableCells(compressed) {
|
|
39930
|
+
return decodeTable(compressed, false);
|
|
39931
|
+
}
|
|
39932
|
+
function decodeTable(compressed, strict) {
|
|
39933
|
+
const root = decodeMessage(gunzipSync3(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
|
|
39934
|
+
const data = sub(sub(root, 2), 3), entries = many(data, 3);
|
|
39935
|
+
if (entries.length > 1e5) throw new Error("Table too large");
|
|
39936
|
+
const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
|
|
39937
|
+
const entry = (index) => {
|
|
39938
|
+
if (!entries[index]) throw new Error("Invalid table reference");
|
|
39939
|
+
return entries[index];
|
|
39940
|
+
};
|
|
39941
|
+
const uuidIndex = (index) => num(sub(many(sub(entry(index), 13), 3)[0], 2), 2);
|
|
39942
|
+
const roots = entries.filter((e) => {
|
|
39943
|
+
const map = embeddedMessage(getField(e, 13));
|
|
39944
|
+
return map && types[num(map, 1)] === "com.apple.notes.ICTable";
|
|
39945
|
+
});
|
|
39946
|
+
if (roots.length !== 1) throw new Error("Ambiguous native table root");
|
|
39947
|
+
const refs = new Map(
|
|
39948
|
+
many(sub(roots[0], 13), 3).filter((m) => ["crRows", "crColumns", "cellColumns"].includes(keys[num(m, 1)] || "")).map((m) => [keys[num(m, 1)], num(sub(m, 2), 6)])
|
|
39949
|
+
);
|
|
39950
|
+
const ordered = (key) => {
|
|
39951
|
+
const ref = refs.get(key);
|
|
39952
|
+
if (ref === void 0) throw new Error("Missing table dimension");
|
|
39953
|
+
const ordering = sub(sub(entry(ref), 16), 1), array2 = sub(ordering, 1);
|
|
39954
|
+
const ids = many(array2, 2).map((a) => hex(getField(a, 2)));
|
|
39955
|
+
const map = /* @__PURE__ */ new Map();
|
|
39956
|
+
ids.forEach((id2, i) => {
|
|
39957
|
+
const index = uuids.indexOf(id2);
|
|
39958
|
+
if (index < 0) throw new Error("Missing dimension UUID");
|
|
39959
|
+
map.set(index, i);
|
|
39960
|
+
});
|
|
39961
|
+
const aliases = many(sub(ordering, 2), 1).map((pair) => [
|
|
39962
|
+
uuidIndex(num(sub(pair, 1), 6)),
|
|
39963
|
+
uuidIndex(num(sub(pair, 2), 6))
|
|
39964
|
+
]);
|
|
39965
|
+
for (let pass = 0; pass < aliases.length + 1; pass++) {
|
|
39966
|
+
let changed = false;
|
|
39967
|
+
for (const [key2, value] of aliases)
|
|
39968
|
+
if (map.has(key2) && !map.has(value)) {
|
|
39969
|
+
map.set(value, map.get(key2));
|
|
39970
|
+
changed = true;
|
|
39971
|
+
}
|
|
39972
|
+
if (!changed) break;
|
|
39973
|
+
}
|
|
39974
|
+
return { ids, map };
|
|
39975
|
+
};
|
|
39976
|
+
const rows = ordered("crRows"), columns = ordered("crColumns");
|
|
39977
|
+
if (!rows.ids.length || !columns.ids.length || rows.ids.length * columns.ids.length > 1e5)
|
|
39978
|
+
throw new Error("Unsupported table size");
|
|
39979
|
+
const values = rows.ids.map(() => columns.ids.map(() => ""));
|
|
39980
|
+
const incomplete = [];
|
|
39981
|
+
const cellRef = refs.get("cellColumns");
|
|
39982
|
+
if (cellRef === void 0) throw new Error("Missing table cells");
|
|
39983
|
+
for (const column of many(sub(entry(cellRef), 6), 1)) {
|
|
39984
|
+
const ci = columns.map.get(uuidIndex(num(sub(column, 1), 6)));
|
|
39985
|
+
const cells = entry(num(sub(column, 2), 6));
|
|
39986
|
+
for (const row of many(sub(cells, 6), 1)) {
|
|
39987
|
+
const ri = rows.map.get(uuidIndex(num(sub(row, 1), 6)));
|
|
39988
|
+
if (ri === void 0 || ci === void 0) continue;
|
|
39989
|
+
const note = sub(entry(num(sub(row, 2), 6)), 10);
|
|
39990
|
+
const text = stringValue(getField(note, 2));
|
|
39991
|
+
if (text === void 0 || text.includes("\uFFFC")) {
|
|
39992
|
+
if (strict) throw new Error("Embedded or unsupported table cell");
|
|
39993
|
+
values[ri][ci] = null;
|
|
39994
|
+
incomplete.push({
|
|
39995
|
+
ri,
|
|
39996
|
+
ci,
|
|
39997
|
+
reason: text === void 0 ? "Cell text is missing" : "Cell contains an embedded object"
|
|
39998
|
+
});
|
|
39999
|
+
continue;
|
|
40000
|
+
}
|
|
40001
|
+
values[ri][ci] = text.replace(/\n$/u, "");
|
|
40002
|
+
}
|
|
40003
|
+
}
|
|
40004
|
+
const rtl = entries.some((e) => {
|
|
40005
|
+
const map = embeddedMessage(getField(e, 13));
|
|
40006
|
+
return map && many(map, 3).some(
|
|
40007
|
+
(m) => stringValue(getField(sub(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
|
|
40008
|
+
);
|
|
40009
|
+
});
|
|
40010
|
+
const width = columns.ids.length;
|
|
40011
|
+
if (rtl) {
|
|
40012
|
+
for (const row of values) row.reverse();
|
|
40013
|
+
columns.ids.reverse();
|
|
40014
|
+
}
|
|
40015
|
+
const incompleteCells = incomplete.map(({ ri, ci, reason }) => ({ row: ri, column: rtl ? width - 1 - ci : ci, reason })).sort((a, b) => a.row - b.row || a.column - b.column);
|
|
40016
|
+
return { rows: values, rowIds: rows.ids, columnIds: columns.ids, incompleteCells };
|
|
40017
|
+
}
|
|
40018
|
+
|
|
40019
|
+
// src/utils/tableMarkdown.ts
|
|
40020
|
+
var UNDECODED_CELL_MARKER = "[undecoded cell]";
|
|
40021
|
+
function escapeMarkdownTableCell(text) {
|
|
40022
|
+
return text.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r\n|[\r\n\u2028\u2029]/g, "<br>");
|
|
40023
|
+
}
|
|
40024
|
+
function renderMarkdownTable(rows) {
|
|
40025
|
+
const width = rows.reduce((max, row) => Math.max(max, row.length), 0);
|
|
40026
|
+
if (rows.length === 0 || width === 0) return "";
|
|
40027
|
+
const line = (row) => "| " + Array.from({ length: width }, (_, i) => {
|
|
40028
|
+
const cell = row[i];
|
|
40029
|
+
return cell === null ? UNDECODED_CELL_MARKER : escapeMarkdownTableCell(cell ?? "");
|
|
40030
|
+
}).join(" | ") + " |";
|
|
40031
|
+
const [header, ...body] = rows;
|
|
40032
|
+
return [line(header), "| " + Array(width).fill("---").join(" | ") + " |", ...body.map(line)].join(
|
|
40033
|
+
"\n"
|
|
40034
|
+
);
|
|
40035
|
+
}
|
|
40036
|
+
function collectNoteTables(rich, noteId3) {
|
|
40037
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40038
|
+
const ordered = (rich.objects || []).filter((object3) => object3.type.includes("table")).sort((a, b) => a.start - b.start).filter((object3) => {
|
|
40039
|
+
if (seen.has(object3.id)) return false;
|
|
40040
|
+
seen.add(object3.id);
|
|
40041
|
+
return true;
|
|
40042
|
+
});
|
|
40043
|
+
const tables = ordered.map((object3, i) => {
|
|
40044
|
+
const index = i + 1;
|
|
40045
|
+
const data = (rich.objectData || []).find((row) => row.id === object3.id);
|
|
40046
|
+
if (!data || !data.mergeable) {
|
|
40047
|
+
return { index, id: object3.id, complete: false, reason: "Native table data is unavailable" };
|
|
40048
|
+
}
|
|
40049
|
+
const attachmentId = noteId3.replace(/ICNote\/p\d+$/, `ICAttachment/p${data.pk}`);
|
|
40050
|
+
try {
|
|
40051
|
+
const table = parseNoteTableCells(Buffer.from(data.mergeable, "hex"));
|
|
40052
|
+
const complete = table.incompleteCells.length === 0;
|
|
40053
|
+
return {
|
|
40054
|
+
index,
|
|
40055
|
+
id: object3.id,
|
|
40056
|
+
attachmentId,
|
|
40057
|
+
complete,
|
|
40058
|
+
...complete ? {} : { reason: `${table.incompleteCells.length} cell(s) could not be decoded` },
|
|
40059
|
+
rows: table.rows,
|
|
40060
|
+
rowIds: table.rowIds,
|
|
40061
|
+
columnIds: table.columnIds,
|
|
40062
|
+
rowCount: table.rowIds.length,
|
|
40063
|
+
columnCount: table.columnIds.length,
|
|
40064
|
+
incompleteCells: table.incompleteCells,
|
|
40065
|
+
markdown: renderMarkdownTable(table.rows)
|
|
40066
|
+
};
|
|
40067
|
+
} catch (error2) {
|
|
40068
|
+
return {
|
|
40069
|
+
index,
|
|
40070
|
+
id: object3.id,
|
|
40071
|
+
attachmentId,
|
|
40072
|
+
complete: false,
|
|
40073
|
+
reason: error2 instanceof Error ? error2.message : String(error2)
|
|
40074
|
+
};
|
|
40075
|
+
}
|
|
40076
|
+
});
|
|
40077
|
+
const markdown = tables.map(
|
|
40078
|
+
(table) => table.markdown !== void 0 ? table.markdown : `[table ${table.index} could not be decoded: ${table.reason}]`
|
|
40079
|
+
).join("\n\n");
|
|
40080
|
+
return { tables, tableCellsComplete: tables.every((table) => table.complete), markdown };
|
|
40081
|
+
}
|
|
40082
|
+
|
|
39842
40083
|
// src/utils/smartFolders.ts
|
|
39843
40084
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
39844
40085
|
import * as fs2 from "fs";
|
|
@@ -41032,6 +41273,21 @@ var AppleNotesManager = class {
|
|
|
41032
41273
|
passwordProtected: parsed.passwordProtected
|
|
41033
41274
|
};
|
|
41034
41275
|
}
|
|
41276
|
+
/**
|
|
41277
|
+
* Reads every native table in one note, in body order, as JSON rows and
|
|
41278
|
+
* GitHub-flavored Markdown.
|
|
41279
|
+
*
|
|
41280
|
+
* Reads the NoteStore database read-only (Full Disk Access required); no
|
|
41281
|
+
* AppleScript is involved. Cells that cannot be decoded are returned as null
|
|
41282
|
+
* and flagged, never guessed.
|
|
41283
|
+
*
|
|
41284
|
+
* @param id - CoreData URL identifier for the note
|
|
41285
|
+
* @returns Tables in body order with a combined Markdown rendering
|
|
41286
|
+
* @throws Error when the note's rich data cannot be read (e.g. no Full Disk Access)
|
|
41287
|
+
*/
|
|
41288
|
+
getNoteTablesById(id2) {
|
|
41289
|
+
return collectNoteTables(readRichNote(id2), id2);
|
|
41290
|
+
}
|
|
41035
41291
|
/**
|
|
41036
41292
|
* Retrieves detailed metadata for a note by title.
|
|
41037
41293
|
*
|
|
@@ -43361,7 +43617,7 @@ function decodeHtmlEntities(text) {
|
|
|
43361
43617
|
}
|
|
43362
43618
|
return String.fromCodePoint(codePoint);
|
|
43363
43619
|
};
|
|
43364
|
-
return text.replace(/&#x([0-9a-f]+);?/gi, (match,
|
|
43620
|
+
return text.replace(/&#x([0-9a-f]+);?/gi, (match, hex3) => decodeCodePoint(match, hex3, 16)).replace(/&#([0-9]+);?/g, (match, decimal) => decodeCodePoint(match, decimal, 10)).replace(/ (?:;|(?![0-9a-z]))/gi, " ").replace(/"(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/<(?:;|(?![0-9a-z]))/gi, "<").replace(/>(?:;|(?![0-9a-z]))/gi, ">").replace(/&(?:;|(?![0-9a-z]))/gi, "&");
|
|
43365
43621
|
}
|
|
43366
43622
|
function firstVisibleHtmlLine(html) {
|
|
43367
43623
|
let text = html;
|
|
@@ -43856,7 +44112,7 @@ import { execFileSync as execFileSync8 } from "child_process";
|
|
|
43856
44112
|
import * as fs6 from "fs";
|
|
43857
44113
|
import * as os6 from "os";
|
|
43858
44114
|
import * as path6 from "path";
|
|
43859
|
-
import { gunzipSync as
|
|
44115
|
+
import { gunzipSync as gunzipSync4 } from "zlib";
|
|
43860
44116
|
var NOTES_DB_PATH6 = path6.join(
|
|
43861
44117
|
os6.homedir(),
|
|
43862
44118
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
@@ -44118,7 +44374,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44118
44374
|
try {
|
|
44119
44375
|
decoded = decodeNoteBody(
|
|
44120
44376
|
new Uint8Array(
|
|
44121
|
-
|
|
44377
|
+
gunzipSync4(Buffer.from(row.data, "hex"), { maxOutputLength: 32 * 1024 * 1024 })
|
|
44122
44378
|
)
|
|
44123
44379
|
);
|
|
44124
44380
|
} catch {
|
|
@@ -44389,10 +44645,10 @@ var LEGACY_ENTITIES = {
|
|
|
44389
44645
|
function comparableVisibleText(html) {
|
|
44390
44646
|
return html.replace(/<br\s*\/?\s*>/gi, " ").replace(/<[^>]*>/g, (tag) => INLINE_TAG.test(tag) ? "" : " ").replace(
|
|
44391
44647
|
/&(?:(nbsp|quot|lt|gt|amp);?|apos;|#(\d+);|#x([0-9a-f]+);)/gi,
|
|
44392
|
-
(_match, legacy, dec,
|
|
44648
|
+
(_match, legacy, dec, hex3) => {
|
|
44393
44649
|
if (legacy) return LEGACY_ENTITIES[legacy.toLowerCase()];
|
|
44394
44650
|
if (dec) return String.fromCodePoint(Number(dec));
|
|
44395
|
-
if (
|
|
44651
|
+
if (hex3) return String.fromCodePoint(Number.parseInt(hex3, 16));
|
|
44396
44652
|
return "'";
|
|
44397
44653
|
}
|
|
44398
44654
|
).replace(/\s+/g, " ").trim();
|
|
@@ -45495,101 +45751,400 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
|
|
|
45495
45751
|
};
|
|
45496
45752
|
}
|
|
45497
45753
|
|
|
45498
|
-
// src/utils/
|
|
45499
|
-
import {
|
|
45500
|
-
|
|
45501
|
-
|
|
45502
|
-
|
|
45503
|
-
|
|
45754
|
+
// src/utils/noteBlocks.ts
|
|
45755
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
45756
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
45757
|
+
import { homedir as homedir11 } from "node:os";
|
|
45758
|
+
import { join as join13 } from "node:path";
|
|
45759
|
+
import { gunzipSync as gunzipSync5 } from "node:zlib";
|
|
45760
|
+
var NoteBlocksError = class extends Error {
|
|
45761
|
+
code;
|
|
45762
|
+
constructor(code, message) {
|
|
45763
|
+
super(message);
|
|
45764
|
+
this.name = "NoteBlocksError";
|
|
45765
|
+
this.code = code;
|
|
45766
|
+
}
|
|
45504
45767
|
};
|
|
45505
|
-
var
|
|
45506
|
-
|
|
45507
|
-
|
|
45508
|
-
|
|
45768
|
+
var STYLE_NAMES = {
|
|
45769
|
+
0: "title",
|
|
45770
|
+
1: "heading",
|
|
45771
|
+
2: "subheading",
|
|
45772
|
+
4: "monospaced",
|
|
45773
|
+
100: "bulleted",
|
|
45774
|
+
101: "dashed",
|
|
45775
|
+
102: "numbered",
|
|
45776
|
+
103: "checklist"
|
|
45509
45777
|
};
|
|
45510
|
-
var
|
|
45511
|
-
|
|
45512
|
-
|
|
45513
|
-
|
|
45514
|
-
|
|
45515
|
-
var hex = (f) => {
|
|
45516
|
-
if (!(f?.value instanceof Uint8Array)) throw new Error("Missing table UUID");
|
|
45517
|
-
return Buffer.from(f.value).toString("hex");
|
|
45778
|
+
var ALIGNMENTS = {
|
|
45779
|
+
0: "left",
|
|
45780
|
+
1: "center",
|
|
45781
|
+
2: "right",
|
|
45782
|
+
3: "justify"
|
|
45518
45783
|
};
|
|
45519
|
-
|
|
45520
|
-
|
|
45521
|
-
|
|
45522
|
-
|
|
45523
|
-
|
|
45524
|
-
|
|
45525
|
-
|
|
45526
|
-
|
|
45784
|
+
var HIGHLIGHTS = {
|
|
45785
|
+
1: "purple",
|
|
45786
|
+
2: "pink",
|
|
45787
|
+
3: "orange",
|
|
45788
|
+
4: "mint",
|
|
45789
|
+
5: "blue"
|
|
45790
|
+
};
|
|
45791
|
+
var KNOWN_RUN_FIELDS = /* @__PURE__ */ new Set([1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14]);
|
|
45792
|
+
var KNOWN_PARAGRAPH_FIELDS = /* @__PURE__ */ new Set([1, 2, 4, 5, 8, 9]);
|
|
45793
|
+
var isSafeLink = (url) => /^(?:https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url) && !Array.from(url).some((char) => char.charCodeAt(0) < 32);
|
|
45794
|
+
var utf8 = new TextDecoder();
|
|
45795
|
+
var first = (fields, n) => fields.find((f) => f.fieldNumber === n);
|
|
45796
|
+
var varintOf = (fields, n) => {
|
|
45797
|
+
const f = first(fields, n);
|
|
45798
|
+
return f?.wireType === 0 && f.varint !== void 0 ? signedVarint(f.varint) : void 0;
|
|
45799
|
+
};
|
|
45800
|
+
var bytesOf = (fields, n) => {
|
|
45801
|
+
const f = first(fields, n);
|
|
45802
|
+
return f?.wireType === 2 ? f.bytes : void 0;
|
|
45803
|
+
};
|
|
45804
|
+
var stringOf = (fields, n) => {
|
|
45805
|
+
const b = bytesOf(fields, n);
|
|
45806
|
+
return b ? utf8.decode(b) : void 0;
|
|
45807
|
+
};
|
|
45808
|
+
var sub2 = (fields, n) => {
|
|
45809
|
+
const b = bytesOf(fields, n);
|
|
45810
|
+
return b ? decodeWireFields(b) : void 0;
|
|
45811
|
+
};
|
|
45812
|
+
var hex2 = (bytes) => Buffer.from(bytes).toString("hex");
|
|
45813
|
+
var uuidString = (bytes) => {
|
|
45814
|
+
const h = hex2(bytes).toUpperCase();
|
|
45815
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
|
|
45816
|
+
};
|
|
45817
|
+
var channel = (value) => Math.max(0, Math.min(255, Math.round((Number.isFinite(value) ? value : 0) * 255))).toString(16).toUpperCase().padStart(2, "0");
|
|
45818
|
+
function colorOf(fields) {
|
|
45819
|
+
const [r, g, b, a] = [1, 2, 3, 4].map((n) => fixed32Float(first(fields, n)));
|
|
45820
|
+
if (r === void 0 && g === void 0 && b === void 0) return void 0;
|
|
45821
|
+
const rgb = `#${channel(r)}${channel(g)}${channel(b)}`;
|
|
45822
|
+
return a === void 0 || a >= 0.999 ? rgb : `${rgb}${channel(a)}`;
|
|
45823
|
+
}
|
|
45824
|
+
var DEFAULT_PARAGRAPH = {
|
|
45825
|
+
styleType: null,
|
|
45826
|
+
alignmentValue: 0,
|
|
45827
|
+
indent: 0,
|
|
45828
|
+
blockQuote: false
|
|
45829
|
+
};
|
|
45830
|
+
function decodeParagraph(fields, tally) {
|
|
45831
|
+
if (!fields) return DEFAULT_PARAGRAPH;
|
|
45832
|
+
for (const n of new Set(fields.map((f) => f.fieldNumber)))
|
|
45833
|
+
if (!KNOWN_PARAGRAPH_FIELDS.has(n)) tally.set(n, (tally.get(n) || 0) + 1);
|
|
45834
|
+
const attrs = {
|
|
45835
|
+
styleType: varintOf(fields, 1) ?? null,
|
|
45836
|
+
alignmentValue: varintOf(fields, 2) ?? 0,
|
|
45837
|
+
indent: Math.max(0, varintOf(fields, 4) ?? 0),
|
|
45838
|
+
blockQuote: (varintOf(fields, 8) ?? 0) !== 0
|
|
45527
45839
|
};
|
|
45528
|
-
|
|
45529
|
-
const
|
|
45530
|
-
|
|
45531
|
-
|
|
45532
|
-
|
|
45533
|
-
|
|
45534
|
-
|
|
45535
|
-
|
|
45536
|
-
|
|
45537
|
-
|
|
45538
|
-
|
|
45539
|
-
|
|
45540
|
-
|
|
45541
|
-
|
|
45542
|
-
|
|
45543
|
-
|
|
45544
|
-
|
|
45545
|
-
|
|
45546
|
-
|
|
45547
|
-
|
|
45548
|
-
|
|
45549
|
-
|
|
45550
|
-
|
|
45551
|
-
|
|
45552
|
-
|
|
45553
|
-
|
|
45554
|
-
|
|
45555
|
-
|
|
45556
|
-
|
|
45557
|
-
|
|
45840
|
+
if (attrs.styleType === -1) attrs.styleType = null;
|
|
45841
|
+
const checklist = sub2(fields, 5);
|
|
45842
|
+
const checklistId = checklist && bytesOf(checklist, 1);
|
|
45843
|
+
if (attrs.styleType === 103 && checklistId)
|
|
45844
|
+
attrs.checklist = { id: hex2(checklistId), done: varintOf(checklist, 2) === 1 };
|
|
45845
|
+
const uuid2 = bytesOf(fields, 9);
|
|
45846
|
+
if (uuid2?.length === 16) attrs.paragraphUuid = uuidString(uuid2);
|
|
45847
|
+
return attrs;
|
|
45848
|
+
}
|
|
45849
|
+
function decodeInline(fields) {
|
|
45850
|
+
const inline = {};
|
|
45851
|
+
const font = sub2(fields, 3);
|
|
45852
|
+
const hints = font ? varintOf(font, 3) ?? 0 : 0;
|
|
45853
|
+
const weight = varintOf(fields, 5) ?? 0;
|
|
45854
|
+
if (weight === 1 || weight === 3 || hints & 1) inline.bold = true;
|
|
45855
|
+
if (weight === 2 || weight === 3 || hints & 2) inline.italic = true;
|
|
45856
|
+
if (varintOf(fields, 6)) inline.underline = true;
|
|
45857
|
+
if (varintOf(fields, 7)) inline.strikethrough = true;
|
|
45858
|
+
const baseline = varintOf(fields, 8) ?? 0;
|
|
45859
|
+
if (baseline > 0) inline.superscript = true;
|
|
45860
|
+
if (baseline < 0) inline.subscript = true;
|
|
45861
|
+
const color = sub2(fields, 10);
|
|
45862
|
+
const colorValue = color && colorOf(color);
|
|
45863
|
+
if (colorValue) inline.color = colorValue;
|
|
45864
|
+
const emphasis = varintOf(fields, 14);
|
|
45865
|
+
if (emphasis !== void 0 && emphasis !== 0) {
|
|
45866
|
+
inline.highlight = HIGHLIGHTS[emphasis] ?? "unknown";
|
|
45867
|
+
if (inline.highlight === "unknown") inline.highlightValue = emphasis;
|
|
45868
|
+
}
|
|
45869
|
+
const link = stringOf(fields, 9);
|
|
45870
|
+
if (link) {
|
|
45871
|
+
inline.link = link;
|
|
45872
|
+
inline.linkSafe = isSafeLink(link);
|
|
45873
|
+
}
|
|
45874
|
+
if (font) {
|
|
45875
|
+
const name = stringOf(font, 1);
|
|
45876
|
+
const size = fixed32Float(first(font, 2));
|
|
45877
|
+
if (name || size !== void 0)
|
|
45878
|
+
inline.font = {
|
|
45879
|
+
...name ? { name } : {},
|
|
45880
|
+
...size !== void 0 && Number.isFinite(size) ? { size } : {}
|
|
45881
|
+
};
|
|
45882
|
+
}
|
|
45883
|
+
const attachment = sub2(fields, 12);
|
|
45884
|
+
const attachmentId = attachment && stringOf(attachment, 1);
|
|
45885
|
+
if (attachmentId)
|
|
45886
|
+
inline.attachment = { id: attachmentId, uti: stringOf(attachment, 2) || "unknown" };
|
|
45887
|
+
return inline;
|
|
45888
|
+
}
|
|
45889
|
+
function wrap(error2) {
|
|
45890
|
+
if (error2 instanceof NoteBlocksError) throw error2;
|
|
45891
|
+
if (error2 instanceof ProtobufDecodeError)
|
|
45892
|
+
throw new NoteBlocksError("malformed-protobuf", error2.message);
|
|
45893
|
+
throw error2;
|
|
45894
|
+
}
|
|
45895
|
+
function decodeNoteBlocks(data) {
|
|
45896
|
+
let note;
|
|
45897
|
+
try {
|
|
45898
|
+
const document = sub2(decodeWireFields(data), 2);
|
|
45899
|
+
note = document && sub2(document, 3);
|
|
45900
|
+
} catch (error2) {
|
|
45901
|
+
wrap(error2);
|
|
45902
|
+
}
|
|
45903
|
+
const textBytes = note && bytesOf(note, 2);
|
|
45904
|
+
if (!note || !textBytes)
|
|
45905
|
+
throw new NoteBlocksError("unsupported-structure", "Unsupported Notes document structure");
|
|
45906
|
+
const text = utf8.decode(textBytes);
|
|
45907
|
+
const runTally = /* @__PURE__ */ new Map();
|
|
45908
|
+
const paragraphTally = /* @__PURE__ */ new Map();
|
|
45909
|
+
const runs = [];
|
|
45910
|
+
let position = 0;
|
|
45911
|
+
try {
|
|
45912
|
+
for (const field of note.filter((f) => f.fieldNumber === 5)) {
|
|
45913
|
+
if (field.wireType !== 2 || !field.bytes)
|
|
45914
|
+
throw new NoteBlocksError("invalid-runs", "Invalid Notes attribute run");
|
|
45915
|
+
const fields = decodeWireFields(field.bytes);
|
|
45916
|
+
const length = varintOf(fields, 1);
|
|
45917
|
+
if (length === void 0 || length < 0 || position + length > text.length)
|
|
45918
|
+
throw new NoteBlocksError("invalid-runs", "Invalid Notes run length");
|
|
45919
|
+
for (const n of new Set(fields.map((f) => f.fieldNumber)))
|
|
45920
|
+
if (!KNOWN_RUN_FIELDS.has(n)) runTally.set(n, (runTally.get(n) || 0) + 1);
|
|
45921
|
+
runs.push({
|
|
45922
|
+
start: position,
|
|
45923
|
+
length,
|
|
45924
|
+
paragraph: decodeParagraph(sub2(fields, 2), paragraphTally),
|
|
45925
|
+
inline: decodeInline(fields)
|
|
45926
|
+
});
|
|
45927
|
+
position += length;
|
|
45928
|
+
}
|
|
45929
|
+
} catch (error2) {
|
|
45930
|
+
wrap(error2);
|
|
45931
|
+
}
|
|
45932
|
+
if (position !== text.length)
|
|
45933
|
+
throw new NoteBlocksError("invalid-runs", "Incomplete Notes attribute runs");
|
|
45934
|
+
const blocks = [];
|
|
45935
|
+
const attachments = [];
|
|
45936
|
+
let runIndex = 0;
|
|
45937
|
+
let paragraphStart = 0;
|
|
45938
|
+
while (paragraphStart < text.length) {
|
|
45939
|
+
const newline = text.indexOf("\n", paragraphStart);
|
|
45940
|
+
const end = newline === -1 ? text.length : newline;
|
|
45941
|
+
while (runIndex < runs.length - 1 && runs[runIndex].start + runs[runIndex].length <= paragraphStart)
|
|
45942
|
+
runIndex++;
|
|
45943
|
+
const attrs = runs[runIndex]?.paragraph ?? DEFAULT_PARAGRAPH;
|
|
45944
|
+
const style = attrs.styleType === null ? "body" : STYLE_NAMES[attrs.styleType] ?? "unknown";
|
|
45945
|
+
const alignment = ALIGNMENTS[attrs.alignmentValue] ?? "unknown";
|
|
45946
|
+
const block = {
|
|
45947
|
+
index: blocks.length,
|
|
45948
|
+
start: paragraphStart,
|
|
45949
|
+
length: end - paragraphStart,
|
|
45950
|
+
text: text.slice(paragraphStart, end),
|
|
45951
|
+
style,
|
|
45952
|
+
styleType: attrs.styleType,
|
|
45953
|
+
indent: attrs.indent,
|
|
45954
|
+
alignment,
|
|
45955
|
+
...alignment === "unknown" ? { alignmentValue: attrs.alignmentValue } : {},
|
|
45956
|
+
blockQuote: attrs.blockQuote,
|
|
45957
|
+
...attrs.checklist ? { checklist: attrs.checklist } : {},
|
|
45958
|
+
...attrs.paragraphUuid ? { paragraphUuid: attrs.paragraphUuid } : {},
|
|
45959
|
+
runs: [],
|
|
45960
|
+
attachments: []
|
|
45961
|
+
};
|
|
45962
|
+
for (let i = runIndex; i < runs.length && runs[i].start < end; i++) {
|
|
45963
|
+
const run = runs[i];
|
|
45964
|
+
const start = Math.max(run.start, paragraphStart);
|
|
45965
|
+
const stop = Math.min(run.start + run.length, end);
|
|
45966
|
+
if (stop <= start) continue;
|
|
45967
|
+
block.runs.push({
|
|
45968
|
+
start,
|
|
45969
|
+
length: stop - start,
|
|
45970
|
+
text: text.slice(start, stop),
|
|
45971
|
+
...run.inline
|
|
45972
|
+
});
|
|
45973
|
+
if (run.inline.attachment)
|
|
45974
|
+
for (let at = text.indexOf("\uFFFC", start); at !== -1 && at < stop; at = text.indexOf("\uFFFC", at + 1)) {
|
|
45975
|
+
const marker = { ...run.inline.attachment, start: at, blockIndex: block.index };
|
|
45976
|
+
block.attachments.push(marker);
|
|
45977
|
+
attachments.push(marker);
|
|
45558
45978
|
}
|
|
45559
|
-
if (!changed) break;
|
|
45560
45979
|
}
|
|
45561
|
-
|
|
45980
|
+
blocks.push(block);
|
|
45981
|
+
paragraphStart = end + 1;
|
|
45982
|
+
}
|
|
45983
|
+
return {
|
|
45984
|
+
text,
|
|
45985
|
+
textLength: text.length,
|
|
45986
|
+
blocks,
|
|
45987
|
+
attachments,
|
|
45988
|
+
undecodedFields: {
|
|
45989
|
+
attributeRun: Object.fromEntries([...runTally].sort((a, b) => a[0] - b[0])),
|
|
45990
|
+
paragraphStyle: Object.fromEntries([...paragraphTally].sort((a, b) => a[0] - b[0]))
|
|
45991
|
+
},
|
|
45992
|
+
summary: summarize(blocks, attachments)
|
|
45562
45993
|
};
|
|
45563
|
-
|
|
45564
|
-
|
|
45565
|
-
|
|
45566
|
-
|
|
45567
|
-
|
|
45568
|
-
|
|
45569
|
-
|
|
45570
|
-
|
|
45571
|
-
|
|
45572
|
-
|
|
45573
|
-
|
|
45574
|
-
|
|
45575
|
-
|
|
45576
|
-
|
|
45577
|
-
|
|
45578
|
-
|
|
45579
|
-
|
|
45994
|
+
}
|
|
45995
|
+
function summarize(blocks, attachments) {
|
|
45996
|
+
const summary = {
|
|
45997
|
+
blocks: blocks.length,
|
|
45998
|
+
styles: {},
|
|
45999
|
+
alignments: {},
|
|
46000
|
+
blockQuotes: 0,
|
|
46001
|
+
indented: 0,
|
|
46002
|
+
checklist: { total: 0, done: 0 },
|
|
46003
|
+
inline: {
|
|
46004
|
+
bold: 0,
|
|
46005
|
+
italic: 0,
|
|
46006
|
+
underline: 0,
|
|
46007
|
+
strikethrough: 0,
|
|
46008
|
+
superscript: 0,
|
|
46009
|
+
subscript: 0,
|
|
46010
|
+
color: 0,
|
|
46011
|
+
highlight: 0,
|
|
46012
|
+
link: 0,
|
|
46013
|
+
unsafeLink: 0
|
|
46014
|
+
},
|
|
46015
|
+
attachments: attachments.length
|
|
46016
|
+
};
|
|
46017
|
+
for (const block of blocks) {
|
|
46018
|
+
summary.styles[block.style] = (summary.styles[block.style] || 0) + 1;
|
|
46019
|
+
summary.alignments[block.alignment] = (summary.alignments[block.alignment] || 0) + 1;
|
|
46020
|
+
if (block.blockQuote) summary.blockQuotes++;
|
|
46021
|
+
if (block.indent > 0) summary.indented++;
|
|
46022
|
+
if (block.checklist) {
|
|
46023
|
+
summary.checklist.total++;
|
|
46024
|
+
if (block.checklist.done) summary.checklist.done++;
|
|
46025
|
+
}
|
|
46026
|
+
for (const run of block.runs) {
|
|
46027
|
+
for (const key of [
|
|
46028
|
+
"bold",
|
|
46029
|
+
"italic",
|
|
46030
|
+
"underline",
|
|
46031
|
+
"strikethrough",
|
|
46032
|
+
"superscript",
|
|
46033
|
+
"subscript",
|
|
46034
|
+
"color",
|
|
46035
|
+
"highlight",
|
|
46036
|
+
"link"
|
|
46037
|
+
])
|
|
46038
|
+
if (run[key]) summary.inline[key]++;
|
|
46039
|
+
if (run.link && !run.linkSafe) summary.inline.unsafeLink++;
|
|
46040
|
+
}
|
|
46041
|
+
}
|
|
46042
|
+
return summary;
|
|
46043
|
+
}
|
|
46044
|
+
function decodeCompressedNoteBlocks(compressed) {
|
|
46045
|
+
let data;
|
|
46046
|
+
try {
|
|
46047
|
+
data = gunzipSync5(compressed, { maxOutputLength: 32 * 1024 * 1024 });
|
|
46048
|
+
} catch (error2) {
|
|
46049
|
+
throw new NoteBlocksError(
|
|
46050
|
+
"decompress-failed",
|
|
46051
|
+
`Failed to decompress note data: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
46052
|
+
);
|
|
46053
|
+
}
|
|
46054
|
+
return decodeNoteBlocks(data);
|
|
46055
|
+
}
|
|
46056
|
+
var DEFAULT_BLOCKS_MAX_BYTES = 4 * 1024 * 1024;
|
|
46057
|
+
function blocksMaxResponseBytes(env = process.env) {
|
|
46058
|
+
const raw = env.APPLE_NOTES_MCP_BLOCKS_MAX_BYTES;
|
|
46059
|
+
if (raw !== void 0) {
|
|
46060
|
+
const n = Number(raw);
|
|
46061
|
+
if (Number.isFinite(n) && n > 0) return Math.floor(n);
|
|
46062
|
+
}
|
|
46063
|
+
return DEFAULT_BLOCKS_MAX_BYTES;
|
|
46064
|
+
}
|
|
46065
|
+
function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResponseBytes() } = {}) {
|
|
46066
|
+
const total = doc.blocks.length;
|
|
46067
|
+
const start = Math.min(Math.max(0, offset), total);
|
|
46068
|
+
const blocks = [];
|
|
46069
|
+
let bytes = 0;
|
|
46070
|
+
for (let i = start; i < total && blocks.length < limit; i++) {
|
|
46071
|
+
let block = doc.blocks[i];
|
|
46072
|
+
let size = Buffer.byteLength(JSON.stringify(block));
|
|
46073
|
+
if (bytes + size > maxBytes) {
|
|
46074
|
+
if (blocks.length) break;
|
|
46075
|
+
block = {
|
|
46076
|
+
...block,
|
|
46077
|
+
text: "",
|
|
46078
|
+
textOmitted: true,
|
|
46079
|
+
runs: block.runs.map((run) => ({ ...run, text: "" }))
|
|
46080
|
+
};
|
|
46081
|
+
size = Buffer.byteLength(JSON.stringify(block));
|
|
45580
46082
|
}
|
|
46083
|
+
blocks.push(block);
|
|
46084
|
+
bytes += size;
|
|
45581
46085
|
}
|
|
45582
|
-
const
|
|
45583
|
-
|
|
45584
|
-
|
|
45585
|
-
|
|
46086
|
+
const next = start + blocks.length;
|
|
46087
|
+
return {
|
|
46088
|
+
textLength: doc.textLength,
|
|
46089
|
+
blocks,
|
|
46090
|
+
page: {
|
|
46091
|
+
offset: start,
|
|
46092
|
+
returned: blocks.length,
|
|
46093
|
+
total,
|
|
46094
|
+
hasMore: next < total,
|
|
46095
|
+
...next < total ? { nextOffset: next } : {}
|
|
46096
|
+
},
|
|
46097
|
+
summary: doc.summary,
|
|
46098
|
+
undecodedFields: doc.undecodedFields
|
|
46099
|
+
};
|
|
46100
|
+
}
|
|
46101
|
+
var NOTES_DB_PATH7 = join13(
|
|
46102
|
+
homedir11(),
|
|
46103
|
+
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
46104
|
+
);
|
|
46105
|
+
function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
|
|
46106
|
+
const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
|
|
46107
|
+
if (!pk)
|
|
46108
|
+
throw new NoteBlocksError(
|
|
46109
|
+
"invalid-id",
|
|
46110
|
+
`Invalid note ID: expected x-coredata://<store>/ICNote/p<number>`
|
|
45586
46111
|
);
|
|
45587
|
-
|
|
45588
|
-
|
|
45589
|
-
|
|
45590
|
-
|
|
46112
|
+
if (!existsSync10(dbPath2))
|
|
46113
|
+
throw new NoteBlocksError("no-full-disk-access", "The Notes database is not readable");
|
|
46114
|
+
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));";
|
|
46115
|
+
let output;
|
|
46116
|
+
try {
|
|
46117
|
+
output = execFileSync12(
|
|
46118
|
+
"/usr/bin/sqlite3",
|
|
46119
|
+
["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
|
|
46120
|
+
{
|
|
46121
|
+
encoding: "utf8",
|
|
46122
|
+
timeout: 5e3,
|
|
46123
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
46124
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
46125
|
+
}
|
|
46126
|
+
).trim();
|
|
46127
|
+
} catch (error2) {
|
|
46128
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
46129
|
+
if (/authorization denied|unable to open database/i.test(message))
|
|
46130
|
+
throw new NoteBlocksError("no-full-disk-access", "The Notes database is not readable");
|
|
46131
|
+
throw new NoteBlocksError("query-failed", "Failed to query the Notes database");
|
|
46132
|
+
}
|
|
46133
|
+
let row;
|
|
46134
|
+
try {
|
|
46135
|
+
row = JSON.parse(output);
|
|
46136
|
+
} catch {
|
|
46137
|
+
throw new NoteBlocksError("query-failed", "Unexpected Notes database response");
|
|
45591
46138
|
}
|
|
45592
|
-
|
|
46139
|
+
if (!row.exists) throw new NoteBlocksError("not-found", `No note found for ID "${id2}"`);
|
|
46140
|
+
if (row.encrypted)
|
|
46141
|
+
throw new NoteBlocksError(
|
|
46142
|
+
"encrypted",
|
|
46143
|
+
"This note is password-protected; its body is encrypted and cannot be decoded"
|
|
46144
|
+
);
|
|
46145
|
+
if (!row.data || !/^[0-9a-f]+$/i.test(row.data))
|
|
46146
|
+
throw new NoteBlocksError("no-body", "No body data is stored for this note");
|
|
46147
|
+
return decodeCompressedNoteBlocks(Buffer.from(row.data, "hex"));
|
|
45593
46148
|
}
|
|
45594
46149
|
|
|
45595
46150
|
// src/utils/linkInsert.ts
|
|
@@ -45720,7 +46275,7 @@ import {
|
|
|
45720
46275
|
writeFileSync as writeFileSync3
|
|
45721
46276
|
} from "node:fs";
|
|
45722
46277
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
45723
|
-
import { basename, isAbsolute as isAbsolute2, join as
|
|
46278
|
+
import { basename, isAbsolute as isAbsolute2, join as join14 } from "node:path";
|
|
45724
46279
|
var noteId = exactIdInput(
|
|
45725
46280
|
"ICNote",
|
|
45726
46281
|
/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
|
|
@@ -45834,8 +46389,8 @@ function registerDirectOperations(server2, manager) {
|
|
|
45834
46389
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
45835
46390
|
const bytes = localAttachment(path7);
|
|
45836
46391
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
45837
|
-
const directory = mkdtempSync4(
|
|
45838
|
-
const temporaryFile =
|
|
46392
|
+
const directory = mkdtempSync4(join14(tmpdir4(), "notes-attachment-add-"));
|
|
46393
|
+
const temporaryFile = join14(directory, basename(path7));
|
|
45839
46394
|
try {
|
|
45840
46395
|
writeFileSync3(temporaryFile, bytes, { mode: 384 });
|
|
45841
46396
|
if (readSnapshot(manager, id2).hash !== before.hash)
|
|
@@ -46381,7 +46936,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
46381
46936
|
|
|
46382
46937
|
// src/setupShortcuts.ts
|
|
46383
46938
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
46384
|
-
import { existsSync as
|
|
46939
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
46385
46940
|
import { release as release2 } from "node:os";
|
|
46386
46941
|
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
46387
46942
|
import { fileURLToPath } from "node:url";
|
|
@@ -46401,7 +46956,7 @@ var shortcutFiles = [
|
|
|
46401
46956
|
];
|
|
46402
46957
|
function setupShortcuts(checkOnly, dependencies = {}) {
|
|
46403
46958
|
const status = dependencies.status || nativeTagsStatus;
|
|
46404
|
-
const exists = dependencies.exists ||
|
|
46959
|
+
const exists = dependencies.exists || existsSync11;
|
|
46405
46960
|
const open = dependencies.open || ((path7) => {
|
|
46406
46961
|
const result = spawnSync2("/usr/bin/open", [path7], { encoding: "utf8" });
|
|
46407
46962
|
return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
|
|
@@ -47217,6 +47772,78 @@ registerTool(
|
|
|
47217
47772
|
});
|
|
47218
47773
|
}, "Error reading native objects")
|
|
47219
47774
|
);
|
|
47775
|
+
registerTool(
|
|
47776
|
+
"get-note-tables",
|
|
47777
|
+
{
|
|
47778
|
+
description: "Use when: reading the native tables in one exact note as data or Markdown.\nReturns: every table in body order as GitHub-flavored Markdown (first row as header) plus JSON rows with stable row/column ids, and tableCellsComplete.\nDo not use when: you need the whole note (get-note-markdown / get-note-content) or native object ranges and checklist ids (get-native-objects).\nSafety: read-only; reads the NoteStore database and requires Full Disk Access. A cell that cannot be decoded is null in rows, listed in incompleteCells, and marked [undecoded cell] in Markdown; it is never guessed. Cell text only: links and styling inside cells are not rendered.",
|
|
47779
|
+
inputSchema: { id: noteIdInput },
|
|
47780
|
+
outputSchema: {
|
|
47781
|
+
id: external_exports.string().optional(),
|
|
47782
|
+
tables: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
|
|
47783
|
+
tableCount: external_exports.number().optional(),
|
|
47784
|
+
tableCellsComplete: external_exports.boolean().optional(),
|
|
47785
|
+
markdown: external_exports.string().optional()
|
|
47786
|
+
},
|
|
47787
|
+
annotations: { readOnlyHint: true }
|
|
47788
|
+
},
|
|
47789
|
+
withErrorHandling(({ id: id2 }) => {
|
|
47790
|
+
const { metadata, message } = getNoteMetadata(id2);
|
|
47791
|
+
if (!metadata) return errorResponse(message || `Failed to read note "${id2}"`);
|
|
47792
|
+
if (metadata.passwordProtected) {
|
|
47793
|
+
return errorResponse(
|
|
47794
|
+
`Note "${id2}" is password-protected; its tables are encrypted. Unlock it in Notes.app first.`
|
|
47795
|
+
);
|
|
47796
|
+
}
|
|
47797
|
+
const result = notesManager.getNoteTablesById(id2);
|
|
47798
|
+
const count = result.tables.length;
|
|
47799
|
+
const summary = count === 0 ? "This note has no native tables." : `${count} table(s)${result.tableCellsComplete ? "" : " (some content could not be decoded; see tables[].reason)"}:
|
|
47800
|
+
|
|
47801
|
+
${result.markdown}`;
|
|
47802
|
+
return successResponse(summary, {
|
|
47803
|
+
id: id2,
|
|
47804
|
+
tables: result.tables,
|
|
47805
|
+
tableCount: count,
|
|
47806
|
+
tableCellsComplete: result.tableCellsComplete,
|
|
47807
|
+
markdown: result.markdown
|
|
47808
|
+
});
|
|
47809
|
+
}, "Error reading note tables")
|
|
47810
|
+
);
|
|
47811
|
+
registerTool(
|
|
47812
|
+
"get-note-blocks",
|
|
47813
|
+
{
|
|
47814
|
+
description: "Use when: you need a note's structure, not just its text: paragraph styles (title, heading, subheading, body, monospaced, bulleted/dashed/numbered list, checklist with done state), indent, alignment, block quote, inline formatting (bold, italic, underline, strikethrough, superscript, subscript, color, highlight, links) and attachment positions, by exact id.\nReturns: one page of blocks in body order (default 500, stopped early under APPLE_NOTES_MCP_BLOCKS_MAX_BYTES, default 4 MB), whole-note summary counts, undecodedFields, and page info; while page.hasMore is true, call again with offset set to page.nextOffset. Offsets and lengths count UTF-16 code units.\nDo not use when: you want the editable HTML body (get-note-content) or Markdown (get-note-markdown).\nSafety: read-only; decodes the NoteStore database directly and requires Full Disk Access. Password-protected notes are refused. Link URLs are returned as stored; linkSafe is false for schemes other than http(s), notes, applenotes and mailto.",
|
|
47815
|
+
inputSchema: {
|
|
47816
|
+
id: noteIdInput,
|
|
47817
|
+
offset: external_exports.number().int().min(0).optional().describe("Index of the first block to return (default 0); use page.nextOffset"),
|
|
47818
|
+
limit: external_exports.number().int().min(1).max(5e3).optional().describe("Maximum blocks to return (default 500, max 5000)")
|
|
47819
|
+
},
|
|
47820
|
+
outputSchema: {
|
|
47821
|
+
id: external_exports.string().optional(),
|
|
47822
|
+
textLength: external_exports.number().optional(),
|
|
47823
|
+
blocks: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
|
|
47824
|
+
page: external_exports.record(external_exports.unknown()).optional(),
|
|
47825
|
+
summary: external_exports.record(external_exports.unknown()).optional(),
|
|
47826
|
+
undecodedFields: external_exports.record(external_exports.unknown()).optional()
|
|
47827
|
+
},
|
|
47828
|
+
annotations: { readOnlyHint: true }
|
|
47829
|
+
},
|
|
47830
|
+
withErrorHandling(({ id: id2, offset, limit }) => {
|
|
47831
|
+
let page;
|
|
47832
|
+
try {
|
|
47833
|
+
page = pageNoteBlocks(readNoteBlocks(id2), { offset, limit });
|
|
47834
|
+
} catch (error2) {
|
|
47835
|
+
if (!(error2 instanceof NoteBlocksError)) throw error2;
|
|
47836
|
+
const hint = error2.code === "no-full-disk-access" ? ` Grant Full Disk Access to the app that launches this server: ${FULL_DISK_ACCESS_GUIDE_URL}` : "";
|
|
47837
|
+
return errorResponse(`Error reading note blocks [${error2.code}]: ${error2.message}${hint}`);
|
|
47838
|
+
}
|
|
47839
|
+
const { summary } = page;
|
|
47840
|
+
const styles = Object.entries(summary.styles).map(([style, count]) => `${style} ${count}`).join(", ");
|
|
47841
|
+
return successResponse(
|
|
47842
|
+
`Decoded ${summary.blocks} blocks (${styles || "none"}); returned ${page.page.returned} from offset ${page.page.offset}` + (page.page.hasMore ? `; more available at offset ${page.page.nextOffset}` : "") + ".",
|
|
47843
|
+
{ id: id2, ...page }
|
|
47844
|
+
);
|
|
47845
|
+
}, "Error reading note blocks")
|
|
47846
|
+
);
|
|
47220
47847
|
registerTool(
|
|
47221
47848
|
"list-native-tags",
|
|
47222
47849
|
{
|
package/package.json
CHANGED