apple-notes-mcp 2.8.29 → 2.8.31
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 +74 -6
- package/build/index.js +786 -191
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -417,11 +417,11 @@ var require_codegen = __commonJS({
|
|
|
417
417
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
418
418
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
419
419
|
}
|
|
420
|
-
optimizeNames(names,
|
|
420
|
+
optimizeNames(names, constants3) {
|
|
421
421
|
if (!names[this.name.str])
|
|
422
422
|
return;
|
|
423
423
|
if (this.rhs)
|
|
424
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
424
|
+
this.rhs = optimizeExpr(this.rhs, names, constants3);
|
|
425
425
|
return this;
|
|
426
426
|
}
|
|
427
427
|
get names() {
|
|
@@ -438,10 +438,10 @@ var require_codegen = __commonJS({
|
|
|
438
438
|
render({ _n }) {
|
|
439
439
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
440
440
|
}
|
|
441
|
-
optimizeNames(names,
|
|
441
|
+
optimizeNames(names, constants3) {
|
|
442
442
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
443
443
|
return;
|
|
444
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
444
|
+
this.rhs = optimizeExpr(this.rhs, names, constants3);
|
|
445
445
|
return this;
|
|
446
446
|
}
|
|
447
447
|
get names() {
|
|
@@ -502,8 +502,8 @@ var require_codegen = __commonJS({
|
|
|
502
502
|
optimizeNodes() {
|
|
503
503
|
return `${this.code}` ? this : void 0;
|
|
504
504
|
}
|
|
505
|
-
optimizeNames(names,
|
|
506
|
-
this.code = optimizeExpr(this.code, names,
|
|
505
|
+
optimizeNames(names, constants3) {
|
|
506
|
+
this.code = optimizeExpr(this.code, names, constants3);
|
|
507
507
|
return this;
|
|
508
508
|
}
|
|
509
509
|
get names() {
|
|
@@ -532,12 +532,12 @@ var require_codegen = __commonJS({
|
|
|
532
532
|
}
|
|
533
533
|
return nodes.length > 0 ? this : void 0;
|
|
534
534
|
}
|
|
535
|
-
optimizeNames(names,
|
|
535
|
+
optimizeNames(names, constants3) {
|
|
536
536
|
const { nodes } = this;
|
|
537
537
|
let i = nodes.length;
|
|
538
538
|
while (i--) {
|
|
539
539
|
const n = nodes[i];
|
|
540
|
-
if (n.optimizeNames(names,
|
|
540
|
+
if (n.optimizeNames(names, constants3))
|
|
541
541
|
continue;
|
|
542
542
|
subtractNames(names, n.names);
|
|
543
543
|
nodes.splice(i, 1);
|
|
@@ -590,12 +590,12 @@ var require_codegen = __commonJS({
|
|
|
590
590
|
return void 0;
|
|
591
591
|
return this;
|
|
592
592
|
}
|
|
593
|
-
optimizeNames(names,
|
|
593
|
+
optimizeNames(names, constants3) {
|
|
594
594
|
var _a;
|
|
595
|
-
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
596
|
-
if (!(super.optimizeNames(names,
|
|
595
|
+
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants3);
|
|
596
|
+
if (!(super.optimizeNames(names, constants3) || this.else))
|
|
597
597
|
return;
|
|
598
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
598
|
+
this.condition = optimizeExpr(this.condition, names, constants3);
|
|
599
599
|
return this;
|
|
600
600
|
}
|
|
601
601
|
get names() {
|
|
@@ -618,10 +618,10 @@ var require_codegen = __commonJS({
|
|
|
618
618
|
render(opts) {
|
|
619
619
|
return `for(${this.iteration})` + super.render(opts);
|
|
620
620
|
}
|
|
621
|
-
optimizeNames(names,
|
|
622
|
-
if (!super.optimizeNames(names,
|
|
621
|
+
optimizeNames(names, constants3) {
|
|
622
|
+
if (!super.optimizeNames(names, constants3))
|
|
623
623
|
return;
|
|
624
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
624
|
+
this.iteration = optimizeExpr(this.iteration, names, constants3);
|
|
625
625
|
return this;
|
|
626
626
|
}
|
|
627
627
|
get names() {
|
|
@@ -657,10 +657,10 @@ var require_codegen = __commonJS({
|
|
|
657
657
|
render(opts) {
|
|
658
658
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
659
659
|
}
|
|
660
|
-
optimizeNames(names,
|
|
661
|
-
if (!super.optimizeNames(names,
|
|
660
|
+
optimizeNames(names, constants3) {
|
|
661
|
+
if (!super.optimizeNames(names, constants3))
|
|
662
662
|
return;
|
|
663
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
663
|
+
this.iterable = optimizeExpr(this.iterable, names, constants3);
|
|
664
664
|
return this;
|
|
665
665
|
}
|
|
666
666
|
get names() {
|
|
@@ -702,11 +702,11 @@ var require_codegen = __commonJS({
|
|
|
702
702
|
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
|
|
703
703
|
return this;
|
|
704
704
|
}
|
|
705
|
-
optimizeNames(names,
|
|
705
|
+
optimizeNames(names, constants3) {
|
|
706
706
|
var _a, _b;
|
|
707
|
-
super.optimizeNames(names,
|
|
708
|
-
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
709
|
-
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names,
|
|
707
|
+
super.optimizeNames(names, constants3);
|
|
708
|
+
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants3);
|
|
709
|
+
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants3);
|
|
710
710
|
return this;
|
|
711
711
|
}
|
|
712
712
|
get names() {
|
|
@@ -1007,7 +1007,7 @@ var require_codegen = __commonJS({
|
|
|
1007
1007
|
function addExprNames(names, from) {
|
|
1008
1008
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
1009
1009
|
}
|
|
1010
|
-
function optimizeExpr(expr, names,
|
|
1010
|
+
function optimizeExpr(expr, names, constants3) {
|
|
1011
1011
|
if (expr instanceof code_1.Name)
|
|
1012
1012
|
return replaceName(expr);
|
|
1013
1013
|
if (!canOptimize(expr))
|
|
@@ -1022,14 +1022,14 @@ var require_codegen = __commonJS({
|
|
|
1022
1022
|
return items;
|
|
1023
1023
|
}, []));
|
|
1024
1024
|
function replaceName(n) {
|
|
1025
|
-
const c =
|
|
1025
|
+
const c = constants3[n.str];
|
|
1026
1026
|
if (c === void 0 || names[n.str] !== 1)
|
|
1027
1027
|
return n;
|
|
1028
1028
|
delete names[n.str];
|
|
1029
1029
|
return c;
|
|
1030
1030
|
}
|
|
1031
1031
|
function canOptimize(e) {
|
|
1032
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
1032
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants3[c.str] !== void 0);
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
1035
|
function subtractNames(names, from) {
|
|
@@ -7422,7 +7422,7 @@ var require_DOMException = __commonJS({
|
|
|
7422
7422
|
"INVALID_NODE_TYPE_ERR (24): the supplied node is invalid or has an invalid ancestor for this operation",
|
|
7423
7423
|
"DATA_CLONE_ERR (25): the object can not be cloned."
|
|
7424
7424
|
];
|
|
7425
|
-
var
|
|
7425
|
+
var constants3 = {
|
|
7426
7426
|
INDEX_SIZE_ERR,
|
|
7427
7427
|
DOMSTRING_SIZE_ERR: 2,
|
|
7428
7428
|
// historical
|
|
@@ -7461,8 +7461,8 @@ var require_DOMException = __commonJS({
|
|
|
7461
7461
|
this.name = names[code];
|
|
7462
7462
|
}
|
|
7463
7463
|
DOMException.prototype.__proto__ = Error.prototype;
|
|
7464
|
-
for (c in
|
|
7465
|
-
v = { value:
|
|
7464
|
+
for (c in constants3) {
|
|
7465
|
+
v = { value: constants3[c] };
|
|
7466
7466
|
Object.defineProperty(DOMException, c, v);
|
|
7467
7467
|
Object.defineProperty(DOMException.prototype, c, v);
|
|
7468
7468
|
}
|
|
@@ -9872,9 +9872,9 @@ var require_select = __commonJS({
|
|
|
9872
9872
|
var makeSubject = function() {
|
|
9873
9873
|
var target;
|
|
9874
9874
|
function subject(el) {
|
|
9875
|
-
var node = el.ownerDocument,
|
|
9875
|
+
var node = el.ownerDocument, scope2 = node.getElementsByTagName(subject.lname), i = scope2.length;
|
|
9876
9876
|
while (i--) {
|
|
9877
|
-
if (subject.test(
|
|
9877
|
+
if (subject.test(scope2[i]) && target === el) {
|
|
9878
9878
|
target = null;
|
|
9879
9879
|
return true;
|
|
9880
9880
|
}
|
|
@@ -9902,16 +9902,16 @@ var require_select = __commonJS({
|
|
|
9902
9902
|
};
|
|
9903
9903
|
};
|
|
9904
9904
|
var find = function(sel, node) {
|
|
9905
|
-
var results = [], test = compile(sel),
|
|
9906
|
-
while (el =
|
|
9905
|
+
var results = [], test = compile(sel), scope2 = node.getElementsByTagName(test.qname), i = 0, el;
|
|
9906
|
+
while (el = scope2[i++]) {
|
|
9907
9907
|
if (test(el)) results.push(el);
|
|
9908
9908
|
}
|
|
9909
9909
|
if (test.sel) {
|
|
9910
9910
|
while (test.sel) {
|
|
9911
9911
|
test = compile(test.sel);
|
|
9912
|
-
|
|
9912
|
+
scope2 = node.getElementsByTagName(test.qname);
|
|
9913
9913
|
i = 0;
|
|
9914
|
-
while (el =
|
|
9914
|
+
while (el = scope2[i++]) {
|
|
9915
9915
|
if (test(el) && indexOf.call(results, el) === -1) {
|
|
9916
9916
|
results.push(el);
|
|
9917
9917
|
}
|
|
@@ -24465,14 +24465,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
24465
24465
|
} else if (node.nodeType === 1) {
|
|
24466
24466
|
replacement = replacementForNode.call(self, node);
|
|
24467
24467
|
}
|
|
24468
|
-
return
|
|
24468
|
+
return join16(output, replacement);
|
|
24469
24469
|
}, "");
|
|
24470
24470
|
}
|
|
24471
24471
|
function postProcess(output) {
|
|
24472
24472
|
var self = this;
|
|
24473
24473
|
this.rules.forEach(function(rule) {
|
|
24474
24474
|
if (typeof rule.append === "function") {
|
|
24475
|
-
output =
|
|
24475
|
+
output = join16(output, rule.append(self.options));
|
|
24476
24476
|
}
|
|
24477
24477
|
});
|
|
24478
24478
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -24484,7 +24484,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
24484
24484
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
24485
24485
|
return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
|
|
24486
24486
|
}
|
|
24487
|
-
function
|
|
24487
|
+
function join16(output, replacement) {
|
|
24488
24488
|
var s1 = trimTrailingNewlines(output);
|
|
24489
24489
|
var s2 = trimLeadingNewlines(replacement);
|
|
24490
24490
|
var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
|
@@ -24777,8 +24777,8 @@ var ZodError = class _ZodError extends Error {
|
|
|
24777
24777
|
constructor(issues) {
|
|
24778
24778
|
super();
|
|
24779
24779
|
this.issues = [];
|
|
24780
|
-
this.addIssue = (
|
|
24781
|
-
this.issues = [...this.issues,
|
|
24780
|
+
this.addIssue = (sub4) => {
|
|
24781
|
+
this.issues = [...this.issues, sub4];
|
|
24782
24782
|
};
|
|
24783
24783
|
this.addIssues = (subs = []) => {
|
|
24784
24784
|
this.issues = [...this.issues, ...subs];
|
|
@@ -24845,13 +24845,13 @@ var ZodError = class _ZodError extends Error {
|
|
|
24845
24845
|
flatten(mapper = (issue2) => issue2.message) {
|
|
24846
24846
|
const fieldErrors = {};
|
|
24847
24847
|
const formErrors = [];
|
|
24848
|
-
for (const
|
|
24849
|
-
if (
|
|
24850
|
-
const firstEl =
|
|
24848
|
+
for (const sub4 of this.issues) {
|
|
24849
|
+
if (sub4.path.length > 0) {
|
|
24850
|
+
const firstEl = sub4.path[0];
|
|
24851
24851
|
fieldErrors[firstEl] = fieldErrors[firstEl] || [];
|
|
24852
|
-
fieldErrors[firstEl].push(mapper(
|
|
24852
|
+
fieldErrors[firstEl].push(mapper(sub4));
|
|
24853
24853
|
} else {
|
|
24854
|
-
formErrors.push(mapper(
|
|
24854
|
+
formErrors.push(mapper(sub4));
|
|
24855
24855
|
}
|
|
24856
24856
|
}
|
|
24857
24857
|
return { formErrors, fieldErrors };
|
|
@@ -29150,12 +29150,12 @@ var $ZodRealError = $constructor("$ZodError", initializer, { Parent: Error });
|
|
|
29150
29150
|
function flattenError(error2, mapper = (issue2) => issue2.message) {
|
|
29151
29151
|
const fieldErrors = {};
|
|
29152
29152
|
const formErrors = [];
|
|
29153
|
-
for (const
|
|
29154
|
-
if (
|
|
29155
|
-
fieldErrors[
|
|
29156
|
-
fieldErrors[
|
|
29153
|
+
for (const sub4 of error2.issues) {
|
|
29154
|
+
if (sub4.path.length > 0) {
|
|
29155
|
+
fieldErrors[sub4.path[0]] = fieldErrors[sub4.path[0]] || [];
|
|
29156
|
+
fieldErrors[sub4.path[0]].push(mapper(sub4));
|
|
29157
29157
|
} else {
|
|
29158
|
-
formErrors.push(mapper(
|
|
29158
|
+
formErrors.push(mapper(sub4));
|
|
29159
29159
|
}
|
|
29160
29160
|
}
|
|
29161
29161
|
return { formErrors, fieldErrors };
|
|
@@ -38990,6 +38990,22 @@ var NODE_RUNTIME_TCC_GUIDE_URL = "https://github.com/sweetrb/apple-notes-mcp/blo
|
|
|
38990
38990
|
var AUTOMATION_PERMISSION_GUIDE_URL = "https://github.com/sweetrb/apple-notes-mcp#permission-denied";
|
|
38991
38991
|
var AUTOMATION_REMEDIATION = `Grant automation access in System Settings > Privacy & Security > Automation to the app that launches this server (Claude Desktop / Terminal / iTerm2), then fully quit and relaunch it \u2014 run the doctor tool to verify. See: ${AUTOMATION_PERMISSION_GUIDE_URL}`;
|
|
38992
38992
|
|
|
38993
|
+
// src/utils/callTimeout.ts
|
|
38994
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
38995
|
+
var CALL_TIMEOUT_SECONDS = { min: 1, max: 120 };
|
|
38996
|
+
var scope = new AsyncLocalStorage();
|
|
38997
|
+
function runWithCallTimeout(seconds, fn) {
|
|
38998
|
+
if (seconds === void 0) return fn();
|
|
38999
|
+
if (!Number.isInteger(seconds) || seconds < CALL_TIMEOUT_SECONDS.min || seconds > CALL_TIMEOUT_SECONDS.max)
|
|
39000
|
+
throw new Error(
|
|
39001
|
+
`timeoutSeconds must be a whole number from ${CALL_TIMEOUT_SECONDS.min} to ${CALL_TIMEOUT_SECONDS.max}`
|
|
39002
|
+
);
|
|
39003
|
+
return scope.run(seconds * 1e3, fn);
|
|
39004
|
+
}
|
|
39005
|
+
function callTimeoutMs() {
|
|
39006
|
+
return scope.getStore();
|
|
39007
|
+
}
|
|
39008
|
+
|
|
38993
39009
|
// src/utils/applescript.ts
|
|
38994
39010
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
38995
39011
|
var DEFAULT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
@@ -39146,7 +39162,7 @@ function parseErrorMessage(errorOutput) {
|
|
|
39146
39162
|
return coreError.trim() || "Unknown AppleScript error";
|
|
39147
39163
|
}
|
|
39148
39164
|
function executeAppleScript(script, options = {}) {
|
|
39149
|
-
const timeoutMs = options.timeoutMs ?? envPositiveNumber("APPLE_NOTES_MCP_TIMEOUT_MS") ?? DEFAULT_TIMEOUT_MS;
|
|
39165
|
+
const timeoutMs = options.timeoutMs ?? callTimeoutMs() ?? envPositiveNumber("APPLE_NOTES_MCP_TIMEOUT_MS") ?? DEFAULT_TIMEOUT_MS;
|
|
39150
39166
|
const maxRetries = options.maxRetries ?? envPositiveNumber("APPLE_NOTES_MCP_MAX_RETRIES") ?? DEFAULT_MAX_RETRIES;
|
|
39151
39167
|
const retryDelayMs = options.retryDelayMs ?? envPositiveNumber("APPLE_NOTES_MCP_RETRY_DELAY_MS") ?? DEFAULT_RETRY_DELAY_MS;
|
|
39152
39168
|
if (!script || !script.trim()) {
|
|
@@ -39275,7 +39291,7 @@ function decodeVarint(buf, offset) {
|
|
|
39275
39291
|
}
|
|
39276
39292
|
throw new Error(`Unexpected end of buffer reading varint at offset ${offset}`);
|
|
39277
39293
|
}
|
|
39278
|
-
function decodeMessage(buf) {
|
|
39294
|
+
function decodeMessage(buf, options = {}) {
|
|
39279
39295
|
const fields = [];
|
|
39280
39296
|
let offset = 0;
|
|
39281
39297
|
while (offset < buf.length) {
|
|
@@ -39296,10 +39312,13 @@ function decodeMessage(buf) {
|
|
|
39296
39312
|
const value = buf.slice(offset, offset + length);
|
|
39297
39313
|
fields.push({ fieldNumber, wireType, value });
|
|
39298
39314
|
offset += length;
|
|
39299
|
-
} else if (wireType === 5) {
|
|
39300
|
-
|
|
39301
|
-
|
|
39302
|
-
|
|
39315
|
+
} else if (wireType === 5 || wireType === 1) {
|
|
39316
|
+
const width = wireType === 5 ? 4 : 8;
|
|
39317
|
+
if (offset + width > buf.length) break;
|
|
39318
|
+
if (options.keepFixed) {
|
|
39319
|
+
fields.push({ fieldNumber, wireType, value: buf.slice(offset, offset + width) });
|
|
39320
|
+
}
|
|
39321
|
+
offset += width;
|
|
39303
39322
|
} else {
|
|
39304
39323
|
break;
|
|
39305
39324
|
}
|
|
@@ -39330,6 +39349,11 @@ function embeddedMessage(field) {
|
|
|
39330
39349
|
if (!bytes) return void 0;
|
|
39331
39350
|
return decodeMessage(bytes);
|
|
39332
39351
|
}
|
|
39352
|
+
function fixed64Double(field) {
|
|
39353
|
+
if (!field || field.wireType !== 1 || !(field.value instanceof Uint8Array)) return void 0;
|
|
39354
|
+
if (field.value.length !== 8) return void 0;
|
|
39355
|
+
return new DataView(field.value.buffer, field.value.byteOffset, 8).getFloat64(0, true);
|
|
39356
|
+
}
|
|
39333
39357
|
var ProtobufDecodeError = class extends Error {
|
|
39334
39358
|
constructor(message) {
|
|
39335
39359
|
super(message);
|
|
@@ -39901,9 +39925,373 @@ function assertLinkedWrite(rich, content, format, allowLinkChanges = false) {
|
|
|
39901
39925
|
}
|
|
39902
39926
|
}
|
|
39903
39927
|
|
|
39904
|
-
// src/utils/
|
|
39928
|
+
// src/utils/audioTranscripts.ts
|
|
39929
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
39930
|
+
import { homedir as homedir3 } from "node:os";
|
|
39931
|
+
import { join as join3 } from "node:path";
|
|
39905
39932
|
import { gunzipSync as gunzipSync3 } from "node:zlib";
|
|
39933
|
+
var NOTES_DB_PATH2 = join3(
|
|
39934
|
+
homedir3(),
|
|
39935
|
+
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
39936
|
+
);
|
|
39937
|
+
var DEFAULT_MAX_SEGMENTS = 2e3;
|
|
39938
|
+
var MAX_SEGMENTS_LIMIT = 2e4;
|
|
39939
|
+
var AudioTranscriptError = class extends Error {
|
|
39940
|
+
constructor(kind, message) {
|
|
39941
|
+
super(message);
|
|
39942
|
+
this.kind = kind;
|
|
39943
|
+
this.name = "AudioTranscriptError";
|
|
39944
|
+
}
|
|
39945
|
+
kind;
|
|
39946
|
+
};
|
|
39947
|
+
var decode = (bytes) => decodeMessage(bytes, { keepFixed: true });
|
|
39906
39948
|
var sub = (f, n) => {
|
|
39949
|
+
const bytes = getField(f, n)?.value;
|
|
39950
|
+
return bytes instanceof Uint8Array ? decode(bytes) : void 0;
|
|
39951
|
+
};
|
|
39952
|
+
var need = (value, what) => {
|
|
39953
|
+
if (value === void 0) throw new Error(`Missing ${what}`);
|
|
39954
|
+
return value;
|
|
39955
|
+
};
|
|
39956
|
+
var many = (f, n) => getFields(f, n).map((field) => {
|
|
39957
|
+
if (!(field.value instanceof Uint8Array)) throw new Error("Invalid nested entry");
|
|
39958
|
+
return decode(field.value);
|
|
39959
|
+
});
|
|
39960
|
+
function parseAudioRecording(data) {
|
|
39961
|
+
const root = decode(data);
|
|
39962
|
+
const entries = many(root, 3);
|
|
39963
|
+
if (entries.length === 0) throw new Error("No mergeable objects");
|
|
39964
|
+
if (entries.length > 2e6) throw new Error("Recording too large");
|
|
39965
|
+
const keys = getFields(root, 4).map((f) => stringValue(f) ?? "");
|
|
39966
|
+
const types = getFields(root, 5).map((f) => stringValue(f) ?? "");
|
|
39967
|
+
const uuids = getFields(root, 6).map(
|
|
39968
|
+
(f) => f.value instanceof Uint8Array ? Buffer.from(f.value).toString("hex") : ""
|
|
39969
|
+
);
|
|
39970
|
+
const uuidSlot = /* @__PURE__ */ new Map();
|
|
39971
|
+
uuids.forEach((uuid2, i) => {
|
|
39972
|
+
if (uuid2 && !uuidSlot.has(uuid2)) uuidSlot.set(uuid2, i);
|
|
39973
|
+
});
|
|
39974
|
+
const entry = (index) => {
|
|
39975
|
+
const value = entries[index];
|
|
39976
|
+
if (!value) throw new Error("Invalid object reference");
|
|
39977
|
+
return value;
|
|
39978
|
+
};
|
|
39979
|
+
const customMap = (e) => {
|
|
39980
|
+
const map = sub(e, 13);
|
|
39981
|
+
if (!map) return void 0;
|
|
39982
|
+
const values = /* @__PURE__ */ new Map();
|
|
39983
|
+
for (const item of many(map, 3)) {
|
|
39984
|
+
const key = keys[need(varintValue(getField(item, 1)), "map key")];
|
|
39985
|
+
const value = sub(item, 2);
|
|
39986
|
+
if (key !== void 0 && value) values.set(key, value);
|
|
39987
|
+
}
|
|
39988
|
+
return { type: types[need(varintValue(getField(map, 1)), "map type")] ?? "", values };
|
|
39989
|
+
};
|
|
39990
|
+
const objectIndex = (id2) => id2 ? varintValue(getField(id2, 6)) : void 0;
|
|
39991
|
+
const registerTarget = (id2) => {
|
|
39992
|
+
const index = objectIndex(id2);
|
|
39993
|
+
if (index === void 0) return void 0;
|
|
39994
|
+
const register = sub(entry(index), 1);
|
|
39995
|
+
if (!register) return void 0;
|
|
39996
|
+
const target = objectIndex(sub(register, 2));
|
|
39997
|
+
return target === void 0 ? void 0 : entry(target);
|
|
39998
|
+
};
|
|
39999
|
+
const registerPrimitive = (id2) => {
|
|
40000
|
+
const target = registerTarget(id2);
|
|
40001
|
+
const map = target && customMap(target);
|
|
40002
|
+
if (!map) return void 0;
|
|
40003
|
+
const self = map.values.get("self");
|
|
40004
|
+
if (self) return stringValue(getField(self, 4));
|
|
40005
|
+
const double = map.values.get("doubleValue");
|
|
40006
|
+
if (double) return fixed64Double(getField(double, 3));
|
|
40007
|
+
const integer2 = map.values.get("integerValue");
|
|
40008
|
+
if (integer2) return varintValue(getField(integer2, 2));
|
|
40009
|
+
return void 0;
|
|
40010
|
+
};
|
|
40011
|
+
const registerNoteText = (id2) => {
|
|
40012
|
+
const target = registerTarget(id2);
|
|
40013
|
+
const note = target && sub(target, 10);
|
|
40014
|
+
const text = note && stringValue(getField(note, 2));
|
|
40015
|
+
return text?.replace(/\n+$/u, "") || void 0;
|
|
40016
|
+
};
|
|
40017
|
+
const recordings = entries.map((e) => customMap(e)).filter((m) => m?.type === "com.apple.notes.ICTTAudioRecording");
|
|
40018
|
+
if (recordings.length !== 1) throw new Error("Expected exactly one audio recording object");
|
|
40019
|
+
const recording = recordings[0];
|
|
40020
|
+
const fragments = [];
|
|
40021
|
+
const fragmentList = objectIndex(recording.values.get("fragments"));
|
|
40022
|
+
if (fragmentList !== void 0) {
|
|
40023
|
+
const list = need(sub(entry(fragmentList), 5), "fragment list");
|
|
40024
|
+
for (const item of many(list, 1)) {
|
|
40025
|
+
const fragment = customMap(entry(need(objectIndex(sub(item, 2)), "fragment reference")));
|
|
40026
|
+
if (fragment?.type !== "com.apple.notes.ICTTAudioRecording.Fragment")
|
|
40027
|
+
throw new Error("Unexpected fragment type");
|
|
40028
|
+
const identity = stringValue(getField(fragment.values.get("identity") ?? [], 4));
|
|
40029
|
+
const segments = [];
|
|
40030
|
+
const transcriptRef = objectIndex(fragment.values.get("transcript"));
|
|
40031
|
+
if (transcriptRef !== void 0) {
|
|
40032
|
+
const set = sub(entry(transcriptRef), 15);
|
|
40033
|
+
if (!set) throw new Error("Unsupported transcript container");
|
|
40034
|
+
const bySlot = /* @__PURE__ */ new Map();
|
|
40035
|
+
for (const element of many(sub(set, 2) ?? [], 1)) {
|
|
40036
|
+
const keyMap = customMap(entry(need(objectIndex(sub(element, 1)), "segment key")));
|
|
40037
|
+
const slot = varintValue(getField(keyMap?.values.get("UUIDIndex") ?? [], 2));
|
|
40038
|
+
const value = objectIndex(sub(element, 2));
|
|
40039
|
+
if (slot === void 0 || value === void 0) throw new Error("Invalid segment key");
|
|
40040
|
+
bySlot.set(slot, value);
|
|
40041
|
+
}
|
|
40042
|
+
const ordering = many(need(sub(set, 1), "transcript ordering"), 2).map((pair) => ({
|
|
40043
|
+
index: need(varintValue(getField(pair, 1)), "segment index"),
|
|
40044
|
+
uuid: getField(pair, 2)?.value
|
|
40045
|
+
})).sort((a, b) => a.index - b.index);
|
|
40046
|
+
for (const { uuid: uuid2 } of ordering) {
|
|
40047
|
+
if (!(uuid2 instanceof Uint8Array)) throw new Error("Invalid segment UUID");
|
|
40048
|
+
const slot = uuidSlot.get(Buffer.from(uuid2).toString("hex"));
|
|
40049
|
+
const segmentIndex = slot === void 0 ? void 0 : bySlot.get(slot);
|
|
40050
|
+
if (segmentIndex === void 0) throw new Error("Unresolved transcript segment");
|
|
40051
|
+
const segment = customMap(entry(segmentIndex));
|
|
40052
|
+
if (segment?.type !== "com.apple.notes.ICTTTranscriptSegment")
|
|
40053
|
+
throw new Error("Unexpected segment type");
|
|
40054
|
+
const text = registerPrimitive(segment.values.get("text"));
|
|
40055
|
+
if (typeof text !== "string") throw new Error("Segment without text");
|
|
40056
|
+
const start = registerPrimitive(segment.values.get("timestamp"));
|
|
40057
|
+
const duration3 = registerPrimitive(segment.values.get("duration"));
|
|
40058
|
+
const speaker = registerPrimitive(segment.values.get("speaker"));
|
|
40059
|
+
segments.push({
|
|
40060
|
+
text,
|
|
40061
|
+
...typeof start === "number" ? { start } : {},
|
|
40062
|
+
...typeof duration3 === "number" ? { duration: duration3 } : {},
|
|
40063
|
+
...typeof speaker === "string" && speaker ? { speaker } : {}
|
|
40064
|
+
});
|
|
40065
|
+
}
|
|
40066
|
+
}
|
|
40067
|
+
fragments.push({ ...identity ? { identity } : {}, segments });
|
|
40068
|
+
}
|
|
40069
|
+
}
|
|
40070
|
+
const summary = registerNoteText(recording.values.get("summary"));
|
|
40071
|
+
const topLineSummary = registerNoteText(recording.values.get("topLineSummary"));
|
|
40072
|
+
return {
|
|
40073
|
+
fragments,
|
|
40074
|
+
...summary ? { summary } : {},
|
|
40075
|
+
...topLineSummary ? { topLineSummary } : {}
|
|
40076
|
+
};
|
|
40077
|
+
}
|
|
40078
|
+
function joinSegments(segments) {
|
|
40079
|
+
let out = "";
|
|
40080
|
+
for (const { text } of segments) {
|
|
40081
|
+
if (!text) continue;
|
|
40082
|
+
const glue = !out || /\s$/u.test(out) || /^\s/u.test(text) || /^[.,!?;:%)\]}…'’]/u.test(text) ? "" : " ";
|
|
40083
|
+
out += glue + text;
|
|
40084
|
+
}
|
|
40085
|
+
return out.trim();
|
|
40086
|
+
}
|
|
40087
|
+
function parseNoteId(noteId3) {
|
|
40088
|
+
const match = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/ICNote\/p(\d+)$/.exec(noteId3);
|
|
40089
|
+
if (!match) {
|
|
40090
|
+
throw new AudioTranscriptError(
|
|
40091
|
+
"invalid_id",
|
|
40092
|
+
`Invalid note ID format: "${noteId3}". Expected format: x-coredata://UUID/ICNote/pNNN`
|
|
40093
|
+
);
|
|
40094
|
+
}
|
|
40095
|
+
return { store: match[1], pk: match[2] };
|
|
40096
|
+
}
|
|
40097
|
+
var AUDIO_UTI_SQL = "(a.ZTYPEUTI LIKE '%audio%' OR a.ZTYPEUTI IN ('public.mp3'))";
|
|
40098
|
+
function buildTranscriptSql(pk, available) {
|
|
40099
|
+
if (!/^\d+$/.test(pk)) throw new Error("Invalid primary key");
|
|
40100
|
+
const optional2 = (column, key, alias = "a") => available.has(column) ? `, '${key}', ${alias}.${column}` : "";
|
|
40101
|
+
const locked = available.has("ZISPASSWORDPROTECTED") ? "COALESCE(n.ZISPASSWORDPROTECTED, 0)" : "0";
|
|
40102
|
+
return [
|
|
40103
|
+
"BEGIN;",
|
|
40104
|
+
// Every statement yields exactly one row, so output lines stay positional.
|
|
40105
|
+
`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}));`,
|
|
40106
|
+
`SELECT COALESCE((SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = ${pk} LIMIT 1), '');`,
|
|
40107
|
+
"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};`,
|
|
40108
|
+
"COMMIT;"
|
|
40109
|
+
].join("\n");
|
|
40110
|
+
}
|
|
40111
|
+
var REQUIRED_COLUMNS = [
|
|
40112
|
+
"ZMERGEABLEDATA1",
|
|
40113
|
+
"ZPARENTATTACHMENT",
|
|
40114
|
+
"ZTYPEUTI",
|
|
40115
|
+
"ZNOTE",
|
|
40116
|
+
"ZIDENTIFIER"
|
|
40117
|
+
];
|
|
40118
|
+
function runSqlite(dbPath2, sql) {
|
|
40119
|
+
return execFileSync4("sqlite3", ["-readonly", dbPath2, sql], {
|
|
40120
|
+
encoding: "utf8",
|
|
40121
|
+
timeout: 1e4,
|
|
40122
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
40123
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
40124
|
+
});
|
|
40125
|
+
}
|
|
40126
|
+
function bodyAttachmentOrder(gzipped) {
|
|
40127
|
+
const doc = decodeMessage(gunzipSync3(gzipped, { maxOutputLength: 64 * 1024 * 1024 }));
|
|
40128
|
+
const body = embeddedMessage(getField(embeddedMessage(getField(doc, 2)) ?? [], 3));
|
|
40129
|
+
if (!body) throw new Error("Unsupported Notes document structure");
|
|
40130
|
+
const ids = [];
|
|
40131
|
+
for (const run of getFields(body, 5)) {
|
|
40132
|
+
const attachment = embeddedMessage(getField(embeddedMessage(run) ?? [], 12));
|
|
40133
|
+
const id2 = attachment && stringValue(getField(attachment, 1));
|
|
40134
|
+
if (id2 && !ids.includes(id2)) ids.push(id2);
|
|
40135
|
+
}
|
|
40136
|
+
return ids;
|
|
40137
|
+
}
|
|
40138
|
+
function readAudioTranscripts(noteId3, options = {}) {
|
|
40139
|
+
const { store, pk } = parseNoteId(noteId3);
|
|
40140
|
+
const dbPath2 = options.dbPath ?? NOTES_DB_PATH2;
|
|
40141
|
+
const maxSegments = Math.min(
|
|
40142
|
+
Math.max(1, Math.floor(options.maxSegments ?? DEFAULT_MAX_SEGMENTS)),
|
|
40143
|
+
MAX_SEGMENTS_LIMIT
|
|
40144
|
+
);
|
|
40145
|
+
let lines;
|
|
40146
|
+
try {
|
|
40147
|
+
const available = new Set(
|
|
40148
|
+
runSqlite(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").split("\n").map((line) => line.trim()).filter(Boolean)
|
|
40149
|
+
);
|
|
40150
|
+
const missing = REQUIRED_COLUMNS.filter((c) => !available.has(c));
|
|
40151
|
+
if (missing.length)
|
|
40152
|
+
throw new AudioTranscriptError(
|
|
40153
|
+
"query_error",
|
|
40154
|
+
`This macOS version's Notes database lacks ${missing.join(", ")}; stored transcripts cannot be read.`
|
|
40155
|
+
);
|
|
40156
|
+
lines = runSqlite(dbPath2, buildTranscriptSql(pk, available)).split("\n");
|
|
40157
|
+
} catch (error2) {
|
|
40158
|
+
if (error2 instanceof AudioTranscriptError) throw error2;
|
|
40159
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
40160
|
+
if (/authorization denied|unable to open database/i.test(message))
|
|
40161
|
+
throw new AudioTranscriptError(
|
|
40162
|
+
"no_fda",
|
|
40163
|
+
`Full Disk Access is required to read stored transcripts. Grant it to the app that launches this server, then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL}`
|
|
40164
|
+
);
|
|
40165
|
+
throw new AudioTranscriptError("query_error", "Failed to read the Notes database.");
|
|
40166
|
+
}
|
|
40167
|
+
const [noteLine = "{}", bodyLine = "", rowsLine = "[]"] = lines;
|
|
40168
|
+
const note = JSON.parse(noteLine || "{}");
|
|
40169
|
+
if (!note.found)
|
|
40170
|
+
throw new AudioTranscriptError(
|
|
40171
|
+
"not_found",
|
|
40172
|
+
`No note found in the database for ID "${noteId3}".`
|
|
40173
|
+
);
|
|
40174
|
+
if (note.locked)
|
|
40175
|
+
throw new AudioTranscriptError(
|
|
40176
|
+
"locked",
|
|
40177
|
+
"This note is password-protected; its transcripts are encrypted and cannot be read."
|
|
40178
|
+
);
|
|
40179
|
+
const rows = JSON.parse(rowsLine || "[]");
|
|
40180
|
+
let order;
|
|
40181
|
+
try {
|
|
40182
|
+
if (/^[0-9A-F]+$/i.test(bodyLine.trim()))
|
|
40183
|
+
order = bodyAttachmentOrder(Buffer.from(bodyLine.trim(), "hex"));
|
|
40184
|
+
} catch {
|
|
40185
|
+
order = void 0;
|
|
40186
|
+
}
|
|
40187
|
+
const ordered = order ? order.flatMap((id2) => rows.filter((row) => row.identifier === id2)) : [...rows].sort((a, b) => a.pk - b.pk);
|
|
40188
|
+
const attachments = ordered.map((row) => describeRow(row, store, options, maxSegments));
|
|
40189
|
+
return { id: noteId3, attachments, bodyOrder: Boolean(order), truncated: false };
|
|
40190
|
+
}
|
|
40191
|
+
function describeRow(row, store, options, maxSegments) {
|
|
40192
|
+
const childDurations = (row.fragments ?? []).map((f) => f.duration).filter((d) => typeof d === "number" && d > 0);
|
|
40193
|
+
const durationSeconds = typeof row.duration === "number" && row.duration > 0 ? row.duration : childDurations.length ? childDurations.reduce((a, b) => a + b, 0) : void 0;
|
|
40194
|
+
const base = {
|
|
40195
|
+
attachmentId: `x-coredata://${store}/ICAttachment/p${row.pk}`,
|
|
40196
|
+
identifier: row.identifier,
|
|
40197
|
+
typeUti: row.uti,
|
|
40198
|
+
status: "none",
|
|
40199
|
+
...durationSeconds !== void 0 ? { durationSeconds } : {},
|
|
40200
|
+
...typeof row.needsTranscription === "number" ? { needsTranscription: row.needsTranscription === 1 } : {}
|
|
40201
|
+
};
|
|
40202
|
+
if (!row.data) return base;
|
|
40203
|
+
let recording;
|
|
40204
|
+
try {
|
|
40205
|
+
recording = parseAudioRecording(Buffer.from(row.data, "hex"));
|
|
40206
|
+
} catch (error2) {
|
|
40207
|
+
return {
|
|
40208
|
+
...base,
|
|
40209
|
+
status: "undecodable",
|
|
40210
|
+
reason: error2 instanceof Error ? error2.message : String(error2)
|
|
40211
|
+
};
|
|
40212
|
+
}
|
|
40213
|
+
const multi = recording.fragments.length > 1;
|
|
40214
|
+
const all = recording.fragments.flatMap(
|
|
40215
|
+
(fragment, i) => fragment.segments.map((s) => multi ? { ...s, fragment: i } : s)
|
|
40216
|
+
);
|
|
40217
|
+
const text = recording.fragments.map((f) => joinSegments(f.segments)).filter(Boolean).join("\n\n");
|
|
40218
|
+
const speakers = [...new Set(all.flatMap((s) => s.speaker ? [s.speaker] : []))];
|
|
40219
|
+
return {
|
|
40220
|
+
...base,
|
|
40221
|
+
status: all.length ? "ok" : "none",
|
|
40222
|
+
fragmentCount: recording.fragments.length,
|
|
40223
|
+
wordCount: all.length,
|
|
40224
|
+
...text ? { text } : {},
|
|
40225
|
+
...speakers.length ? { speakers } : {},
|
|
40226
|
+
...recording.summary ? { summary: recording.summary } : {},
|
|
40227
|
+
...recording.topLineSummary ? { topLineSummary: recording.topLineSummary } : {},
|
|
40228
|
+
...options.includeSegments && all.length ? {
|
|
40229
|
+
segments: all.slice(0, maxSegments),
|
|
40230
|
+
...all.length > maxSegments ? { segmentsTruncated: true } : {}
|
|
40231
|
+
} : {}
|
|
40232
|
+
};
|
|
40233
|
+
}
|
|
40234
|
+
function fitTranscriptsToBudget(result, maxBytes, measure) {
|
|
40235
|
+
if (measure(result) <= maxBytes) return result;
|
|
40236
|
+
let next = {
|
|
40237
|
+
...result,
|
|
40238
|
+
truncated: true,
|
|
40239
|
+
attachments: result.attachments.map((a) => {
|
|
40240
|
+
if (!a.segments) return a;
|
|
40241
|
+
const rest = { ...a, segmentsTruncated: true };
|
|
40242
|
+
delete rest.segments;
|
|
40243
|
+
return rest;
|
|
40244
|
+
})
|
|
40245
|
+
};
|
|
40246
|
+
for (let share = 0.5; measure(next) > maxBytes && share > 1e-4; share /= 2) {
|
|
40247
|
+
next = {
|
|
40248
|
+
...next,
|
|
40249
|
+
attachments: next.attachments.map((a) => {
|
|
40250
|
+
const original = result.attachments.find((o) => o.attachmentId === a.attachmentId);
|
|
40251
|
+
if (!original?.text) return a;
|
|
40252
|
+
const keep = Math.floor(original.text.length * share);
|
|
40253
|
+
return { ...a, text: original.text.slice(0, keep), textTruncated: true };
|
|
40254
|
+
})
|
|
40255
|
+
};
|
|
40256
|
+
}
|
|
40257
|
+
return next;
|
|
40258
|
+
}
|
|
40259
|
+
var clock = (seconds) => {
|
|
40260
|
+
const total = Math.round(seconds);
|
|
40261
|
+
const h = Math.floor(total / 3600);
|
|
40262
|
+
const m = Math.floor(total % 3600 / 60);
|
|
40263
|
+
const s = total % 60;
|
|
40264
|
+
const mm = h ? String(m).padStart(2, "0") : String(m);
|
|
40265
|
+
return `${h ? `${h}:` : ""}${mm}:${String(s).padStart(2, "0")}`;
|
|
40266
|
+
};
|
|
40267
|
+
function formatTranscriptsText(result) {
|
|
40268
|
+
const count = result.attachments.length;
|
|
40269
|
+
if (count === 0) return `No audio attachments found in note ${result.id}.`;
|
|
40270
|
+
const lines = [`${count} audio attachment${count === 1 ? "" : "s"} in note ${result.id}:`];
|
|
40271
|
+
result.attachments.forEach((a, i) => {
|
|
40272
|
+
const facts = [
|
|
40273
|
+
`status ${a.status}`,
|
|
40274
|
+
...a.durationSeconds !== void 0 ? [clock(a.durationSeconds)] : [],
|
|
40275
|
+
...a.wordCount !== void 0 ? [`${a.wordCount} words`] : [],
|
|
40276
|
+
...a.fragmentCount && a.fragmentCount > 1 ? [`${a.fragmentCount} fragments`] : [],
|
|
40277
|
+
...a.speakers?.length ? [`${a.speakers.length} speakers`] : [],
|
|
40278
|
+
...a.reason ? [`reason: ${a.reason}`] : []
|
|
40279
|
+
];
|
|
40280
|
+
lines.push("", `[${i + 1}] ${a.attachmentId} (${facts.join(", ")})`);
|
|
40281
|
+
if (a.summary) lines.push(`Summary: ${a.summary}`);
|
|
40282
|
+
if (a.text) lines.push(a.textTruncated ? `${a.text} [truncated]` : a.text);
|
|
40283
|
+
});
|
|
40284
|
+
if (result.truncated)
|
|
40285
|
+
lines.push(
|
|
40286
|
+
"",
|
|
40287
|
+
"Some segments or text were left out to stay under the response size limit (APPLE_NOTES_MCP_EXPORT_MAX_BYTES)."
|
|
40288
|
+
);
|
|
40289
|
+
return lines.join("\n");
|
|
40290
|
+
}
|
|
40291
|
+
|
|
40292
|
+
// src/utils/noteTables.ts
|
|
40293
|
+
import { gunzipSync as gunzipSync4 } from "node:zlib";
|
|
40294
|
+
var sub2 = (f, n) => {
|
|
39907
40295
|
const value = embeddedMessage(getField(f, n));
|
|
39908
40296
|
if (!value) throw new Error(`Missing table field ${n}`);
|
|
39909
40297
|
return value;
|
|
@@ -39913,7 +40301,7 @@ var num = (f, n) => {
|
|
|
39913
40301
|
if (v === void 0) throw new Error(`Missing table index ${n}`);
|
|
39914
40302
|
return v;
|
|
39915
40303
|
};
|
|
39916
|
-
var
|
|
40304
|
+
var many2 = (f, n) => getFields(f, n).map((v) => {
|
|
39917
40305
|
const m = embeddedMessage(v);
|
|
39918
40306
|
if (!m) throw new Error("Invalid table entry");
|
|
39919
40307
|
return m;
|
|
@@ -39930,37 +40318,37 @@ function parseNoteTableCells(compressed) {
|
|
|
39930
40318
|
return decodeTable(compressed, false);
|
|
39931
40319
|
}
|
|
39932
40320
|
function decodeTable(compressed, strict) {
|
|
39933
|
-
const root = decodeMessage(
|
|
39934
|
-
const data =
|
|
40321
|
+
const root = decodeMessage(gunzipSync4(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
|
|
40322
|
+
const data = sub2(sub2(root, 2), 3), entries = many2(data, 3);
|
|
39935
40323
|
if (entries.length > 1e5) throw new Error("Table too large");
|
|
39936
40324
|
const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
|
|
39937
40325
|
const entry = (index) => {
|
|
39938
40326
|
if (!entries[index]) throw new Error("Invalid table reference");
|
|
39939
40327
|
return entries[index];
|
|
39940
40328
|
};
|
|
39941
|
-
const uuidIndex = (index) => num(
|
|
40329
|
+
const uuidIndex = (index) => num(sub2(many2(sub2(entry(index), 13), 3)[0], 2), 2);
|
|
39942
40330
|
const roots = entries.filter((e) => {
|
|
39943
40331
|
const map = embeddedMessage(getField(e, 13));
|
|
39944
40332
|
return map && types[num(map, 1)] === "com.apple.notes.ICTable";
|
|
39945
40333
|
});
|
|
39946
40334
|
if (roots.length !== 1) throw new Error("Ambiguous native table root");
|
|
39947
40335
|
const refs = new Map(
|
|
39948
|
-
|
|
40336
|
+
many2(sub2(roots[0], 13), 3).filter((m) => ["crRows", "crColumns", "cellColumns"].includes(keys[num(m, 1)] || "")).map((m) => [keys[num(m, 1)], num(sub2(m, 2), 6)])
|
|
39949
40337
|
);
|
|
39950
40338
|
const ordered = (key) => {
|
|
39951
40339
|
const ref = refs.get(key);
|
|
39952
40340
|
if (ref === void 0) throw new Error("Missing table dimension");
|
|
39953
|
-
const ordering =
|
|
39954
|
-
const ids =
|
|
40341
|
+
const ordering = sub2(sub2(entry(ref), 16), 1), array2 = sub2(ordering, 1);
|
|
40342
|
+
const ids = many2(array2, 2).map((a) => hex(getField(a, 2)));
|
|
39955
40343
|
const map = /* @__PURE__ */ new Map();
|
|
39956
40344
|
ids.forEach((id2, i) => {
|
|
39957
40345
|
const index = uuids.indexOf(id2);
|
|
39958
40346
|
if (index < 0) throw new Error("Missing dimension UUID");
|
|
39959
40347
|
map.set(index, i);
|
|
39960
40348
|
});
|
|
39961
|
-
const aliases =
|
|
39962
|
-
uuidIndex(num(
|
|
39963
|
-
uuidIndex(num(
|
|
40349
|
+
const aliases = many2(sub2(ordering, 2), 1).map((pair) => [
|
|
40350
|
+
uuidIndex(num(sub2(pair, 1), 6)),
|
|
40351
|
+
uuidIndex(num(sub2(pair, 2), 6))
|
|
39964
40352
|
]);
|
|
39965
40353
|
for (let pass = 0; pass < aliases.length + 1; pass++) {
|
|
39966
40354
|
let changed = false;
|
|
@@ -39980,13 +40368,13 @@ function decodeTable(compressed, strict) {
|
|
|
39980
40368
|
const incomplete = [];
|
|
39981
40369
|
const cellRef = refs.get("cellColumns");
|
|
39982
40370
|
if (cellRef === void 0) throw new Error("Missing table cells");
|
|
39983
|
-
for (const column of
|
|
39984
|
-
const ci = columns.map.get(uuidIndex(num(
|
|
39985
|
-
const cells = entry(num(
|
|
39986
|
-
for (const row of
|
|
39987
|
-
const ri = rows.map.get(uuidIndex(num(
|
|
40371
|
+
for (const column of many2(sub2(entry(cellRef), 6), 1)) {
|
|
40372
|
+
const ci = columns.map.get(uuidIndex(num(sub2(column, 1), 6)));
|
|
40373
|
+
const cells = entry(num(sub2(column, 2), 6));
|
|
40374
|
+
for (const row of many2(sub2(cells, 6), 1)) {
|
|
40375
|
+
const ri = rows.map.get(uuidIndex(num(sub2(row, 1), 6)));
|
|
39988
40376
|
if (ri === void 0 || ci === void 0) continue;
|
|
39989
|
-
const note =
|
|
40377
|
+
const note = sub2(entry(num(sub2(row, 2), 6)), 10);
|
|
39990
40378
|
const text = stringValue(getField(note, 2));
|
|
39991
40379
|
if (text === void 0 || text.includes("\uFFFC")) {
|
|
39992
40380
|
if (strict) throw new Error("Embedded or unsupported table cell");
|
|
@@ -40003,8 +40391,8 @@ function decodeTable(compressed, strict) {
|
|
|
40003
40391
|
}
|
|
40004
40392
|
const rtl = entries.some((e) => {
|
|
40005
40393
|
const map = embeddedMessage(getField(e, 13));
|
|
40006
|
-
return map &&
|
|
40007
|
-
(m) => stringValue(getField(
|
|
40394
|
+
return map && many2(map, 3).some(
|
|
40395
|
+
(m) => stringValue(getField(sub2(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
|
|
40008
40396
|
);
|
|
40009
40397
|
});
|
|
40010
40398
|
const width = columns.ids.length;
|
|
@@ -40081,11 +40469,11 @@ function collectNoteTables(rich, noteId3) {
|
|
|
40081
40469
|
}
|
|
40082
40470
|
|
|
40083
40471
|
// src/utils/smartFolders.ts
|
|
40084
|
-
import { execFileSync as
|
|
40472
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
40085
40473
|
import * as fs2 from "fs";
|
|
40086
40474
|
import * as os2 from "os";
|
|
40087
40475
|
import * as path2 from "path";
|
|
40088
|
-
var
|
|
40476
|
+
var NOTES_DB_PATH3 = path2.join(
|
|
40089
40477
|
os2.homedir(),
|
|
40090
40478
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
40091
40479
|
);
|
|
@@ -40376,7 +40764,7 @@ WHERE Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICHashtag')
|
|
|
40376
40764
|
AND ZSTANDARDIZEDCONTENT IS NOT NULL
|
|
40377
40765
|
AND COALESCE(ZMARKEDFORDELETION, 0) = 0;
|
|
40378
40766
|
COMMIT;`;
|
|
40379
|
-
var
|
|
40767
|
+
var REQUIRED_COLUMNS2 = [
|
|
40380
40768
|
"ZIDENTIFIER",
|
|
40381
40769
|
"ZTITLE2",
|
|
40382
40770
|
"ZFOLDERTYPE",
|
|
@@ -40430,21 +40818,21 @@ function buildSmartFolders(output) {
|
|
|
40430
40818
|
(a, b) => (a.account ?? "").localeCompare(b.account ?? "") || (a.name ?? "").localeCompare(b.name ?? "") || a.id.localeCompare(b.id)
|
|
40431
40819
|
);
|
|
40432
40820
|
}
|
|
40433
|
-
function
|
|
40434
|
-
return
|
|
40821
|
+
function runSqlite2(dbPath2, sql) {
|
|
40822
|
+
return execFileSync5("sqlite3", ["-readonly", dbPath2, sql], {
|
|
40435
40823
|
encoding: "utf8",
|
|
40436
40824
|
timeout: 5e3,
|
|
40437
40825
|
maxBuffer: 16 * 1024 * 1024,
|
|
40438
40826
|
stdio: ["pipe", "pipe", "pipe"]
|
|
40439
40827
|
});
|
|
40440
40828
|
}
|
|
40441
|
-
function readSmartFolders(dbPath2 =
|
|
40829
|
+
function readSmartFolders(dbPath2 = NOTES_DB_PATH3) {
|
|
40442
40830
|
if (!fs2.existsSync(dbPath2)) return { folders: null, error: "no_fda", message: FDA_MESSAGE };
|
|
40443
40831
|
try {
|
|
40444
40832
|
const columns = new Set(
|
|
40445
|
-
|
|
40833
|
+
runSqlite2(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").trim().split("\n")
|
|
40446
40834
|
);
|
|
40447
|
-
const missing =
|
|
40835
|
+
const missing = REQUIRED_COLUMNS2.filter((column) => !columns.has(column));
|
|
40448
40836
|
if (missing.length) {
|
|
40449
40837
|
return {
|
|
40450
40838
|
folders: null,
|
|
@@ -40452,7 +40840,7 @@ function readSmartFolders(dbPath2 = NOTES_DB_PATH2) {
|
|
|
40452
40840
|
message: `This Notes database does not have the columns smart folders need (${missing.join(", ")}).`
|
|
40453
40841
|
};
|
|
40454
40842
|
}
|
|
40455
|
-
return { folders: buildSmartFolders(
|
|
40843
|
+
return { folders: buildSmartFolders(runSqlite2(dbPath2, SMART_FOLDERS_SQL)) };
|
|
40456
40844
|
} catch (error2) {
|
|
40457
40845
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
40458
40846
|
if (message.includes("authorization denied") || message.includes("unable to open database")) {
|
|
@@ -40465,20 +40853,24 @@ function readSmartFolders(dbPath2 = NOTES_DB_PATH2) {
|
|
|
40465
40853
|
|
|
40466
40854
|
// src/utils/attachmentFs.ts
|
|
40467
40855
|
import {
|
|
40856
|
+
closeSync,
|
|
40857
|
+
constants,
|
|
40468
40858
|
existsSync as existsSync3,
|
|
40859
|
+
fstatSync,
|
|
40469
40860
|
lstatSync,
|
|
40470
40861
|
mkdirSync,
|
|
40471
40862
|
mkdtempSync,
|
|
40863
|
+
openSync,
|
|
40472
40864
|
readFileSync,
|
|
40473
40865
|
realpathSync,
|
|
40474
40866
|
rmSync,
|
|
40475
40867
|
statSync
|
|
40476
40868
|
} from "fs";
|
|
40477
|
-
import { dirname, isAbsolute, join as
|
|
40478
|
-
import { homedir as
|
|
40869
|
+
import { dirname, isAbsolute, join as join5, relative, resolve, sep } from "path";
|
|
40870
|
+
import { homedir as homedir5, tmpdir } from "os";
|
|
40479
40871
|
function allowedSaveRoots() {
|
|
40480
40872
|
return [
|
|
40481
|
-
resolve(
|
|
40873
|
+
resolve(homedir5()),
|
|
40482
40874
|
resolve(tmpdir()),
|
|
40483
40875
|
"/Volumes",
|
|
40484
40876
|
"/private/var/folders",
|
|
@@ -40552,7 +40944,7 @@ function assertSafeSavePath(p, roots = allowedSaveRoots()) {
|
|
|
40552
40944
|
if (suffix.split(sep).includes("..")) {
|
|
40553
40945
|
throw new Error(`Refusing to write outside allowed locations (home, temp, /Volumes): "${abs}"`);
|
|
40554
40946
|
}
|
|
40555
|
-
const canonicalDest = suffix ?
|
|
40947
|
+
const canonicalDest = suffix ? join5(canonicalAncestor, suffix) : canonicalAncestor;
|
|
40556
40948
|
const allowed = canonicalRoots(roots);
|
|
40557
40949
|
if (!isWithinRoots(canonicalAncestor, allowed) || !isWithinRoots(canonicalDest, allowed)) {
|
|
40558
40950
|
throw new Error(
|
|
@@ -40592,6 +40984,48 @@ function fileSize(p) {
|
|
|
40592
40984
|
function makeTempDir() {
|
|
40593
40985
|
return mkdtempSync(resolve(tmpdir(), "apple-notes-att-"));
|
|
40594
40986
|
}
|
|
40987
|
+
function readAllowedTextFile(p, maxBytes, roots = allowedSaveRoots()) {
|
|
40988
|
+
if (!p || !p.trim()) throw new Error("A content file path is required.");
|
|
40989
|
+
if (!isAbsolute(p)) throw new Error(`Content file path must be absolute: "${p}"`);
|
|
40990
|
+
const abs = resolve(p);
|
|
40991
|
+
if (!isWithinRoots(abs, roots))
|
|
40992
|
+
throw new Error(`Refusing to read outside allowed locations (home, temp, /Volumes): "${abs}"`);
|
|
40993
|
+
let canonical;
|
|
40994
|
+
try {
|
|
40995
|
+
canonical = canonicalize(abs);
|
|
40996
|
+
} catch {
|
|
40997
|
+
throw new Error(`Content file does not exist or cannot be resolved: "${abs}"`);
|
|
40998
|
+
}
|
|
40999
|
+
if (!isWithinRoots(canonical, canonicalRoots(roots)))
|
|
41000
|
+
throw new Error(
|
|
41001
|
+
`Refusing to read outside allowed locations (home, temp, /Volumes): "${abs}" resolves to "${canonical}".`
|
|
41002
|
+
);
|
|
41003
|
+
let descriptor;
|
|
41004
|
+
try {
|
|
41005
|
+
descriptor = openSync(abs, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
41006
|
+
} catch (error2) {
|
|
41007
|
+
if (error2.code === "ELOOP")
|
|
41008
|
+
throw new Error(`Refusing to read the symbolic link "${abs}".`);
|
|
41009
|
+
throw error2;
|
|
41010
|
+
}
|
|
41011
|
+
try {
|
|
41012
|
+
const stat = fstatSync(descriptor);
|
|
41013
|
+
if (!stat.isFile()) throw new Error(`Content file is not a regular file: "${abs}"`);
|
|
41014
|
+
if (stat.size === 0) throw new Error(`Content file is empty: "${abs}"`);
|
|
41015
|
+
if (stat.size > maxBytes)
|
|
41016
|
+
throw new Error(`Content file is ${stat.size} bytes, over the ${maxBytes}-byte limit.`);
|
|
41017
|
+
const bytes = readFileSync(descriptor);
|
|
41018
|
+
if (bytes.length !== stat.size)
|
|
41019
|
+
throw new Error("Content file changed while it was being read; try again");
|
|
41020
|
+
try {
|
|
41021
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes).replace(/^\uFEFF/, "");
|
|
41022
|
+
} catch {
|
|
41023
|
+
throw new Error(`Content file is not valid UTF-8 text: "${abs}"`);
|
|
41024
|
+
}
|
|
41025
|
+
} finally {
|
|
41026
|
+
closeSync(descriptor);
|
|
41027
|
+
}
|
|
41028
|
+
}
|
|
40595
41029
|
function cleanupTempDir(dir) {
|
|
40596
41030
|
try {
|
|
40597
41031
|
if (existsSync3(dir)) rmSync(dir, { recursive: true, force: true });
|
|
@@ -40602,8 +41036,8 @@ function cleanupTempDir(dir) {
|
|
|
40602
41036
|
// src/services/appleNotesManager.ts
|
|
40603
41037
|
var import_turndown = __toESM(require_turndown_cjs(), 1);
|
|
40604
41038
|
import { existsSync as existsSync4 } from "fs";
|
|
40605
|
-
import { homedir as
|
|
40606
|
-
import { join as
|
|
41039
|
+
import { homedir as homedir6 } from "os";
|
|
41040
|
+
import { join as join6 } from "path";
|
|
40607
41041
|
var FIELD_SEP = "";
|
|
40608
41042
|
var RECORD_SEP = "";
|
|
40609
41043
|
var AS_FIELD_SEP = "(character id 31)";
|
|
@@ -40810,10 +41244,10 @@ function buildAccountResolution(account) {
|
|
|
40810
41244
|
if ${AS_ACCOUNT_REF} is missing value then error "${ACCOUNT_RESOLUTION_ERROR} Account \\"${safeAccount}\\" not found"
|
|
40811
41245
|
`;
|
|
40812
41246
|
}
|
|
40813
|
-
function buildAccountScopedScript(
|
|
41247
|
+
function buildAccountScopedScript(scope2, command) {
|
|
40814
41248
|
return `
|
|
40815
41249
|
tell application "Notes"
|
|
40816
|
-
${buildAccountResolution(
|
|
41250
|
+
${buildAccountResolution(scope2.account)}
|
|
40817
41251
|
tell ${AS_ACCOUNT_REF}
|
|
40818
41252
|
${command}
|
|
40819
41253
|
end tell
|
|
@@ -40831,7 +41265,7 @@ function getNoteLinkFromDB(coreDataId2) {
|
|
|
40831
41265
|
const match = coreDataId2.match(/\/p(\d+)$/);
|
|
40832
41266
|
if (!match) return null;
|
|
40833
41267
|
const pk = parseInt(match[1], 10);
|
|
40834
|
-
const dbPath2 =
|
|
41268
|
+
const dbPath2 = join6(homedir6(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
|
|
40835
41269
|
if (!existsSync4(dbPath2)) return null;
|
|
40836
41270
|
try {
|
|
40837
41271
|
const { DatabaseSync } = __require("node:sqlite");
|
|
@@ -41393,11 +41827,20 @@ var AppleNotesManager = class {
|
|
|
41393
41827
|
const safeExpectedBody = escapeHtmlForAppleScript(expectedBody);
|
|
41394
41828
|
const script = buildAppLevelScript(`
|
|
41395
41829
|
set noteRef to note id "${safeId}"
|
|
41830
|
+
set originalFolder to missing value
|
|
41831
|
+
try
|
|
41832
|
+
set originalFolder to container of noteRef
|
|
41833
|
+
end try
|
|
41396
41834
|
set currentBody to body of noteRef
|
|
41397
41835
|
considering case
|
|
41398
41836
|
if currentBody is not "${safeExpectedBody}" and currentBody is not "${safeExpectedBody}" & linefeed then return "SAFETY_CONFLICT"
|
|
41399
41837
|
delete noteRef
|
|
41400
41838
|
end considering
|
|
41839
|
+
if originalFolder is not missing value then
|
|
41840
|
+
try
|
|
41841
|
+
if (id of notes of originalFolder) contains "${safeId}" then return "SAFETY_NOT_DELETED"
|
|
41842
|
+
end try
|
|
41843
|
+
end if
|
|
41401
41844
|
return "SAFETY_DELETED"
|
|
41402
41845
|
`);
|
|
41403
41846
|
const result = executeMutationAppleScript(script);
|
|
@@ -41407,6 +41850,7 @@ var AppleNotesManager = class {
|
|
|
41407
41850
|
}
|
|
41408
41851
|
const status = result.output.trim();
|
|
41409
41852
|
if (status === "SAFETY_CONFLICT") return { status: "conflict" };
|
|
41853
|
+
if (status === "SAFETY_NOT_DELETED") return { status: "not-deleted" };
|
|
41410
41854
|
return status === "SAFETY_DELETED" ? { status: "deleted" } : { status: "failed" };
|
|
41411
41855
|
}
|
|
41412
41856
|
/**
|
|
@@ -43140,18 +43584,31 @@ var AppleNotesManager = class {
|
|
|
43140
43584
|
}
|
|
43141
43585
|
return markdown;
|
|
43142
43586
|
}
|
|
43587
|
+
/**
|
|
43588
|
+
* Reads the transcripts Notes has stored for a note's top-level audio
|
|
43589
|
+
* recordings, one entry per attachment in body order. Read-only: queries the
|
|
43590
|
+
* NoteStore database with `sqlite3 -readonly` and needs Full Disk Access.
|
|
43591
|
+
*
|
|
43592
|
+
* @param id - CoreData URL identifier for the note
|
|
43593
|
+
* @param options - word-level segment inclusion and cap
|
|
43594
|
+
* @throws AudioTranscriptError for an invalid id, a missing or locked note,
|
|
43595
|
+
* missing Full Disk Access, or a database read failure
|
|
43596
|
+
*/
|
|
43597
|
+
getAudioTranscripts(id2, options = {}) {
|
|
43598
|
+
return readAudioTranscripts(id2, options);
|
|
43599
|
+
}
|
|
43143
43600
|
};
|
|
43144
43601
|
|
|
43145
43602
|
// src/utils/syncDetection.ts
|
|
43146
|
-
import { execFileSync as
|
|
43603
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
43147
43604
|
import * as fs3 from "fs";
|
|
43148
43605
|
import * as path3 from "path";
|
|
43149
43606
|
import * as os3 from "os";
|
|
43150
|
-
var
|
|
43607
|
+
var NOTES_DB_PATH4 = path3.join(
|
|
43151
43608
|
os3.homedir(),
|
|
43152
43609
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
43153
43610
|
);
|
|
43154
|
-
var WAL_PATH = `${
|
|
43611
|
+
var WAL_PATH = `${NOTES_DB_PATH4}-wal`;
|
|
43155
43612
|
var RECENT_ACTIVITY_THRESHOLD_SECONDS = 5;
|
|
43156
43613
|
var SYNC_STATUS_CACHE_TTL_MS = 2e3;
|
|
43157
43614
|
var cachedSyncStatus = null;
|
|
@@ -43167,7 +43624,7 @@ function getSyncStatus(useCache = true) {
|
|
|
43167
43624
|
recentActivity: false
|
|
43168
43625
|
};
|
|
43169
43626
|
try {
|
|
43170
|
-
if (!fs3.existsSync(
|
|
43627
|
+
if (!fs3.existsSync(NOTES_DB_PATH4)) {
|
|
43171
43628
|
status.error = "Notes database not found";
|
|
43172
43629
|
cachedSyncStatus = status;
|
|
43173
43630
|
cacheTimestamp = Date.now();
|
|
@@ -43188,9 +43645,9 @@ function getSyncStatus(useCache = true) {
|
|
|
43188
43645
|
WHERE object.ZCLOUDSTATE = state.Z_PK
|
|
43189
43646
|
);
|
|
43190
43647
|
`;
|
|
43191
|
-
const result =
|
|
43648
|
+
const result = execFileSync6(
|
|
43192
43649
|
"sqlite3",
|
|
43193
|
-
["-readonly",
|
|
43650
|
+
["-readonly", NOTES_DB_PATH4, query.replace(/\n/g, " ")],
|
|
43194
43651
|
{
|
|
43195
43652
|
encoding: "utf8",
|
|
43196
43653
|
timeout: 5e3,
|
|
@@ -43247,11 +43704,11 @@ function withSyncAwarenessSync(operation, fn) {
|
|
|
43247
43704
|
}
|
|
43248
43705
|
|
|
43249
43706
|
// src/utils/noteMetadata.ts
|
|
43250
|
-
import { execFileSync as
|
|
43707
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
43251
43708
|
import * as fs4 from "fs";
|
|
43252
43709
|
import * as path4 from "path";
|
|
43253
43710
|
import * as os4 from "os";
|
|
43254
|
-
var
|
|
43711
|
+
var NOTES_DB_PATH5 = path4.join(
|
|
43255
43712
|
os4.homedir(),
|
|
43256
43713
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
43257
43714
|
);
|
|
@@ -43267,15 +43724,15 @@ var COLUMN_MAP = [
|
|
|
43267
43724
|
{ key: "widgetSnippet", column: "ZWIDGETSNIPPET", type: "text" },
|
|
43268
43725
|
{ key: "smartFolderQuery", column: "ZSMARTFOLDERQUERYJSON", type: "text" }
|
|
43269
43726
|
];
|
|
43270
|
-
function
|
|
43271
|
-
return
|
|
43727
|
+
function runSqlite3(query) {
|
|
43728
|
+
return execFileSync7("sqlite3", ["-readonly", NOTES_DB_PATH5, query], {
|
|
43272
43729
|
encoding: "utf8",
|
|
43273
43730
|
timeout: 5e3,
|
|
43274
43731
|
stdio: ["pipe", "pipe", "pipe"]
|
|
43275
43732
|
}).trim();
|
|
43276
43733
|
}
|
|
43277
43734
|
function presentColumns() {
|
|
43278
|
-
const out =
|
|
43735
|
+
const out = runSqlite3("PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);");
|
|
43279
43736
|
const cols = /* @__PURE__ */ new Set();
|
|
43280
43737
|
for (const line of out.split("\n")) {
|
|
43281
43738
|
const name = line.split("|")[1];
|
|
@@ -43293,7 +43750,7 @@ function getNoteMetadata(noteId3) {
|
|
|
43293
43750
|
};
|
|
43294
43751
|
}
|
|
43295
43752
|
const pk = pkMatch[1];
|
|
43296
|
-
if (!fs4.existsSync(
|
|
43753
|
+
if (!fs4.existsSync(NOTES_DB_PATH5)) {
|
|
43297
43754
|
return { metadata: null, error: "no_fda", message: FDA_MESSAGE2 };
|
|
43298
43755
|
}
|
|
43299
43756
|
try {
|
|
@@ -43303,7 +43760,7 @@ function getNoteMetadata(noteId3) {
|
|
|
43303
43760
|
return { metadata: {} };
|
|
43304
43761
|
}
|
|
43305
43762
|
const pairs = selected.map((c) => `'${c.key}', ${c.column}`).join(", ");
|
|
43306
|
-
const row =
|
|
43763
|
+
const row = runSqlite3(
|
|
43307
43764
|
`SELECT json_object(${pairs}) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = ${pk};`
|
|
43308
43765
|
);
|
|
43309
43766
|
if (!row) {
|
|
@@ -43336,11 +43793,11 @@ function getNoteMetadata(noteId3) {
|
|
|
43336
43793
|
}
|
|
43337
43794
|
|
|
43338
43795
|
// src/utils/noteIdentifiers.ts
|
|
43339
|
-
import { execFileSync as
|
|
43796
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
43340
43797
|
import * as fs5 from "fs";
|
|
43341
43798
|
import * as os5 from "os";
|
|
43342
43799
|
import * as path5 from "path";
|
|
43343
|
-
var
|
|
43800
|
+
var NOTES_DB_PATH6 = path5.join(
|
|
43344
43801
|
os5.homedir(),
|
|
43345
43802
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
43346
43803
|
);
|
|
@@ -43418,7 +43875,7 @@ function runJsonQuery(sql, dbPath2) {
|
|
|
43418
43875
|
if (!fs5.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
|
|
43419
43876
|
let out;
|
|
43420
43877
|
try {
|
|
43421
|
-
out =
|
|
43878
|
+
out = execFileSync8("sqlite3", ["-readonly", dbPath2, sql], {
|
|
43422
43879
|
encoding: "utf8",
|
|
43423
43880
|
timeout: 5e3,
|
|
43424
43881
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -43438,7 +43895,7 @@ function runJsonQuery(sql, dbPath2) {
|
|
|
43438
43895
|
function coreDataId(store, entity2, pk) {
|
|
43439
43896
|
return `x-coredata://${store}/${entity2}/p${pk}`;
|
|
43440
43897
|
}
|
|
43441
|
-
function resolveIdentifiers(values, entity2, dbPath2 =
|
|
43898
|
+
function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH6) {
|
|
43442
43899
|
const resolved = /* @__PURE__ */ new Map();
|
|
43443
43900
|
const keys = [];
|
|
43444
43901
|
const uuids = [];
|
|
@@ -43484,7 +43941,7 @@ function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH5) {
|
|
|
43484
43941
|
return resolved;
|
|
43485
43942
|
}
|
|
43486
43943
|
var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
|
|
43487
|
-
function lookupStableIdentifiers(ids, entity2, dbPath2 =
|
|
43944
|
+
function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH6) {
|
|
43488
43945
|
const result = /* @__PURE__ */ new Map();
|
|
43489
43946
|
const wanted = /* @__PURE__ */ new Map();
|
|
43490
43947
|
for (const id2 of ids) {
|
|
@@ -43520,7 +43977,7 @@ function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH5) {
|
|
|
43520
43977
|
}
|
|
43521
43978
|
return result;
|
|
43522
43979
|
}
|
|
43523
|
-
function withStableIdentifiers(items, entity2, dbPath2 =
|
|
43980
|
+
function withStableIdentifiers(items, entity2, dbPath2 = NOTES_DB_PATH6) {
|
|
43524
43981
|
const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
|
|
43525
43982
|
if (ids.length === 0) return items;
|
|
43526
43983
|
const found = lookupStableIdentifiers(ids, entity2, dbPath2);
|
|
@@ -44108,12 +44565,12 @@ function positiveTextTerms(node, negated = false) {
|
|
|
44108
44565
|
}
|
|
44109
44566
|
|
|
44110
44567
|
// src/utils/noteQueryStore.ts
|
|
44111
|
-
import { execFileSync as
|
|
44568
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
44112
44569
|
import * as fs6 from "fs";
|
|
44113
44570
|
import * as os6 from "os";
|
|
44114
44571
|
import * as path6 from "path";
|
|
44115
|
-
import { gunzipSync as
|
|
44116
|
-
var
|
|
44572
|
+
import { gunzipSync as gunzipSync5 } from "zlib";
|
|
44573
|
+
var NOTES_DB_PATH7 = path6.join(
|
|
44117
44574
|
os6.homedir(),
|
|
44118
44575
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
44119
44576
|
);
|
|
@@ -44201,13 +44658,13 @@ function countWords(text) {
|
|
|
44201
44658
|
}
|
|
44202
44659
|
return count;
|
|
44203
44660
|
}
|
|
44204
|
-
var
|
|
44661
|
+
var REQUIRED_COLUMNS3 = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
|
|
44205
44662
|
function col(available, alias, name) {
|
|
44206
44663
|
return available.has(name) ? `${alias}.${name}` : "NULL";
|
|
44207
44664
|
}
|
|
44208
44665
|
var entity = (name) => `(SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME='${name}')`;
|
|
44209
44666
|
function buildScanSql(available, options) {
|
|
44210
|
-
const missing =
|
|
44667
|
+
const missing = REQUIRED_COLUMNS3.filter((c) => !available.has(c));
|
|
44211
44668
|
if (missing.length) {
|
|
44212
44669
|
throw new NoteQueryStoreError(
|
|
44213
44670
|
`This macOS version's Notes database lacks columns query-notes needs (${missing.join(", ")}).`,
|
|
@@ -44248,8 +44705,8 @@ function buildScanSql(available, options) {
|
|
|
44248
44705
|
"COMMIT;"
|
|
44249
44706
|
].join(" ");
|
|
44250
44707
|
}
|
|
44251
|
-
function
|
|
44252
|
-
return
|
|
44708
|
+
function runSqlite4(dbPath2, query) {
|
|
44709
|
+
return execFileSync9("sqlite3", ["-readonly", dbPath2, query], {
|
|
44253
44710
|
encoding: "utf8",
|
|
44254
44711
|
timeout: 3e4,
|
|
44255
44712
|
maxBuffer: 512 * 1024 * 1024,
|
|
@@ -44258,7 +44715,7 @@ function runSqlite3(dbPath2, query) {
|
|
|
44258
44715
|
}
|
|
44259
44716
|
function presentColumns2(dbPath2) {
|
|
44260
44717
|
const cols = /* @__PURE__ */ new Set();
|
|
44261
|
-
for (const line of
|
|
44718
|
+
for (const line of runSqlite4(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
|
|
44262
44719
|
const name = line.split("|")[1];
|
|
44263
44720
|
if (name) cols.add(name);
|
|
44264
44721
|
}
|
|
@@ -44313,12 +44770,12 @@ function queryNotes(expression, options = {}) {
|
|
|
44313
44770
|
const includeDeleted = options.includeDeleted ?? false;
|
|
44314
44771
|
const withBodies = needsContent(ast);
|
|
44315
44772
|
const withTags = needsTags(ast);
|
|
44316
|
-
const dbPath2 = options.dbPath ??
|
|
44773
|
+
const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
|
|
44317
44774
|
if (!fs6.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
|
|
44318
44775
|
let output;
|
|
44319
44776
|
try {
|
|
44320
44777
|
const available = presentColumns2(dbPath2);
|
|
44321
|
-
output =
|
|
44778
|
+
output = runSqlite4(
|
|
44322
44779
|
dbPath2,
|
|
44323
44780
|
buildScanSql(available, { scanLimit, includeDeleted, withBodies, withTags })
|
|
44324
44781
|
);
|
|
@@ -44363,7 +44820,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44363
44820
|
const locked = Boolean(row.locked);
|
|
44364
44821
|
let decoded;
|
|
44365
44822
|
let content;
|
|
44366
|
-
const
|
|
44823
|
+
const decode2 = () => {
|
|
44367
44824
|
if (decoded !== void 0) return decoded;
|
|
44368
44825
|
decoded = null;
|
|
44369
44826
|
if (locked) return decoded;
|
|
@@ -44374,7 +44831,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44374
44831
|
try {
|
|
44375
44832
|
decoded = decodeNoteBody(
|
|
44376
44833
|
new Uint8Array(
|
|
44377
|
-
|
|
44834
|
+
gunzipSync5(Buffer.from(row.data, "hex"), { maxOutputLength: 32 * 1024 * 1024 })
|
|
44378
44835
|
)
|
|
44379
44836
|
);
|
|
44380
44837
|
} catch {
|
|
@@ -44394,7 +44851,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44394
44851
|
modified: coreDataMs(row.modified),
|
|
44395
44852
|
content: () => {
|
|
44396
44853
|
if (content !== void 0) return content;
|
|
44397
|
-
const body2 =
|
|
44854
|
+
const body2 = decode2();
|
|
44398
44855
|
if (!body2) return content = null;
|
|
44399
44856
|
const text = body2.text;
|
|
44400
44857
|
const firstBreak = text.indexOf("\n");
|
|
@@ -44419,7 +44876,7 @@ function queryNotes(expression, options = {}) {
|
|
|
44419
44876
|
if (!evaluateNoteQuery(ast, note)) continue;
|
|
44420
44877
|
matched++;
|
|
44421
44878
|
if (hits.length >= limit) continue;
|
|
44422
|
-
const body = locked ? null :
|
|
44879
|
+
const body = locked ? null : decode2();
|
|
44423
44880
|
hits.push({
|
|
44424
44881
|
id: `x-coredata://${uuid2}/ICNote/p${row.pk}`,
|
|
44425
44882
|
title: row.title ?? "",
|
|
@@ -44449,10 +44906,10 @@ function queryNotes(expression, options = {}) {
|
|
|
44449
44906
|
import { spawnSync } from "child_process";
|
|
44450
44907
|
|
|
44451
44908
|
// src/services/nativeTags.ts
|
|
44452
|
-
import { execFileSync as
|
|
44909
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
44453
44910
|
import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
|
|
44454
44911
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
44455
|
-
import { join as
|
|
44912
|
+
import { join as join11 } from "node:path";
|
|
44456
44913
|
|
|
44457
44914
|
// src/services/shortcutConsent.ts
|
|
44458
44915
|
function shortcutConsentHint(shortcut) {
|
|
@@ -44528,7 +44985,7 @@ function addNativeTags(request, deps) {
|
|
|
44528
44985
|
};
|
|
44529
44986
|
}
|
|
44530
44987
|
function listInstalledShortcuts() {
|
|
44531
|
-
return
|
|
44988
|
+
return execFileSync10("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
|
|
44532
44989
|
encoding: "utf8",
|
|
44533
44990
|
timeout: 15e3,
|
|
44534
44991
|
maxBuffer: 1024 * 1024,
|
|
@@ -44554,11 +45011,11 @@ function runNativeTagsShortcut(input) {
|
|
|
44554
45011
|
const status = nativeTagsStatus();
|
|
44555
45012
|
if (!status.installed)
|
|
44556
45013
|
throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
|
|
44557
|
-
const directory = mkdtempSync2(
|
|
45014
|
+
const directory = mkdtempSync2(join11(tmpdir2(), "apple-notes-native-tags-"));
|
|
44558
45015
|
try {
|
|
44559
|
-
const path7 =
|
|
45016
|
+
const path7 = join11(directory, "request.json");
|
|
44560
45017
|
writeFileSync(path7, JSON.stringify(input), { mode: 384 });
|
|
44561
|
-
|
|
45018
|
+
execFileSync10("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path7], {
|
|
44562
45019
|
encoding: "utf8",
|
|
44563
45020
|
timeout: 6e4,
|
|
44564
45021
|
maxBuffer: 1024 * 1024,
|
|
@@ -44577,14 +45034,29 @@ function runNativeTagsShortcut(input) {
|
|
|
44577
45034
|
}
|
|
44578
45035
|
|
|
44579
45036
|
// src/services/backgroundNotes.ts
|
|
44580
|
-
import { execFileSync as
|
|
45037
|
+
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
44581
45038
|
import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
|
|
44582
45039
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
44583
|
-
import { join as
|
|
45040
|
+
import { join as join12 } from "node:path";
|
|
44584
45041
|
|
|
44585
45042
|
// src/utils/appendMarkdown.ts
|
|
44586
45043
|
var escape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
44587
|
-
|
|
45044
|
+
var TASK_GLYPHS = { open: "\u2610", done: "\u2611" };
|
|
45045
|
+
var TASK_ITEM = /^\[([ xX])\][ \t]+(\S.*)$/;
|
|
45046
|
+
function stripDuplicateTitleHeading(markdown, title) {
|
|
45047
|
+
const normalized2 = markdown.replace(/^\uFEFF/, "");
|
|
45048
|
+
const lines = normalized2.split(/\r?\n/);
|
|
45049
|
+
if (lines[0] !== `# ${title}`) return { content: markdown, stripped: false };
|
|
45050
|
+
const rest = lines.slice(lines.length > 1 && lines[1] === "" ? 2 : 1);
|
|
45051
|
+
return { content: rest.join("\n"), stripped: true };
|
|
45052
|
+
}
|
|
45053
|
+
function countTaskItems(markdown) {
|
|
45054
|
+
return markdown.replace(/\r\n/g, "\n").split("\n").filter((line) => {
|
|
45055
|
+
const item = /^[-+*]\s+(.+)$/.exec(line);
|
|
45056
|
+
return item !== null && TASK_ITEM.test(item[1]);
|
|
45057
|
+
}).length;
|
|
45058
|
+
}
|
|
45059
|
+
function appendMarkdownHtml(markdown, options = {}) {
|
|
44588
45060
|
if (markdown.includes("\uE000") || markdown.includes("\uE001"))
|
|
44589
45061
|
throw new Error("Unsupported reserved characters");
|
|
44590
45062
|
if (Array.from(markdown).some((c) => c.charCodeAt(0) < 32 && !["\n", "\r", " "].includes(c)))
|
|
@@ -44623,7 +45095,8 @@ function appendMarkdownHtml(markdown) {
|
|
|
44623
45095
|
list = kind;
|
|
44624
45096
|
html += `<${kind}>`;
|
|
44625
45097
|
}
|
|
44626
|
-
|
|
45098
|
+
const task = options.taskGlyphs && item[1] ? TASK_ITEM.exec(item[2]) : null;
|
|
45099
|
+
html += task ? `<li>${task[1] === " " ? TASK_GLYPHS.open : TASK_GLYPHS.done} ${inline(task[2])}</li>` : `<li>${inline(item[2])}</li>`;
|
|
44627
45100
|
continue;
|
|
44628
45101
|
}
|
|
44629
45102
|
close();
|
|
@@ -44769,9 +45242,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
44769
45242
|
throw new Error(
|
|
44770
45243
|
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
44771
45244
|
);
|
|
44772
|
-
const directory = mkdtempSync3(
|
|
45245
|
+
const directory = mkdtempSync3(join12(tmpdir3(), "apple-notes-background-"));
|
|
44773
45246
|
try {
|
|
44774
|
-
const file =
|
|
45247
|
+
const file = join12(directory, "request.json");
|
|
44775
45248
|
writeFileSync2(
|
|
44776
45249
|
file,
|
|
44777
45250
|
JSON.stringify({
|
|
@@ -44791,9 +45264,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
44791
45264
|
{ mode: 384 }
|
|
44792
45265
|
);
|
|
44793
45266
|
try {
|
|
44794
|
-
|
|
45267
|
+
execFileSync11("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
|
|
44795
45268
|
encoding: "utf8",
|
|
44796
|
-
timeout: 6e4,
|
|
45269
|
+
timeout: callTimeoutMs() ?? 6e4,
|
|
44797
45270
|
maxBuffer: 1024 * 1024,
|
|
44798
45271
|
stdio: ["ignore", "pipe", "pipe"]
|
|
44799
45272
|
});
|
|
@@ -44808,9 +45281,9 @@ function backgroundDependencies(manager) {
|
|
|
44808
45281
|
return {
|
|
44809
45282
|
read: (id2) => readBackgroundSnapshot(manager, id2),
|
|
44810
45283
|
run: runBackgroundShortcut,
|
|
44811
|
-
candidates: (title,
|
|
45284
|
+
candidates: (title, scope2) => manager.listAccounts().flatMap(
|
|
44812
45285
|
(account) => manager.searchNotes(title, false, account.name).filter(
|
|
44813
|
-
(note) => note.title === title && !note.passwordProtected && manager.getNotePlaintextById(note.id).includes(
|
|
45286
|
+
(note) => note.title === title && !note.passwordProtected && manager.getNotePlaintextById(note.id).includes(scope2)
|
|
44814
45287
|
).map((note) => note.id)
|
|
44815
45288
|
)
|
|
44816
45289
|
};
|
|
@@ -45083,7 +45556,7 @@ ${request.content}`
|
|
|
45083
45556
|
}
|
|
45084
45557
|
|
|
45085
45558
|
// src/services/capabilityMatrix.ts
|
|
45086
|
-
import { execFileSync as
|
|
45559
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
45087
45560
|
import { release } from "node:os";
|
|
45088
45561
|
var MACOS_SHORTCUTS_CLI = "12.0";
|
|
45089
45562
|
var MACOS_MARKDOWN_IMPORT = "26.0";
|
|
@@ -45290,7 +45763,7 @@ function evaluateFeatures(env, features = FEATURES) {
|
|
|
45290
45763
|
function readMacOSVersion() {
|
|
45291
45764
|
if (process.platform !== "darwin") return null;
|
|
45292
45765
|
try {
|
|
45293
|
-
const out =
|
|
45766
|
+
const out = execFileSync12("/usr/bin/sw_vers", ["-productVersion"], {
|
|
45294
45767
|
encoding: "utf8",
|
|
45295
45768
|
timeout: 3e3,
|
|
45296
45769
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -45444,12 +45917,12 @@ function formatDoctorReport(r) {
|
|
|
45444
45917
|
|
|
45445
45918
|
// src/services/fileConfig.ts
|
|
45446
45919
|
import { existsSync as existsSync9, readFileSync as readFileSync2 } from "fs";
|
|
45447
|
-
import { join as
|
|
45448
|
-
import { homedir as
|
|
45920
|
+
import { join as join13 } from "path";
|
|
45921
|
+
import { homedir as homedir11 } from "os";
|
|
45449
45922
|
function fileConfigPath(env = process.env) {
|
|
45450
45923
|
const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
|
|
45451
45924
|
if (override && override.trim()) return override.trim();
|
|
45452
|
-
return
|
|
45925
|
+
return join13(homedir11(), "Library", "Application Support", "apple-notes-mcp", "config.json");
|
|
45453
45926
|
}
|
|
45454
45927
|
function loadFileConfig(env = process.env, path7 = fileConfigPath(env)) {
|
|
45455
45928
|
const applied = [];
|
|
@@ -45752,11 +46225,11 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
|
|
|
45752
46225
|
}
|
|
45753
46226
|
|
|
45754
46227
|
// src/utils/noteBlocks.ts
|
|
45755
|
-
import { execFileSync as
|
|
46228
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
45756
46229
|
import { existsSync as existsSync10 } from "node:fs";
|
|
45757
|
-
import { homedir as
|
|
45758
|
-
import { join as
|
|
45759
|
-
import { gunzipSync as
|
|
46230
|
+
import { homedir as homedir12 } from "node:os";
|
|
46231
|
+
import { join as join14 } from "node:path";
|
|
46232
|
+
import { gunzipSync as gunzipSync6 } from "node:zlib";
|
|
45760
46233
|
var NoteBlocksError = class extends Error {
|
|
45761
46234
|
code;
|
|
45762
46235
|
constructor(code, message) {
|
|
@@ -45805,7 +46278,7 @@ var stringOf = (fields, n) => {
|
|
|
45805
46278
|
const b = bytesOf(fields, n);
|
|
45806
46279
|
return b ? utf8.decode(b) : void 0;
|
|
45807
46280
|
};
|
|
45808
|
-
var
|
|
46281
|
+
var sub3 = (fields, n) => {
|
|
45809
46282
|
const b = bytesOf(fields, n);
|
|
45810
46283
|
return b ? decodeWireFields(b) : void 0;
|
|
45811
46284
|
};
|
|
@@ -45838,7 +46311,7 @@ function decodeParagraph(fields, tally) {
|
|
|
45838
46311
|
blockQuote: (varintOf(fields, 8) ?? 0) !== 0
|
|
45839
46312
|
};
|
|
45840
46313
|
if (attrs.styleType === -1) attrs.styleType = null;
|
|
45841
|
-
const checklist =
|
|
46314
|
+
const checklist = sub3(fields, 5);
|
|
45842
46315
|
const checklistId = checklist && bytesOf(checklist, 1);
|
|
45843
46316
|
if (attrs.styleType === 103 && checklistId)
|
|
45844
46317
|
attrs.checklist = { id: hex2(checklistId), done: varintOf(checklist, 2) === 1 };
|
|
@@ -45848,7 +46321,7 @@ function decodeParagraph(fields, tally) {
|
|
|
45848
46321
|
}
|
|
45849
46322
|
function decodeInline(fields) {
|
|
45850
46323
|
const inline = {};
|
|
45851
|
-
const font =
|
|
46324
|
+
const font = sub3(fields, 3);
|
|
45852
46325
|
const hints = font ? varintOf(font, 3) ?? 0 : 0;
|
|
45853
46326
|
const weight = varintOf(fields, 5) ?? 0;
|
|
45854
46327
|
if (weight === 1 || weight === 3 || hints & 1) inline.bold = true;
|
|
@@ -45858,7 +46331,7 @@ function decodeInline(fields) {
|
|
|
45858
46331
|
const baseline = varintOf(fields, 8) ?? 0;
|
|
45859
46332
|
if (baseline > 0) inline.superscript = true;
|
|
45860
46333
|
if (baseline < 0) inline.subscript = true;
|
|
45861
|
-
const color =
|
|
46334
|
+
const color = sub3(fields, 10);
|
|
45862
46335
|
const colorValue = color && colorOf(color);
|
|
45863
46336
|
if (colorValue) inline.color = colorValue;
|
|
45864
46337
|
const emphasis = varintOf(fields, 14);
|
|
@@ -45880,7 +46353,7 @@ function decodeInline(fields) {
|
|
|
45880
46353
|
...size !== void 0 && Number.isFinite(size) ? { size } : {}
|
|
45881
46354
|
};
|
|
45882
46355
|
}
|
|
45883
|
-
const attachment =
|
|
46356
|
+
const attachment = sub3(fields, 12);
|
|
45884
46357
|
const attachmentId = attachment && stringOf(attachment, 1);
|
|
45885
46358
|
if (attachmentId)
|
|
45886
46359
|
inline.attachment = { id: attachmentId, uti: stringOf(attachment, 2) || "unknown" };
|
|
@@ -45895,8 +46368,8 @@ function wrap(error2) {
|
|
|
45895
46368
|
function decodeNoteBlocks(data) {
|
|
45896
46369
|
let note;
|
|
45897
46370
|
try {
|
|
45898
|
-
const document =
|
|
45899
|
-
note = document &&
|
|
46371
|
+
const document = sub3(decodeWireFields(data), 2);
|
|
46372
|
+
note = document && sub3(document, 3);
|
|
45900
46373
|
} catch (error2) {
|
|
45901
46374
|
wrap(error2);
|
|
45902
46375
|
}
|
|
@@ -45921,7 +46394,7 @@ function decodeNoteBlocks(data) {
|
|
|
45921
46394
|
runs.push({
|
|
45922
46395
|
start: position,
|
|
45923
46396
|
length,
|
|
45924
|
-
paragraph: decodeParagraph(
|
|
46397
|
+
paragraph: decodeParagraph(sub3(fields, 2), paragraphTally),
|
|
45925
46398
|
inline: decodeInline(fields)
|
|
45926
46399
|
});
|
|
45927
46400
|
position += length;
|
|
@@ -46044,7 +46517,7 @@ function summarize(blocks, attachments) {
|
|
|
46044
46517
|
function decodeCompressedNoteBlocks(compressed) {
|
|
46045
46518
|
let data;
|
|
46046
46519
|
try {
|
|
46047
|
-
data =
|
|
46520
|
+
data = gunzipSync6(compressed, { maxOutputLength: 32 * 1024 * 1024 });
|
|
46048
46521
|
} catch (error2) {
|
|
46049
46522
|
throw new NoteBlocksError(
|
|
46050
46523
|
"decompress-failed",
|
|
@@ -46098,11 +46571,11 @@ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResp
|
|
|
46098
46571
|
undecodedFields: doc.undecodedFields
|
|
46099
46572
|
};
|
|
46100
46573
|
}
|
|
46101
|
-
var
|
|
46102
|
-
|
|
46574
|
+
var NOTES_DB_PATH8 = join14(
|
|
46575
|
+
homedir12(),
|
|
46103
46576
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
46104
46577
|
);
|
|
46105
|
-
function readNoteBlocks(id2, { dbPath: dbPath2 =
|
|
46578
|
+
function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH8 } = {}) {
|
|
46106
46579
|
const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
|
|
46107
46580
|
if (!pk)
|
|
46108
46581
|
throw new NoteBlocksError(
|
|
@@ -46114,7 +46587,7 @@ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
|
|
|
46114
46587
|
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
46588
|
let output;
|
|
46116
46589
|
try {
|
|
46117
|
-
output =
|
|
46590
|
+
output = execFileSync13(
|
|
46118
46591
|
"/usr/bin/sqlite3",
|
|
46119
46592
|
["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
|
|
46120
46593
|
{
|
|
@@ -46265,17 +46738,17 @@ function insertLink(request, deps) {
|
|
|
46265
46738
|
// src/tools/directOperations.ts
|
|
46266
46739
|
import { createHash as createHash2 } from "node:crypto";
|
|
46267
46740
|
import {
|
|
46268
|
-
closeSync,
|
|
46269
|
-
constants,
|
|
46270
|
-
fstatSync,
|
|
46741
|
+
closeSync as closeSync2,
|
|
46742
|
+
constants as constants2,
|
|
46743
|
+
fstatSync as fstatSync2,
|
|
46271
46744
|
mkdtempSync as mkdtempSync4,
|
|
46272
|
-
openSync,
|
|
46745
|
+
openSync as openSync2,
|
|
46273
46746
|
readFileSync as readFileSync3,
|
|
46274
46747
|
rmSync as rmSync4,
|
|
46275
46748
|
writeFileSync as writeFileSync3
|
|
46276
46749
|
} from "node:fs";
|
|
46277
46750
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
46278
|
-
import { basename, isAbsolute as isAbsolute2, join as
|
|
46751
|
+
import { basename, isAbsolute as isAbsolute2, join as join15 } from "node:path";
|
|
46279
46752
|
var noteId = exactIdInput(
|
|
46280
46753
|
"ICNote",
|
|
46281
46754
|
/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
|
|
@@ -46320,9 +46793,9 @@ function assertExistingContentPreserved(before, after) {
|
|
|
46320
46793
|
}
|
|
46321
46794
|
function localAttachment(path7) {
|
|
46322
46795
|
if (!isAbsolute2(path7)) throw new Error("An absolute local file path is required");
|
|
46323
|
-
const descriptor =
|
|
46796
|
+
const descriptor = openSync2(path7, constants2.O_RDONLY | constants2.O_NOFOLLOW);
|
|
46324
46797
|
try {
|
|
46325
|
-
const stat =
|
|
46798
|
+
const stat = fstatSync2(descriptor);
|
|
46326
46799
|
if (!stat.isFile() || stat.size === 0 || stat.size > 64 * 1024 * 1024)
|
|
46327
46800
|
throw new Error("Attachment must be a nonempty regular file of at most 64 MiB");
|
|
46328
46801
|
const bytes = readFileSync3(descriptor);
|
|
@@ -46330,7 +46803,7 @@ function localAttachment(path7) {
|
|
|
46330
46803
|
throw new Error("Attachment changed while it was being read; try again");
|
|
46331
46804
|
return bytes;
|
|
46332
46805
|
} finally {
|
|
46333
|
-
|
|
46806
|
+
closeSync2(descriptor);
|
|
46334
46807
|
}
|
|
46335
46808
|
}
|
|
46336
46809
|
function registerDirectOperations(server2, manager) {
|
|
@@ -46389,8 +46862,8 @@ function registerDirectOperations(server2, manager) {
|
|
|
46389
46862
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
46390
46863
|
const bytes = localAttachment(path7);
|
|
46391
46864
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
46392
|
-
const directory = mkdtempSync4(
|
|
46393
|
-
const temporaryFile =
|
|
46865
|
+
const directory = mkdtempSync4(join15(tmpdir4(), "notes-attachment-add-"));
|
|
46866
|
+
const temporaryFile = join15(directory, basename(path7));
|
|
46394
46867
|
try {
|
|
46395
46868
|
writeFileSync3(temporaryFile, bytes, { mode: 384 });
|
|
46396
46869
|
if (readSnapshot(manager, id2).hash !== before.hash)
|
|
@@ -46511,9 +46984,9 @@ function registerNativeTagsBridge(server2, manager) {
|
|
|
46511
46984
|
{ id: id2, expectedContentHash, scopeText, tags, title: initial.title },
|
|
46512
46985
|
{
|
|
46513
46986
|
read,
|
|
46514
|
-
candidates: (title,
|
|
46987
|
+
candidates: (title, scope2) => manager.listAccounts().flatMap(
|
|
46515
46988
|
(account) => manager.searchNotes(title, false, account.name).filter(
|
|
46516
|
-
(note) => !note.passwordProtected && manager.getNotePlaintextById(note.id).toLocaleLowerCase().includes(
|
|
46989
|
+
(note) => !note.passwordProtected && manager.getNotePlaintextById(note.id).toLocaleLowerCase().includes(scope2.toLocaleLowerCase())
|
|
46517
46990
|
).map((note) => note.id)
|
|
46518
46991
|
),
|
|
46519
46992
|
run: runNativeTagsShortcut
|
|
@@ -47052,7 +47525,11 @@ function errorResponse(message, cause) {
|
|
|
47052
47525
|
function withErrorHandling(handler, errorPrefix) {
|
|
47053
47526
|
return async (params) => {
|
|
47054
47527
|
try {
|
|
47055
|
-
|
|
47528
|
+
const seconds = params.timeoutSeconds;
|
|
47529
|
+
return runWithCallTimeout(
|
|
47530
|
+
typeof seconds === "number" ? seconds : void 0,
|
|
47531
|
+
() => handler(params)
|
|
47532
|
+
);
|
|
47056
47533
|
} catch (error2) {
|
|
47057
47534
|
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
47058
47535
|
return errorResponse(`${errorPrefix}: ${message}`, error2);
|
|
@@ -47072,6 +47549,7 @@ var MAX = {
|
|
|
47072
47549
|
TAGS: 100,
|
|
47073
47550
|
BATCH_IDS: 500
|
|
47074
47551
|
};
|
|
47552
|
+
var MAX_CONTENT_FILE_BYTES = 1024 * 1024;
|
|
47075
47553
|
var noteTitleSchema = {
|
|
47076
47554
|
title: external_exports.string().min(1, "Note title is required").max(MAX.TITLE),
|
|
47077
47555
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe(
|
|
@@ -47100,6 +47578,9 @@ function noteIdentifiers(id2) {
|
|
|
47100
47578
|
var expectedContentHashInput = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/, "expectedContentHash must come from get-note-content").describe(
|
|
47101
47579
|
"Revision token returned by get-note-content for this exact ID. The mutation stops if the note changed since that read."
|
|
47102
47580
|
);
|
|
47581
|
+
var timeoutSecondsInput = external_exports.number().int().min(CALL_TIMEOUT_SECONDS.min).max(CALL_TIMEOUT_SECONDS.max).optional().describe(
|
|
47582
|
+
`Per-call timeout in seconds (${CALL_TIMEOUT_SECONDS.min}-${CALL_TIMEOUT_SECONDS.max}) for each Notes.app automation step this call runs, overriding APPLE_NOTES_MCP_TIMEOUT_MS. A timed-out write is uncertain, not failed: read the note by id before any retry.`
|
|
47583
|
+
);
|
|
47103
47584
|
function readExactNoteSnapshot(id2) {
|
|
47104
47585
|
const note = notesManager.getNoteById(id2);
|
|
47105
47586
|
if (!note) return { error: `Note with ID "${id2}" not found` };
|
|
@@ -47113,6 +47594,7 @@ function readExactNoteSnapshot(id2) {
|
|
|
47113
47594
|
const rich = enrichNoteRead(id2, body);
|
|
47114
47595
|
return { note, body, rich, contentHash: richContentHash(body, rich) };
|
|
47115
47596
|
}
|
|
47597
|
+
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.";
|
|
47116
47598
|
function revisionConflictMessage(title) {
|
|
47117
47599
|
return `Note "${title}" changed after it was read. Read it again and review the newer version before retrying.`;
|
|
47118
47600
|
}
|
|
@@ -47136,11 +47618,17 @@ registerTool(
|
|
|
47136
47618
|
description: "Use when: the user wants to create a brand-new Apple Note.\nReturns: the new note's title and id \u2014 reuse the id for follow-up reads/edits.\nDo not use when: editing an existing note (use update-note).\nNote: the title is prepended as an <h1>; true Apple Notes checklists cannot be created via AppleScript (see the content field). A 'folder' must already exist \u2014 create-folder first (it is idempotent), since this tool does not create it.",
|
|
47137
47619
|
inputSchema: {
|
|
47138
47620
|
title: external_exports.string().min(1, "Title is required").max(MAX.TITLE),
|
|
47139
|
-
content: external_exports.string().min(1, "Content is required").max(MAX.CONTENT).describe(
|
|
47140
|
-
'Note body. AppleScript cannot create true Apple Notes checklists \u2014 `<input type="checkbox">`, checklist CSS classes, and markdown `- [ ]` lines do not render as checkable items. To produce a checklist, create the note with a plain `<ul>` or `- ` list and convert it in Notes.app with \u21E7\u2318L.'
|
|
47621
|
+
content: external_exports.string().min(1, "Content is required").max(MAX.CONTENT).optional().describe(
|
|
47622
|
+
'Note body; required unless contentPath is given (pass exactly one). AppleScript cannot create true Apple Notes checklists \u2014 `<input type="checkbox">`, checklist CSS classes, and markdown `- [ ]` lines do not render as checkable items. To produce a checklist, create the note with a plain `<ul>` or `- ` list and convert it in Notes.app with \u21E7\u2318L.'
|
|
47623
|
+
),
|
|
47624
|
+
contentPath: external_exports.string().min(1).max(MAX.SAVE_PATH).optional().describe(
|
|
47625
|
+
`Absolute path of a local UTF-8 file to use as the body instead of content (pass exactly one). The same locations save-attachment may write to are allowed (home, temp, /Volumes); symbolic links and non-regular files are refused. Limit ${MAX_CONTENT_FILE_BYTES} bytes.`
|
|
47141
47626
|
),
|
|
47142
47627
|
format: external_exports.enum(["plaintext", "html", "markdown"]).optional().default("plaintext").describe(
|
|
47143
|
-
"Content format: 'plaintext' (default), 'html' for rich formatting, or 'markdown'
|
|
47628
|
+
"Content format: 'plaintext' (default), 'html' for rich formatting, or 'markdown'. Markdown whose first line is exactly `# <title>` (same case and spacing) has that line and one blank line after it removed, since the title is supplied separately."
|
|
47629
|
+
),
|
|
47630
|
+
markdownRoute: external_exports.enum(["shortcut", "html"]).optional().default("shortcut").describe(
|
|
47631
|
+
"How format 'markdown' is imported. 'shortcut' (default) uses the Create Markdown Note Shortcut for real Title/Heading/Subheading styles (iCloud only, no tags; see get-capabilities). 'html' is the fallback when that Shortcut is not installed or the note is outside iCloud: it converts the same bounded Markdown subset to HTML and creates the note through AppleScript in any account, rendering `- [ ]` / `- [x]` task items as ordinary list rows that start with a visible \u2610 / \u2611 character \u2014 not native, checkable checklist items."
|
|
47144
47632
|
),
|
|
47145
47633
|
tags: external_exports.array(external_exports.string().max(MAX.TAG)).max(MAX.TAGS).optional().describe(
|
|
47146
47634
|
"Returned-only metadata \u2014 NOT written to Notes.app. Apple Notes tags can't be set via AppleScript, so any values passed here are echoed back in the response but do not appear on the created note. Use #hashtags in the body for searchable text; this does not create native tag objects. Native tags need the Notes Shortcuts action. Refused with format 'markdown': add tags afterwards with add-native-tags."
|
|
@@ -47150,7 +47638,8 @@ registerTool(
|
|
|
47150
47638
|
),
|
|
47151
47639
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe(
|
|
47152
47640
|
"Account name (defaults to the account Notes.app itself reports as default). Matched exactly, or by a unique prefix; an ambiguous prefix is refused. Must be an account Notes.app already has configured \u2014 see list-accounts."
|
|
47153
|
-
)
|
|
47641
|
+
),
|
|
47642
|
+
timeoutSeconds: timeoutSecondsInput
|
|
47154
47643
|
},
|
|
47155
47644
|
outputSchema: {
|
|
47156
47645
|
ok: external_exports.boolean().optional(),
|
|
@@ -47159,10 +47648,66 @@ registerTool(
|
|
|
47159
47648
|
folder: external_exports.string().optional(),
|
|
47160
47649
|
account: external_exports.string().optional(),
|
|
47161
47650
|
contentHash: external_exports.string().optional(),
|
|
47162
|
-
verified: external_exports.boolean().optional()
|
|
47651
|
+
verified: external_exports.boolean().optional(),
|
|
47652
|
+
strippedDuplicateTitle: external_exports.boolean().optional(),
|
|
47653
|
+
taskItemsRendered: external_exports.number().optional()
|
|
47163
47654
|
}
|
|
47164
47655
|
},
|
|
47165
|
-
withErrorHandling((
|
|
47656
|
+
withErrorHandling((params) => {
|
|
47657
|
+
const {
|
|
47658
|
+
title,
|
|
47659
|
+
contentPath,
|
|
47660
|
+
format = "plaintext",
|
|
47661
|
+
markdownRoute = "shortcut",
|
|
47662
|
+
tags = [],
|
|
47663
|
+
folder,
|
|
47664
|
+
account
|
|
47665
|
+
} = params;
|
|
47666
|
+
if (params.content === void 0 === (contentPath === void 0))
|
|
47667
|
+
return errorResponse("Provide exactly one of content or contentPath");
|
|
47668
|
+
let content = params.content ?? readAllowedTextFile(contentPath, MAX_CONTENT_FILE_BYTES);
|
|
47669
|
+
if (format !== "markdown" && markdownRoute !== "shortcut")
|
|
47670
|
+
return errorResponse('markdownRoute applies to format "markdown" only');
|
|
47671
|
+
let strippedDuplicateTitle = false;
|
|
47672
|
+
if (format === "markdown") {
|
|
47673
|
+
const stripped = stripDuplicateTitleHeading(content, title);
|
|
47674
|
+
strippedDuplicateTitle = stripped.stripped;
|
|
47675
|
+
content = stripped.content;
|
|
47676
|
+
if (!content.trim())
|
|
47677
|
+
return errorResponse(
|
|
47678
|
+
"The Markdown holds only the title heading; add body content, or use format 'plaintext' for a title-only note"
|
|
47679
|
+
);
|
|
47680
|
+
}
|
|
47681
|
+
const titleNote = strippedDuplicateTitle ? { strippedDuplicateTitle: true } : {};
|
|
47682
|
+
if (format === "markdown" && markdownRoute === "html") {
|
|
47683
|
+
const taskItemsRendered = countTaskItems(content);
|
|
47684
|
+
const html = appendMarkdownHtml(content, { taskGlyphs: true });
|
|
47685
|
+
const note2 = notesManager.createNote(title, html, tags, folder, account, "html");
|
|
47686
|
+
if (!note2)
|
|
47687
|
+
return errorResponse(
|
|
47688
|
+
`Failed to create note "${title}". Check that the folder and account exist (list-folders, list-accounts) and that this server has Automation access (run the doctor tool).`
|
|
47689
|
+
);
|
|
47690
|
+
const createdBody2 = notesManager.getNoteContentById(note2.id);
|
|
47691
|
+
if (!notesManager.getNoteById(note2.id) || !createdBody2)
|
|
47692
|
+
return errorResponse(
|
|
47693
|
+
`A note may have been created, but its exact ID could not be verified. Do not retry automatically. Returned ID: ${note2.id}`
|
|
47694
|
+
);
|
|
47695
|
+
const glyphNote = taskItemsRendered ? ` ${taskItemsRendered} task item(s) were rendered as visible \u2610 / \u2611 text, not native checklist items.` : "";
|
|
47696
|
+
return successResponse(
|
|
47697
|
+
`Note created from Markdown: "${note2.title}" [id: ${note2.id}]${glyphNote}`,
|
|
47698
|
+
{
|
|
47699
|
+
ok: true,
|
|
47700
|
+
id: note2.id,
|
|
47701
|
+
title: note2.title,
|
|
47702
|
+
folder,
|
|
47703
|
+
account,
|
|
47704
|
+
contentHash: richContentHash(createdBody2, enrichNoteRead(note2.id, createdBody2)),
|
|
47705
|
+
verified: true,
|
|
47706
|
+
taskItemsRendered,
|
|
47707
|
+
...titleNote
|
|
47708
|
+
}
|
|
47709
|
+
);
|
|
47710
|
+
}
|
|
47166
47711
|
if (format === "markdown") {
|
|
47167
47712
|
if (account)
|
|
47168
47713
|
return errorResponse(
|
|
@@ -47174,7 +47719,10 @@ registerTool(
|
|
|
47174
47719
|
);
|
|
47175
47720
|
requireValidated("create-note-markdown");
|
|
47176
47721
|
const result = createMarkdownNote(notesManager, { title, content, folder });
|
|
47177
|
-
return successResponse(`Note created from Markdown: "${title}" [id: ${result.id}]`,
|
|
47722
|
+
return successResponse(`Note created from Markdown: "${title}" [id: ${result.id}]`, {
|
|
47723
|
+
...result,
|
|
47724
|
+
...titleNote
|
|
47725
|
+
});
|
|
47178
47726
|
}
|
|
47179
47727
|
const note = notesManager.createNote(title, content, tags, folder, account, format);
|
|
47180
47728
|
if (!note) {
|
|
@@ -47320,7 +47868,7 @@ registerTool(
|
|
|
47320
47868
|
}
|
|
47321
47869
|
throw error2;
|
|
47322
47870
|
}
|
|
47323
|
-
const
|
|
47871
|
+
const scope2 = `scanned ${result.scanned} of ${result.eligible} notes` + (result.scanTruncated ? `, the most recent ${result.scanLimit}; pass a higher scanLimit to include older notes` : "");
|
|
47324
47872
|
const notes = [];
|
|
47325
47873
|
if (result.truncated) {
|
|
47326
47874
|
notes.push(
|
|
@@ -47336,7 +47884,7 @@ registerTool(
|
|
|
47336
47884
|
|
|
47337
47885
|
${notes.join("\n")}` : "";
|
|
47338
47886
|
if (result.count === 0) {
|
|
47339
|
-
return successResponse(`No notes matched (${
|
|
47887
|
+
return successResponse(`No notes matched (${scope2}).${footer}`, { ...result });
|
|
47340
47888
|
}
|
|
47341
47889
|
const lines = result.notes.map((n) => {
|
|
47342
47890
|
const where = [n.account, n.folder].filter(Boolean).join(" / ");
|
|
@@ -47345,7 +47893,7 @@ ${notes.join("\n")}` : "";
|
|
|
47345
47893
|
return ` - ${n.title}${where ? ` (${where})` : ""}${n.locked ? " [locked]" : ""} [id: ${n.id}]${snippet}`;
|
|
47346
47894
|
}).join("\n");
|
|
47347
47895
|
return successResponse(
|
|
47348
|
-
`Found ${result.matched} matching notes (${
|
|
47896
|
+
`Found ${result.matched} matching notes (${scope2}):
|
|
47349
47897
|
${lines}${footer}`,
|
|
47350
47898
|
{ ...result }
|
|
47351
47899
|
);
|
|
@@ -47892,7 +48440,8 @@ registerTool(
|
|
|
47892
48440
|
newContent: external_exports.string().min(1, "New content is required").max(MAX.CONTENT).describe(
|
|
47893
48441
|
"New note body. AppleScript cannot produce true Apple Notes checklists; checkbox inputs and `- [ ]` markdown do not render as checkable items. Use a plain list and convert in Notes.app with \u21E7\u2318L."
|
|
47894
48442
|
),
|
|
47895
|
-
format: external_exports.enum(["plaintext", "html"]).optional().default("plaintext").describe("Content format: 'plaintext' (default) or 'html' for rich formatting")
|
|
48443
|
+
format: external_exports.enum(["plaintext", "html"]).optional().default("plaintext").describe("Content format: 'plaintext' (default) or 'html' for rich formatting"),
|
|
48444
|
+
timeoutSeconds: timeoutSecondsInput
|
|
47896
48445
|
},
|
|
47897
48446
|
outputSchema: {
|
|
47898
48447
|
ok: external_exports.boolean().optional(),
|
|
@@ -48103,10 +48652,11 @@ registerTool(
|
|
|
48103
48652
|
content: external_exports.string().min(1, "Content to append is required").max(MAX.CONTENT).describe("Text to append to the note body"),
|
|
48104
48653
|
scopeText: external_exports.string().min(12).max(500).optional().describe("Existing unique phrase required for native append to protected notes"),
|
|
48105
48654
|
position: external_exports.enum(["after", "before"]).optional().default("after").describe(
|
|
48106
|
-
"Where to insert: 'after' appends to the end (default)
|
|
48655
|
+
"Where to insert: 'after' appends to the end (default); 'before' inserts directly below the note's title line, so the title stays first"
|
|
48107
48656
|
),
|
|
48108
48657
|
separator: external_exports.string().max(20).optional().default("\n\n").describe("String placed between existing content and new content (default: two newlines)"),
|
|
48109
|
-
format: external_exports.enum(["plaintext", "html"]).optional().default("plaintext").describe("Format of the content being appended: 'plaintext' (default) or 'html'")
|
|
48658
|
+
format: external_exports.enum(["plaintext", "html"]).optional().default("plaintext").describe("Format of the content being appended: 'plaintext' (default) or 'html'"),
|
|
48659
|
+
timeoutSeconds: timeoutSecondsInput
|
|
48110
48660
|
},
|
|
48111
48661
|
outputSchema: {
|
|
48112
48662
|
ok: external_exports.boolean().optional(),
|
|
@@ -48195,7 +48745,8 @@ registerTool(
|
|
|
48195
48745
|
description: "Use when: moving one exact note to Recently Deleted after reading and reviewing it.\nReturns: confirmation with the exact id.\nDo not use when: you only have a title or the note changed since review.\nSafety: requires id and expectedContentHash from get-note-content. The body comparison and delete happen in one AppleScript, so a newer edit is preserved.",
|
|
48196
48746
|
inputSchema: {
|
|
48197
48747
|
id: noteIdInput,
|
|
48198
|
-
expectedContentHash: expectedContentHashInput
|
|
48748
|
+
expectedContentHash: expectedContentHashInput,
|
|
48749
|
+
timeoutSeconds: timeoutSecondsInput
|
|
48199
48750
|
},
|
|
48200
48751
|
outputSchema: {
|
|
48201
48752
|
ok: external_exports.boolean().optional(),
|
|
@@ -48215,6 +48766,9 @@ registerTool(
|
|
|
48215
48766
|
if (result.status === "conflict") {
|
|
48216
48767
|
return errorResponse(revisionConflictMessage(snapshot.note.title));
|
|
48217
48768
|
}
|
|
48769
|
+
if (result.status === "not-deleted") {
|
|
48770
|
+
return errorResponse(NOT_DELETED_MESSAGE);
|
|
48771
|
+
}
|
|
48218
48772
|
if (result.status !== "deleted") {
|
|
48219
48773
|
return errorResponse(
|
|
48220
48774
|
`The delete result for note "${snapshot.note.title}" is uncertain. Inspect exact ID ${id2} before retrying.`
|
|
@@ -48240,7 +48794,8 @@ registerTool(
|
|
|
48240
48794
|
inputSchema: {
|
|
48241
48795
|
id: noteIdInput,
|
|
48242
48796
|
folder: external_exports.string().min(1, "Destination folder is required").max(MAX.FOLDER),
|
|
48243
|
-
account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account containing the note/folder")
|
|
48797
|
+
account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account containing the note/folder"),
|
|
48798
|
+
timeoutSeconds: timeoutSecondsInput
|
|
48244
48799
|
},
|
|
48245
48800
|
outputSchema: {
|
|
48246
48801
|
ok: external_exports.boolean().optional(),
|
|
@@ -48803,6 +49358,8 @@ registerTool(
|
|
|
48803
49358
|
if (result.status === "conflict") {
|
|
48804
49359
|
return { id: id2, success: false, error: revisionConflictMessage(snapshot.note.title) };
|
|
48805
49360
|
}
|
|
49361
|
+
if (result.status === "not-deleted")
|
|
49362
|
+
return { id: id2, success: false, error: NOT_DELETED_MESSAGE };
|
|
48806
49363
|
return { id: id2, success: false, error: "Delete result uncertain; inspect this exact ID" };
|
|
48807
49364
|
});
|
|
48808
49365
|
const succeeded = results.filter((r) => r.success).length;
|
|
@@ -49098,6 +49655,44 @@ ${summary}`,
|
|
|
49098
49655
|
);
|
|
49099
49656
|
}, "Error reading checklist state")
|
|
49100
49657
|
);
|
|
49658
|
+
registerTool(
|
|
49659
|
+
"get-audio-transcripts",
|
|
49660
|
+
{
|
|
49661
|
+
description: "Use when: reading the transcript (and summary, if any) that Notes already computed for the audio recordings in one note, by id.\nReturns: one entry per top-level audio attachment in body order, with attachmentId, durationSeconds, status (ok, none when no transcript is stored, or undecodable with a reason), joined transcript text, wordCount, speakers and summary when stored, and optional word-level segments.\nDo not use when: you need the audio file itself (save-attachment) or the note body (get-note-content). This tool does not transcribe; it only reads what Notes stored.\nSafety: read-only; requires Full Disk Access and reads the NoteStore database. Password-protected notes are refused. Large responses drop segments first, then shorten text, to stay under APPLE_NOTES_MCP_EXPORT_MAX_BYTES.",
|
|
49662
|
+
inputSchema: {
|
|
49663
|
+
id: noteIdInput,
|
|
49664
|
+
includeSegments: external_exports.boolean().optional().describe(
|
|
49665
|
+
"Also return word-level segments (text, start and duration in seconds, speaker). Default false."
|
|
49666
|
+
),
|
|
49667
|
+
maxSegments: external_exports.number().int().min(1).max(MAX_SEGMENTS_LIMIT).optional().describe(
|
|
49668
|
+
`Cap on segments returned per attachment when includeSegments is true (default ${DEFAULT_MAX_SEGMENTS}).`
|
|
49669
|
+
)
|
|
49670
|
+
},
|
|
49671
|
+
outputSchema: {
|
|
49672
|
+
id: external_exports.string().optional(),
|
|
49673
|
+
attachments: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
49674
|
+
bodyOrder: external_exports.boolean().optional(),
|
|
49675
|
+
truncated: external_exports.boolean().optional()
|
|
49676
|
+
},
|
|
49677
|
+
annotations: { readOnlyHint: true }
|
|
49678
|
+
},
|
|
49679
|
+
withErrorHandling(({ id: id2, includeSegments, maxSegments }) => {
|
|
49680
|
+
let result;
|
|
49681
|
+
try {
|
|
49682
|
+
result = notesManager.getAudioTranscripts(id2, { includeSegments, maxSegments });
|
|
49683
|
+
} catch (error2) {
|
|
49684
|
+
if (error2 instanceof AudioTranscriptError) return errorResponse(error2.message);
|
|
49685
|
+
throw error2;
|
|
49686
|
+
}
|
|
49687
|
+
const toResponse = (r) => successResponse(formatTranscriptsText(r), { ...r });
|
|
49688
|
+
const fitted = fitTranscriptsToBudget(
|
|
49689
|
+
result,
|
|
49690
|
+
exportMaxResponseBytes(),
|
|
49691
|
+
(r) => Buffer.byteLength(JSON.stringify(toResponse(r)))
|
|
49692
|
+
);
|
|
49693
|
+
return toResponse(fitted);
|
|
49694
|
+
}, "Error reading audio transcripts")
|
|
49695
|
+
);
|
|
49101
49696
|
registerTool(
|
|
49102
49697
|
"get-note-metadata",
|
|
49103
49698
|
{
|