apple-notes-mcp 2.15.1 → 2.15.3
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 +285 -253
- package/build/index.js +1072 -711
- package/docs/FULL-DISK-ACCESS.md +95 -117
- package/native/private-helper/apple-notes-private-helper.m +132 -1
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -4010,11 +4010,11 @@ var require_fast_uri = __commonJS({
|
|
|
4010
4010
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
4011
4011
|
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
4012
4012
|
var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
|
|
4013
|
-
function getParseError(
|
|
4014
|
-
if (matches[2] !== void 0 &&
|
|
4013
|
+
function getParseError(parsed2, matches) {
|
|
4014
|
+
if (matches[2] !== void 0 && parsed2.path && parsed2.path[0] !== "/") {
|
|
4015
4015
|
return 'URI path must start with "/" when authority is present.';
|
|
4016
4016
|
}
|
|
4017
|
-
if (typeof
|
|
4017
|
+
if (typeof parsed2.port === "number" && (parsed2.port < 0 || parsed2.port > 65535)) {
|
|
4018
4018
|
return "URI port is malformed.";
|
|
4019
4019
|
}
|
|
4020
4020
|
return void 0;
|
|
@@ -4037,12 +4037,12 @@ var require_fast_uri = __commonJS({
|
|
|
4037
4037
|
const host = matches[4];
|
|
4038
4038
|
return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
|
|
4039
4039
|
}
|
|
4040
|
-
function canonicalizeHost(
|
|
4041
|
-
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) &&
|
|
4040
|
+
function canonicalizeHost(parsed2, options, schemeHandler, isIP) {
|
|
4041
|
+
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed2.host && !isIPLiteral(parsed2.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed2.host)) {
|
|
4042
4042
|
try {
|
|
4043
|
-
|
|
4043
|
+
parsed2.host = new URL("http://" + parsed2.host).hostname;
|
|
4044
4044
|
} catch (e) {
|
|
4045
|
-
|
|
4045
|
+
parsed2.error = parsed2.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
4046
4046
|
return true;
|
|
4047
4047
|
}
|
|
4048
4048
|
}
|
|
@@ -4050,7 +4050,7 @@ var require_fast_uri = __commonJS({
|
|
|
4050
4050
|
}
|
|
4051
4051
|
function parseWithStatus(uri, opts) {
|
|
4052
4052
|
const options = Object.assign({}, opts);
|
|
4053
|
-
const
|
|
4053
|
+
const parsed2 = {
|
|
4054
4054
|
scheme: void 0,
|
|
4055
4055
|
userinfo: void 0,
|
|
4056
4056
|
host: "",
|
|
@@ -4075,7 +4075,7 @@ var require_fast_uri = __commonJS({
|
|
|
4075
4075
|
}
|
|
4076
4076
|
const authorityMatch = uri.match(AUTHORITY_PREFIX);
|
|
4077
4077
|
if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
|
|
4078
|
-
|
|
4078
|
+
parsed2.error = "URI authority must not contain a literal backslash.";
|
|
4079
4079
|
malformedAuthorityOrPort = true;
|
|
4080
4080
|
}
|
|
4081
4081
|
const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
|
|
@@ -4084,105 +4084,105 @@ var require_fast_uri = __commonJS({
|
|
|
4084
4084
|
const normalizedRegion = region.replace(/[\t\n\r]/g, "");
|
|
4085
4085
|
if (normalizedRegion.length >= 2) {
|
|
4086
4086
|
if (normalizedRegion.slice(0, 2) !== "//") {
|
|
4087
|
-
|
|
4087
|
+
parsed2.error = parsed2.error || "URI authority must not contain a literal backslash.";
|
|
4088
4088
|
malformedAuthorityOrPort = true;
|
|
4089
4089
|
} else if (region.length !== normalizedRegion.length) {
|
|
4090
|
-
|
|
4090
|
+
parsed2.error = parsed2.error || "URI authority introducer must not contain whitespace.";
|
|
4091
4091
|
malformedAuthorityOrPort = true;
|
|
4092
4092
|
}
|
|
4093
4093
|
}
|
|
4094
4094
|
}
|
|
4095
4095
|
const matches = uri.match(URI_PARSE);
|
|
4096
4096
|
if (matches) {
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
if (
|
|
4105
|
-
const decodedScheme = unescape(
|
|
4097
|
+
parsed2.scheme = matches[1];
|
|
4098
|
+
parsed2.userinfo = matches[3];
|
|
4099
|
+
parsed2.host = matches[4];
|
|
4100
|
+
parsed2.port = parseInt(matches[5], 10);
|
|
4101
|
+
parsed2.path = matches[6] || "";
|
|
4102
|
+
parsed2.query = matches[7];
|
|
4103
|
+
parsed2.fragment = matches[8];
|
|
4104
|
+
if (parsed2.scheme !== void 0) {
|
|
4105
|
+
const decodedScheme = unescape(parsed2.scheme);
|
|
4106
4106
|
if (VALID_SCHEME.test(decodedScheme)) {
|
|
4107
|
-
|
|
4107
|
+
parsed2.scheme = decodedScheme.toLowerCase();
|
|
4108
4108
|
} else {
|
|
4109
|
-
|
|
4109
|
+
parsed2.error = parsed2.error || MALFORMED_SCHEME_ERROR;
|
|
4110
4110
|
malformedScheme = true;
|
|
4111
4111
|
}
|
|
4112
4112
|
}
|
|
4113
4113
|
malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
|
|
4114
4114
|
if (malformedPercentEncoding) {
|
|
4115
|
-
|
|
4115
|
+
parsed2.error = parsed2.error || "URI contains malformed percent-encoding.";
|
|
4116
4116
|
}
|
|
4117
|
-
if (isNaN(
|
|
4118
|
-
|
|
4117
|
+
if (isNaN(parsed2.port)) {
|
|
4118
|
+
parsed2.port = matches[5];
|
|
4119
4119
|
}
|
|
4120
|
-
const parseError = getParseError(
|
|
4120
|
+
const parseError = getParseError(parsed2, matches);
|
|
4121
4121
|
if (parseError !== void 0) {
|
|
4122
|
-
|
|
4122
|
+
parsed2.error = parsed2.error || parseError;
|
|
4123
4123
|
malformedAuthorityOrPort = true;
|
|
4124
4124
|
}
|
|
4125
|
-
if (
|
|
4126
|
-
const ipv4result = isIPv4(
|
|
4125
|
+
if (parsed2.host) {
|
|
4126
|
+
const ipv4result = isIPv4(parsed2.host);
|
|
4127
4127
|
if (ipv4result === false) {
|
|
4128
|
-
const bracketedIPLiteral = isIPLiteral(
|
|
4129
|
-
const hasIPLiteralBracket =
|
|
4130
|
-
const ipv6result = normalizeIPv6(
|
|
4128
|
+
const bracketedIPLiteral = isIPLiteral(parsed2.host);
|
|
4129
|
+
const hasIPLiteralBracket = parsed2.host.indexOf("[") !== -1 || parsed2.host.indexOf("]") !== -1;
|
|
4130
|
+
const ipv6result = normalizeIPv6(parsed2.host);
|
|
4131
4131
|
isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
4132
4132
|
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
4133
|
-
|
|
4133
|
+
parsed2.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
4134
4134
|
if (malformedIPLiteral) {
|
|
4135
|
-
|
|
4135
|
+
parsed2.error = parsed2.error || "URI host is malformed.";
|
|
4136
4136
|
malformedAuthorityOrPort = true;
|
|
4137
4137
|
}
|
|
4138
4138
|
} else {
|
|
4139
4139
|
isIP = true;
|
|
4140
4140
|
}
|
|
4141
4141
|
}
|
|
4142
|
-
if (
|
|
4143
|
-
|
|
4144
|
-
} else if (
|
|
4145
|
-
|
|
4146
|
-
} else if (
|
|
4147
|
-
|
|
4142
|
+
if (parsed2.scheme === void 0 && parsed2.userinfo === void 0 && parsed2.host === void 0 && parsed2.port === void 0 && parsed2.query === void 0 && !parsed2.path) {
|
|
4143
|
+
parsed2.reference = "same-document";
|
|
4144
|
+
} else if (parsed2.scheme === void 0) {
|
|
4145
|
+
parsed2.reference = "relative";
|
|
4146
|
+
} else if (parsed2.fragment === void 0) {
|
|
4147
|
+
parsed2.reference = "absolute";
|
|
4148
4148
|
} else {
|
|
4149
|
-
|
|
4149
|
+
parsed2.reference = "uri";
|
|
4150
4150
|
}
|
|
4151
|
-
if (options.reference && options.reference !== "suffix" && options.reference !==
|
|
4152
|
-
|
|
4151
|
+
if (options.reference && options.reference !== "suffix" && options.reference !== parsed2.reference) {
|
|
4152
|
+
parsed2.error = parsed2.error || "URI is not a " + options.reference + " reference.";
|
|
4153
4153
|
}
|
|
4154
|
-
const schemeHandler = getSchemeHandler(options.scheme ||
|
|
4154
|
+
const schemeHandler = getSchemeHandler(options.scheme || parsed2.scheme);
|
|
4155
4155
|
if (!malformedIPLiteral) {
|
|
4156
|
-
malformedHost = canonicalizeHost(
|
|
4156
|
+
malformedHost = canonicalizeHost(parsed2, options, schemeHandler, isIP);
|
|
4157
4157
|
}
|
|
4158
|
-
if (uri.indexOf("%") !== -1 &&
|
|
4159
|
-
let host = isIP ?
|
|
4158
|
+
if (uri.indexOf("%") !== -1 && parsed2.host !== void 0 && !malformedIPLiteral) {
|
|
4159
|
+
let host = isIP ? parsed2.host : normalizePercentEncoding(parsed2.host, true);
|
|
4160
4160
|
if (!isIP) {
|
|
4161
4161
|
host = normalizePercentEncoding(host.toLowerCase());
|
|
4162
4162
|
}
|
|
4163
|
-
|
|
4163
|
+
parsed2.host = reescapeHostDelimiters(host, isIP);
|
|
4164
4164
|
}
|
|
4165
4165
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
4166
|
-
if (
|
|
4167
|
-
|
|
4166
|
+
if (parsed2.path) {
|
|
4167
|
+
parsed2.path = normalizePathEncoding(parsed2.path);
|
|
4168
4168
|
}
|
|
4169
|
-
if (
|
|
4170
|
-
|
|
4169
|
+
if (parsed2.query) {
|
|
4170
|
+
parsed2.query = normalizeQueryFragmentEncoding(parsed2.query);
|
|
4171
4171
|
}
|
|
4172
|
-
if (
|
|
4173
|
-
|
|
4172
|
+
if (parsed2.fragment) {
|
|
4173
|
+
parsed2.fragment = normalizeQueryFragmentEncoding(parsed2.fragment);
|
|
4174
4174
|
}
|
|
4175
4175
|
}
|
|
4176
4176
|
if (schemeHandler && schemeHandler.parse) {
|
|
4177
|
-
schemeHandler.parse(
|
|
4178
|
-
if (schemeHandler === SCHEMES.urn &&
|
|
4177
|
+
schemeHandler.parse(parsed2, options);
|
|
4178
|
+
if (schemeHandler === SCHEMES.urn && parsed2.nid === void 0) {
|
|
4179
4179
|
malformedSchemeSpecific = true;
|
|
4180
4180
|
}
|
|
4181
4181
|
}
|
|
4182
4182
|
} else {
|
|
4183
|
-
|
|
4183
|
+
parsed2.error = parsed2.error || "URI can not be parsed.";
|
|
4184
4184
|
}
|
|
4185
|
-
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
4185
|
+
return { parsed: parsed2, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
4186
4186
|
}
|
|
4187
4187
|
function parse3(uri, opts) {
|
|
4188
4188
|
return parseWithStatus(uri, opts).parsed;
|
|
@@ -4191,9 +4191,9 @@ var require_fast_uri = __commonJS({
|
|
|
4191
4191
|
return normalizeStringWithStatus(uri, opts).normalized;
|
|
4192
4192
|
}
|
|
4193
4193
|
function normalizeStringWithStatus(uri, opts) {
|
|
4194
|
-
const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
|
|
4194
|
+
const { parsed: parsed2, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
|
|
4195
4195
|
return {
|
|
4196
|
-
normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(
|
|
4196
|
+
normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed2, opts),
|
|
4197
4197
|
malformedAuthorityOrPort,
|
|
4198
4198
|
malformedPercentEncoding,
|
|
4199
4199
|
malformedSchemeSpecific,
|
|
@@ -24510,8 +24510,130 @@ var require_turndown_cjs = __commonJS({
|
|
|
24510
24510
|
}
|
|
24511
24511
|
});
|
|
24512
24512
|
|
|
24513
|
-
// src/
|
|
24514
|
-
import { createRequire } from "module";
|
|
24513
|
+
// src/cliEntry.ts
|
|
24514
|
+
import { createRequire } from "node:module";
|
|
24515
|
+
|
|
24516
|
+
// src/cli.ts
|
|
24517
|
+
var CLI_USAGE = `Usage: apple-notes-mcp [command]
|
|
24518
|
+
|
|
24519
|
+
With no arguments, run the local MCP server over stdio.
|
|
24520
|
+
|
|
24521
|
+
Commands:
|
|
24522
|
+
setup [options] Install or inspect Shortcuts and optional native helpers
|
|
24523
|
+
templates [options] Manage the local Markdown template editor (templates --help)
|
|
24524
|
+
anchors [options] Run the optional paragraph anchor resolver (anchors --help)
|
|
24525
|
+
--help, -h Show this help without running setup or the MCP server
|
|
24526
|
+
--version, -v Print the installed package version
|
|
24527
|
+
|
|
24528
|
+
Run apple-notes-mcp setup --help for supported setup options.
|
|
24529
|
+
`;
|
|
24530
|
+
var SETUP_USAGE = `Usage: apple-notes-mcp setup [target] [options]
|
|
24531
|
+
|
|
24532
|
+
Targets (choose at most one; default: packaged Shortcuts):
|
|
24533
|
+
--public-helper Build and install the public drawing/speech helper
|
|
24534
|
+
--native-helper Build and install the opt-in read-only private helper
|
|
24535
|
+
--permissions-window Build and install the optional permission checklist window
|
|
24536
|
+
--permissions Check permissions; does not grant them
|
|
24537
|
+
--broker Build and install the opt-in permission broker
|
|
24538
|
+
|
|
24539
|
+
Options for every target:
|
|
24540
|
+
--check Inspect only; do not install or import anything
|
|
24541
|
+
--help, -h Show this help without performing any checks
|
|
24542
|
+
|
|
24543
|
+
Options only with --permissions:
|
|
24544
|
+
--once Print one report and exit
|
|
24545
|
+
--json Print the report as JSON
|
|
24546
|
+
--open Open System Settings panes for missing grants
|
|
24547
|
+
--window Show the checklist in the installed optional window
|
|
24548
|
+
--probe-automation Send a read-only Apple event (may show a consent prompt)
|
|
24549
|
+
|
|
24550
|
+
Options only with --broker:
|
|
24551
|
+
--uninstall Remove the installed broker and LaunchAgent
|
|
24552
|
+
--sign-identity <id> Select an existing signing identity
|
|
24553
|
+
|
|
24554
|
+
--check and SSH sessions suppress the Automation probe. Without an explicit
|
|
24555
|
+
--probe-automation, Automation is unverified. Helper installation requires
|
|
24556
|
+
Apple developer tools; setup never silently grants macOS permissions.
|
|
24557
|
+
`;
|
|
24558
|
+
var SETUP_TARGETS = /* @__PURE__ */ new Map([
|
|
24559
|
+
["--public-helper", "public-helper"],
|
|
24560
|
+
["--native-helper", "native-helper"],
|
|
24561
|
+
["--permissions-window", "permissions-window"],
|
|
24562
|
+
["--permissions", "permissions"],
|
|
24563
|
+
["--broker", "broker"]
|
|
24564
|
+
]);
|
|
24565
|
+
var PERMISSION_OPTIONS = /* @__PURE__ */ new Set([
|
|
24566
|
+
"--once",
|
|
24567
|
+
"--json",
|
|
24568
|
+
"--open",
|
|
24569
|
+
"--window",
|
|
24570
|
+
"--probe-automation"
|
|
24571
|
+
]);
|
|
24572
|
+
function parseSetupArgs(args) {
|
|
24573
|
+
let target = "shortcuts";
|
|
24574
|
+
let selected = false;
|
|
24575
|
+
for (let index = 0; index < args.length; index++) {
|
|
24576
|
+
const arg = args[index];
|
|
24577
|
+
if (arg === "--sign-identity") {
|
|
24578
|
+
const identity = args[++index];
|
|
24579
|
+
if (!identity?.trim() || identity.startsWith("-"))
|
|
24580
|
+
throw new Error("--sign-identity requires a signing identity.");
|
|
24581
|
+
continue;
|
|
24582
|
+
}
|
|
24583
|
+
const next = SETUP_TARGETS.get(arg);
|
|
24584
|
+
if (next) {
|
|
24585
|
+
if (selected && target !== next)
|
|
24586
|
+
throw new Error("Choose only one setup target. Run each target separately.");
|
|
24587
|
+
target = next;
|
|
24588
|
+
selected = true;
|
|
24589
|
+
} else if (!["--check", "--help", "-h"].includes(arg) && !PERMISSION_OPTIONS.has(arg) && arg !== "--uninstall") {
|
|
24590
|
+
throw new Error(`Unknown setup option "${arg}".`);
|
|
24591
|
+
}
|
|
24592
|
+
}
|
|
24593
|
+
if (args.some((arg) => arg === "--uninstall" || arg === "--sign-identity") && target !== "broker")
|
|
24594
|
+
throw new Error("--uninstall and --sign-identity require setup --broker.");
|
|
24595
|
+
for (const arg of args) {
|
|
24596
|
+
if (PERMISSION_OPTIONS.has(arg) && target !== "permissions")
|
|
24597
|
+
throw new Error(`${arg} requires setup --permissions.`);
|
|
24598
|
+
}
|
|
24599
|
+
if (args.includes("--help") || args.includes("-h")) return { kind: "help", topic: "setup" };
|
|
24600
|
+
return { kind: "setup", target, args: [...args], checkOnly: args.includes("--check") };
|
|
24601
|
+
}
|
|
24602
|
+
function parseCliArgs(args) {
|
|
24603
|
+
if (!args.length) return { kind: "mcp" };
|
|
24604
|
+
const [command2, ...rest] = args;
|
|
24605
|
+
if (command2 === "setup") return parseSetupArgs(rest);
|
|
24606
|
+
if (command2 === "templates" || command2 === "anchors") return { kind: command2, args: rest };
|
|
24607
|
+
if (["--help", "-h", "help", "--version", "-v"].includes(command2)) {
|
|
24608
|
+
if (rest.length) throw new Error(`Unexpected argument "${rest[0]}" after ${command2}.`);
|
|
24609
|
+
return command2 === "--version" || command2 === "-v" ? { kind: "version" } : { kind: "help", topic: "main" };
|
|
24610
|
+
}
|
|
24611
|
+
throw new Error(`Unknown command or option "${command2}".`);
|
|
24612
|
+
}
|
|
24613
|
+
|
|
24614
|
+
// src/cliEntry.ts
|
|
24615
|
+
var parsed;
|
|
24616
|
+
try {
|
|
24617
|
+
parsed = parseCliArgs(process.argv.slice(2));
|
|
24618
|
+
} catch (error2) {
|
|
24619
|
+
const usage = process.argv[2] === "setup" ? SETUP_USAGE : CLI_USAGE;
|
|
24620
|
+
process.stderr.write(`${error2.message}
|
|
24621
|
+
|
|
24622
|
+
${usage}`);
|
|
24623
|
+
process.exit(2);
|
|
24624
|
+
}
|
|
24625
|
+
var command = parsed;
|
|
24626
|
+
if (command.kind === "help") {
|
|
24627
|
+
process.stdout.write(command.topic === "setup" ? SETUP_USAGE : CLI_USAGE);
|
|
24628
|
+
process.exit(0);
|
|
24629
|
+
}
|
|
24630
|
+
var require2 = createRequire(import.meta.url);
|
|
24631
|
+
var { version } = require2("../package.json");
|
|
24632
|
+
if (command.kind === "version") {
|
|
24633
|
+
process.stdout.write(`${version}
|
|
24634
|
+
`);
|
|
24635
|
+
process.exit(0);
|
|
24636
|
+
}
|
|
24515
24637
|
|
|
24516
24638
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
24517
24639
|
var external_exports = {};
|
|
@@ -29741,7 +29863,7 @@ var Doc = class {
|
|
|
29741
29863
|
};
|
|
29742
29864
|
|
|
29743
29865
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/versions.js
|
|
29744
|
-
var
|
|
29866
|
+
var version2 = {
|
|
29745
29867
|
major: 4,
|
|
29746
29868
|
minor: 0,
|
|
29747
29869
|
patch: 0
|
|
@@ -29753,7 +29875,7 @@ var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
|
|
|
29753
29875
|
inst ?? (inst = {});
|
|
29754
29876
|
inst._zod.def = def;
|
|
29755
29877
|
inst._zod.bag = inst._zod.bag || {};
|
|
29756
|
-
inst._zod.version =
|
|
29878
|
+
inst._zod.version = version2;
|
|
29757
29879
|
const checks = [...inst._zod.def.checks ?? []];
|
|
29758
29880
|
if (inst._zod.traits.has("$ZodCheck")) {
|
|
29759
29881
|
checks.unshift(inst);
|
|
@@ -36873,8 +36995,8 @@ var Protocol = class {
|
|
|
36873
36995
|
const method = getMethodLiteral(requestSchema);
|
|
36874
36996
|
this.assertRequestHandlerCapability(method);
|
|
36875
36997
|
this._requestHandlers.set(method, (request, extra) => {
|
|
36876
|
-
const
|
|
36877
|
-
return Promise.resolve(handler(
|
|
36998
|
+
const parsed2 = parseWithCompat(requestSchema, request);
|
|
36999
|
+
return Promise.resolve(handler(parsed2, extra));
|
|
36878
37000
|
});
|
|
36879
37001
|
}
|
|
36880
37002
|
/**
|
|
@@ -36899,8 +37021,8 @@ var Protocol = class {
|
|
|
36899
37021
|
setNotificationHandler(notificationSchema, handler) {
|
|
36900
37022
|
const method = getMethodLiteral(notificationSchema);
|
|
36901
37023
|
this._notificationHandlers.set(method, (notification) => {
|
|
36902
|
-
const
|
|
36903
|
-
return Promise.resolve(handler(
|
|
37024
|
+
const parsed2 = parseWithCompat(notificationSchema, notification);
|
|
37025
|
+
return Promise.resolve(handler(parsed2));
|
|
36904
37026
|
});
|
|
36905
37027
|
}
|
|
36906
37028
|
/**
|
|
@@ -39096,6 +39218,7 @@ function sleep(ms) {
|
|
|
39096
39218
|
}
|
|
39097
39219
|
var PERMISSION_DENIED_PATTERN = /not author(?:i[sz])ed|not permitted|access.*denied|\(-1743\)/i;
|
|
39098
39220
|
var PERMISSION_DENIED_MESSAGE = `Permission denied. ${AUTOMATION_REMEDIATION}`;
|
|
39221
|
+
var NOTE_ID_NOT_FOUND_MESSAGE = "Note not found. The note may have been deleted or the ID is invalid.";
|
|
39099
39222
|
function isPermissionDenied(error2) {
|
|
39100
39223
|
if (!error2) return false;
|
|
39101
39224
|
return PERMISSION_DENIED_PATTERN.test(error2) || error2.includes(PERMISSION_DENIED_MESSAGE);
|
|
@@ -39128,7 +39251,7 @@ var ERROR_MAPPINGS = [
|
|
|
39128
39251
|
// Note not found by ID
|
|
39129
39252
|
{
|
|
39130
39253
|
pattern: /can't get note id/i,
|
|
39131
|
-
message:
|
|
39254
|
+
message: NOTE_ID_NOT_FOUND_MESSAGE
|
|
39132
39255
|
},
|
|
39133
39256
|
// Folder not found
|
|
39134
39257
|
{
|
|
@@ -39727,9 +39850,9 @@ function runJsonQuery(sql, dbPath2) {
|
|
|
39727
39850
|
console.error(`Identifier query failed: ${detail}`);
|
|
39728
39851
|
throw new IdentifierResolutionError("query_error", "Failed to read the Notes database.");
|
|
39729
39852
|
}
|
|
39730
|
-
const
|
|
39731
|
-
const rows = typeof
|
|
39732
|
-
return { store:
|
|
39853
|
+
const parsed2 = JSON.parse(out || "{}");
|
|
39854
|
+
const rows = typeof parsed2.rows === "string" ? JSON.parse(parsed2.rows) : parsed2.rows;
|
|
39855
|
+
return { store: parsed2.store ?? null, rows: rows ?? [] };
|
|
39733
39856
|
}
|
|
39734
39857
|
function coreDataId(store, entity3, pk) {
|
|
39735
39858
|
return `x-coredata://${store}/${entity3}/p${pk}`;
|
|
@@ -41432,19 +41555,19 @@ function decodeSmartFolderQuery(raw, context = {}) {
|
|
|
41432
41555
|
if (raw === null || raw.trim() === "") {
|
|
41433
41556
|
return { match: null, filters: [], query: null, fullyDecoded: false };
|
|
41434
41557
|
}
|
|
41435
|
-
let
|
|
41558
|
+
let parsed2;
|
|
41436
41559
|
try {
|
|
41437
|
-
|
|
41560
|
+
parsed2 = JSON.parse(raw);
|
|
41438
41561
|
} catch {
|
|
41439
41562
|
return { match: null, filters: [], query: raw, fullyDecoded: false };
|
|
41440
41563
|
}
|
|
41441
41564
|
const decoder = new Decoder(context);
|
|
41442
|
-
const type = isClause(
|
|
41565
|
+
const type = isClause(parsed2) ? parsed2.type : void 0;
|
|
41443
41566
|
if (!isClause(type)) {
|
|
41444
41567
|
return {
|
|
41445
41568
|
match: null,
|
|
41446
|
-
filters: [decoder.unknown(null,
|
|
41447
|
-
query:
|
|
41569
|
+
filters: [decoder.unknown(null, parsed2, false)],
|
|
41570
|
+
query: parsed2,
|
|
41448
41571
|
fullyDecoded: false
|
|
41449
41572
|
};
|
|
41450
41573
|
}
|
|
@@ -43087,8 +43210,8 @@ function parseAppleScriptDate(appleScriptDate) {
|
|
|
43087
43210
|
}
|
|
43088
43211
|
const withoutPrefix = s.replace(/^date\s+/, "");
|
|
43089
43212
|
const normalized2 = withoutPrefix.replace(" at ", " ");
|
|
43090
|
-
const
|
|
43091
|
-
return isNaN(
|
|
43213
|
+
const parsed2 = new Date(normalized2);
|
|
43214
|
+
return isNaN(parsed2.getTime()) ? /* @__PURE__ */ new Date() : parsed2;
|
|
43092
43215
|
}
|
|
43093
43216
|
function buildAppleScriptDateVar(date3, varName = "thresholdDate") {
|
|
43094
43217
|
const year = date3.getFullYear();
|
|
@@ -43279,20 +43402,20 @@ function buildAccountResolution(account) {
|
|
|
43279
43402
|
if ${AS_ACCOUNT_REF} is missing value then error "${ACCOUNT_RESOLUTION_ERROR} Account \\"${safeAccount}\\" not found"
|
|
43280
43403
|
`;
|
|
43281
43404
|
}
|
|
43282
|
-
function buildAccountScopedScript(scope2,
|
|
43405
|
+
function buildAccountScopedScript(scope2, command2) {
|
|
43283
43406
|
return `
|
|
43284
43407
|
tell application "Notes"
|
|
43285
43408
|
${buildAccountResolution(scope2.account)}
|
|
43286
43409
|
tell ${AS_ACCOUNT_REF}
|
|
43287
|
-
${
|
|
43410
|
+
${command2}
|
|
43288
43411
|
end tell
|
|
43289
43412
|
end tell
|
|
43290
43413
|
`;
|
|
43291
43414
|
}
|
|
43292
|
-
function buildAppLevelScript(
|
|
43415
|
+
function buildAppLevelScript(command2) {
|
|
43293
43416
|
return `
|
|
43294
43417
|
tell application "Notes"
|
|
43295
|
-
${
|
|
43418
|
+
${command2}
|
|
43296
43419
|
end tell
|
|
43297
43420
|
`;
|
|
43298
43421
|
}
|
|
@@ -43789,9 +43912,20 @@ var AppleNotesManager = class {
|
|
|
43789
43912
|
* This method fetches the note and its metadata using this ID.
|
|
43790
43913
|
*
|
|
43791
43914
|
* @param id - CoreData URL identifier for the note
|
|
43792
|
-
* @returns Note object with metadata, or null if
|
|
43915
|
+
* @returns Note object with metadata, or null if the lookup fails
|
|
43793
43916
|
*/
|
|
43794
43917
|
getNoteById(id2) {
|
|
43918
|
+
return this.readNoteById(id2).note;
|
|
43919
|
+
}
|
|
43920
|
+
/**
|
|
43921
|
+
* Reads exact-ID metadata while retaining the executor or parser failure.
|
|
43922
|
+
* Unlike the legacy nullable getter, callers can distinguish a missing note
|
|
43923
|
+
* from denied Automation, a timeout, or an unreadable metadata response.
|
|
43924
|
+
*
|
|
43925
|
+
* @param id - CoreData URL identifier for the note
|
|
43926
|
+
* @returns The note, or the diagnostic that prevented its metadata read
|
|
43927
|
+
*/
|
|
43928
|
+
readNoteById(id2) {
|
|
43795
43929
|
const safeId = sanitizeId(id2);
|
|
43796
43930
|
const getCommand = `
|
|
43797
43931
|
set n to note id "${safeId}"
|
|
@@ -43805,22 +43939,27 @@ var AppleNotesManager = class {
|
|
|
43805
43939
|
const result = executeAppleScript(script);
|
|
43806
43940
|
if (!result.success) {
|
|
43807
43941
|
console.error(`Failed to get note with ID "${id2}":`, result.error);
|
|
43808
|
-
return
|
|
43942
|
+
return {
|
|
43943
|
+
note: null,
|
|
43944
|
+
error: result.error?.trim() ? result.error : "AppleScript note metadata read failed"
|
|
43945
|
+
};
|
|
43809
43946
|
}
|
|
43810
|
-
const
|
|
43811
|
-
if (!
|
|
43812
|
-
return null;
|
|
43947
|
+
const parsed2 = parseNotePropertiesOutput(result.output);
|
|
43948
|
+
if (!parsed2) {
|
|
43949
|
+
return { note: null, error: "Unexpected note metadata response" };
|
|
43813
43950
|
}
|
|
43814
43951
|
return {
|
|
43815
|
-
|
|
43816
|
-
|
|
43817
|
-
|
|
43818
|
-
|
|
43819
|
-
|
|
43820
|
-
|
|
43821
|
-
|
|
43822
|
-
|
|
43823
|
-
|
|
43952
|
+
note: {
|
|
43953
|
+
id: parsed2.id,
|
|
43954
|
+
title: parsed2.title,
|
|
43955
|
+
content: "",
|
|
43956
|
+
// Not fetched to keep response small
|
|
43957
|
+
tags: [],
|
|
43958
|
+
created: parsed2.created,
|
|
43959
|
+
modified: parsed2.modified,
|
|
43960
|
+
shared: parsed2.shared,
|
|
43961
|
+
passwordProtected: parsed2.passwordProtected
|
|
43962
|
+
}
|
|
43824
43963
|
};
|
|
43825
43964
|
}
|
|
43826
43965
|
/**
|
|
@@ -43866,20 +44005,20 @@ var AppleNotesManager = class {
|
|
|
43866
44005
|
console.error(`Failed to get details for note "${title}":`, result.error);
|
|
43867
44006
|
return null;
|
|
43868
44007
|
}
|
|
43869
|
-
const
|
|
43870
|
-
if (!
|
|
44008
|
+
const parsed2 = parseNotePropertiesOutput(result.output);
|
|
44009
|
+
if (!parsed2) {
|
|
43871
44010
|
return null;
|
|
43872
44011
|
}
|
|
43873
44012
|
return {
|
|
43874
|
-
id:
|
|
43875
|
-
title:
|
|
44013
|
+
id: parsed2.id,
|
|
44014
|
+
title: parsed2.title,
|
|
43876
44015
|
content: "",
|
|
43877
44016
|
// Not fetched
|
|
43878
44017
|
tags: [],
|
|
43879
|
-
created:
|
|
43880
|
-
modified:
|
|
43881
|
-
shared:
|
|
43882
|
-
passwordProtected:
|
|
44018
|
+
created: parsed2.created,
|
|
44019
|
+
modified: parsed2.modified,
|
|
44020
|
+
shared: parsed2.shared,
|
|
44021
|
+
passwordProtected: parsed2.passwordProtected,
|
|
43883
44022
|
account: this.reportedAccount(targetAccount)
|
|
43884
44023
|
};
|
|
43885
44024
|
}
|
|
@@ -44289,12 +44428,12 @@ var AppleNotesManager = class {
|
|
|
44289
44428
|
const header = sepIdx === -1 ? body : body.slice(0, sepIdx);
|
|
44290
44429
|
const totalCount = Number.parseInt(header.trim(), 10);
|
|
44291
44430
|
const records2 = sepIdx === -1 ? "" : body.slice(sepIdx + 1);
|
|
44292
|
-
const
|
|
44431
|
+
const parsed2 = this.parseBulkListRecords(records2, safeLimit, {
|
|
44293
44432
|
ids: trashIds2,
|
|
44294
44433
|
include: includeRecentlyDeleted
|
|
44295
44434
|
});
|
|
44296
|
-
if (!Number.isNaN(totalCount) && (
|
|
44297
|
-
return
|
|
44435
|
+
if (!Number.isNaN(totalCount) && (parsed2.refs.length >= safeLimit || totalCount <= safeLimit)) {
|
|
44436
|
+
return parsed2;
|
|
44298
44437
|
}
|
|
44299
44438
|
}
|
|
44300
44439
|
const script = buildAccountScopedScript(
|
|
@@ -44498,9 +44637,9 @@ var AppleNotesManager = class {
|
|
|
44498
44637
|
const parts = result.output.replace(/\n$/, "").split(FIELD_SEP);
|
|
44499
44638
|
if (parts.length !== 7 || !parts[0] || !parts[2]) throw new Error("Incomplete folder metadata");
|
|
44500
44639
|
const count = (value) => {
|
|
44501
|
-
const
|
|
44502
|
-
if (!Number.isInteger(
|
|
44503
|
-
return
|
|
44640
|
+
const parsed2 = Number(value.trim());
|
|
44641
|
+
if (!Number.isInteger(parsed2) || parsed2 < 0) throw new Error("Incomplete folder metadata");
|
|
44642
|
+
return parsed2;
|
|
44504
44643
|
};
|
|
44505
44644
|
return {
|
|
44506
44645
|
id: id2,
|
|
@@ -44881,12 +45020,12 @@ var AppleNotesManager = class {
|
|
|
44881
45020
|
* @returns Default account and folder metadata
|
|
44882
45021
|
*/
|
|
44883
45022
|
getDefaultLocation() {
|
|
44884
|
-
const
|
|
45023
|
+
const command2 = `
|
|
44885
45024
|
set aRef to default account
|
|
44886
45025
|
set fRef to default folder of aRef
|
|
44887
45026
|
return (id of aRef) & ${AS_FIELD_SEP} & (name of aRef) & ${AS_FIELD_SEP} & (upgraded of aRef as text) & ${AS_FIELD_SEP} & (id of fRef) & ${AS_FIELD_SEP} & (name of fRef) & ${AS_FIELD_SEP} & (shared of fRef as text)
|
|
44888
45027
|
`;
|
|
44889
|
-
const result = executeAppleScript(buildAppLevelScript(
|
|
45028
|
+
const result = executeAppleScript(buildAppLevelScript(command2));
|
|
44890
45029
|
if (!result.success) {
|
|
44891
45030
|
throw new Error(`Failed to get default Notes location: ${result.error ?? "unknown error"}`);
|
|
44892
45031
|
}
|
|
@@ -44917,7 +45056,7 @@ var AppleNotesManager = class {
|
|
|
44917
45056
|
* @returns Array of selected notes, or an empty array when nothing is selected
|
|
44918
45057
|
*/
|
|
44919
45058
|
getSelectedNotes() {
|
|
44920
|
-
const
|
|
45059
|
+
const command2 = `
|
|
44921
45060
|
set selectedNotes to selection
|
|
44922
45061
|
set noteList to {}
|
|
44923
45062
|
repeat with n in selectedNotes
|
|
@@ -44939,7 +45078,7 @@ var AppleNotesManager = class {
|
|
|
44939
45078
|
set AppleScript's text item delimiters to ${AS_RECORD_SEP}
|
|
44940
45079
|
return noteList as text
|
|
44941
45080
|
`;
|
|
44942
|
-
const result = executeAppleScript(buildAppLevelScript(
|
|
45081
|
+
const result = executeAppleScript(buildAppLevelScript(command2));
|
|
44943
45082
|
if (!result.success) {
|
|
44944
45083
|
throw new Error(`Failed to get selected notes: ${result.error ?? "unknown error"}`);
|
|
44945
45084
|
}
|
|
@@ -48479,7 +48618,7 @@ function describeMatchDetails(hit) {
|
|
|
48479
48618
|
}
|
|
48480
48619
|
|
|
48481
48620
|
// src/tools/doctor.ts
|
|
48482
|
-
import { spawnSync } from "child_process";
|
|
48621
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
48483
48622
|
|
|
48484
48623
|
// src/services/nativeTags.ts
|
|
48485
48624
|
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
@@ -49335,7 +49474,545 @@ ${request.content}`
|
|
|
49335
49474
|
|
|
49336
49475
|
// src/services/capabilityMatrix.ts
|
|
49337
49476
|
import { execFileSync as execFileSync17 } from "node:child_process";
|
|
49338
|
-
import { release } from "node:os";
|
|
49477
|
+
import { release as release2 } from "node:os";
|
|
49478
|
+
|
|
49479
|
+
// src/services/publicHelper.ts
|
|
49480
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
49481
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
49482
|
+
import {
|
|
49483
|
+
chmodSync,
|
|
49484
|
+
existsSync as existsSync12,
|
|
49485
|
+
mkdirSync as mkdirSync4,
|
|
49486
|
+
mkdtempSync as mkdtempSync5,
|
|
49487
|
+
readFileSync as readFileSync2,
|
|
49488
|
+
renameSync,
|
|
49489
|
+
rmSync as rmSync5,
|
|
49490
|
+
writeFileSync as writeFileSync4
|
|
49491
|
+
} from "node:fs";
|
|
49492
|
+
import { homedir as homedir16, release } from "node:os";
|
|
49493
|
+
import { dirname as dirname4, join as join19, resolve as resolve3 } from "node:path";
|
|
49494
|
+
import { fileURLToPath } from "node:url";
|
|
49495
|
+
var PUBLIC_HELPER_PROTOCOL = 1;
|
|
49496
|
+
var PUBLIC_HELPER_DIR_ENV = "APPLE_NOTES_MCP_PUBLIC_HELPER_DIR";
|
|
49497
|
+
var PUBLIC_HELPER_TIMEOUT_ENV = "APPLE_NOTES_MCP_PUBLIC_HELPER_TIMEOUT_MS";
|
|
49498
|
+
var PUBLIC_HELPER_BINARY = "apple-notes-public-helper";
|
|
49499
|
+
var PUBLIC_HELPER_SOURCE = "native/public-helper/apple-notes-public-helper.swift";
|
|
49500
|
+
var PUBLIC_HELPER_MANIFEST = "manifest.json";
|
|
49501
|
+
var PUBLIC_HELPER_SETUP_COMMAND = "apple-notes-mcp setup --public-helper";
|
|
49502
|
+
var PUBLIC_HELPER_ACTIONS = /* @__PURE__ */ new Set([
|
|
49503
|
+
"hello",
|
|
49504
|
+
"decode_drawing",
|
|
49505
|
+
"transcribe",
|
|
49506
|
+
"speech_status"
|
|
49507
|
+
]);
|
|
49508
|
+
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
49509
|
+
var MAX_OUTPUT_BYTES2 = 256 * 1024 * 1024;
|
|
49510
|
+
var publicManifestSchema = external_exports.object({
|
|
49511
|
+
schemaVersion: external_exports.literal(1),
|
|
49512
|
+
protocolVersion: external_exports.number().int(),
|
|
49513
|
+
sourceSha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
49514
|
+
binarySha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
49515
|
+
builtAt: external_exports.string(),
|
|
49516
|
+
osVersion: external_exports.string(),
|
|
49517
|
+
compiler: external_exports.string()
|
|
49518
|
+
});
|
|
49519
|
+
function packageRoot(fromDir = dirname4(fileURLToPath(import.meta.url))) {
|
|
49520
|
+
let dir = fromDir;
|
|
49521
|
+
for (; ; ) {
|
|
49522
|
+
const candidate = join19(dir, "package.json");
|
|
49523
|
+
if (existsSync12(candidate)) {
|
|
49524
|
+
try {
|
|
49525
|
+
if (JSON.parse(readFileSync2(candidate, "utf8")).name === "apple-notes-mcp")
|
|
49526
|
+
return dir;
|
|
49527
|
+
} catch {
|
|
49528
|
+
}
|
|
49529
|
+
}
|
|
49530
|
+
const parent = dirname4(dir);
|
|
49531
|
+
if (parent === dir) return resolve3(fromDir, "..");
|
|
49532
|
+
dir = parent;
|
|
49533
|
+
}
|
|
49534
|
+
}
|
|
49535
|
+
function defaultPublicHelperDeps(overrides = {}) {
|
|
49536
|
+
return {
|
|
49537
|
+
env: process.env,
|
|
49538
|
+
platform: process.platform,
|
|
49539
|
+
sourcePath: join19(packageRoot(), PUBLIC_HELPER_SOURCE),
|
|
49540
|
+
exists: existsSync12,
|
|
49541
|
+
readFile: (path10) => readFileSync2(path10),
|
|
49542
|
+
spawn: spawnSync,
|
|
49543
|
+
spawnAsync: spawn,
|
|
49544
|
+
...overrides
|
|
49545
|
+
};
|
|
49546
|
+
}
|
|
49547
|
+
function publicHelperInstallDir(env = process.env) {
|
|
49548
|
+
const override = env[PUBLIC_HELPER_DIR_ENV]?.trim();
|
|
49549
|
+
if (override) return override;
|
|
49550
|
+
return join19(homedir16(), "Library", "Application Support", "apple-notes-mcp", "public-helper");
|
|
49551
|
+
}
|
|
49552
|
+
function sha256Hex(data) {
|
|
49553
|
+
return createHash2("sha256").update(data).digest("hex");
|
|
49554
|
+
}
|
|
49555
|
+
function inspectPublicHelper(deps = defaultPublicHelperDeps()) {
|
|
49556
|
+
const installDir = publicHelperInstallDir(deps.env);
|
|
49557
|
+
const binaryPath = join19(installDir, PUBLIC_HELPER_BINARY);
|
|
49558
|
+
const base = { installDir, binaryPath, sourcePath: deps.sourcePath, manifest: null };
|
|
49559
|
+
const fail = (reason, detail) => ({
|
|
49560
|
+
...base,
|
|
49561
|
+
ready: false,
|
|
49562
|
+
reason,
|
|
49563
|
+
detail
|
|
49564
|
+
});
|
|
49565
|
+
if (deps.platform !== "darwin") return fail("unsupported_platform", "macOS only");
|
|
49566
|
+
if (!deps.exists(deps.sourcePath))
|
|
49567
|
+
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
49568
|
+
const manifestPath = join19(installDir, PUBLIC_HELPER_MANIFEST);
|
|
49569
|
+
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
49570
|
+
return fail(
|
|
49571
|
+
"helper_not_installed",
|
|
49572
|
+
`The public native helper is not built. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\`.`
|
|
49573
|
+
);
|
|
49574
|
+
let manifest;
|
|
49575
|
+
try {
|
|
49576
|
+
manifest = publicManifestSchema.parse(JSON.parse(deps.readFile(manifestPath).toString("utf8")));
|
|
49577
|
+
} catch {
|
|
49578
|
+
return fail(
|
|
49579
|
+
"helper_manifest_invalid",
|
|
49580
|
+
`The helper manifest is unreadable. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\`.`
|
|
49581
|
+
);
|
|
49582
|
+
}
|
|
49583
|
+
if (manifest.sourceSha256 !== sha256Hex(deps.readFile(deps.sourcePath)) || manifest.protocolVersion !== PUBLIC_HELPER_PROTOCOL)
|
|
49584
|
+
return {
|
|
49585
|
+
...fail(
|
|
49586
|
+
"helper_stale",
|
|
49587
|
+
`The installed helper was built from a different source than this apple-notes-mcp version ships. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\` again.`
|
|
49588
|
+
),
|
|
49589
|
+
manifest
|
|
49590
|
+
};
|
|
49591
|
+
if (sha256Hex(deps.readFile(binaryPath)) !== manifest.binarySha256)
|
|
49592
|
+
return {
|
|
49593
|
+
...fail(
|
|
49594
|
+
"helper_modified",
|
|
49595
|
+
`The helper binary no longer matches the checksum recorded when it was built. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\` to rebuild it.`
|
|
49596
|
+
),
|
|
49597
|
+
manifest
|
|
49598
|
+
};
|
|
49599
|
+
return { ...base, manifest, ready: true, reason: null, detail: null };
|
|
49600
|
+
}
|
|
49601
|
+
var UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
|
|
49602
|
+
"unsupported_platform",
|
|
49603
|
+
"helper_not_installed",
|
|
49604
|
+
"helper_stale",
|
|
49605
|
+
"helper_modified",
|
|
49606
|
+
"helper_manifest_invalid"
|
|
49607
|
+
]);
|
|
49608
|
+
var ENVELOPE_CODES = {
|
|
49609
|
+
invalid_request: "validation_error",
|
|
49610
|
+
attachment_not_found: "not_found"
|
|
49611
|
+
};
|
|
49612
|
+
var PublicHelperError = class extends CodedError {
|
|
49613
|
+
constructor(code, message) {
|
|
49614
|
+
super(message, {
|
|
49615
|
+
code: UNAVAILABLE_CODES.has(code) ? "unsupported" : ENVELOPE_CODES[code] ?? "operation_failed",
|
|
49616
|
+
helperCode: code
|
|
49617
|
+
});
|
|
49618
|
+
this.code = code;
|
|
49619
|
+
this.name = "PublicHelperError";
|
|
49620
|
+
}
|
|
49621
|
+
code;
|
|
49622
|
+
};
|
|
49623
|
+
var errorSchema = external_exports.object({ status: external_exports.literal("error"), code: external_exports.string(), message: external_exports.string() });
|
|
49624
|
+
var publicHelloSchema = external_exports.object({
|
|
49625
|
+
status: external_exports.literal("ok"),
|
|
49626
|
+
protocolVersion: external_exports.number().int(),
|
|
49627
|
+
sourceSha256: external_exports.string(),
|
|
49628
|
+
actions: external_exports.array(external_exports.string())
|
|
49629
|
+
}).passthrough();
|
|
49630
|
+
function callPublicHelper(action, fields = {}, deps = defaultPublicHelperDeps(), options = {}) {
|
|
49631
|
+
const { binaryPath, timeout, input } = prepareCall(action, fields, deps, options);
|
|
49632
|
+
const result = deps.spawn(binaryPath, [], {
|
|
49633
|
+
input,
|
|
49634
|
+
encoding: "utf8",
|
|
49635
|
+
timeout,
|
|
49636
|
+
killSignal: "SIGKILL",
|
|
49637
|
+
maxBuffer: MAX_OUTPUT_BYTES2
|
|
49638
|
+
});
|
|
49639
|
+
const errno = result.error?.code;
|
|
49640
|
+
if (errno === "ETIMEDOUT" || result.signal && result.status === null)
|
|
49641
|
+
throw new PublicHelperError("timeout", `The helper did not answer within ${timeout} ms.`);
|
|
49642
|
+
if (result.error)
|
|
49643
|
+
throw new PublicHelperError(
|
|
49644
|
+
"helper_unreachable",
|
|
49645
|
+
`Could not run the helper: ${result.error.message}`
|
|
49646
|
+
);
|
|
49647
|
+
return parseHelperOutput(result.status, String(result.stdout ?? ""));
|
|
49648
|
+
}
|
|
49649
|
+
async function callPublicHelperAsync(action, fields = {}, deps = defaultPublicHelperDeps(), options = {}) {
|
|
49650
|
+
const { binaryPath, timeout, input } = prepareCall(action, fields, deps, options);
|
|
49651
|
+
const { signal } = options;
|
|
49652
|
+
const aborted2 = () => new PublicHelperError("aborted", "The request was cancelled; the helper was stopped.");
|
|
49653
|
+
if (signal?.aborted) throw aborted2();
|
|
49654
|
+
return new Promise((resolvePromise, reject) => {
|
|
49655
|
+
const child2 = (deps.spawnAsync ?? spawn)(binaryPath, [], {
|
|
49656
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
49657
|
+
});
|
|
49658
|
+
const chunks = [];
|
|
49659
|
+
let size = 0;
|
|
49660
|
+
let failure2 = null;
|
|
49661
|
+
const stop = (error2) => {
|
|
49662
|
+
failure2 ??= error2;
|
|
49663
|
+
child2.kill("SIGKILL");
|
|
49664
|
+
};
|
|
49665
|
+
const timer = setTimeout(
|
|
49666
|
+
() => stop(new PublicHelperError("timeout", `The helper did not answer within ${timeout} ms.`)),
|
|
49667
|
+
timeout
|
|
49668
|
+
);
|
|
49669
|
+
const onAbort = () => stop(aborted2());
|
|
49670
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
49671
|
+
const settle = () => {
|
|
49672
|
+
clearTimeout(timer);
|
|
49673
|
+
signal?.removeEventListener("abort", onAbort);
|
|
49674
|
+
};
|
|
49675
|
+
child2.stdout?.on("data", (chunk) => {
|
|
49676
|
+
size += chunk.length;
|
|
49677
|
+
if (size > MAX_OUTPUT_BYTES2)
|
|
49678
|
+
stop(new PublicHelperError("invalid_response", "The helper response is too large."));
|
|
49679
|
+
else chunks.push(chunk);
|
|
49680
|
+
});
|
|
49681
|
+
child2.on("error", (error2) => {
|
|
49682
|
+
settle();
|
|
49683
|
+
reject(
|
|
49684
|
+
failure2 ?? new PublicHelperError("helper_unreachable", `Could not run the helper: ${error2.message}`)
|
|
49685
|
+
);
|
|
49686
|
+
});
|
|
49687
|
+
child2.on("close", (status, exitSignal) => {
|
|
49688
|
+
settle();
|
|
49689
|
+
if (failure2) return reject(failure2);
|
|
49690
|
+
if (status === null && exitSignal)
|
|
49691
|
+
return reject(
|
|
49692
|
+
new PublicHelperError("helper_crashed", `The helper stopped on signal ${exitSignal}.`)
|
|
49693
|
+
);
|
|
49694
|
+
try {
|
|
49695
|
+
resolvePromise(parseHelperOutput(status, Buffer.concat(chunks).toString("utf8")));
|
|
49696
|
+
} catch (error2) {
|
|
49697
|
+
reject(error2);
|
|
49698
|
+
}
|
|
49699
|
+
});
|
|
49700
|
+
child2.stdin?.on("error", () => {
|
|
49701
|
+
});
|
|
49702
|
+
child2.stdin?.end(input);
|
|
49703
|
+
});
|
|
49704
|
+
}
|
|
49705
|
+
function prepareCall(action, fields, deps, options) {
|
|
49706
|
+
if (!PUBLIC_HELPER_ACTIONS.has(action))
|
|
49707
|
+
throw new PublicHelperError(
|
|
49708
|
+
"unknown_action",
|
|
49709
|
+
`"${action}" is not an action the server sends to the public helper.`
|
|
49710
|
+
);
|
|
49711
|
+
let binaryPath = options.binaryPath;
|
|
49712
|
+
if (!binaryPath) {
|
|
49713
|
+
const install = inspectPublicHelper(deps);
|
|
49714
|
+
if (!install.ready)
|
|
49715
|
+
throw new PublicHelperError(install.reason ?? "helper_not_installed", install.detail ?? "");
|
|
49716
|
+
binaryPath = install.binaryPath;
|
|
49717
|
+
}
|
|
49718
|
+
const timeout = options.timeoutMs || Number.parseInt(deps.env[PUBLIC_HELPER_TIMEOUT_ENV] || "", 10) || DEFAULT_TIMEOUT_MS2;
|
|
49719
|
+
const input = JSON.stringify({ protocol: PUBLIC_HELPER_PROTOCOL, action, ...fields });
|
|
49720
|
+
return { binaryPath, timeout, input };
|
|
49721
|
+
}
|
|
49722
|
+
function parseHelperOutput(status, stdout) {
|
|
49723
|
+
let parsed2;
|
|
49724
|
+
try {
|
|
49725
|
+
parsed2 = JSON.parse(stdout.trim());
|
|
49726
|
+
} catch {
|
|
49727
|
+
throw new PublicHelperError(
|
|
49728
|
+
"invalid_response",
|
|
49729
|
+
`The helper exited with status ${status} and no JSON response.`
|
|
49730
|
+
);
|
|
49731
|
+
}
|
|
49732
|
+
if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2))
|
|
49733
|
+
throw new PublicHelperError("invalid_response", "The helper response is not a JSON object.");
|
|
49734
|
+
const object3 = parsed2;
|
|
49735
|
+
if (status !== 0 || object3.status !== "ok") {
|
|
49736
|
+
const error2 = errorSchema.safeParse(object3);
|
|
49737
|
+
if (!error2.success)
|
|
49738
|
+
throw new PublicHelperError(
|
|
49739
|
+
"invalid_response",
|
|
49740
|
+
`The helper failed with an unrecognized response (exit ${status}).`
|
|
49741
|
+
);
|
|
49742
|
+
throw new PublicHelperError(error2.data.code, error2.data.message);
|
|
49743
|
+
}
|
|
49744
|
+
return object3;
|
|
49745
|
+
}
|
|
49746
|
+
function defaultPublicHelperBuildDeps() {
|
|
49747
|
+
return {
|
|
49748
|
+
...defaultPublicHelperDeps(),
|
|
49749
|
+
osVersion: () => {
|
|
49750
|
+
const r = spawnSync("/usr/bin/sw_vers", ["-productVersion"], { encoding: "utf8" });
|
|
49751
|
+
return r.status === 0 ? r.stdout.trim() : `Darwin ${release()}`;
|
|
49752
|
+
},
|
|
49753
|
+
now: () => /* @__PURE__ */ new Date()
|
|
49754
|
+
};
|
|
49755
|
+
}
|
|
49756
|
+
function sourceDigestSwift(sourceSha) {
|
|
49757
|
+
return `let helperSourceSHA256 = "${sourceSha}"
|
|
49758
|
+
`;
|
|
49759
|
+
}
|
|
49760
|
+
var PUBLIC_HELPER_BUNDLE_ID = "apple-notes-mcp.public-helper";
|
|
49761
|
+
function publicHelperInfoPlist() {
|
|
49762
|
+
return [
|
|
49763
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
49764
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
49765
|
+
'<plist version="1.0">',
|
|
49766
|
+
"<dict>",
|
|
49767
|
+
" <key>CFBundleIdentifier</key>",
|
|
49768
|
+
` <string>${PUBLIC_HELPER_BUNDLE_ID}</string>`,
|
|
49769
|
+
" <key>CFBundleName</key>",
|
|
49770
|
+
" <string>apple-notes-mcp public helper</string>",
|
|
49771
|
+
" <key>CFBundleInfoDictionaryVersion</key>",
|
|
49772
|
+
" <string>6.0</string>",
|
|
49773
|
+
"</dict>",
|
|
49774
|
+
"</plist>",
|
|
49775
|
+
""
|
|
49776
|
+
].join("\n");
|
|
49777
|
+
}
|
|
49778
|
+
function publicHelperCompileArguments(sourcePath, digestPath, plistPath, outputPath) {
|
|
49779
|
+
return [
|
|
49780
|
+
"swiftc",
|
|
49781
|
+
"-O",
|
|
49782
|
+
"-parse-as-library",
|
|
49783
|
+
"-framework",
|
|
49784
|
+
"AppKit",
|
|
49785
|
+
"-framework",
|
|
49786
|
+
"PencilKit",
|
|
49787
|
+
"-framework",
|
|
49788
|
+
"AVFoundation",
|
|
49789
|
+
"-framework",
|
|
49790
|
+
"Speech",
|
|
49791
|
+
"-Xlinker",
|
|
49792
|
+
"-sectcreate",
|
|
49793
|
+
"-Xlinker",
|
|
49794
|
+
"__TEXT",
|
|
49795
|
+
"-Xlinker",
|
|
49796
|
+
"__info_plist",
|
|
49797
|
+
"-Xlinker",
|
|
49798
|
+
plistPath,
|
|
49799
|
+
sourcePath,
|
|
49800
|
+
digestPath,
|
|
49801
|
+
"-o",
|
|
49802
|
+
outputPath
|
|
49803
|
+
];
|
|
49804
|
+
}
|
|
49805
|
+
function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
49806
|
+
const steps = [];
|
|
49807
|
+
const done = (ok2) => ({
|
|
49808
|
+
ok: ok2,
|
|
49809
|
+
checkOnly,
|
|
49810
|
+
steps,
|
|
49811
|
+
installation: inspectPublicHelper(deps)
|
|
49812
|
+
});
|
|
49813
|
+
if (checkOnly) {
|
|
49814
|
+
const installation2 = inspectPublicHelper(deps);
|
|
49815
|
+
steps.push({
|
|
49816
|
+
step: "inspect installed helper",
|
|
49817
|
+
ok: installation2.ready,
|
|
49818
|
+
detail: installation2.ready ? installation2.binaryPath : installation2.detail ?? void 0
|
|
49819
|
+
});
|
|
49820
|
+
return { ok: installation2.ready, checkOnly, steps, installation: installation2 };
|
|
49821
|
+
}
|
|
49822
|
+
if (deps.platform !== "darwin") {
|
|
49823
|
+
steps.push({ step: "platform", ok: false, detail: "macOS only" });
|
|
49824
|
+
return done(false);
|
|
49825
|
+
}
|
|
49826
|
+
if (!deps.exists(deps.sourcePath)) {
|
|
49827
|
+
steps.push({ step: "locate source", ok: false, detail: deps.sourcePath });
|
|
49828
|
+
return done(false);
|
|
49829
|
+
}
|
|
49830
|
+
const sourceSha = sha256Hex(deps.readFile(deps.sourcePath));
|
|
49831
|
+
steps.push({ step: "locate source", ok: true, detail: `sha256 ${sourceSha}` });
|
|
49832
|
+
const version3 = deps.spawn("/usr/bin/xcrun", ["swiftc", "--version"], { encoding: "utf8" });
|
|
49833
|
+
if (version3.status !== 0) {
|
|
49834
|
+
steps.push({
|
|
49835
|
+
step: "find compiler",
|
|
49836
|
+
ok: false,
|
|
49837
|
+
detail: "No Swift compiler found. Install the Command Line Tools with `xcode-select --install`."
|
|
49838
|
+
});
|
|
49839
|
+
return done(false);
|
|
49840
|
+
}
|
|
49841
|
+
const compiler = String(version3.stdout || version3.stderr || "").split("\n").find((line) => line.includes("Swift version"))?.trim() || "swiftc";
|
|
49842
|
+
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
49843
|
+
const installDir = publicHelperInstallDir(deps.env);
|
|
49844
|
+
mkdirSync4(installDir, { recursive: true, mode: 448 });
|
|
49845
|
+
const staging = mkdtempSync5(join19(installDir, ".staging-"));
|
|
49846
|
+
try {
|
|
49847
|
+
const stagedBinary = join19(staging, PUBLIC_HELPER_BINARY);
|
|
49848
|
+
const digestPath = join19(staging, "source-digest.swift");
|
|
49849
|
+
const plistPath = join19(staging, "Info.plist");
|
|
49850
|
+
writeFileSync4(digestPath, sourceDigestSwift(sourceSha), { mode: 384 });
|
|
49851
|
+
writeFileSync4(plistPath, publicHelperInfoPlist(), { mode: 384 });
|
|
49852
|
+
const compile = deps.spawn(
|
|
49853
|
+
"/usr/bin/xcrun",
|
|
49854
|
+
publicHelperCompileArguments(deps.sourcePath, digestPath, plistPath, stagedBinary),
|
|
49855
|
+
{ encoding: "utf8", timeout: 3e5 }
|
|
49856
|
+
);
|
|
49857
|
+
if (compile.status !== 0) {
|
|
49858
|
+
steps.push({
|
|
49859
|
+
step: "compile",
|
|
49860
|
+
ok: false,
|
|
49861
|
+
detail: String(compile.stderr || compile.error?.message || "swiftc failed").slice(0, 4e3)
|
|
49862
|
+
});
|
|
49863
|
+
return done(false);
|
|
49864
|
+
}
|
|
49865
|
+
steps.push({ step: "compile", ok: true });
|
|
49866
|
+
const sign = deps.spawn(
|
|
49867
|
+
"/usr/bin/codesign",
|
|
49868
|
+
["--force", "--sign", "-", "--identifier", PUBLIC_HELPER_BUNDLE_ID, stagedBinary],
|
|
49869
|
+
{ encoding: "utf8" }
|
|
49870
|
+
);
|
|
49871
|
+
if (sign.status !== 0) {
|
|
49872
|
+
steps.push({
|
|
49873
|
+
step: "ad-hoc sign",
|
|
49874
|
+
ok: false,
|
|
49875
|
+
detail: String(sign.stderr || "codesign failed")
|
|
49876
|
+
});
|
|
49877
|
+
return done(false);
|
|
49878
|
+
}
|
|
49879
|
+
steps.push({ step: "ad-hoc sign", ok: true });
|
|
49880
|
+
let hello;
|
|
49881
|
+
try {
|
|
49882
|
+
hello = publicHelloSchema.parse(
|
|
49883
|
+
callPublicHelper("hello", {}, deps, { binaryPath: stagedBinary })
|
|
49884
|
+
);
|
|
49885
|
+
} catch (error2) {
|
|
49886
|
+
steps.push({
|
|
49887
|
+
step: "handshake",
|
|
49888
|
+
ok: false,
|
|
49889
|
+
detail: error2 instanceof Error ? error2.message : String(error2)
|
|
49890
|
+
});
|
|
49891
|
+
return done(false);
|
|
49892
|
+
}
|
|
49893
|
+
if (hello.protocolVersion !== PUBLIC_HELPER_PROTOCOL || hello.sourceSha256 !== sourceSha) {
|
|
49894
|
+
steps.push({
|
|
49895
|
+
step: "handshake",
|
|
49896
|
+
ok: false,
|
|
49897
|
+
detail: `helper reported protocol ${hello.protocolVersion}, source ${hello.sourceSha256}`
|
|
49898
|
+
});
|
|
49899
|
+
return done(false);
|
|
49900
|
+
}
|
|
49901
|
+
steps.push({ step: "handshake", ok: true, detail: `actions: ${hello.actions.join(", ")}` });
|
|
49902
|
+
const manifest = {
|
|
49903
|
+
schemaVersion: 1,
|
|
49904
|
+
protocolVersion: hello.protocolVersion,
|
|
49905
|
+
sourceSha256: sourceSha,
|
|
49906
|
+
binarySha256: sha256Hex(deps.readFile(stagedBinary)),
|
|
49907
|
+
builtAt: deps.now().toISOString(),
|
|
49908
|
+
osVersion: deps.osVersion(),
|
|
49909
|
+
compiler
|
|
49910
|
+
};
|
|
49911
|
+
chmodSync(stagedBinary, 448);
|
|
49912
|
+
renameSync(stagedBinary, join19(installDir, PUBLIC_HELPER_BINARY));
|
|
49913
|
+
writeFileSync4(
|
|
49914
|
+
join19(installDir, PUBLIC_HELPER_MANIFEST),
|
|
49915
|
+
JSON.stringify(manifest, null, 2) + "\n",
|
|
49916
|
+
{ mode: 384 }
|
|
49917
|
+
);
|
|
49918
|
+
steps.push({ step: "install", ok: true, detail: installDir });
|
|
49919
|
+
} finally {
|
|
49920
|
+
rmSync5(staging, { recursive: true, force: true });
|
|
49921
|
+
}
|
|
49922
|
+
const installation = inspectPublicHelper(deps);
|
|
49923
|
+
steps.push({
|
|
49924
|
+
step: "verify installation",
|
|
49925
|
+
ok: installation.ready,
|
|
49926
|
+
detail: installation.ready ? void 0 : installation.detail ?? void 0
|
|
49927
|
+
});
|
|
49928
|
+
return { ok: installation.ready, checkOnly, steps, installation };
|
|
49929
|
+
}
|
|
49930
|
+
function formatPublicHelperBuild(report) {
|
|
49931
|
+
const lines = ["Apple Notes MCP public native helper", ""];
|
|
49932
|
+
for (const step of report.steps)
|
|
49933
|
+
lines.push(`${step.ok ? "\u2713" : "\u2717"} ${step.step}${step.detail ? `: ${step.detail}` : ""}`);
|
|
49934
|
+
lines.push("");
|
|
49935
|
+
if (report.ok) lines.push(`Installed at ${report.installation.binaryPath}.`);
|
|
49936
|
+
else if (report.checkOnly) lines.push(`Run \`${PUBLIC_HELPER_SETUP_COMMAND}\` to build it.`);
|
|
49937
|
+
else lines.push("The helper was not installed. Fix the failed step above and run setup again.");
|
|
49938
|
+
return lines.join("\n");
|
|
49939
|
+
}
|
|
49940
|
+
|
|
49941
|
+
// src/services/publicHelperStatus.ts
|
|
49942
|
+
var speechStatusSchema = external_exports.object({
|
|
49943
|
+
status: external_exports.literal("ok"),
|
|
49944
|
+
speechAuthorization: external_exports.enum(["authorized", "denied", "restricted", "notDetermined"]),
|
|
49945
|
+
requiresGrant: external_exports.boolean()
|
|
49946
|
+
});
|
|
49947
|
+
function unprobedPublicHelperStatus() {
|
|
49948
|
+
return {
|
|
49949
|
+
ready: false,
|
|
49950
|
+
reason: "not_probed",
|
|
49951
|
+
detail: "The public native helper has not been checked.",
|
|
49952
|
+
actions: [],
|
|
49953
|
+
speech: { authorization: null, requiresGrant: null, verified: false, detail: "Not probed." }
|
|
49954
|
+
};
|
|
49955
|
+
}
|
|
49956
|
+
function probePublicHelperStatus(deps = {
|
|
49957
|
+
inspect: inspectPublicHelper,
|
|
49958
|
+
call: (action) => callPublicHelper(action, {}, void 0, { timeoutMs: 3e3 })
|
|
49959
|
+
}) {
|
|
49960
|
+
const result = unprobedPublicHelperStatus();
|
|
49961
|
+
try {
|
|
49962
|
+
const installed = deps.inspect();
|
|
49963
|
+
if (!installed.ready) return { ...result, reason: installed.reason, detail: installed.detail };
|
|
49964
|
+
const hello = publicHelloSchema.safeParse(deps.call("hello"));
|
|
49965
|
+
if (!hello.success)
|
|
49966
|
+
return {
|
|
49967
|
+
...result,
|
|
49968
|
+
reason: "invalid_response",
|
|
49969
|
+
detail: "The helper returned an invalid handshake."
|
|
49970
|
+
};
|
|
49971
|
+
if (hello.data.protocolVersion !== PUBLIC_HELPER_PROTOCOL || hello.data.sourceSha256 !== installed.manifest?.sourceSha256)
|
|
49972
|
+
return {
|
|
49973
|
+
...result,
|
|
49974
|
+
reason: "helper_stale",
|
|
49975
|
+
detail: "The helper handshake does not match the installed protocol and source."
|
|
49976
|
+
};
|
|
49977
|
+
result.actions = hello.data.actions;
|
|
49978
|
+
const missing = ["decode_drawing", "transcribe", "speech_status"].filter(
|
|
49979
|
+
(action) => !result.actions.includes(action)
|
|
49980
|
+
);
|
|
49981
|
+
if (missing.length)
|
|
49982
|
+
return {
|
|
49983
|
+
...result,
|
|
49984
|
+
reason: "helper_action_missing",
|
|
49985
|
+
detail: `The helper does not advertise: ${missing.join(", ")}.`
|
|
49986
|
+
};
|
|
49987
|
+
result.ready = true;
|
|
49988
|
+
result.reason = null;
|
|
49989
|
+
result.detail = "Installed digests, protocol, and required actions verified; no drawing or audio was processed.";
|
|
49990
|
+
} catch (error2) {
|
|
49991
|
+
return {
|
|
49992
|
+
...result,
|
|
49993
|
+
reason: "helper_unreachable",
|
|
49994
|
+
detail: error2 instanceof Error ? error2.message : String(error2)
|
|
49995
|
+
};
|
|
49996
|
+
}
|
|
49997
|
+
try {
|
|
49998
|
+
const speech = speechStatusSchema.safeParse(deps.call("speech_status"));
|
|
49999
|
+
if (!speech.success) {
|
|
50000
|
+
result.speech.detail = "The helper returned an unknown or invalid Speech Recognition status.";
|
|
50001
|
+
} else {
|
|
50002
|
+
result.speech = {
|
|
50003
|
+
authorization: speech.data.speechAuthorization,
|
|
50004
|
+
requiresGrant: speech.data.requiresGrant,
|
|
50005
|
+
verified: true,
|
|
50006
|
+
detail: "Permission status only; locale support and model availability are checked when transcription is requested."
|
|
50007
|
+
};
|
|
50008
|
+
}
|
|
50009
|
+
} catch (error2) {
|
|
50010
|
+
result.speech.detail = error2 instanceof Error ? error2.message : String(error2);
|
|
50011
|
+
}
|
|
50012
|
+
return result;
|
|
50013
|
+
}
|
|
50014
|
+
|
|
50015
|
+
// src/services/capabilityMatrix.ts
|
|
49339
50016
|
var MACOS_SHORTCUTS_CLI = "12.0";
|
|
49340
50017
|
var MACOS_MARKDOWN_IMPORT = "26.0";
|
|
49341
50018
|
var FEATURES = [
|
|
@@ -49412,7 +50089,8 @@ var FEATURES = [
|
|
|
49412
50089
|
{ kind: "notes_automation" },
|
|
49413
50090
|
{ kind: "full_disk_access" },
|
|
49414
50091
|
{ kind: "shortcuts_cli" },
|
|
49415
|
-
{ kind: "shortcut", name: backgroundShortcutName }
|
|
50092
|
+
{ kind: "shortcut", name: backgroundShortcutName },
|
|
50093
|
+
{ kind: "shortcut_consent" }
|
|
49416
50094
|
]
|
|
49417
50095
|
},
|
|
49418
50096
|
{
|
|
@@ -49424,7 +50102,8 @@ var FEATURES = [
|
|
|
49424
50102
|
{ kind: "notes_automation" },
|
|
49425
50103
|
{ kind: "full_disk_access" },
|
|
49426
50104
|
{ kind: "shortcuts_cli" },
|
|
49427
|
-
{ kind: "shortcut", name: nativeTagsShortcutName }
|
|
50105
|
+
{ kind: "shortcut", name: nativeTagsShortcutName },
|
|
50106
|
+
{ kind: "shortcut_consent" }
|
|
49428
50107
|
]
|
|
49429
50108
|
},
|
|
49430
50109
|
{
|
|
@@ -49436,7 +50115,8 @@ var FEATURES = [
|
|
|
49436
50115
|
{ kind: "notes_automation" },
|
|
49437
50116
|
{ kind: "full_disk_access" },
|
|
49438
50117
|
{ kind: "shortcuts_cli" },
|
|
49439
|
-
{ kind: "shortcut", name: markdownShortcutName }
|
|
50118
|
+
{ kind: "shortcut", name: markdownShortcutName },
|
|
50119
|
+
{ kind: "shortcut_consent" }
|
|
49440
50120
|
]
|
|
49441
50121
|
},
|
|
49442
50122
|
// Placeholders for features that need a native WRITE helper, which this
|
|
@@ -49473,12 +50153,31 @@ var FEATURES = [
|
|
|
49473
50153
|
requirements: [{ kind: "full_disk_access" }]
|
|
49474
50154
|
},
|
|
49475
50155
|
{
|
|
49476
|
-
name: "
|
|
49477
|
-
description: "Read
|
|
49478
|
-
tools: ["get-audio-transcripts"
|
|
50156
|
+
name: "storedAudioTranscripts",
|
|
50157
|
+
description: "Read transcripts Notes already stored for a note's audio recordings; no speech helper is needed",
|
|
50158
|
+
tools: ["get-audio-transcripts"],
|
|
49479
50159
|
minimumMacOSVersion: null,
|
|
49480
50160
|
requirements: [{ kind: "full_disk_access" }]
|
|
49481
50161
|
},
|
|
50162
|
+
{
|
|
50163
|
+
name: "classicDrawingDecode",
|
|
50164
|
+
description: "Decode classic PencilKit drawings with the verified public native helper",
|
|
50165
|
+
tools: ["get-note-drawings"],
|
|
50166
|
+
minimumMacOSVersion: null,
|
|
50167
|
+
requirements: [{ kind: "full_disk_access" }, { kind: "public_native_helper" }]
|
|
50168
|
+
},
|
|
50169
|
+
{
|
|
50170
|
+
name: "audioTranscription",
|
|
50171
|
+
description: "Transcribe audio on this Mac with the verified public helper; locale and speech-model readiness are checked per request",
|
|
50172
|
+
tools: ["transcribe-note-audio"],
|
|
50173
|
+
minimumMacOSVersion: null,
|
|
50174
|
+
requirements: [
|
|
50175
|
+
{ kind: "full_disk_access" },
|
|
50176
|
+
{ kind: "public_native_helper" },
|
|
50177
|
+
{ kind: "speech_recognition" },
|
|
50178
|
+
{ kind: "speech_locale_and_assets" }
|
|
50179
|
+
]
|
|
50180
|
+
},
|
|
49482
50181
|
{
|
|
49483
50182
|
name: "markdownTemplateLibrary",
|
|
49484
50183
|
description: "Validate, save, list, show and delete Markdown export templates (local JSON files; exporting with one still needs Full Disk Access)",
|
|
@@ -49519,6 +50218,9 @@ function evaluateFeature(feature, env) {
|
|
|
49519
50218
|
case "notes_automation":
|
|
49520
50219
|
unverified.push(label);
|
|
49521
50220
|
break;
|
|
50221
|
+
case "shortcut_consent":
|
|
50222
|
+
unverified.push(label);
|
|
50223
|
+
break;
|
|
49522
50224
|
case "full_disk_access":
|
|
49523
50225
|
if (!env.fullDiskAccess) {
|
|
49524
50226
|
missing.push(label);
|
|
@@ -49542,6 +50244,29 @@ function evaluateFeature(feature, env) {
|
|
|
49542
50244
|
missing.push(label);
|
|
49543
50245
|
failures.add("not_implemented");
|
|
49544
50246
|
break;
|
|
50247
|
+
case "public_native_helper":
|
|
50248
|
+
if (!env.publicHelper || env.publicHelper.reason === "not_probed") {
|
|
50249
|
+
unverified.push(label);
|
|
50250
|
+
failures.add("public_helper_unverified");
|
|
50251
|
+
} else if (!env.publicHelper.ready) {
|
|
50252
|
+
missing.push(label);
|
|
50253
|
+
failures.add("public_helper_unavailable");
|
|
50254
|
+
}
|
|
50255
|
+
break;
|
|
50256
|
+
case "speech_recognition": {
|
|
50257
|
+
const speech = env.publicHelper?.speech;
|
|
50258
|
+
if (!speech?.verified || speech.authorization === null || speech.requiresGrant === null) {
|
|
50259
|
+
unverified.push(label);
|
|
50260
|
+
failures.add("speech_status_unverified");
|
|
50261
|
+
} else if (speech.authorization === "denied" || speech.authorization === "restricted" || speech.requiresGrant && speech.authorization !== "authorized") {
|
|
50262
|
+
missing.push(label);
|
|
50263
|
+
failures.add("speech_permission_required");
|
|
50264
|
+
}
|
|
50265
|
+
break;
|
|
50266
|
+
}
|
|
50267
|
+
case "speech_locale_and_assets":
|
|
50268
|
+
unverified.push(label);
|
|
50269
|
+
break;
|
|
49545
50270
|
}
|
|
49546
50271
|
}
|
|
49547
50272
|
let reason = null;
|
|
@@ -49552,6 +50277,10 @@ function evaluateFeature(feature, env) {
|
|
|
49552
50277
|
else if (failures.has("full_disk_access_missing")) reason = "full_disk_access_missing";
|
|
49553
50278
|
else if (failures.has("shortcuts_unavailable")) reason = "shortcuts_unavailable";
|
|
49554
50279
|
else if (failures.has("shortcut_not_installed")) reason = "shortcut_not_installed";
|
|
50280
|
+
else if (failures.has("public_helper_unavailable")) reason = "public_helper_unavailable";
|
|
50281
|
+
else if (failures.has("public_helper_unverified")) reason = "public_helper_unverified";
|
|
50282
|
+
else if (failures.has("speech_permission_required")) reason = "speech_permission_required";
|
|
50283
|
+
else if (failures.has("speech_status_unverified")) reason = "speech_status_unverified";
|
|
49555
50284
|
return {
|
|
49556
50285
|
description: feature.description,
|
|
49557
50286
|
tools: feature.tools,
|
|
@@ -49571,7 +50300,8 @@ function evaluateFeatures(env, features = FEATURES) {
|
|
|
49571
50300
|
macOSVersion: env.macOSVersion,
|
|
49572
50301
|
darwinRelease: env.darwinRelease
|
|
49573
50302
|
},
|
|
49574
|
-
features: Object.fromEntries(features.map((f) => [f.name, evaluateFeature(f, env)]))
|
|
50303
|
+
features: Object.fromEntries(features.map((f) => [f.name, evaluateFeature(f, env)])),
|
|
50304
|
+
publicHelper: env.publicHelper ?? unprobedPublicHelperStatus()
|
|
49575
50305
|
};
|
|
49576
50306
|
}
|
|
49577
50307
|
function readMacOSVersion() {
|
|
@@ -49601,9 +50331,10 @@ function probeCapabilityEnvironment() {
|
|
|
49601
50331
|
return {
|
|
49602
50332
|
platform,
|
|
49603
50333
|
macOSVersion: readMacOSVersion(),
|
|
49604
|
-
darwinRelease:
|
|
50334
|
+
darwinRelease: release2(),
|
|
49605
50335
|
fullDiskAccess: isMac && hasFullDiskAccess(),
|
|
49606
|
-
shortcutLines
|
|
50336
|
+
shortcutLines,
|
|
50337
|
+
publicHelper: isMac ? probePublicHelperStatus() : unprobedPublicHelperStatus()
|
|
49607
50338
|
};
|
|
49608
50339
|
}
|
|
49609
50340
|
function getCapabilityMatrix(probe = probeCapabilityEnvironment) {
|
|
@@ -49616,7 +50347,7 @@ function formatCapabilityMatrix(matrix) {
|
|
|
49616
50347
|
];
|
|
49617
50348
|
for (const [name, status] of Object.entries(matrix.features)) {
|
|
49618
50349
|
lines.push(
|
|
49619
|
-
status.available ? ` \u2713 ${name}: available${status.unverified.length ? ` (unverified: ${status.unverified.join(", ")})` : ""}` : ` \u2717 ${name}: ${status.reason}${status.missing.length ? ` (missing: ${status.missing.join(", ")})` : ""}`
|
|
50350
|
+
status.available ? ` \u2713 ${name}: available${status.unverified.length ? ` (unverified: ${status.unverified.join(", ")})` : ""}` : ` \u2717 ${name}: ${status.reason}${status.missing.length ? ` (missing: ${status.missing.join(", ")})` : ""}${status.unverified.length ? ` (unverified: ${status.unverified.join(", ")})` : ""}`
|
|
49620
50351
|
);
|
|
49621
50352
|
}
|
|
49622
50353
|
return lines.join("\n");
|
|
@@ -49651,7 +50382,7 @@ function runDoctor(manager, capabilityMatrix = getCapabilityMatrix) {
|
|
|
49651
50382
|
checks.push({
|
|
49652
50383
|
name: "Full Disk Access",
|
|
49653
50384
|
status: fda ? "ok" : "warn",
|
|
49654
|
-
detail: fda ? "granted \u2014 the Notes database is readable (
|
|
50385
|
+
detail: fda ? "granted \u2014 the Notes database is readable (queries, native structure, exports, mutation guards, and sync detail)" : "not granted \u2014 database-backed queries, native structure and checklist state, media and portable exports, and database-backed mutation guards/readback are unavailable. Whole-body updates cannot proceed when their safety metadata is unreadable. Basic AppleScript read/create paths may still work with Automation permission. get-note-link fails on macOS 26+ (macOS 12-15 has an AppleScript fallback); get-sync-status cannot verify pending uploads or active sync. " + fdaRemediation()
|
|
49655
50386
|
});
|
|
49656
50387
|
const consentReminder = "After install or upgrade, run each bridge Shortcut once in the foreground in Shortcuts.app and choose Always Allow: a background run cannot display a first-run consent prompt, so an unanswered one stalls that bridge's native writes until they time out while this check stays ok";
|
|
49657
50388
|
try {
|
|
@@ -49680,7 +50411,21 @@ function runDoctor(manager, capabilityMatrix = getCapabilityMatrix) {
|
|
|
49680
50411
|
} catch {
|
|
49681
50412
|
matrix = void 0;
|
|
49682
50413
|
}
|
|
49683
|
-
|
|
50414
|
+
const publicHelper = matrix?.publicHelper ?? unprobedPublicHelperStatus();
|
|
50415
|
+
checks.push({
|
|
50416
|
+
name: "Public native helper",
|
|
50417
|
+
status: publicHelper.ready ? "ok" : "warn",
|
|
50418
|
+
detail: publicHelper.ready ? publicHelper.detail ?? "Installed helper handshake verified." : `${publicHelper.reason ?? "unknown"}: ${publicHelper.detail ?? "Could not verify helper readiness."} Classic drawing decode and on-device transcription need a current public helper; run apple-notes-mcp setup --public-helper --check.`
|
|
50419
|
+
});
|
|
50420
|
+
if (publicHelper.ready) {
|
|
50421
|
+
const audio = matrix?.features.audioTranscription;
|
|
50422
|
+
checks.push({
|
|
50423
|
+
name: "On-device transcription",
|
|
50424
|
+
status: audio?.available ? "ok" : "warn",
|
|
50425
|
+
detail: audio ? `${audio.available ? "Prerequisites available" : audio.reason ?? "unverified"}. Unverified: ${audio.unverified.join(", ") || "none"}. ` + (publicHelper.speech.detail ?? "") : "Transcription prerequisites were not verified."
|
|
50426
|
+
});
|
|
50427
|
+
}
|
|
50428
|
+
return matrix ? { healthy, checks, runtimeOS: matrix.runtimeOS, features: matrix.features, publicHelper } : { healthy, checks, publicHelper };
|
|
49684
50429
|
}
|
|
49685
50430
|
function markdownBridgeDetail() {
|
|
49686
50431
|
const purpose = 'needed only for create-note format: "markdown" on macOS 26+';
|
|
@@ -49693,7 +50438,7 @@ function markdownBridgeDetail() {
|
|
|
49693
50438
|
function checkNodeRuntimeSignature() {
|
|
49694
50439
|
const name = "Node runtime signature";
|
|
49695
50440
|
try {
|
|
49696
|
-
const r =
|
|
50441
|
+
const r = spawnSync2("codesign", ["-dvvv", process.execPath], { encoding: "utf8" });
|
|
49697
50442
|
const out = `${r.stdout ?? ""}${r.stderr ?? ""}`;
|
|
49698
50443
|
if (r.error || !out.trim()) {
|
|
49699
50444
|
return {
|
|
@@ -49730,22 +50475,22 @@ function formatDoctorReport(r) {
|
|
|
49730
50475
|
}
|
|
49731
50476
|
|
|
49732
50477
|
// src/services/fileConfig.ts
|
|
49733
|
-
import { existsSync as
|
|
49734
|
-
import { join as
|
|
49735
|
-
import { homedir as
|
|
50478
|
+
import { existsSync as existsSync13, readFileSync as readFileSync3 } from "fs";
|
|
50479
|
+
import { join as join20 } from "path";
|
|
50480
|
+
import { homedir as homedir17 } from "os";
|
|
49736
50481
|
function fileConfigPath(env = process.env) {
|
|
49737
50482
|
const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
|
|
49738
50483
|
if (override && override.trim()) return override.trim();
|
|
49739
|
-
return
|
|
50484
|
+
return join20(homedir17(), "Library", "Application Support", "apple-notes-mcp", "config.json");
|
|
49740
50485
|
}
|
|
49741
50486
|
function loadFileConfig(env = process.env, path10 = fileConfigPath(env)) {
|
|
49742
50487
|
if (env.APPLE_NOTES_MCP_BROKERED === "1") return [];
|
|
49743
50488
|
const applied = [];
|
|
49744
50489
|
try {
|
|
49745
|
-
if (!
|
|
49746
|
-
const
|
|
49747
|
-
if (!
|
|
49748
|
-
for (const [k, v] of Object.entries(
|
|
50490
|
+
if (!existsSync13(path10)) return applied;
|
|
50491
|
+
const parsed2 = JSON.parse(readFileSync3(path10, "utf8"));
|
|
50492
|
+
if (!parsed2 || typeof parsed2 !== "object") return applied;
|
|
50493
|
+
for (const [k, v] of Object.entries(parsed2)) {
|
|
49749
50494
|
if (typeof v !== "string") continue;
|
|
49750
50495
|
if (env[k] === void 0 || env[k] === "") {
|
|
49751
50496
|
env[k] = v;
|
|
@@ -50271,7 +51016,7 @@ function paragraphLink(note, selector) {
|
|
|
50271
51016
|
}
|
|
50272
51017
|
|
|
50273
51018
|
// src/utils/noteStructure.ts
|
|
50274
|
-
import { dirname as
|
|
51019
|
+
import { dirname as dirname5 } from "node:path";
|
|
50275
51020
|
|
|
50276
51021
|
// src/utils/noteLinks.ts
|
|
50277
51022
|
var NOTE_LINK_UTI = "com.apple.notes.inlinetextattachment.link";
|
|
@@ -50457,7 +51202,7 @@ function readNoteStructure(id2, { dbPath: dbPath2 = NOTES_DB_PATH8, includeText
|
|
|
50457
51202
|
bodyError = error2.code;
|
|
50458
51203
|
}
|
|
50459
51204
|
const { rows: attachmentRows, bodyOrder } = readNoteAttachmentRows(id2, dbPath2);
|
|
50460
|
-
const assets = assembleAttachmentAssets(attachmentRows, bodyOrder,
|
|
51205
|
+
const assets = assembleAttachmentAssets(attachmentRows, bodyOrder, dirname5(dbPath2));
|
|
50461
51206
|
const roots = /* @__PURE__ */ new Map();
|
|
50462
51207
|
const attachments = [];
|
|
50463
51208
|
const pks = /* @__PURE__ */ new Map();
|
|
@@ -50599,7 +51344,7 @@ ${cause} ${remedy}`;
|
|
|
50599
51344
|
}
|
|
50600
51345
|
|
|
50601
51346
|
// src/utils/noteLinkInventory.ts
|
|
50602
|
-
import { dirname as
|
|
51347
|
+
import { dirname as dirname6 } from "node:path";
|
|
50603
51348
|
var BODY_BATCH = 100;
|
|
50604
51349
|
function matchFolder(folders, wanted, account) {
|
|
50605
51350
|
const paths = folderPaths(folders);
|
|
@@ -50735,7 +51480,7 @@ function listNoteLinks(options = {}) {
|
|
|
50735
51480
|
accountIdentifier: account?.identifier ?? null
|
|
50736
51481
|
};
|
|
50737
51482
|
};
|
|
50738
|
-
const containerDir =
|
|
51483
|
+
const containerDir = dirname6(dbPath2);
|
|
50739
51484
|
const previewEntries = /* @__PURE__ */ new Map();
|
|
50740
51485
|
const previewFor = (identifier, account) => {
|
|
50741
51486
|
const accountDir = identifier ? resolveAccountDir(containerDir, account) : null;
|
|
@@ -50929,7 +51674,7 @@ import {
|
|
|
50929
51674
|
closeSync as closeSync4,
|
|
50930
51675
|
constants as constants4,
|
|
50931
51676
|
fstatSync as fstatSync4,
|
|
50932
|
-
mkdirSync as
|
|
51677
|
+
mkdirSync as mkdirSync5,
|
|
50933
51678
|
openSync as openSync4,
|
|
50934
51679
|
readdirSync as readdirSync3,
|
|
50935
51680
|
readSync as readSync3,
|
|
@@ -50937,9 +51682,9 @@ import {
|
|
|
50937
51682
|
statSync as statSync3,
|
|
50938
51683
|
writeSync as writeSync3
|
|
50939
51684
|
} from "node:fs";
|
|
50940
|
-
import { homedir as
|
|
50941
|
-
import { basename as basename3, extname as extname3, join as
|
|
50942
|
-
var NOTES_CONTAINER =
|
|
51685
|
+
import { homedir as homedir18 } from "node:os";
|
|
51686
|
+
import { basename as basename3, extname as extname3, join as join21, relative as relative3, resolve as resolve4, sep as sep3 } from "node:path";
|
|
51687
|
+
var NOTES_CONTAINER = join21(homedir18(), "Library/Group Containers/group.com.apple.notes");
|
|
50943
51688
|
var MAX_EMBED_BYTES = 10 * 1024 * 1024;
|
|
50944
51689
|
var MAX_EMBED_TOTAL_BYTES = 256 * 1024 * 1024;
|
|
50945
51690
|
var MAX_DIRECTORY_ENTRIES = 2e4;
|
|
@@ -50995,11 +51740,11 @@ var AssetLocator = class {
|
|
|
50995
51740
|
constructor(container = NOTES_CONTAINER) {
|
|
50996
51741
|
let accountsRoot = "";
|
|
50997
51742
|
try {
|
|
50998
|
-
accountsRoot = realpathSync3.native(
|
|
51743
|
+
accountsRoot = realpathSync3.native(join21(container, "Accounts"));
|
|
50999
51744
|
} catch {
|
|
51000
51745
|
}
|
|
51001
51746
|
this.accounts = accountsRoot ? listDirectory(accountsRoot, 1e3).flatMap((name) => {
|
|
51002
|
-
const dir = safeComponent2(name) && confine(
|
|
51747
|
+
const dir = safeComponent2(name) && confine(join21(accountsRoot, name), accountsRoot);
|
|
51003
51748
|
return dir && isDirectory2(dir) ? [dir] : [];
|
|
51004
51749
|
}) : [];
|
|
51005
51750
|
}
|
|
@@ -51073,8 +51818,8 @@ var AssetLocator = class {
|
|
|
51073
51818
|
if (!mediaId || !filename) return void 0;
|
|
51074
51819
|
const generation = safeComponent2(source.mediaGeneration);
|
|
51075
51820
|
const candidates = [
|
|
51076
|
-
...generation ? [
|
|
51077
|
-
|
|
51821
|
+
...generation ? [join21(account, "Media", mediaId, generation, filename)] : [],
|
|
51822
|
+
join21(account, "Media", mediaId, filename)
|
|
51078
51823
|
];
|
|
51079
51824
|
for (const candidate of candidates) {
|
|
51080
51825
|
const path10 = confine(candidate, account);
|
|
@@ -51088,7 +51833,7 @@ var AssetLocator = class {
|
|
|
51088
51833
|
* somewhere else.
|
|
51089
51834
|
*/
|
|
51090
51835
|
fallback(account, dir, id2, generation, names) {
|
|
51091
|
-
const base =
|
|
51836
|
+
const base = join21(account, dir, id2);
|
|
51092
51837
|
const gen = safeComponent2(generation);
|
|
51093
51838
|
const generations = [
|
|
51094
51839
|
...gen ? [gen] : [],
|
|
@@ -51097,7 +51842,7 @@ var AssetLocator = class {
|
|
|
51097
51842
|
];
|
|
51098
51843
|
for (const g of generations)
|
|
51099
51844
|
for (const name of names) {
|
|
51100
|
-
const path10 = confine(g ?
|
|
51845
|
+
const path10 = confine(g ? join21(base, g, name) : join21(base, name), account);
|
|
51101
51846
|
if (path10 && isFile(path10)) return { path: path10, stale: Boolean(gen) && g !== gen };
|
|
51102
51847
|
}
|
|
51103
51848
|
return void 0;
|
|
@@ -51107,7 +51852,7 @@ var AssetLocator = class {
|
|
|
51107
51852
|
let index = this.previewIndex.get(account);
|
|
51108
51853
|
if (!index) {
|
|
51109
51854
|
index = /* @__PURE__ */ new Map();
|
|
51110
|
-
for (const name of listDirectory(
|
|
51855
|
+
for (const name of listDirectory(join21(account, "Previews"), MAX_DIRECTORY_ENTRIES)) {
|
|
51111
51856
|
const key = name.slice(0, 36).toUpperCase();
|
|
51112
51857
|
const entries3 = index.get(key) ?? [];
|
|
51113
51858
|
entries3.push(name);
|
|
@@ -51120,7 +51865,7 @@ var AssetLocator = class {
|
|
|
51120
51865
|
return !ext || IMAGE_EXTENSIONS.has(ext) || /^\.\d+$/.test(ext);
|
|
51121
51866
|
}).sort((a, b) => previewArea(b) - previewArea(a) || a.localeCompare(b));
|
|
51122
51867
|
for (const name of entries2) {
|
|
51123
|
-
const entry = confine(
|
|
51868
|
+
const entry = confine(join21(account, "Previews", name), account);
|
|
51124
51869
|
if (!entry) continue;
|
|
51125
51870
|
const file = isFile(entry) ? entry : this.bundleImage(entry, account);
|
|
51126
51871
|
if (file) return { path: file, name: "preview", role: "preview" };
|
|
@@ -51131,12 +51876,12 @@ var AssetLocator = class {
|
|
|
51131
51876
|
bundleImage(bundle, account) {
|
|
51132
51877
|
const found = [];
|
|
51133
51878
|
for (const child2 of listDirectory(bundle, MAX_BUNDLE_ENTRIES2)) {
|
|
51134
|
-
const path10 = confine(
|
|
51879
|
+
const path10 = confine(join21(bundle, child2), account);
|
|
51135
51880
|
if (!path10) continue;
|
|
51136
51881
|
if (isFile(path10)) found.push(path10);
|
|
51137
51882
|
else
|
|
51138
51883
|
for (const grandchild of listDirectory(path10, MAX_BUNDLE_ENTRIES2)) {
|
|
51139
|
-
const inner = confine(
|
|
51884
|
+
const inner = confine(join21(path10, grandchild), account);
|
|
51140
51885
|
if (inner && isFile(inner)) found.push(inner);
|
|
51141
51886
|
}
|
|
51142
51887
|
}
|
|
@@ -51197,11 +51942,11 @@ function canonicalForm(abs) {
|
|
|
51197
51942
|
for (; ; ) {
|
|
51198
51943
|
try {
|
|
51199
51944
|
const real = realpathSync3.native(current);
|
|
51200
|
-
return rest ?
|
|
51945
|
+
return rest ? join21(real, rest) : real;
|
|
51201
51946
|
} catch {
|
|
51202
|
-
const parent =
|
|
51947
|
+
const parent = resolve4(current, "..");
|
|
51203
51948
|
if (parent === current) return abs;
|
|
51204
|
-
rest = rest ?
|
|
51949
|
+
rest = rest ? join21(basename3(current), rest) : basename3(current);
|
|
51205
51950
|
current = parent;
|
|
51206
51951
|
}
|
|
51207
51952
|
}
|
|
@@ -51209,7 +51954,7 @@ function canonicalForm(abs) {
|
|
|
51209
51954
|
function assertExportPath(p, container = NOTES_CONTAINER) {
|
|
51210
51955
|
const abs = assertSafeSavePath(p);
|
|
51211
51956
|
const within = (path10, root) => path10 === root || path10.startsWith(root + sep3);
|
|
51212
|
-
const roots = [
|
|
51957
|
+
const roots = [resolve4(container), canonicalForm(resolve4(container))];
|
|
51213
51958
|
const forms = [abs, canonicalForm(abs)];
|
|
51214
51959
|
if (forms.some((form) => roots.some((root) => within(form, root))))
|
|
51215
51960
|
throw new Error("Refusing to write inside the Notes library container.");
|
|
@@ -51315,7 +52060,7 @@ var SidecarWriter = class {
|
|
|
51315
52060
|
if (!this.created) {
|
|
51316
52061
|
try {
|
|
51317
52062
|
assertExportPath(this.dir);
|
|
51318
|
-
|
|
52063
|
+
mkdirSync5(this.dir, { recursive: true });
|
|
51319
52064
|
} catch (error2) {
|
|
51320
52065
|
this.directoryError = directoryFailure(this.dir, error2);
|
|
51321
52066
|
return { error: this.directoryError };
|
|
@@ -51328,7 +52073,7 @@ var SidecarWriter = class {
|
|
|
51328
52073
|
let target;
|
|
51329
52074
|
let out;
|
|
51330
52075
|
for (let n = 1; n <= 1e3 && out === void 0; n++) {
|
|
51331
|
-
target =
|
|
52076
|
+
target = join21(this.dir, n === 1 ? safe2 : `${stem}-${n}${ext}`);
|
|
51332
52077
|
try {
|
|
51333
52078
|
out = openSync4(target, CREATE_FLAGS, 420);
|
|
51334
52079
|
} catch (error2) {
|
|
@@ -51987,468 +52732,6 @@ function drawingToSvg(strokes, fallback) {
|
|
|
51987
52732
|
return parts.join("\n");
|
|
51988
52733
|
}
|
|
51989
52734
|
|
|
51990
|
-
// src/services/publicHelper.ts
|
|
51991
|
-
import { spawn, spawnSync as spawnSync2 } from "node:child_process";
|
|
51992
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
51993
|
-
import {
|
|
51994
|
-
chmodSync,
|
|
51995
|
-
existsSync as existsSync13,
|
|
51996
|
-
mkdirSync as mkdirSync5,
|
|
51997
|
-
mkdtempSync as mkdtempSync5,
|
|
51998
|
-
readFileSync as readFileSync3,
|
|
51999
|
-
renameSync,
|
|
52000
|
-
rmSync as rmSync5,
|
|
52001
|
-
writeFileSync as writeFileSync4
|
|
52002
|
-
} from "node:fs";
|
|
52003
|
-
import { homedir as homedir18, release as release2 } from "node:os";
|
|
52004
|
-
import { dirname as dirname6, join as join21, resolve as resolve4 } from "node:path";
|
|
52005
|
-
import { fileURLToPath } from "node:url";
|
|
52006
|
-
var PUBLIC_HELPER_PROTOCOL = 1;
|
|
52007
|
-
var PUBLIC_HELPER_DIR_ENV = "APPLE_NOTES_MCP_PUBLIC_HELPER_DIR";
|
|
52008
|
-
var PUBLIC_HELPER_TIMEOUT_ENV = "APPLE_NOTES_MCP_PUBLIC_HELPER_TIMEOUT_MS";
|
|
52009
|
-
var PUBLIC_HELPER_BINARY = "apple-notes-public-helper";
|
|
52010
|
-
var PUBLIC_HELPER_SOURCE = "native/public-helper/apple-notes-public-helper.swift";
|
|
52011
|
-
var PUBLIC_HELPER_MANIFEST = "manifest.json";
|
|
52012
|
-
var PUBLIC_HELPER_SETUP_COMMAND = "apple-notes-mcp setup --public-helper";
|
|
52013
|
-
var PUBLIC_HELPER_ACTIONS = /* @__PURE__ */ new Set([
|
|
52014
|
-
"hello",
|
|
52015
|
-
"decode_drawing",
|
|
52016
|
-
"transcribe",
|
|
52017
|
-
"speech_status"
|
|
52018
|
-
]);
|
|
52019
|
-
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
52020
|
-
var MAX_OUTPUT_BYTES2 = 256 * 1024 * 1024;
|
|
52021
|
-
var publicManifestSchema = external_exports.object({
|
|
52022
|
-
schemaVersion: external_exports.literal(1),
|
|
52023
|
-
protocolVersion: external_exports.number().int(),
|
|
52024
|
-
sourceSha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
52025
|
-
binarySha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
52026
|
-
builtAt: external_exports.string(),
|
|
52027
|
-
osVersion: external_exports.string(),
|
|
52028
|
-
compiler: external_exports.string()
|
|
52029
|
-
});
|
|
52030
|
-
function packageRoot(fromDir = dirname6(fileURLToPath(import.meta.url))) {
|
|
52031
|
-
let dir = fromDir;
|
|
52032
|
-
for (; ; ) {
|
|
52033
|
-
const candidate = join21(dir, "package.json");
|
|
52034
|
-
if (existsSync13(candidate)) {
|
|
52035
|
-
try {
|
|
52036
|
-
if (JSON.parse(readFileSync3(candidate, "utf8")).name === "apple-notes-mcp")
|
|
52037
|
-
return dir;
|
|
52038
|
-
} catch {
|
|
52039
|
-
}
|
|
52040
|
-
}
|
|
52041
|
-
const parent = dirname6(dir);
|
|
52042
|
-
if (parent === dir) return resolve4(fromDir, "..");
|
|
52043
|
-
dir = parent;
|
|
52044
|
-
}
|
|
52045
|
-
}
|
|
52046
|
-
function defaultPublicHelperDeps(overrides = {}) {
|
|
52047
|
-
return {
|
|
52048
|
-
env: process.env,
|
|
52049
|
-
platform: process.platform,
|
|
52050
|
-
sourcePath: join21(packageRoot(), PUBLIC_HELPER_SOURCE),
|
|
52051
|
-
exists: existsSync13,
|
|
52052
|
-
readFile: (path10) => readFileSync3(path10),
|
|
52053
|
-
spawn: spawnSync2,
|
|
52054
|
-
spawnAsync: spawn,
|
|
52055
|
-
...overrides
|
|
52056
|
-
};
|
|
52057
|
-
}
|
|
52058
|
-
function publicHelperInstallDir(env = process.env) {
|
|
52059
|
-
const override = env[PUBLIC_HELPER_DIR_ENV]?.trim();
|
|
52060
|
-
if (override) return override;
|
|
52061
|
-
return join21(homedir18(), "Library", "Application Support", "apple-notes-mcp", "public-helper");
|
|
52062
|
-
}
|
|
52063
|
-
function sha256Hex(data) {
|
|
52064
|
-
return createHash2("sha256").update(data).digest("hex");
|
|
52065
|
-
}
|
|
52066
|
-
function inspectPublicHelper(deps = defaultPublicHelperDeps()) {
|
|
52067
|
-
const installDir = publicHelperInstallDir(deps.env);
|
|
52068
|
-
const binaryPath = join21(installDir, PUBLIC_HELPER_BINARY);
|
|
52069
|
-
const base = { installDir, binaryPath, sourcePath: deps.sourcePath, manifest: null };
|
|
52070
|
-
const fail = (reason, detail) => ({
|
|
52071
|
-
...base,
|
|
52072
|
-
ready: false,
|
|
52073
|
-
reason,
|
|
52074
|
-
detail
|
|
52075
|
-
});
|
|
52076
|
-
if (deps.platform !== "darwin") return fail("unsupported_platform", "macOS only");
|
|
52077
|
-
if (!deps.exists(deps.sourcePath))
|
|
52078
|
-
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
52079
|
-
const manifestPath = join21(installDir, PUBLIC_HELPER_MANIFEST);
|
|
52080
|
-
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
52081
|
-
return fail(
|
|
52082
|
-
"helper_not_installed",
|
|
52083
|
-
`The public native helper is not built. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\`.`
|
|
52084
|
-
);
|
|
52085
|
-
let manifest;
|
|
52086
|
-
try {
|
|
52087
|
-
manifest = publicManifestSchema.parse(JSON.parse(deps.readFile(manifestPath).toString("utf8")));
|
|
52088
|
-
} catch {
|
|
52089
|
-
return fail(
|
|
52090
|
-
"helper_manifest_invalid",
|
|
52091
|
-
`The helper manifest is unreadable. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\`.`
|
|
52092
|
-
);
|
|
52093
|
-
}
|
|
52094
|
-
if (manifest.sourceSha256 !== sha256Hex(deps.readFile(deps.sourcePath)) || manifest.protocolVersion !== PUBLIC_HELPER_PROTOCOL)
|
|
52095
|
-
return {
|
|
52096
|
-
...fail(
|
|
52097
|
-
"helper_stale",
|
|
52098
|
-
`The installed helper was built from a different source than this apple-notes-mcp version ships. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\` again.`
|
|
52099
|
-
),
|
|
52100
|
-
manifest
|
|
52101
|
-
};
|
|
52102
|
-
if (sha256Hex(deps.readFile(binaryPath)) !== manifest.binarySha256)
|
|
52103
|
-
return {
|
|
52104
|
-
...fail(
|
|
52105
|
-
"helper_modified",
|
|
52106
|
-
`The helper binary no longer matches the checksum recorded when it was built. Run \`${PUBLIC_HELPER_SETUP_COMMAND}\` to rebuild it.`
|
|
52107
|
-
),
|
|
52108
|
-
manifest
|
|
52109
|
-
};
|
|
52110
|
-
return { ...base, manifest, ready: true, reason: null, detail: null };
|
|
52111
|
-
}
|
|
52112
|
-
var UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
|
|
52113
|
-
"unsupported_platform",
|
|
52114
|
-
"helper_not_installed",
|
|
52115
|
-
"helper_stale",
|
|
52116
|
-
"helper_modified",
|
|
52117
|
-
"helper_manifest_invalid"
|
|
52118
|
-
]);
|
|
52119
|
-
var ENVELOPE_CODES = {
|
|
52120
|
-
invalid_request: "validation_error",
|
|
52121
|
-
attachment_not_found: "not_found"
|
|
52122
|
-
};
|
|
52123
|
-
var PublicHelperError = class extends CodedError {
|
|
52124
|
-
constructor(code, message) {
|
|
52125
|
-
super(message, {
|
|
52126
|
-
code: UNAVAILABLE_CODES.has(code) ? "unsupported" : ENVELOPE_CODES[code] ?? "operation_failed",
|
|
52127
|
-
helperCode: code
|
|
52128
|
-
});
|
|
52129
|
-
this.code = code;
|
|
52130
|
-
this.name = "PublicHelperError";
|
|
52131
|
-
}
|
|
52132
|
-
code;
|
|
52133
|
-
};
|
|
52134
|
-
var errorSchema = external_exports.object({ status: external_exports.literal("error"), code: external_exports.string(), message: external_exports.string() });
|
|
52135
|
-
var publicHelloSchema = external_exports.object({
|
|
52136
|
-
status: external_exports.literal("ok"),
|
|
52137
|
-
protocolVersion: external_exports.number().int(),
|
|
52138
|
-
sourceSha256: external_exports.string(),
|
|
52139
|
-
actions: external_exports.array(external_exports.string())
|
|
52140
|
-
}).passthrough();
|
|
52141
|
-
function callPublicHelper(action, fields = {}, deps = defaultPublicHelperDeps(), options = {}) {
|
|
52142
|
-
const { binaryPath, timeout, input } = prepareCall(action, fields, deps, options);
|
|
52143
|
-
const result = deps.spawn(binaryPath, [], {
|
|
52144
|
-
input,
|
|
52145
|
-
encoding: "utf8",
|
|
52146
|
-
timeout,
|
|
52147
|
-
killSignal: "SIGKILL",
|
|
52148
|
-
maxBuffer: MAX_OUTPUT_BYTES2
|
|
52149
|
-
});
|
|
52150
|
-
const errno = result.error?.code;
|
|
52151
|
-
if (errno === "ETIMEDOUT" || result.signal && result.status === null)
|
|
52152
|
-
throw new PublicHelperError("timeout", `The helper did not answer within ${timeout} ms.`);
|
|
52153
|
-
if (result.error)
|
|
52154
|
-
throw new PublicHelperError(
|
|
52155
|
-
"helper_unreachable",
|
|
52156
|
-
`Could not run the helper: ${result.error.message}`
|
|
52157
|
-
);
|
|
52158
|
-
return parseHelperOutput(result.status, String(result.stdout ?? ""));
|
|
52159
|
-
}
|
|
52160
|
-
async function callPublicHelperAsync(action, fields = {}, deps = defaultPublicHelperDeps(), options = {}) {
|
|
52161
|
-
const { binaryPath, timeout, input } = prepareCall(action, fields, deps, options);
|
|
52162
|
-
const { signal } = options;
|
|
52163
|
-
const aborted2 = () => new PublicHelperError("aborted", "The request was cancelled; the helper was stopped.");
|
|
52164
|
-
if (signal?.aborted) throw aborted2();
|
|
52165
|
-
return new Promise((resolvePromise, reject) => {
|
|
52166
|
-
const child2 = (deps.spawnAsync ?? spawn)(binaryPath, [], {
|
|
52167
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
52168
|
-
});
|
|
52169
|
-
const chunks = [];
|
|
52170
|
-
let size = 0;
|
|
52171
|
-
let failure2 = null;
|
|
52172
|
-
const stop = (error2) => {
|
|
52173
|
-
failure2 ??= error2;
|
|
52174
|
-
child2.kill("SIGKILL");
|
|
52175
|
-
};
|
|
52176
|
-
const timer = setTimeout(
|
|
52177
|
-
() => stop(new PublicHelperError("timeout", `The helper did not answer within ${timeout} ms.`)),
|
|
52178
|
-
timeout
|
|
52179
|
-
);
|
|
52180
|
-
const onAbort = () => stop(aborted2());
|
|
52181
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
52182
|
-
const settle = () => {
|
|
52183
|
-
clearTimeout(timer);
|
|
52184
|
-
signal?.removeEventListener("abort", onAbort);
|
|
52185
|
-
};
|
|
52186
|
-
child2.stdout?.on("data", (chunk) => {
|
|
52187
|
-
size += chunk.length;
|
|
52188
|
-
if (size > MAX_OUTPUT_BYTES2)
|
|
52189
|
-
stop(new PublicHelperError("invalid_response", "The helper response is too large."));
|
|
52190
|
-
else chunks.push(chunk);
|
|
52191
|
-
});
|
|
52192
|
-
child2.on("error", (error2) => {
|
|
52193
|
-
settle();
|
|
52194
|
-
reject(
|
|
52195
|
-
failure2 ?? new PublicHelperError("helper_unreachable", `Could not run the helper: ${error2.message}`)
|
|
52196
|
-
);
|
|
52197
|
-
});
|
|
52198
|
-
child2.on("close", (status, exitSignal) => {
|
|
52199
|
-
settle();
|
|
52200
|
-
if (failure2) return reject(failure2);
|
|
52201
|
-
if (status === null && exitSignal)
|
|
52202
|
-
return reject(
|
|
52203
|
-
new PublicHelperError("helper_crashed", `The helper stopped on signal ${exitSignal}.`)
|
|
52204
|
-
);
|
|
52205
|
-
try {
|
|
52206
|
-
resolvePromise(parseHelperOutput(status, Buffer.concat(chunks).toString("utf8")));
|
|
52207
|
-
} catch (error2) {
|
|
52208
|
-
reject(error2);
|
|
52209
|
-
}
|
|
52210
|
-
});
|
|
52211
|
-
child2.stdin?.on("error", () => {
|
|
52212
|
-
});
|
|
52213
|
-
child2.stdin?.end(input);
|
|
52214
|
-
});
|
|
52215
|
-
}
|
|
52216
|
-
function prepareCall(action, fields, deps, options) {
|
|
52217
|
-
if (!PUBLIC_HELPER_ACTIONS.has(action))
|
|
52218
|
-
throw new PublicHelperError(
|
|
52219
|
-
"unknown_action",
|
|
52220
|
-
`"${action}" is not an action the server sends to the public helper.`
|
|
52221
|
-
);
|
|
52222
|
-
let binaryPath = options.binaryPath;
|
|
52223
|
-
if (!binaryPath) {
|
|
52224
|
-
const install = inspectPublicHelper(deps);
|
|
52225
|
-
if (!install.ready)
|
|
52226
|
-
throw new PublicHelperError(install.reason ?? "helper_not_installed", install.detail ?? "");
|
|
52227
|
-
binaryPath = install.binaryPath;
|
|
52228
|
-
}
|
|
52229
|
-
const timeout = options.timeoutMs || Number.parseInt(deps.env[PUBLIC_HELPER_TIMEOUT_ENV] || "", 10) || DEFAULT_TIMEOUT_MS2;
|
|
52230
|
-
const input = JSON.stringify({ protocol: PUBLIC_HELPER_PROTOCOL, action, ...fields });
|
|
52231
|
-
return { binaryPath, timeout, input };
|
|
52232
|
-
}
|
|
52233
|
-
function parseHelperOutput(status, stdout) {
|
|
52234
|
-
let parsed;
|
|
52235
|
-
try {
|
|
52236
|
-
parsed = JSON.parse(stdout.trim());
|
|
52237
|
-
} catch {
|
|
52238
|
-
throw new PublicHelperError(
|
|
52239
|
-
"invalid_response",
|
|
52240
|
-
`The helper exited with status ${status} and no JSON response.`
|
|
52241
|
-
);
|
|
52242
|
-
}
|
|
52243
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
52244
|
-
throw new PublicHelperError("invalid_response", "The helper response is not a JSON object.");
|
|
52245
|
-
const object3 = parsed;
|
|
52246
|
-
if (status !== 0 || object3.status !== "ok") {
|
|
52247
|
-
const error2 = errorSchema.safeParse(object3);
|
|
52248
|
-
if (!error2.success)
|
|
52249
|
-
throw new PublicHelperError(
|
|
52250
|
-
"invalid_response",
|
|
52251
|
-
`The helper failed with an unrecognized response (exit ${status}).`
|
|
52252
|
-
);
|
|
52253
|
-
throw new PublicHelperError(error2.data.code, error2.data.message);
|
|
52254
|
-
}
|
|
52255
|
-
return object3;
|
|
52256
|
-
}
|
|
52257
|
-
function defaultPublicHelperBuildDeps() {
|
|
52258
|
-
return {
|
|
52259
|
-
...defaultPublicHelperDeps(),
|
|
52260
|
-
osVersion: () => {
|
|
52261
|
-
const r = spawnSync2("/usr/bin/sw_vers", ["-productVersion"], { encoding: "utf8" });
|
|
52262
|
-
return r.status === 0 ? r.stdout.trim() : `Darwin ${release2()}`;
|
|
52263
|
-
},
|
|
52264
|
-
now: () => /* @__PURE__ */ new Date()
|
|
52265
|
-
};
|
|
52266
|
-
}
|
|
52267
|
-
function sourceDigestSwift(sourceSha) {
|
|
52268
|
-
return `let helperSourceSHA256 = "${sourceSha}"
|
|
52269
|
-
`;
|
|
52270
|
-
}
|
|
52271
|
-
var PUBLIC_HELPER_BUNDLE_ID = "apple-notes-mcp.public-helper";
|
|
52272
|
-
function publicHelperInfoPlist() {
|
|
52273
|
-
return [
|
|
52274
|
-
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
52275
|
-
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
52276
|
-
'<plist version="1.0">',
|
|
52277
|
-
"<dict>",
|
|
52278
|
-
" <key>CFBundleIdentifier</key>",
|
|
52279
|
-
` <string>${PUBLIC_HELPER_BUNDLE_ID}</string>`,
|
|
52280
|
-
" <key>CFBundleName</key>",
|
|
52281
|
-
" <string>apple-notes-mcp public helper</string>",
|
|
52282
|
-
" <key>CFBundleInfoDictionaryVersion</key>",
|
|
52283
|
-
" <string>6.0</string>",
|
|
52284
|
-
"</dict>",
|
|
52285
|
-
"</plist>",
|
|
52286
|
-
""
|
|
52287
|
-
].join("\n");
|
|
52288
|
-
}
|
|
52289
|
-
function publicHelperCompileArguments(sourcePath, digestPath, plistPath, outputPath) {
|
|
52290
|
-
return [
|
|
52291
|
-
"swiftc",
|
|
52292
|
-
"-O",
|
|
52293
|
-
"-parse-as-library",
|
|
52294
|
-
"-framework",
|
|
52295
|
-
"AppKit",
|
|
52296
|
-
"-framework",
|
|
52297
|
-
"PencilKit",
|
|
52298
|
-
"-framework",
|
|
52299
|
-
"AVFoundation",
|
|
52300
|
-
"-framework",
|
|
52301
|
-
"Speech",
|
|
52302
|
-
"-Xlinker",
|
|
52303
|
-
"-sectcreate",
|
|
52304
|
-
"-Xlinker",
|
|
52305
|
-
"__TEXT",
|
|
52306
|
-
"-Xlinker",
|
|
52307
|
-
"__info_plist",
|
|
52308
|
-
"-Xlinker",
|
|
52309
|
-
plistPath,
|
|
52310
|
-
sourcePath,
|
|
52311
|
-
digestPath,
|
|
52312
|
-
"-o",
|
|
52313
|
-
outputPath
|
|
52314
|
-
];
|
|
52315
|
-
}
|
|
52316
|
-
function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
52317
|
-
const steps = [];
|
|
52318
|
-
const done = (ok2) => ({
|
|
52319
|
-
ok: ok2,
|
|
52320
|
-
checkOnly,
|
|
52321
|
-
steps,
|
|
52322
|
-
installation: inspectPublicHelper(deps)
|
|
52323
|
-
});
|
|
52324
|
-
if (checkOnly) {
|
|
52325
|
-
const installation2 = inspectPublicHelper(deps);
|
|
52326
|
-
steps.push({
|
|
52327
|
-
step: "inspect installed helper",
|
|
52328
|
-
ok: installation2.ready,
|
|
52329
|
-
detail: installation2.ready ? installation2.binaryPath : installation2.detail ?? void 0
|
|
52330
|
-
});
|
|
52331
|
-
return { ok: installation2.ready, checkOnly, steps, installation: installation2 };
|
|
52332
|
-
}
|
|
52333
|
-
if (deps.platform !== "darwin") {
|
|
52334
|
-
steps.push({ step: "platform", ok: false, detail: "macOS only" });
|
|
52335
|
-
return done(false);
|
|
52336
|
-
}
|
|
52337
|
-
if (!deps.exists(deps.sourcePath)) {
|
|
52338
|
-
steps.push({ step: "locate source", ok: false, detail: deps.sourcePath });
|
|
52339
|
-
return done(false);
|
|
52340
|
-
}
|
|
52341
|
-
const sourceSha = sha256Hex(deps.readFile(deps.sourcePath));
|
|
52342
|
-
steps.push({ step: "locate source", ok: true, detail: `sha256 ${sourceSha}` });
|
|
52343
|
-
const version3 = deps.spawn("/usr/bin/xcrun", ["swiftc", "--version"], { encoding: "utf8" });
|
|
52344
|
-
if (version3.status !== 0) {
|
|
52345
|
-
steps.push({
|
|
52346
|
-
step: "find compiler",
|
|
52347
|
-
ok: false,
|
|
52348
|
-
detail: "No Swift compiler found. Install the Command Line Tools with `xcode-select --install`."
|
|
52349
|
-
});
|
|
52350
|
-
return done(false);
|
|
52351
|
-
}
|
|
52352
|
-
const compiler = String(version3.stdout || version3.stderr || "").split("\n").find((line) => line.includes("Swift version"))?.trim() || "swiftc";
|
|
52353
|
-
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
52354
|
-
const installDir = publicHelperInstallDir(deps.env);
|
|
52355
|
-
mkdirSync5(installDir, { recursive: true, mode: 448 });
|
|
52356
|
-
const staging = mkdtempSync5(join21(installDir, ".staging-"));
|
|
52357
|
-
try {
|
|
52358
|
-
const stagedBinary = join21(staging, PUBLIC_HELPER_BINARY);
|
|
52359
|
-
const digestPath = join21(staging, "source-digest.swift");
|
|
52360
|
-
const plistPath = join21(staging, "Info.plist");
|
|
52361
|
-
writeFileSync4(digestPath, sourceDigestSwift(sourceSha), { mode: 384 });
|
|
52362
|
-
writeFileSync4(plistPath, publicHelperInfoPlist(), { mode: 384 });
|
|
52363
|
-
const compile = deps.spawn(
|
|
52364
|
-
"/usr/bin/xcrun",
|
|
52365
|
-
publicHelperCompileArguments(deps.sourcePath, digestPath, plistPath, stagedBinary),
|
|
52366
|
-
{ encoding: "utf8", timeout: 3e5 }
|
|
52367
|
-
);
|
|
52368
|
-
if (compile.status !== 0) {
|
|
52369
|
-
steps.push({
|
|
52370
|
-
step: "compile",
|
|
52371
|
-
ok: false,
|
|
52372
|
-
detail: String(compile.stderr || compile.error?.message || "swiftc failed").slice(0, 4e3)
|
|
52373
|
-
});
|
|
52374
|
-
return done(false);
|
|
52375
|
-
}
|
|
52376
|
-
steps.push({ step: "compile", ok: true });
|
|
52377
|
-
const sign = deps.spawn(
|
|
52378
|
-
"/usr/bin/codesign",
|
|
52379
|
-
["--force", "--sign", "-", "--identifier", PUBLIC_HELPER_BUNDLE_ID, stagedBinary],
|
|
52380
|
-
{ encoding: "utf8" }
|
|
52381
|
-
);
|
|
52382
|
-
if (sign.status !== 0) {
|
|
52383
|
-
steps.push({
|
|
52384
|
-
step: "ad-hoc sign",
|
|
52385
|
-
ok: false,
|
|
52386
|
-
detail: String(sign.stderr || "codesign failed")
|
|
52387
|
-
});
|
|
52388
|
-
return done(false);
|
|
52389
|
-
}
|
|
52390
|
-
steps.push({ step: "ad-hoc sign", ok: true });
|
|
52391
|
-
let hello;
|
|
52392
|
-
try {
|
|
52393
|
-
hello = publicHelloSchema.parse(
|
|
52394
|
-
callPublicHelper("hello", {}, deps, { binaryPath: stagedBinary })
|
|
52395
|
-
);
|
|
52396
|
-
} catch (error2) {
|
|
52397
|
-
steps.push({
|
|
52398
|
-
step: "handshake",
|
|
52399
|
-
ok: false,
|
|
52400
|
-
detail: error2 instanceof Error ? error2.message : String(error2)
|
|
52401
|
-
});
|
|
52402
|
-
return done(false);
|
|
52403
|
-
}
|
|
52404
|
-
if (hello.protocolVersion !== PUBLIC_HELPER_PROTOCOL || hello.sourceSha256 !== sourceSha) {
|
|
52405
|
-
steps.push({
|
|
52406
|
-
step: "handshake",
|
|
52407
|
-
ok: false,
|
|
52408
|
-
detail: `helper reported protocol ${hello.protocolVersion}, source ${hello.sourceSha256}`
|
|
52409
|
-
});
|
|
52410
|
-
return done(false);
|
|
52411
|
-
}
|
|
52412
|
-
steps.push({ step: "handshake", ok: true, detail: `actions: ${hello.actions.join(", ")}` });
|
|
52413
|
-
const manifest = {
|
|
52414
|
-
schemaVersion: 1,
|
|
52415
|
-
protocolVersion: hello.protocolVersion,
|
|
52416
|
-
sourceSha256: sourceSha,
|
|
52417
|
-
binarySha256: sha256Hex(deps.readFile(stagedBinary)),
|
|
52418
|
-
builtAt: deps.now().toISOString(),
|
|
52419
|
-
osVersion: deps.osVersion(),
|
|
52420
|
-
compiler
|
|
52421
|
-
};
|
|
52422
|
-
chmodSync(stagedBinary, 448);
|
|
52423
|
-
renameSync(stagedBinary, join21(installDir, PUBLIC_HELPER_BINARY));
|
|
52424
|
-
writeFileSync4(
|
|
52425
|
-
join21(installDir, PUBLIC_HELPER_MANIFEST),
|
|
52426
|
-
JSON.stringify(manifest, null, 2) + "\n",
|
|
52427
|
-
{ mode: 384 }
|
|
52428
|
-
);
|
|
52429
|
-
steps.push({ step: "install", ok: true, detail: installDir });
|
|
52430
|
-
} finally {
|
|
52431
|
-
rmSync5(staging, { recursive: true, force: true });
|
|
52432
|
-
}
|
|
52433
|
-
const installation = inspectPublicHelper(deps);
|
|
52434
|
-
steps.push({
|
|
52435
|
-
step: "verify installation",
|
|
52436
|
-
ok: installation.ready,
|
|
52437
|
-
detail: installation.ready ? void 0 : installation.detail ?? void 0
|
|
52438
|
-
});
|
|
52439
|
-
return { ok: installation.ready, checkOnly, steps, installation };
|
|
52440
|
-
}
|
|
52441
|
-
function formatPublicHelperBuild(report) {
|
|
52442
|
-
const lines = ["Apple Notes MCP public native helper", ""];
|
|
52443
|
-
for (const step of report.steps)
|
|
52444
|
-
lines.push(`${step.ok ? "\u2713" : "\u2717"} ${step.step}${step.detail ? `: ${step.detail}` : ""}`);
|
|
52445
|
-
lines.push("");
|
|
52446
|
-
if (report.ok) lines.push(`Installed at ${report.installation.binaryPath}.`);
|
|
52447
|
-
else if (report.checkOnly) lines.push(`Run \`${PUBLIC_HELPER_SETUP_COMMAND}\` to build it.`);
|
|
52448
|
-
else lines.push("The helper was not installed. Fix the failed step above and run setup again.");
|
|
52449
|
-
return lines.join("\n");
|
|
52450
|
-
}
|
|
52451
|
-
|
|
52452
52735
|
// src/services/noteDrawings.ts
|
|
52453
52736
|
var bounds = external_exports.object({ x: external_exports.number(), y: external_exports.number(), width: external_exports.number(), height: external_exports.number() });
|
|
52454
52737
|
var strokeSchema = external_exports.object({
|
|
@@ -52500,10 +52783,10 @@ function decodeRow(row, format, includePoints, deps) {
|
|
|
52500
52783
|
{ dataBase64: row.data.toString("base64"), includePoints: needPoints },
|
|
52501
52784
|
deps
|
|
52502
52785
|
);
|
|
52503
|
-
const
|
|
52504
|
-
if (!
|
|
52786
|
+
const parsed2 = decodedDrawingSchema.safeParse(raw);
|
|
52787
|
+
if (!parsed2.success)
|
|
52505
52788
|
return { ...base, code: "invalid_response", message: "Unexpected helper response." };
|
|
52506
|
-
decoded =
|
|
52789
|
+
decoded = parsed2.data;
|
|
52507
52790
|
} catch (error2) {
|
|
52508
52791
|
const code = error2 instanceof PublicHelperError ? error2.code : "internal_error";
|
|
52509
52792
|
return { ...base, code, message: error2 instanceof Error ? error2.message : String(error2) };
|
|
@@ -53026,18 +53309,18 @@ function readExportNote(id2, { dbPath: dbPath2 = NOTES_DB_PATH11 } = {}) {
|
|
|
53026
53309
|
`.parameter set @pk ${pk}`,
|
|
53027
53310
|
attachmentQuery(objectColumns(dbPath2))
|
|
53028
53311
|
]);
|
|
53029
|
-
let
|
|
53312
|
+
let parsed2;
|
|
53030
53313
|
try {
|
|
53031
|
-
|
|
53314
|
+
parsed2 = JSON.parse(output);
|
|
53032
53315
|
} catch {
|
|
53033
53316
|
throw new NoteBlocksError("query-failed", "Unexpected Notes database response");
|
|
53034
53317
|
}
|
|
53035
|
-
const rows = Array.isArray(
|
|
53318
|
+
const rows = Array.isArray(parsed2.attachments) ? parsed2.attachments : [];
|
|
53036
53319
|
const { byId, ordered } = buildAttachments(rows);
|
|
53037
53320
|
const firstLine = doc.blocks.find((block) => block.text.trim())?.text.trim() ?? "";
|
|
53038
53321
|
return {
|
|
53039
53322
|
id: id2,
|
|
53040
|
-
title: text(
|
|
53323
|
+
title: text(parsed2.title) ?? firstLine,
|
|
53041
53324
|
doc,
|
|
53042
53325
|
attachments: byId,
|
|
53043
53326
|
ordered
|
|
@@ -53529,9 +53812,9 @@ function parseTemplate(text2) {
|
|
|
53529
53812
|
throw new TemplateValidationError([
|
|
53530
53813
|
{ path: "$", message: `template is ${bytes} bytes; the limit is ${MAX_TEMPLATE_BYTES}` }
|
|
53531
53814
|
]);
|
|
53532
|
-
let
|
|
53815
|
+
let parsed2;
|
|
53533
53816
|
try {
|
|
53534
|
-
|
|
53817
|
+
parsed2 = JSON.parse(text2);
|
|
53535
53818
|
} catch (error2) {
|
|
53536
53819
|
const at = jsonErrorLocation(text2, error2.message);
|
|
53537
53820
|
throw new TemplateValidationError([
|
|
@@ -53541,7 +53824,7 @@ function parseTemplate(text2) {
|
|
|
53541
53824
|
}
|
|
53542
53825
|
]);
|
|
53543
53826
|
}
|
|
53544
|
-
return validateTemplate(
|
|
53827
|
+
return validateTemplate(parsed2);
|
|
53545
53828
|
}
|
|
53546
53829
|
function resolveTemplate(template, name) {
|
|
53547
53830
|
if (template === STANDARD) return merge2(void 0, STANDARD, name);
|
|
@@ -53560,15 +53843,15 @@ function merge2(base, template, name) {
|
|
|
53560
53843
|
var plainValue = (text2) => ({ md: text2, raw: text2 });
|
|
53561
53844
|
function fillPlaceholders(text2, values) {
|
|
53562
53845
|
return text2.replace(TOKEN, (token, body) => {
|
|
53563
|
-
const
|
|
53564
|
-
const raw =
|
|
53846
|
+
const parsed2 = TOKEN_BODY.exec(body);
|
|
53847
|
+
const raw = parsed2 ? values[parsed2[1]] : void 0;
|
|
53565
53848
|
const value = typeof raw === "string" ? plainValue(raw) : raw;
|
|
53566
|
-
if (
|
|
53849
|
+
if (parsed2?.[2] === "yaml") {
|
|
53567
53850
|
if (!value) return "null";
|
|
53568
53851
|
return JSON.stringify(value.list ?? value.raw);
|
|
53569
53852
|
}
|
|
53570
53853
|
if (!value) return "";
|
|
53571
|
-
return
|
|
53854
|
+
return parsed2?.[2] === "raw" ? value.raw : value.md;
|
|
53572
53855
|
});
|
|
53573
53856
|
}
|
|
53574
53857
|
function ruleUses(rule, name) {
|
|
@@ -53606,9 +53889,9 @@ var KIND_RULES = {
|
|
|
53606
53889
|
var IMAGE_URL_EXTENSIONS = /\.(?:apng|avif|bmp|gif|heic|heif|ico|jfif|jpe?g|jxl|png|svgz?|tiff?|webp)$/i;
|
|
53607
53890
|
function isImageUrl(url) {
|
|
53608
53891
|
try {
|
|
53609
|
-
const
|
|
53610
|
-
if (
|
|
53611
|
-
let path10 =
|
|
53892
|
+
const parsed2 = new URL(url);
|
|
53893
|
+
if (parsed2.protocol !== "http:" && parsed2.protocol !== "https:") return false;
|
|
53894
|
+
let path10 = parsed2.pathname;
|
|
53612
53895
|
try {
|
|
53613
53896
|
path10 = decodeURIComponent(path10);
|
|
53614
53897
|
} catch {
|
|
@@ -56561,7 +56844,7 @@ function parsePathData(d) {
|
|
|
56561
56844
|
let lastCubic = null;
|
|
56562
56845
|
let lastQuad = null;
|
|
56563
56846
|
let curved = false;
|
|
56564
|
-
let
|
|
56847
|
+
let command2 = "";
|
|
56565
56848
|
const result = (error2) => ({ subpaths, error: error2, curved });
|
|
56566
56849
|
const lineTo = (to) => {
|
|
56567
56850
|
current.segments.push({ kind: "L", to });
|
|
@@ -56569,9 +56852,9 @@ function parsePathData(d) {
|
|
|
56569
56852
|
};
|
|
56570
56853
|
while (!s.done()) {
|
|
56571
56854
|
const next = s.peek();
|
|
56572
|
-
if (/[a-zA-Z]/.test(next))
|
|
56573
|
-
else if (!
|
|
56574
|
-
const rel =
|
|
56855
|
+
if (/[a-zA-Z]/.test(next)) command2 = s.take();
|
|
56856
|
+
else if (!command2 || command2 === "z" || command2 === "Z") return result(true);
|
|
56857
|
+
const rel = command2 === command2.toLowerCase();
|
|
56575
56858
|
const base = rel ? pen : [0, 0];
|
|
56576
56859
|
const num2 = () => s.number();
|
|
56577
56860
|
const pt = () => {
|
|
@@ -56579,7 +56862,7 @@ function parsePathData(d) {
|
|
|
56579
56862
|
const y = x === null ? null : num2();
|
|
56580
56863
|
return x === null || y === null ? null : [x + base[0], y + base[1]];
|
|
56581
56864
|
};
|
|
56582
|
-
const upper =
|
|
56865
|
+
const upper = command2.toUpperCase();
|
|
56583
56866
|
if (!current && upper !== "M") return result(true);
|
|
56584
56867
|
let cubicCtrl = null;
|
|
56585
56868
|
let quadCtrl = null;
|
|
@@ -56591,7 +56874,7 @@ function parsePathData(d) {
|
|
|
56591
56874
|
subpaths.push(current);
|
|
56592
56875
|
pen = p;
|
|
56593
56876
|
start = p;
|
|
56594
|
-
|
|
56877
|
+
command2 = rel ? "l" : "L";
|
|
56595
56878
|
break;
|
|
56596
56879
|
}
|
|
56597
56880
|
case "L": {
|
|
@@ -57865,22 +58148,22 @@ var Analyzer = class {
|
|
|
57865
58148
|
case "path": {
|
|
57866
58149
|
const d = attr(el, "d");
|
|
57867
58150
|
if (d === void 0 || !d.trim()) return null;
|
|
57868
|
-
const
|
|
57869
|
-
const segs =
|
|
58151
|
+
const parsed2 = parsePathData(d);
|
|
58152
|
+
const segs = parsed2.subpaths.reduce((s, p) => s + p.segments.length, 0);
|
|
57870
58153
|
this.pathSegments += segs;
|
|
57871
58154
|
if (this.pathSegments > SVG_LIMITS.maxPathSegments)
|
|
57872
58155
|
throw new SvgError(
|
|
57873
58156
|
"svg_complexity_limit",
|
|
57874
58157
|
`More than ${SVG_LIMITS.maxPathSegments} path segments`
|
|
57875
58158
|
);
|
|
57876
|
-
if (
|
|
58159
|
+
if (parsed2.error)
|
|
57877
58160
|
this.issue(
|
|
57878
58161
|
"path_data_error",
|
|
57879
58162
|
"drop-content",
|
|
57880
58163
|
ctx.location,
|
|
57881
58164
|
"Path data has an error; the part after it is dropped"
|
|
57882
58165
|
);
|
|
57883
|
-
return { subpaths:
|
|
58166
|
+
return { subpaths: parsed2.subpaths, fillable: true };
|
|
57884
58167
|
}
|
|
57885
58168
|
case "rect": {
|
|
57886
58169
|
const x = len("x", pw) ?? 0;
|
|
@@ -59922,9 +60205,9 @@ async function transcribeTake(take, ctx) {
|
|
|
59922
60205
|
text: ""
|
|
59923
60206
|
};
|
|
59924
60207
|
}
|
|
59925
|
-
const
|
|
59926
|
-
if (!
|
|
59927
|
-
const { transcript, complete, stopReason, engine, durationSeconds } =
|
|
60208
|
+
const parsed2 = helperTranscriptSchema.safeParse(raw);
|
|
60209
|
+
if (!parsed2.success) return failed("invalid_response", "Unexpected helper response.");
|
|
60210
|
+
const { transcript, complete, stopReason, engine, durationSeconds } = parsed2.data;
|
|
59928
60211
|
const text2 = transcript.trim();
|
|
59929
60212
|
return {
|
|
59930
60213
|
take: {
|
|
@@ -60082,7 +60365,8 @@ var TIMEOUT_ENV = "APPLE_NOTES_MCP_PRIVATE_HELPER_TIMEOUT_MS";
|
|
|
60082
60365
|
var READ_ONLY_ACTIONS = /* @__PURE__ */ new Set([
|
|
60083
60366
|
"hello",
|
|
60084
60367
|
"probe",
|
|
60085
|
-
"read_note_state"
|
|
60368
|
+
"read_note_state",
|
|
60369
|
+
"read_checklist"
|
|
60086
60370
|
]);
|
|
60087
60371
|
var HELPER_BINARY_NAME = "apple-notes-private-helper";
|
|
60088
60372
|
var HELPER_SOURCE_RELATIVE = "native/private-helper/apple-notes-private-helper.m";
|
|
@@ -60198,6 +60482,16 @@ var PrivateHelperError = class extends Error {
|
|
|
60198
60482
|
code;
|
|
60199
60483
|
details;
|
|
60200
60484
|
};
|
|
60485
|
+
function assertPrivateHelperReady(deps = defaultDeps2()) {
|
|
60486
|
+
if (!privateHelperEnabled(deps.env))
|
|
60487
|
+
throw new PrivateHelperError(
|
|
60488
|
+
"disabled",
|
|
60489
|
+
`The private helper is off. Set ${ENABLE_ENV}=1 to opt in.`
|
|
60490
|
+
);
|
|
60491
|
+
const install = inspectInstallation(deps);
|
|
60492
|
+
if (!install.ready)
|
|
60493
|
+
throw new PrivateHelperError(install.reason || "helper_not_installed", install.detail || "");
|
|
60494
|
+
}
|
|
60201
60495
|
var errorSchema2 = external_exports.object({
|
|
60202
60496
|
status: external_exports.literal("error"),
|
|
60203
60497
|
code: external_exports.string(),
|
|
@@ -60228,7 +60522,7 @@ var probeSchema = external_exports.object({
|
|
|
60228
60522
|
noteRows: external_exports.number().int().nullable()
|
|
60229
60523
|
}).passthrough(),
|
|
60230
60524
|
syncHostRunning: external_exports.boolean(),
|
|
60231
|
-
features: external_exports.object({ readNoteState: featureSchema }).passthrough()
|
|
60525
|
+
features: external_exports.object({ readNoteState: featureSchema, readChecklist: featureSchema }).passthrough()
|
|
60232
60526
|
}).passthrough();
|
|
60233
60527
|
var cloudSyncSchema = external_exports.object({
|
|
60234
60528
|
available: external_exports.boolean(),
|
|
@@ -60252,12 +60546,53 @@ var noteStateSchema = external_exports.object({
|
|
|
60252
60546
|
cloudSync: cloudSyncSchema,
|
|
60253
60547
|
syncHostRunning: external_exports.boolean()
|
|
60254
60548
|
}).passthrough();
|
|
60549
|
+
var checklistItemSchema = external_exports.object({
|
|
60550
|
+
todoIdentifier: external_exports.string().regex(/^[a-f0-9]{32}$/),
|
|
60551
|
+
uuid: external_exports.string().uuid(),
|
|
60552
|
+
index: external_exports.number().int().nonnegative(),
|
|
60553
|
+
done: external_exports.boolean(),
|
|
60554
|
+
text: external_exports.string(),
|
|
60555
|
+
lineStart: external_exports.number().int().nonnegative(),
|
|
60556
|
+
lineLengthUTF16: external_exports.number().int().nonnegative(),
|
|
60557
|
+
styledStart: external_exports.number().int().nonnegative(),
|
|
60558
|
+
styledLengthUTF16: external_exports.number().int().positive(),
|
|
60559
|
+
contiguous: external_exports.boolean(),
|
|
60560
|
+
consistent: external_exports.boolean(),
|
|
60561
|
+
spansLines: external_exports.boolean()
|
|
60562
|
+
});
|
|
60563
|
+
var checklistStateSchema = external_exports.object({
|
|
60564
|
+
status: external_exports.literal("ok"),
|
|
60565
|
+
identifier: external_exports.string().uuid(),
|
|
60566
|
+
revision: external_exports.string().regex(/^r1:[a-f0-9]{64}$/),
|
|
60567
|
+
items: external_exports.array(checklistItemSchema),
|
|
60568
|
+
total: external_exports.number().int().nonnegative(),
|
|
60569
|
+
checked: external_exports.number().int().nonnegative(),
|
|
60570
|
+
syncHostRunning: external_exports.boolean()
|
|
60571
|
+
}).passthrough().superRefine((state, ctx) => {
|
|
60572
|
+
const identities = /* @__PURE__ */ new Set();
|
|
60573
|
+
if (state.total !== state.items.length || state.checked !== state.items.filter((item) => item.done).length)
|
|
60574
|
+
ctx.addIssue({ code: "custom", message: "Checklist totals disagree with items" });
|
|
60575
|
+
state.items.forEach((item, index) => {
|
|
60576
|
+
if (item.index !== index || identities.has(item.todoIdentifier) || item.uuid.replaceAll("-", "").toLowerCase() !== item.todoIdentifier || item.text.length !== item.lineLengthUTF16)
|
|
60577
|
+
ctx.addIssue({
|
|
60578
|
+
code: "custom",
|
|
60579
|
+
path: ["items", index],
|
|
60580
|
+
message: "Invalid checklist identity or line"
|
|
60581
|
+
});
|
|
60582
|
+
identities.add(item.todoIdentifier);
|
|
60583
|
+
});
|
|
60584
|
+
});
|
|
60255
60585
|
function callPrivateHelper(action, fields = {}, deps = defaultDeps2(), options = {}) {
|
|
60256
60586
|
if (!READ_ONLY_ACTIONS.has(action))
|
|
60257
60587
|
throw new PrivateHelperError(
|
|
60258
60588
|
"unknown_action",
|
|
60259
60589
|
`The private helper is read-only; "${action}" is not a supported action.`
|
|
60260
60590
|
);
|
|
60591
|
+
if (Object.hasOwn(fields, "action") || Object.hasOwn(fields, "protocol"))
|
|
60592
|
+
throw new PrivateHelperError(
|
|
60593
|
+
"invalid_request",
|
|
60594
|
+
"Request fields cannot override action or protocol"
|
|
60595
|
+
);
|
|
60261
60596
|
if (!options.allowDisabled && !privateHelperEnabled(deps.env))
|
|
60262
60597
|
throw new PrivateHelperError(
|
|
60263
60598
|
"disabled",
|
|
@@ -60298,18 +60633,18 @@ function callPrivateHelper(action, fields = {}, deps = defaultDeps2(), options =
|
|
|
60298
60633
|
`The helper was terminated by ${result.signal} before it answered.`
|
|
60299
60634
|
);
|
|
60300
60635
|
const stdout = String(result.stdout ?? "").trim();
|
|
60301
|
-
let
|
|
60636
|
+
let parsed2;
|
|
60302
60637
|
try {
|
|
60303
|
-
|
|
60638
|
+
parsed2 = JSON.parse(stdout);
|
|
60304
60639
|
} catch {
|
|
60305
60640
|
throw new PrivateHelperError(
|
|
60306
60641
|
"invalid_response",
|
|
60307
60642
|
`The helper exited with status ${result.status} and no JSON response.`
|
|
60308
60643
|
);
|
|
60309
60644
|
}
|
|
60310
|
-
if (!
|
|
60645
|
+
if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2))
|
|
60311
60646
|
throw new PrivateHelperError("invalid_response", "The helper response is not a JSON object");
|
|
60312
|
-
const object3 =
|
|
60647
|
+
const object3 = parsed2;
|
|
60313
60648
|
if (result.status !== 0 || object3.status === "error") {
|
|
60314
60649
|
const error2 = errorSchema2.safeParse(object3);
|
|
60315
60650
|
if (!error2.success)
|
|
@@ -60324,13 +60659,13 @@ function callPrivateHelper(action, fields = {}, deps = defaultDeps2(), options =
|
|
|
60324
60659
|
return object3;
|
|
60325
60660
|
}
|
|
60326
60661
|
function parseOrThrow(schema, value) {
|
|
60327
|
-
const
|
|
60328
|
-
if (!
|
|
60662
|
+
const parsed2 = schema.safeParse(value);
|
|
60663
|
+
if (!parsed2.success)
|
|
60329
60664
|
throw new PrivateHelperError(
|
|
60330
60665
|
"invalid_response",
|
|
60331
|
-
`Unexpected helper response: ${
|
|
60666
|
+
`Unexpected helper response: ${parsed2.error.issues.map((i) => i.path.join(".") + " " + i.message).join("; ")}`
|
|
60332
60667
|
);
|
|
60333
|
-
return
|
|
60668
|
+
return parsed2.data;
|
|
60334
60669
|
}
|
|
60335
60670
|
var UUID2 = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
|
|
60336
60671
|
function assertNoteIdentifier(identifier) {
|
|
@@ -60344,6 +60679,19 @@ function readNoteState(identifier, deps = defaultDeps2()) {
|
|
|
60344
60679
|
assertNoteIdentifier(identifier);
|
|
60345
60680
|
return parseOrThrow(noteStateSchema, callPrivateHelper("read_note_state", { identifier }, deps));
|
|
60346
60681
|
}
|
|
60682
|
+
function readChecklistState(identifier, deps = defaultDeps2()) {
|
|
60683
|
+
assertNoteIdentifier(identifier);
|
|
60684
|
+
const state = parseOrThrow(
|
|
60685
|
+
checklistStateSchema,
|
|
60686
|
+
callPrivateHelper("read_checklist", { identifier }, deps)
|
|
60687
|
+
);
|
|
60688
|
+
if (state.identifier.toLowerCase() !== identifier.toLowerCase())
|
|
60689
|
+
throw new PrivateHelperError(
|
|
60690
|
+
"invalid_response",
|
|
60691
|
+
"The helper returned a different note identifier"
|
|
60692
|
+
);
|
|
60693
|
+
return state;
|
|
60694
|
+
}
|
|
60347
60695
|
function featureFromProbe(feature) {
|
|
60348
60696
|
if (!feature) return { available: false, reason: "private_api_unavailable", detail: null };
|
|
60349
60697
|
if (feature.available) return { available: true, reason: null, detail: null };
|
|
@@ -60362,7 +60710,7 @@ function privateHelperCapabilities(deps = defaultDeps2()) {
|
|
|
60362
60710
|
reason,
|
|
60363
60711
|
detail
|
|
60364
60712
|
});
|
|
60365
|
-
const both = (status) => ({ readNoteState: status });
|
|
60713
|
+
const both = (status) => ({ readNoteState: status, readChecklist: status });
|
|
60366
60714
|
if (installation.reason === "unsupported_platform")
|
|
60367
60715
|
return {
|
|
60368
60716
|
enabled,
|
|
@@ -60405,7 +60753,10 @@ function privateHelperCapabilities(deps = defaultDeps2()) {
|
|
|
60405
60753
|
readOnly: true,
|
|
60406
60754
|
installation,
|
|
60407
60755
|
probe,
|
|
60408
|
-
features: {
|
|
60756
|
+
features: {
|
|
60757
|
+
readNoteState: featureFromProbe(probe.features.readNoteState),
|
|
60758
|
+
readChecklist: featureFromProbe(probe.features.readChecklist)
|
|
60759
|
+
}
|
|
60409
60760
|
};
|
|
60410
60761
|
}
|
|
60411
60762
|
|
|
@@ -61196,24 +61547,24 @@ function buildPermissionsWindow(checkOnly, deps = defaultPermissionsWindowDeps()
|
|
|
61196
61547
|
timeout: 1e4,
|
|
61197
61548
|
killSignal: "SIGKILL"
|
|
61198
61549
|
});
|
|
61199
|
-
let
|
|
61550
|
+
let parsed2 = null;
|
|
61200
61551
|
try {
|
|
61201
|
-
|
|
61552
|
+
parsed2 = helloSchema2.parse(JSON.parse(String(hello.stdout ?? "").trim()));
|
|
61202
61553
|
} catch {
|
|
61203
|
-
|
|
61554
|
+
parsed2 = null;
|
|
61204
61555
|
}
|
|
61205
|
-
if (!
|
|
61556
|
+
if (!parsed2 || parsed2.protocolVersion !== PERMISSIONS_WINDOW_PROTOCOL || parsed2.sourceSha256 !== sourceSha) {
|
|
61206
61557
|
steps.push({
|
|
61207
61558
|
step: "handshake",
|
|
61208
61559
|
ok: false,
|
|
61209
|
-
detail:
|
|
61560
|
+
detail: parsed2 ? `window reported protocol ${parsed2.protocolVersion}, source ${parsed2.sourceSha256}` : `no valid hello (exit ${hello.status})`
|
|
61210
61561
|
});
|
|
61211
61562
|
return finish();
|
|
61212
61563
|
}
|
|
61213
61564
|
steps.push({ step: "handshake", ok: true });
|
|
61214
61565
|
const manifest = {
|
|
61215
61566
|
schemaVersion: 1,
|
|
61216
|
-
protocolVersion:
|
|
61567
|
+
protocolVersion: parsed2.protocolVersion,
|
|
61217
61568
|
sourceSha256: sourceSha,
|
|
61218
61569
|
binarySha256: sha256Hex(deps.readFile(stagedBinary)),
|
|
61219
61570
|
builtAt: deps.now().toISOString(),
|
|
@@ -61587,19 +61938,19 @@ function relayBroker(socket, leftover, deps) {
|
|
|
61587
61938
|
// src/tools/privateHelperTools.ts
|
|
61588
61939
|
var coreDataId2 = external_exports.string().regex(/^x-coredata:\/\/[0-9A-F-]+\/ICNote\/p\d+$/i);
|
|
61589
61940
|
var notesUuid = external_exports.string().regex(UUID_PATTERN);
|
|
61590
|
-
function resolveIdentifier(
|
|
61941
|
+
function resolveIdentifier(deps, args) {
|
|
61591
61942
|
if (args.identifier && args.id)
|
|
61592
61943
|
throw new PrivateHelperError("invalid_request", "Pass identifier or id, not both");
|
|
61593
61944
|
if (args.identifier) return args.identifier;
|
|
61594
61945
|
if (!args.id) throw new PrivateHelperError("invalid_request", "identifier or id is required");
|
|
61595
|
-
|
|
61596
|
-
const
|
|
61597
|
-
if (!
|
|
61946
|
+
assertPrivateHelperReady(deps);
|
|
61947
|
+
const identifier = lookupStableIdentifiers([args.id], "ICNote").get(args.id)?.identifier;
|
|
61948
|
+
if (!identifier || !UUID_PATTERN.test(identifier))
|
|
61598
61949
|
throw new PrivateHelperError(
|
|
61599
61950
|
"not_found",
|
|
61600
61951
|
"Could not resolve that id to a Notes UUID (needs Full Disk Access); pass identifier instead"
|
|
61601
61952
|
);
|
|
61602
|
-
return
|
|
61953
|
+
return identifier;
|
|
61603
61954
|
}
|
|
61604
61955
|
function envelopeCode(helperCode, message) {
|
|
61605
61956
|
switch (helperCode) {
|
|
@@ -61645,7 +61996,7 @@ function helperErrorResult(error2) {
|
|
|
61645
61996
|
})
|
|
61646
61997
|
);
|
|
61647
61998
|
}
|
|
61648
|
-
function registerPrivateHelperTools(server2,
|
|
61999
|
+
function registerPrivateHelperTools(server2, _manager, depsFactory = () => defaultDeps2()) {
|
|
61649
62000
|
function tool(name, description, inputSchema, annotations, handler) {
|
|
61650
62001
|
server2.registerTool(
|
|
61651
62002
|
name,
|
|
@@ -61670,7 +62021,7 @@ function registerPrivateHelperTools(server2, manager, depsFactory = () => defaul
|
|
|
61670
62021
|
}
|
|
61671
62022
|
tool(
|
|
61672
62023
|
"native-helper-status",
|
|
61673
|
-
"Use when: checking whether the opt-in, read-only native private helper is enabled, built, current, and working on this macOS before calling native-note-state.\nReturns: enabled flag, installation state (path, manifest, stale/modified checks), the live probe (macOS and Notes versions, framework, store access), and per-feature availability with a machine reason.\nDo not use when: checking the Shortcuts bridges (native-tags-status, get-capabilities).\nSafety: read-only. The helper is read-only by design (write support was deliberately deferred by the maintainer); the probe opens the Notes store read-only and only when the helper is enabled and installed.",
|
|
62024
|
+
"Use when: checking whether the opt-in, read-only native private helper is enabled, built, current, and working on this macOS before calling native-note-state or native-checklist-state.\nReturns: enabled flag, installation state (path, manifest, stale/modified checks), the live probe (macOS and Notes versions, framework, store access), and per-feature availability with a machine reason.\nDo not use when: checking the Shortcuts bridges (native-tags-status, get-capabilities).\nSafety: read-only. The helper is read-only by design (write support was deliberately deferred by the maintainer); the probe opens the Notes store read-only and only when the helper is enabled and installed.",
|
|
61674
62025
|
{},
|
|
61675
62026
|
{ readOnlyHint: true, openWorldHint: false },
|
|
61676
62027
|
(_args, deps) => {
|
|
@@ -61689,7 +62040,17 @@ function registerPrivateHelperTools(server2, manager, depsFactory = () => defaul
|
|
|
61689
62040
|
id: coreDataId2.optional().describe("x-coredata note id; resolved to a UUID via the database")
|
|
61690
62041
|
},
|
|
61691
62042
|
{ readOnlyHint: true, openWorldHint: false },
|
|
61692
|
-
(args, deps) => ({ ...readNoteState(resolveIdentifier(
|
|
62043
|
+
(args, deps) => ({ ...readNoteState(resolveIdentifier(deps, args), deps) })
|
|
62044
|
+
);
|
|
62045
|
+
tool(
|
|
62046
|
+
"native-checklist-state",
|
|
62047
|
+
"Use when: reading a note's exact native checklist todo identifiers and done state, including ambiguous style runs.\nReturns: identifier, revision change token, items in first native-style occurrence order, total, checked, and syncHostRunning. Each item has todoIdentifier (32 lowercase hex digits), uuid, index, text and done; line and styled offsets use UTF-16. contiguous=false means the styled envelope includes gaps, consistent=false means runs disagree on done, and spansLines=true means the identity spans multiple lines.\nDo not use when: reading ordinary checkbox-like text (get-note-content) or changing checklist items. Flags describe native identity ambiguity; text is the first visible styled character's line.\nSafety: read-only; requires APPLE_NOTES_MCP_ENABLE_PRIVATE=1, Full Disk Access, and a current built helper. Locked or unavailable native bodies are refused. The helper has no write action.",
|
|
62048
|
+
{
|
|
62049
|
+
identifier: notesUuid.optional().describe("Notes UUID (the notes://showNote identifier)"),
|
|
62050
|
+
id: coreDataId2.optional().describe("x-coredata note id; resolved to a UUID via the database")
|
|
62051
|
+
},
|
|
62052
|
+
{ readOnlyHint: true, openWorldHint: false },
|
|
62053
|
+
(args, deps) => ({ ...readChecklistState(resolveIdentifier(deps, args), deps) })
|
|
61693
62054
|
);
|
|
61694
62055
|
}
|
|
61695
62056
|
|
|
@@ -62084,15 +62445,15 @@ async function readEditorJson(req) {
|
|
|
62084
62445
|
throw new EditorHttpError(413, "too-large", "Request body too large.");
|
|
62085
62446
|
chunks.push(chunk);
|
|
62086
62447
|
}
|
|
62087
|
-
let
|
|
62448
|
+
let parsed2;
|
|
62088
62449
|
try {
|
|
62089
|
-
|
|
62450
|
+
parsed2 = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
62090
62451
|
} catch {
|
|
62091
62452
|
throw new EditorHttpError(400, "bad-request", "Request body is not valid JSON.");
|
|
62092
62453
|
}
|
|
62093
|
-
if (!
|
|
62454
|
+
if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2))
|
|
62094
62455
|
throw new EditorHttpError(400, "bad-request", "Request body must be a JSON object.");
|
|
62095
|
-
return
|
|
62456
|
+
return parsed2;
|
|
62096
62457
|
}
|
|
62097
62458
|
var editorStringField = (body, key) => {
|
|
62098
62459
|
const value = body[key];
|
|
@@ -62319,8 +62680,8 @@ Every request needs the per-run token in the printed URL. Press Ctrl-C to stop.
|
|
|
62319
62680
|
`;
|
|
62320
62681
|
function parseTemplatesArgs(argv) {
|
|
62321
62682
|
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) return "help";
|
|
62322
|
-
const [
|
|
62323
|
-
if (
|
|
62683
|
+
const [command2, ...rest] = argv;
|
|
62684
|
+
if (command2 !== "edit") throw new Error(`Unknown templates command "${command2}".`);
|
|
62324
62685
|
const args = {
|
|
62325
62686
|
idleMinutes: DEFAULT_EDITOR_IDLE_MS / 6e4,
|
|
62326
62687
|
tailnet: false
|
|
@@ -62947,17 +63308,17 @@ var AnchorRegistry = class _AnchorRegistry {
|
|
|
62947
63308
|
closeSync9(fd);
|
|
62948
63309
|
}
|
|
62949
63310
|
if (text2.trim() === "") return [];
|
|
62950
|
-
let
|
|
63311
|
+
let parsed2;
|
|
62951
63312
|
try {
|
|
62952
|
-
|
|
63313
|
+
parsed2 = JSON.parse(text2);
|
|
62953
63314
|
} catch {
|
|
62954
63315
|
throw new AnchorRegistryError(
|
|
62955
63316
|
"corrupt-registry",
|
|
62956
63317
|
`${this.path} is not valid JSON; it was left unchanged. Move it aside to start a new registry.`
|
|
62957
63318
|
);
|
|
62958
63319
|
}
|
|
62959
|
-
const anchors =
|
|
62960
|
-
if (
|
|
63320
|
+
const anchors = parsed2?.anchors;
|
|
63321
|
+
if (parsed2?.version !== 1 || !Array.isArray(anchors) || !anchors.every(isAnchor))
|
|
62961
63322
|
throw new AnchorRegistryError(
|
|
62962
63323
|
"corrupt-registry",
|
|
62963
63324
|
`${this.path} is not a version 1 anchor registry; it was left unchanged.`
|
|
@@ -63183,25 +63544,23 @@ function registryLookup(registry2 = new AnchorRegistry(), dbPath2) {
|
|
|
63183
63544
|
|
|
63184
63545
|
// src/index.ts
|
|
63185
63546
|
loadFileConfig();
|
|
63186
|
-
|
|
63187
|
-
|
|
63188
|
-
if (process.argv[2] === "setup" && process.argv.slice(3).includes("--public-helper")) {
|
|
63189
|
-
const report = buildPublicHelper(process.argv.slice(3).includes("--check"));
|
|
63547
|
+
if (command.kind === "setup" && command.target === "public-helper") {
|
|
63548
|
+
const report = buildPublicHelper(command.checkOnly);
|
|
63190
63549
|
process.stdout.write(formatPublicHelperBuild(report) + "\n");
|
|
63191
63550
|
process.exit(report.ok ? 0 : 1);
|
|
63192
63551
|
}
|
|
63193
|
-
if (
|
|
63194
|
-
const report = buildPrivateHelper(
|
|
63552
|
+
if (command.kind === "setup" && command.target === "native-helper") {
|
|
63553
|
+
const report = buildPrivateHelper(command.checkOnly);
|
|
63195
63554
|
process.stdout.write(formatHelperBuild(report) + "\n");
|
|
63196
63555
|
process.exit(report.ok ? 0 : 1);
|
|
63197
63556
|
}
|
|
63198
|
-
if (
|
|
63199
|
-
const report = buildPermissionsWindow(
|
|
63557
|
+
if (command.kind === "setup" && command.target === "permissions-window") {
|
|
63558
|
+
const report = buildPermissionsWindow(command.checkOnly);
|
|
63200
63559
|
process.stdout.write(formatPermissionsWindowBuild(report) + "\n");
|
|
63201
63560
|
process.exit(report.ok ? 0 : 1);
|
|
63202
63561
|
}
|
|
63203
|
-
if (
|
|
63204
|
-
const args =
|
|
63562
|
+
if (command.kind === "setup" && command.target === "permissions") {
|
|
63563
|
+
const args = command.args;
|
|
63205
63564
|
const options = parsePermissionsArgs(args);
|
|
63206
63565
|
const cli = defaultPermissionsCliDeps(options);
|
|
63207
63566
|
let code;
|
|
@@ -63221,22 +63580,22 @@ if (process.argv[2] === "setup" && process.argv.slice(3).includes("--permissions
|
|
|
63221
63580
|
cli.close();
|
|
63222
63581
|
process.exit(code);
|
|
63223
63582
|
}
|
|
63224
|
-
if (
|
|
63225
|
-
const report = await setupBroker(parseBrokerArgs(
|
|
63583
|
+
if (command.kind === "setup" && command.target === "broker") {
|
|
63584
|
+
const report = await setupBroker(parseBrokerArgs(command.args));
|
|
63226
63585
|
process.stdout.write(formatBrokerSetup(report) + "\n");
|
|
63227
63586
|
process.exit(report.ok ? 0 : 1);
|
|
63228
63587
|
}
|
|
63229
|
-
if (
|
|
63230
|
-
const report = setupShortcuts(
|
|
63588
|
+
if (command.kind === "setup") {
|
|
63589
|
+
const report = setupShortcuts(command.checkOnly);
|
|
63231
63590
|
process.stdout.write(formatShortcutSetup(report) + "\n");
|
|
63232
63591
|
process.exit(report.ready || !report.checkOnly ? 0 : 1);
|
|
63233
63592
|
}
|
|
63234
|
-
if (
|
|
63235
|
-
process.exit(await runTemplatesCommand(
|
|
63593
|
+
if (command.kind === "templates") {
|
|
63594
|
+
process.exit(await runTemplatesCommand(command.args));
|
|
63236
63595
|
}
|
|
63237
|
-
if (
|
|
63596
|
+
if (command.kind === "anchors") {
|
|
63238
63597
|
process.exit(
|
|
63239
|
-
await runAnchorsCli(
|
|
63598
|
+
await runAnchorsCli(command.args, { resolve: registryLookup(new AnchorRegistry()) })
|
|
63240
63599
|
);
|
|
63241
63600
|
}
|
|
63242
63601
|
if (await startBrokerProxy()) {
|
|
@@ -63245,7 +63604,7 @@ if (await startBrokerProxy()) {
|
|
|
63245
63604
|
}
|
|
63246
63605
|
var server = new McpServer({
|
|
63247
63606
|
name: "apple-notes",
|
|
63248
|
-
version
|
|
63607
|
+
version,
|
|
63249
63608
|
description: "MCP server for managing Apple Notes - create, search, update, and organize notes"
|
|
63250
63609
|
});
|
|
63251
63610
|
installSdkErrorCodes(server);
|
|
@@ -63886,9 +64245,10 @@ registerTool(
|
|
|
63886
64245
|
},
|
|
63887
64246
|
withErrorHandling(({ id: id2, title, account }) => {
|
|
63888
64247
|
if (id2) {
|
|
63889
|
-
const note2 = notesManager.
|
|
64248
|
+
const { note: note2, error: metadataError } = notesManager.readNoteById(id2);
|
|
63890
64249
|
if (!note2) {
|
|
63891
|
-
|
|
64250
|
+
const message = `Failed to read metadata of note with ID "${id2}": ${metadataError}`;
|
|
64251
|
+
return metadataError === NOTE_ID_NOT_FOUND_MESSAGE ? notFoundResponse(message) : errorResponse(message);
|
|
63892
64252
|
}
|
|
63893
64253
|
if (note2.passwordProtected) {
|
|
63894
64254
|
return errorResponse(
|
|
@@ -65665,6 +66025,7 @@ registerTool(
|
|
|
65665
66025
|
checks: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
65666
66026
|
runtimeOS: external_exports.object({}).passthrough().optional(),
|
|
65667
66027
|
features: external_exports.record(external_exports.string(), external_exports.object({}).passthrough()).optional(),
|
|
66028
|
+
publicHelper: external_exports.object({}).passthrough().optional(),
|
|
65668
66029
|
broker: external_exports.object({}).passthrough().optional()
|
|
65669
66030
|
}
|
|
65670
66031
|
},
|