@mgcrea/mcp-apple-core 0.0.0-bootstrap → 1.3.0
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/dist/index.d.ts +121 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +190 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -119,6 +119,7 @@ type JxaEnvelope<T> = {
|
|
|
119
119
|
error: {
|
|
120
120
|
code: string;
|
|
121
121
|
message: string;
|
|
122
|
+
[key: string]: unknown;
|
|
122
123
|
};
|
|
123
124
|
};
|
|
124
125
|
type OsascriptRunner = {
|
|
@@ -197,6 +198,7 @@ declare const parseList: (v: string | undefined) => string[] | undefined;
|
|
|
197
198
|
*/
|
|
198
199
|
declare const BaseConfigSchema: z.ZodObject<{
|
|
199
200
|
allowWrites: z.ZodDefault<z.ZodBoolean>;
|
|
201
|
+
exposePrompts: z.ZodDefault<z.ZodBoolean>;
|
|
200
202
|
debug: z.ZodDefault<z.ZodBoolean>;
|
|
201
203
|
osascriptPath: z.ZodDefault<z.ZodString>;
|
|
202
204
|
osascriptTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
@@ -241,6 +243,124 @@ type StoreFacts = FileFacts & {
|
|
|
241
243
|
*/
|
|
242
244
|
declare const describeStore: (path: string) => StoreFacts;
|
|
243
245
|
//#endregion
|
|
246
|
+
//#region src/prompts.d.ts
|
|
247
|
+
/**
|
|
248
|
+
* The workflow prompts.
|
|
249
|
+
*
|
|
250
|
+
* ## What a prompt is for here
|
|
251
|
+
*
|
|
252
|
+
* The same thing every tool description in this repo is for: holding a
|
|
253
|
+
* constraint the model would otherwise re-derive. A tool holds the ones that
|
|
254
|
+
* are about *one call* — that a body search wants a narrowing filter, that a
|
|
255
|
+
* ref is opaque. A prompt holds the ones that are about the *order of calls*,
|
|
256
|
+
* and those have nowhere else to live. "Search before you list", "read the
|
|
257
|
+
* thread before you answer it", "check what exists before you create a
|
|
258
|
+
* duplicate" are not properties of any single tool, so no single tool
|
|
259
|
+
* description can carry them, and the model rebuilds them from scratch every
|
|
260
|
+
* session — usually correctly, sometimes not, always at a cost.
|
|
261
|
+
*
|
|
262
|
+
* ## Every prompt embeds its surface guide
|
|
263
|
+
*
|
|
264
|
+
* A prompt returns messages, and one of them is the `cupertino://<surface>/guide`
|
|
265
|
+
* resource. That is the coupling that makes both primitives worth more than
|
|
266
|
+
* either alone: the guide is the reference, the prompt is the task, and a host
|
|
267
|
+
* that expands the prompt gets both without the model having to know the guide
|
|
268
|
+
* exists.
|
|
269
|
+
*
|
|
270
|
+
* ## Write-gated prompts follow the tools
|
|
271
|
+
*
|
|
272
|
+
* A prompt that ends in a mutation is registered only when writes are on, for
|
|
273
|
+
* the same reason the mutating tools are: with the gate closed it must not
|
|
274
|
+
* merely refuse, it must be *invisible*. A visible `draft_reply` on a
|
|
275
|
+
* read-only server is an offer the server cannot keep.
|
|
276
|
+
*/
|
|
277
|
+
/** MCP prompt arguments are strings on the wire. This is the only shape they take. */
|
|
278
|
+
declare const promptArg: (description: string) => z.ZodOptional<z.ZodString>;
|
|
279
|
+
/** Same, for an argument the prompt is useless without. */
|
|
280
|
+
declare const requiredPromptArg: (description: string) => z.ZodString;
|
|
281
|
+
type PromptContext = {
|
|
282
|
+
/** Surface id, e.g. "mail". */
|
|
283
|
+
surface: string;
|
|
284
|
+
/** The static guide, embedded ahead of every prompt's instruction. */
|
|
285
|
+
guide: string;
|
|
286
|
+
};
|
|
287
|
+
type WorkflowPrompt<Args extends z.ZodRawShape> = {
|
|
288
|
+
/** Namespaced like the tools, e.g. "apple_mail_triage". */
|
|
289
|
+
name: string;
|
|
290
|
+
title: string;
|
|
291
|
+
/** What this does and when to reach for it. Shown in the host's prompt list. */
|
|
292
|
+
description: string;
|
|
293
|
+
argsSchema?: Args;
|
|
294
|
+
/**
|
|
295
|
+
* The instruction. Receives validated arguments; returns the text that does
|
|
296
|
+
* the actual work of ordering the calls.
|
|
297
|
+
*/
|
|
298
|
+
build: (args: { [K in keyof Args]: z.infer<Args[K]>; }) => string;
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Register one workflow prompt.
|
|
302
|
+
*
|
|
303
|
+
* Called once per prompt rather than handed an array, because the argument
|
|
304
|
+
* shape is generic and an array of prompts with differing shapes loses the
|
|
305
|
+
* inference that makes `build`'s parameter typed at all.
|
|
306
|
+
*/
|
|
307
|
+
declare const registerWorkflowPrompt: <Args extends z.ZodRawShape>(server: McpServer, ctx: PromptContext, prompt: WorkflowPrompt<Args>) => void;
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/resources.d.ts
|
|
310
|
+
/**
|
|
311
|
+
* The surface resources.
|
|
312
|
+
*
|
|
313
|
+
* ## Why a resource when a tool already returns this
|
|
314
|
+
*
|
|
315
|
+
* `apple_mail_diagnostics` and `apple_mail_list_accounts` answer these same
|
|
316
|
+
* questions, and they stay. What they cannot do is be *addressed*. A tool result
|
|
317
|
+
* exists only after the model decided to spend a call on it, which means the
|
|
318
|
+
* account list is re-derived every session and the diagnostics report is read
|
|
319
|
+
* after the failure rather than before it. A resource is a URI: a host can
|
|
320
|
+
* attach it, cache it, or let a user paste it in, and none of that costs a tool
|
|
321
|
+
* call or depends on the model guessing that it should look.
|
|
322
|
+
*
|
|
323
|
+
* ## The scheme is `cupertino://`, not `apple://`
|
|
324
|
+
*
|
|
325
|
+
* The tools are named `apple_mail_*` because they say what they drive. A URI
|
|
326
|
+
* scheme is a different kind of claim — it is a namespace, and taking Apple's
|
|
327
|
+
* would read as affiliation this project spends a README line disclaiming. So
|
|
328
|
+
* the authority is the surface id and the scheme is the project's own name, the
|
|
329
|
+
* one already in the bundle identifier.
|
|
330
|
+
*
|
|
331
|
+
* ## Three per surface, and only one of them can fail
|
|
332
|
+
*
|
|
333
|
+
* - `guide` is static text. It needs no permission, touches no store and spawns
|
|
334
|
+
* no process, so it is readable when every other lane is denied — which is
|
|
335
|
+
* exactly the moment its contents are worth reading.
|
|
336
|
+
* - `diagnostics` is the live capability report.
|
|
337
|
+
* - `inventory` is the set of containers you address by name: accounts, and
|
|
338
|
+
* whatever the surface calls its folders. Surfaces with no such containers
|
|
339
|
+
* (Messages, Safari) register two resources rather than inventing a third.
|
|
340
|
+
*/
|
|
341
|
+
declare const RESOURCE_SCHEME = "cupertino";
|
|
342
|
+
/** `cupertino://mail/guide`. The one place this string is built. */
|
|
343
|
+
declare const surfaceUri: (surface: string, leaf: string) => string;
|
|
344
|
+
type ResourceReader = () => Promise<unknown>;
|
|
345
|
+
type SurfaceResourceOptions = {
|
|
346
|
+
/** Surface id as it appears in surfaces.json, e.g. "mail". */
|
|
347
|
+
surface: string;
|
|
348
|
+
/** Display name, e.g. "Mail". Used only in resource titles. */
|
|
349
|
+
displayName: string;
|
|
350
|
+
/** The operating manual. Static markdown — see `guide.ts` in each surface. */
|
|
351
|
+
guide: string;
|
|
352
|
+
/** The live capability report, normally the diagnostics tool's own payload. */
|
|
353
|
+
diagnostics: ResourceReader;
|
|
354
|
+
/** The addressable containers. Omitted by surfaces that have none. */
|
|
355
|
+
inventory?: {
|
|
356
|
+
/** What this surface calls them, e.g. "accounts and mailboxes". */
|
|
357
|
+
describes: string;
|
|
358
|
+
read: ResourceReader;
|
|
359
|
+
};
|
|
360
|
+
};
|
|
361
|
+
/** Register a surface's guide, diagnostics and (where it has one) inventory. */
|
|
362
|
+
declare const registerSurfaceResources: (server: McpServer, opts: SurfaceResourceOptions) => void;
|
|
363
|
+
//#endregion
|
|
244
364
|
//#region src/schema.d.ts
|
|
245
365
|
/**
|
|
246
366
|
* Schema introspection for stores Apple owns and can reshape in any release.
|
|
@@ -351,5 +471,5 @@ declare const compact: <T extends Record<string, unknown>>(obj: T) => Partial<T>
|
|
|
351
471
|
declare const limitArg: z.ZodOptional<z.ZodNumber>;
|
|
352
472
|
declare const confirmArg: z.ZodLiteral<true>;
|
|
353
473
|
//#endregion
|
|
354
|
-
export { AppBusyError, AppNotRunningError, AppleAutomationError, BaseConfigSchema, type BuildInfo, CORE_DATA_EPOCH_OFFSET, type ExecImpl, type FileFacts, IndexUnavailableError, type JxaEnvelope, type Logger, type OpenOptions, type OpenedStore, type OsascriptOptions, type OsascriptRunner, OsascriptTimeoutError, type PackageIdentity, PlatformError, PreconditionError, ProtocolError, type ReadOnlyMode, SchemaDriftError, type StdioServerOptions, type StoreFacts, type SurfaceContext, TccDeniedError, type ToolResult, WritesDisabledError, assertStaticScript, columnsOf, compact, confirmArg, createOsascriptRunner, describeStore, detectEpoch, escapeLike, fail, fingerprintSchema, inspectFile, limitArg, mapOsaError, ok, okText, openReadOnly, parseBool, parseConfig, parseIntOpt, parseList, readPackageIdentity, runStdioServer, tableMap, toFailure, toFileUri, trimmed, withBusyRetry, wrap, wrapResult };
|
|
474
|
+
export { AppBusyError, AppNotRunningError, AppleAutomationError, BaseConfigSchema, type BuildInfo, CORE_DATA_EPOCH_OFFSET, type ExecImpl, type FileFacts, IndexUnavailableError, type JxaEnvelope, type Logger, type OpenOptions, type OpenedStore, type OsascriptOptions, type OsascriptRunner, OsascriptTimeoutError, type PackageIdentity, PlatformError, PreconditionError, type PromptContext, ProtocolError, RESOURCE_SCHEME, type ReadOnlyMode, type ResourceReader, SchemaDriftError, type StdioServerOptions, type StoreFacts, type SurfaceContext, type SurfaceResourceOptions, TccDeniedError, type ToolResult, type WorkflowPrompt, WritesDisabledError, assertStaticScript, columnsOf, compact, confirmArg, createOsascriptRunner, describeStore, detectEpoch, escapeLike, fail, fingerprintSchema, inspectFile, limitArg, mapOsaError, ok, okText, openReadOnly, parseBool, parseConfig, parseIntOpt, parseList, promptArg, readPackageIdentity, registerSurfaceResources, registerWorkflowPrompt, requiredPromptArg, runStdioServer, surfaceUri, tableMap, toFailure, toFileUri, trimmed, withBusyRetry, wrap, wrapResult };
|
|
355
475
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/build-info.ts","../src/errors.ts","../src/osascript.ts","../src/cli.ts","../src/config.ts","../src/fs.ts","../src/schema.ts","../src/sqlite.ts","../src/tools.ts"],"mappings":";;;;KAEY;EAAoB;EAAc;;KAElC,YAAY;EACtB;EACA;;;;;;;;;;;cAYW,sBAAmB,gBACd,KAAG,UACT,oBACT;;;;;;;;;;;;;;;;;;;;;;;KCAS;;EAEV;;EAEA;;;cAIW,6BAA6B;WACtB;WACT,SAAS;EAElB,YAAY,iBAAiB,UAAU;;;cAO5B,uBAAuB;WAChB;EAElB,YAAY,SAAS;;;cAWV,2BAA2B;WACpB;EAElB,YAAY,SAAS;;;cASV,qBAAqB;WACd;EAElB,YAAY,SAAS;;;cASV,8BAA8B;WACvB;EAElB,YAAY,mBAAmB,SAAS;;;;;;;cAc7B,4BAA4B;WACrB;EAElB,YAAY,SAAS;;;cAQV,8BAA8B;WACvB;;;cAIP,yBAAyB;WAClB;;;cAIP,sBAAsB;WACf;;;cAIP,sBAAsB;WACf;;;cAIP,0BAA0B;WACnB;;;;KCnFR;EACV,YAAY;EACZ,WAAW;EACX,YAAY;;;KAIF,YAAY;EAClB;EAAU,MAAM;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/build-info.ts","../src/errors.ts","../src/osascript.ts","../src/cli.ts","../src/config.ts","../src/fs.ts","../src/prompts.ts","../src/resources.ts","../src/schema.ts","../src/sqlite.ts","../src/tools.ts"],"mappings":";;;;KAEY;EAAoB;EAAc;;KAElC,YAAY;EACtB;EACA;;;;;;;;;;;cAYW,sBAAmB,gBACd,KAAG,UACT,oBACT;;;;;;;;;;;;;;;;;;;;;;;KCAS;;EAEV;;EAEA;;;cAIW,6BAA6B;WACtB;WACT,SAAS;EAElB,YAAY,iBAAiB,UAAU;;;cAO5B,uBAAuB;WAChB;EAElB,YAAY,SAAS;;;cAWV,2BAA2B;WACpB;EAElB,YAAY,SAAS;;;cASV,qBAAqB;WACd;EAElB,YAAY,SAAS;;;cASV,8BAA8B;WACvB;EAElB,YAAY,mBAAmB,SAAS;;;;;;;cAc7B,4BAA4B;WACrB;EAElB,YAAY,SAAS;;;cAQV,8BAA8B;WACvB;;;cAIP,yBAAyB;WAClB;;;cAIP,sBAAsB;WACf;;;cAIP,sBAAsB;WACf;;;cAIP,0BAA0B;WACnB;;;;KCnFR;EACV,YAAY;EACZ,WAAW;EACX,YAAY;;;KAIF,YAAY;EAClB;EAAU,MAAM;;EAIhB;EAAW;IAAS;IAAc;KAAkB;;;KAE9C;;EAEV,MAAM,GAAG,gBAAgB,qBAAqB,QAAQ;;;;;;;;KAS5C,YACV,cACA,gBACA,gBACA,sBACG;KAEO;EACV;EACA;;EAEA,SAAS;EACT,SAAS;EACT,OAAO;;;;;;;cAUI,qBAAkB;;cAUlB,cAAW,gBAAkB,mBAAmB,SAAW,mBAAiB;cAyE5E,wBAAqB,MAAU,qBAAmB;;cAqClD,gBAAuB,GAAC,UAAY,QAAQ,IAAE,qBAAmB,QAAQ;;;KC7M1E;EACV,OAAO;EACP,SAAS;;EAET;;;;;EAKA,QAAQ,QAAQ,WAAW;IAAU,QAAQ;IAAW;;;;;;;;;;;cAW7C,iBAAc,MAAgB,uBAAqB;;;;;;;;;;;cChBnD,UAAO;cAKP,YAAS;cAMT,cAAW;cAOX,YAAS;;;;;;cAcT,kBAAgB,EAAA;;;;;;;GA2B3B,EAAA,KAAA;;;;;;;cAQW,cAAe,UAAU,EAAE,SAAO,QACrC,GAAC,KACJ,4BACJ,EAAE,MAAM;;;;;;;;;;;;KCtEC;EACV;EACA;EACA;EACA;;cAGW,cAAW,iBAAmB;KAoB/B,aAAa;;EAEvB;EACA;;;;;;;;;cAUW,gBAAa,iBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCbhC,YAAS,wBAA0B,EAAE,YAAY,EAAE;;cAInD,oBAAiB,wBAA0B,EAAE;KAG9C;;EAEV;;EAEA;;KAGU,eAAe,aAAa,EAAE;;EAExC;EACA;;EAEA;EACA,aAAa;;;;;EAKb,QAAQ,SAAS,WAAW,OAAO,EAAE,MAAM,KAAK;;;;;;;;;cAUrC,yBAA0B,aAAa,EAAE,aAAW,QACvD,WAAS,KACZ,eAAa,QACV,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cC1CZ;;cAGA,aAAU,iBAAmB;KAG9B,uBAAuB;KAEvB;;EAEV;;EAEA;;EAEA;;EAEA,aAAa;;EAEb;;IAEE;IACA,MAAM;;;;cA2CG,2BAAwB,QAAY,WAAS,MAAQ;;;;;;;;;;;cCtFrD;cAEA,YAAS,IAAQ,cAAY;;cAW7B,WAAQ,IAAQ,iBAAe;;;;;;cAc/B,oBAAiB,IAAQ;;;;;;;cAgBzB,cAAW,6BACK;EAExB;EAAgB;;;;;;;;;;;;;;;;;;;KCvCT;KAEA,YAAY;EACtB,IAAI;;EAEJ;;EAEA,WAAW;;;;;;cAOA,YAAS,cAAgB;;cAIzB,aAAU;KAGX,YAAY;;EAEtB;;EAEA;;EAEA;;;;;;EAMA,aAAa,IAAI,iBAAiB;;;;;EAKlC,UAAU;;EAEV;;cAGW,eAAgB,eAAa,cAC5B,MACN,cAAY,OACZ,YAAY,OACjB,YAAY;;;KC9DH;EACV;IAAW;IAAc;;EACzB;;cAGW,KAAE,kBAAoB;;;;;cAQtB,SAAM,iBAAmB;cAIzB,OAAI,iBAAmB,oBAAoB;;cAW3C,YAAS,iBAAmB;;cAY5B,OAAc,GAAC,UAAY,QAAQ,OAAK,QAAQ;;cAShD,aAAU,UAAoB,QAAQ,gBAAc,QAAQ;;cAS5D,UAAW,UAAU,yBAAuB,KAAO,MAAI,QAAQ;cAK/D,UAAQ,EAAA,YAAA,EAAA;cAQR,YAAU,EAAA"}
|
package/dist/index.js
CHANGED
|
@@ -93,6 +93,27 @@ const parseList = (v) => {
|
|
|
93
93
|
*/
|
|
94
94
|
const BaseConfigSchema = z.object({
|
|
95
95
|
allowWrites: z.boolean().default(false),
|
|
96
|
+
/**
|
|
97
|
+
* Register the workflow prompts and the surface resources.
|
|
98
|
+
*
|
|
99
|
+
* ON by default, unlike `allowWrites`, and the difference is the point: the
|
|
100
|
+
* write gate is a SAFETY invariant — off means a mutation cannot be reached
|
|
101
|
+
* even by name — while this is a COST knob, in the same family as
|
|
102
|
+
* `maxResults`. Conflating the two would muddy the one that matters.
|
|
103
|
+
*
|
|
104
|
+
* What it costs, measured across all seven servers with writes on: the
|
|
105
|
+
* prompt and resource listings come to ~3.4k tokens against ~18.5k for the
|
|
106
|
+
* tool definitions, so roughly 18% on top of a bill that is dominated by
|
|
107
|
+
* tools either way. Resource CONTENTS cost nothing until something reads
|
|
108
|
+
* them. The knob exists for hosts that put every listing in the prompt and
|
|
109
|
+
* for people counting bytes; if context is the problem, running fewer
|
|
110
|
+
* servers is the bigger lever by far.
|
|
111
|
+
*
|
|
112
|
+
* One flag for both, not two, because they ship as a pair: every prompt
|
|
113
|
+
* embeds its surface guide, and a prompt naming a `cupertino://…/guide` that
|
|
114
|
+
* nothing serves would be a dangling reference by configuration.
|
|
115
|
+
*/
|
|
116
|
+
exposePrompts: z.boolean().default(true),
|
|
96
117
|
debug: z.boolean().default(false),
|
|
97
118
|
osascriptPath: z.string().default("/usr/bin/osascript"),
|
|
98
119
|
osascriptTimeoutMs: z.number().int().min(1e3).max(6e5).default(3e4),
|
|
@@ -338,10 +359,13 @@ const createOsascriptRunner = (opts) => {
|
|
|
338
359
|
throw new ProtocolError(`osascript returned non-JSON output: ${stdout.slice(0, 500)}`);
|
|
339
360
|
}
|
|
340
361
|
if (!envelope.ok) {
|
|
341
|
-
const { code, message } = envelope.error;
|
|
362
|
+
const { code, message, ...rest } = envelope.error;
|
|
342
363
|
if (code === "APP_NOT_RUNNING" || code === "MAIL_NOT_RUNNING") throw new AppNotRunningError(opts.surface);
|
|
343
364
|
if (code === "NOT_AUTHORIZED") throw new TccDeniedError(opts.surface);
|
|
344
|
-
throw new ProtocolError(message, {
|
|
365
|
+
throw new ProtocolError(message, {
|
|
366
|
+
code,
|
|
367
|
+
...rest
|
|
368
|
+
});
|
|
345
369
|
}
|
|
346
370
|
opts.logger?.debug?.("osascript ok");
|
|
347
371
|
return envelope.data;
|
|
@@ -359,6 +383,169 @@ const withBusyRetry = async (fn, delayMs = 1500) => {
|
|
|
359
383
|
}
|
|
360
384
|
};
|
|
361
385
|
//#endregion
|
|
386
|
+
//#region src/resources.ts
|
|
387
|
+
/**
|
|
388
|
+
* The surface resources.
|
|
389
|
+
*
|
|
390
|
+
* ## Why a resource when a tool already returns this
|
|
391
|
+
*
|
|
392
|
+
* `apple_mail_diagnostics` and `apple_mail_list_accounts` answer these same
|
|
393
|
+
* questions, and they stay. What they cannot do is be *addressed*. A tool result
|
|
394
|
+
* exists only after the model decided to spend a call on it, which means the
|
|
395
|
+
* account list is re-derived every session and the diagnostics report is read
|
|
396
|
+
* after the failure rather than before it. A resource is a URI: a host can
|
|
397
|
+
* attach it, cache it, or let a user paste it in, and none of that costs a tool
|
|
398
|
+
* call or depends on the model guessing that it should look.
|
|
399
|
+
*
|
|
400
|
+
* ## The scheme is `cupertino://`, not `apple://`
|
|
401
|
+
*
|
|
402
|
+
* The tools are named `apple_mail_*` because they say what they drive. A URI
|
|
403
|
+
* scheme is a different kind of claim — it is a namespace, and taking Apple's
|
|
404
|
+
* would read as affiliation this project spends a README line disclaiming. So
|
|
405
|
+
* the authority is the surface id and the scheme is the project's own name, the
|
|
406
|
+
* one already in the bundle identifier.
|
|
407
|
+
*
|
|
408
|
+
* ## Three per surface, and only one of them can fail
|
|
409
|
+
*
|
|
410
|
+
* - `guide` is static text. It needs no permission, touches no store and spawns
|
|
411
|
+
* no process, so it is readable when every other lane is denied — which is
|
|
412
|
+
* exactly the moment its contents are worth reading.
|
|
413
|
+
* - `diagnostics` is the live capability report.
|
|
414
|
+
* - `inventory` is the set of containers you address by name: accounts, and
|
|
415
|
+
* whatever the surface calls its folders. Surfaces with no such containers
|
|
416
|
+
* (Messages, Safari) register two resources rather than inventing a third.
|
|
417
|
+
*/
|
|
418
|
+
const RESOURCE_SCHEME = "cupertino";
|
|
419
|
+
/** `cupertino://mail/guide`. The one place this string is built. */
|
|
420
|
+
const surfaceUri = (surface, leaf) => `${RESOURCE_SCHEME}://${surface}/${leaf}`;
|
|
421
|
+
const jsonContents = (uri, data) => ({ contents: [{
|
|
422
|
+
uri,
|
|
423
|
+
mimeType: "application/json",
|
|
424
|
+
text: JSON.stringify(data, null, 2)
|
|
425
|
+
}] });
|
|
426
|
+
/**
|
|
427
|
+
* Read a resource without ever throwing.
|
|
428
|
+
*
|
|
429
|
+
* A tool that fails returns `isError` and keeps its text; a resource read that
|
|
430
|
+
* throws becomes a JSON-RPC error and keeps nothing. That asymmetry is worst
|
|
431
|
+
* precisely on `diagnostics`, the resource whose whole job is to explain a
|
|
432
|
+
* broken machine: letting a TCC denial replace the report with "resource read
|
|
433
|
+
* failed" would delete the answer at the only moment anyone wants it.
|
|
434
|
+
*
|
|
435
|
+
* So a failed read is *data*, shaped like the `degraded` results the tools
|
|
436
|
+
* already return, and the caller can tell an unreadable store from an empty one.
|
|
437
|
+
*/
|
|
438
|
+
const guardedRead = async (surface, uri, read) => {
|
|
439
|
+
try {
|
|
440
|
+
return jsonContents(uri, await read());
|
|
441
|
+
} catch (err) {
|
|
442
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
443
|
+
const details = err?.details;
|
|
444
|
+
return jsonContents(uri, {
|
|
445
|
+
degraded: true,
|
|
446
|
+
error: message,
|
|
447
|
+
...err instanceof Error ? { kind: err.name } : {},
|
|
448
|
+
...details ? { details } : {},
|
|
449
|
+
hint: `Read ${surfaceUri(surface, "diagnostics")} for what this server can currently reach.`
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
/** Register a surface's guide, diagnostics and (where it has one) inventory. */
|
|
454
|
+
const registerSurfaceResources = (server, opts) => {
|
|
455
|
+
const { surface, displayName, guide, diagnostics, inventory } = opts;
|
|
456
|
+
const guideUri = surfaceUri(surface, "guide");
|
|
457
|
+
server.registerResource(`${surface}-guide`, guideUri, {
|
|
458
|
+
title: `${displayName}: how to drive this server`,
|
|
459
|
+
description: `How to use the ${displayName} tools well: what each ref means, which tool to reach for under which constraint, what a degraded result does and does not say, and what the write gate is currently hiding. Static text — readable even with every permission denied.`,
|
|
460
|
+
mimeType: "text/markdown"
|
|
461
|
+
}, (uri) => ({ contents: [{
|
|
462
|
+
uri: uri.href,
|
|
463
|
+
mimeType: "text/markdown",
|
|
464
|
+
text: guide
|
|
465
|
+
}] }));
|
|
466
|
+
const diagnosticsUri = surfaceUri(surface, "diagnostics");
|
|
467
|
+
server.registerResource(`${surface}-diagnostics`, diagnosticsUri, {
|
|
468
|
+
title: `${displayName}: capabilities and permissions`,
|
|
469
|
+
description: `What this ${displayName} server can currently do and why — the same report as the diagnostics tool, addressable without spending a tool call. Read it before trusting an empty result.`,
|
|
470
|
+
mimeType: "application/json"
|
|
471
|
+
}, () => guardedRead(surface, diagnosticsUri, diagnostics));
|
|
472
|
+
if (!inventory) return;
|
|
473
|
+
const inventoryUri = surfaceUri(surface, "inventory");
|
|
474
|
+
server.registerResource(`${surface}-inventory`, inventoryUri, {
|
|
475
|
+
title: `${displayName}: ${inventory.describes}`,
|
|
476
|
+
description: `The ${inventory.describes} this server can see, spelled exactly as ${displayName} spells them. These are the names every other tool takes, so reading this first is the difference between a filter that matches and one that silently matches nothing.`,
|
|
477
|
+
mimeType: "application/json"
|
|
478
|
+
}, () => guardedRead(surface, inventoryUri, inventory.read));
|
|
479
|
+
};
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region src/prompts.ts
|
|
482
|
+
/**
|
|
483
|
+
* The workflow prompts.
|
|
484
|
+
*
|
|
485
|
+
* ## What a prompt is for here
|
|
486
|
+
*
|
|
487
|
+
* The same thing every tool description in this repo is for: holding a
|
|
488
|
+
* constraint the model would otherwise re-derive. A tool holds the ones that
|
|
489
|
+
* are about *one call* — that a body search wants a narrowing filter, that a
|
|
490
|
+
* ref is opaque. A prompt holds the ones that are about the *order of calls*,
|
|
491
|
+
* and those have nowhere else to live. "Search before you list", "read the
|
|
492
|
+
* thread before you answer it", "check what exists before you create a
|
|
493
|
+
* duplicate" are not properties of any single tool, so no single tool
|
|
494
|
+
* description can carry them, and the model rebuilds them from scratch every
|
|
495
|
+
* session — usually correctly, sometimes not, always at a cost.
|
|
496
|
+
*
|
|
497
|
+
* ## Every prompt embeds its surface guide
|
|
498
|
+
*
|
|
499
|
+
* A prompt returns messages, and one of them is the `cupertino://<surface>/guide`
|
|
500
|
+
* resource. That is the coupling that makes both primitives worth more than
|
|
501
|
+
* either alone: the guide is the reference, the prompt is the task, and a host
|
|
502
|
+
* that expands the prompt gets both without the model having to know the guide
|
|
503
|
+
* exists.
|
|
504
|
+
*
|
|
505
|
+
* ## Write-gated prompts follow the tools
|
|
506
|
+
*
|
|
507
|
+
* A prompt that ends in a mutation is registered only when writes are on, for
|
|
508
|
+
* the same reason the mutating tools are: with the gate closed it must not
|
|
509
|
+
* merely refuse, it must be *invisible*. A visible `draft_reply` on a
|
|
510
|
+
* read-only server is an offer the server cannot keep.
|
|
511
|
+
*/
|
|
512
|
+
/** MCP prompt arguments are strings on the wire. This is the only shape they take. */
|
|
513
|
+
const promptArg = (description) => z.string().optional().describe(description);
|
|
514
|
+
/** Same, for an argument the prompt is useless without. */
|
|
515
|
+
const requiredPromptArg = (description) => z.string().min(1).describe(description);
|
|
516
|
+
/**
|
|
517
|
+
* Register one workflow prompt.
|
|
518
|
+
*
|
|
519
|
+
* Called once per prompt rather than handed an array, because the argument
|
|
520
|
+
* shape is generic and an array of prompts with differing shapes loses the
|
|
521
|
+
* inference that makes `build`'s parameter typed at all.
|
|
522
|
+
*/
|
|
523
|
+
const registerWorkflowPrompt = (server, ctx, prompt) => {
|
|
524
|
+
const guideUri = surfaceUri(ctx.surface, "guide");
|
|
525
|
+
const result = (instruction) => ({ messages: [{
|
|
526
|
+
role: "user",
|
|
527
|
+
content: {
|
|
528
|
+
type: "resource",
|
|
529
|
+
resource: {
|
|
530
|
+
uri: guideUri,
|
|
531
|
+
mimeType: "text/markdown",
|
|
532
|
+
text: ctx.guide
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}, {
|
|
536
|
+
role: "user",
|
|
537
|
+
content: {
|
|
538
|
+
type: "text",
|
|
539
|
+
text: instruction
|
|
540
|
+
}
|
|
541
|
+
}] });
|
|
542
|
+
server.registerPrompt(prompt.name, {
|
|
543
|
+
title: prompt.title,
|
|
544
|
+
description: prompt.description,
|
|
545
|
+
...prompt.argsSchema ? { argsSchema: prompt.argsSchema } : {}
|
|
546
|
+
}, ((args) => result(prompt.build(args ?? {}))));
|
|
547
|
+
};
|
|
548
|
+
//#endregion
|
|
362
549
|
//#region src/schema.ts
|
|
363
550
|
/**
|
|
364
551
|
* Schema introspection for stores Apple owns and can reshape in any release.
|
|
@@ -511,6 +698,6 @@ const compact = (obj) => Object.fromEntries(Object.entries(obj).filter(([, v]) =
|
|
|
511
698
|
const limitArg = z.number().int().min(1).max(200).optional().describe("Maximum number of results. Defaults to 25.");
|
|
512
699
|
const confirmArg = z.literal(true).describe("Must be true. This action changes data and is not undoable from here.");
|
|
513
700
|
//#endregion
|
|
514
|
-
export { AppBusyError, AppNotRunningError, AppleAutomationError, BaseConfigSchema, CORE_DATA_EPOCH_OFFSET, IndexUnavailableError, OsascriptTimeoutError, PlatformError, PreconditionError, ProtocolError, SchemaDriftError, TccDeniedError, WritesDisabledError, assertStaticScript, columnsOf, compact, confirmArg, createOsascriptRunner, describeStore, detectEpoch, escapeLike, fail, fingerprintSchema, inspectFile, limitArg, mapOsaError, ok, okText, openReadOnly, parseBool, parseConfig, parseIntOpt, parseList, readPackageIdentity, runStdioServer, tableMap, toFailure, toFileUri, trimmed, withBusyRetry, wrap, wrapResult };
|
|
701
|
+
export { AppBusyError, AppNotRunningError, AppleAutomationError, BaseConfigSchema, CORE_DATA_EPOCH_OFFSET, IndexUnavailableError, OsascriptTimeoutError, PlatformError, PreconditionError, ProtocolError, RESOURCE_SCHEME, SchemaDriftError, TccDeniedError, WritesDisabledError, assertStaticScript, columnsOf, compact, confirmArg, createOsascriptRunner, describeStore, detectEpoch, escapeLike, fail, fingerprintSchema, inspectFile, limitArg, mapOsaError, ok, okText, openReadOnly, parseBool, parseConfig, parseIntOpt, parseList, promptArg, readPackageIdentity, registerSurfaceResources, registerWorkflowPrompt, requiredPromptArg, runStdioServer, surfaceUri, tableMap, toFailure, toFileUri, trimmed, withBusyRetry, wrap, wrapResult };
|
|
515
702
|
|
|
516
703
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/build-info.ts","../src/cli.ts","../src/config.ts","../src/errors.ts","../src/fs.ts","../src/osascript.ts","../src/schema.ts","../src/sqlite.ts","../src/tools.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\n\nexport type PackageIdentity = { name: string; version: string };\n\nexport type BuildInfo = PackageIdentity & {\n gitCommit: string;\n gitCommitDate: string;\n};\n\n/**\n * Read a package's own name and version at startup, so they are always accurate\n * rather than baked in at build time.\n *\n * Callers pass their own `new URL(\"../package.json\", import.meta.url)`: resolving\n * it here would find *this* package, not theirs. The git fields stay with the\n * caller too, because `__GIT_COMMIT__` is substituted by whichever bundler build\n * compiles the file that mentions it.\n */\nexport const readPackageIdentity = (\n packageJsonUrl: URL,\n fallback: PackageIdentity,\n): PackageIdentity => {\n try {\n return JSON.parse(readFileSync(packageJsonUrl, \"utf8\")) as PackageIdentity;\n } catch {\n return fallback;\n }\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport type { BuildInfo } from \"./build-info.js\";\nimport type { SurfaceContext } from \"./errors.js\";\nimport type { Logger } from \"./osascript.js\";\n\nexport type StdioServerOptions = {\n build: BuildInfo;\n surface: SurfaceContext;\n /** Prefix on every stderr line, e.g. \"apple-mail-mcp\". */\n logPrefix: string;\n /**\n * Build and return the server, plus a one-line summary of the settings it\n * came up with. Called only after the platform guard passes.\n */\n start: (logger: Logger) => Promise<{ server: McpServer; banner: string }>;\n};\n\n/**\n * Boot a server on stdio.\n *\n * The load-bearing rule: **everything goes to stderr**. stdout is the JSON-RPC\n * channel under stdio, and a stray `console.log` there corrupts the protocol —\n * which surfaces as an unintelligible client-side parse error rather than as\n * anything pointing at the log line that caused it.\n */\nexport const runStdioServer = async (opts: StdioServerOptions): Promise<void> => {\n const { build, surface, logPrefix } = opts;\n const debugEnabled = Boolean(process.env[`${surface.envPrefix}_DEBUG`]);\n const logger: Required<Logger> = {\n debug: (...args: unknown[]) => {\n if (debugEnabled) console.error(`[${logPrefix}]`, ...args);\n },\n warn: (...args: unknown[]) => console.error(`[${logPrefix}]`, ...args),\n error: (...args: unknown[]) => console.error(`[${logPrefix}]`, ...args),\n };\n\n logger.warn(\n `${build.name}@${build.version} (git ${build.gitCommit} ${build.gitCommitDate}, node ${process.version})`,\n );\n\n if (process.platform !== \"darwin\") {\n logger.error(\n `fatal: this server drives the macOS ${surface.appName} app and cannot run on ${process.platform}.`,\n );\n process.exit(1);\n }\n\n const { server, banner } = await opts.start(logger);\n await server.connect(new StdioServerTransport());\n logger.warn(`${logPrefix} connected (${banner})`);\n\n const shutdown = (signal: string): void => {\n logger.warn(`received ${signal}, shutting down`);\n process.exit(0);\n };\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n};\n","import { z } from \"zod\";\n\n/**\n * Environment parsing shared by every server.\n *\n * Configuration is environment-only. The sibling servers that also read a\n * `~/.config/<service>/config.json` do so because they hold a private key or an\n * OAuth token; these servers hold no secret at all — their access is the macOS\n * permission the user granted.\n */\n\nexport const trimmed = (v: string | undefined): string | undefined => {\n const t = v?.trim();\n return t ? t : undefined;\n};\n\nexport const parseBool = (v: string | undefined): boolean | undefined => {\n const t = trimmed(v)?.toLowerCase();\n if (t === undefined) return undefined;\n return t === \"1\" || t === \"true\" || t === \"yes\" || t === \"on\";\n};\n\nexport const parseIntOpt = (v: string | undefined): number | undefined => {\n const t = trimmed(v);\n if (t === undefined) return undefined;\n const n = Number(t);\n return Number.isFinite(n) ? n : undefined;\n};\n\nexport const parseList = (v: string | undefined): string[] | undefined => {\n const t = trimmed(v);\n if (t === undefined) return undefined;\n return t\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n};\n\n/**\n * Settings every Apple-app server has. Extend it rather than repeating them:\n *\n * const ConfigSchema = BaseConfigSchema.extend({ ... }).strict();\n */\nexport const BaseConfigSchema = z.object({\n allowWrites: z.boolean().default(false),\n debug: z.boolean().default(false),\n osascriptPath: z.string().default(\"/usr/bin/osascript\"),\n osascriptTimeoutMs: z.number().int().min(1_000).max(600_000).default(30_000),\n maxResults: z.number().int().min(1).max(1_000).default(200),\n});\n\n/**\n * Parse an env-derived object against a schema.\n *\n * Undefined values are dropped so zod's defaults apply, rather than failing on\n * an explicitly-undefined key.\n */\nexport const parseConfig = <T extends z.ZodType>(\n schema: T,\n raw: Record<string, unknown>,\n): z.infer<T> => {\n const compacted = Object.fromEntries(Object.entries(raw).filter(([, v]) => v !== undefined));\n const parsed = schema.safeParse(compacted);\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`Invalid configuration: ${issues}`);\n }\n return parsed.data as z.infer<T>;\n};\n","/**\n * Error taxonomy shared by every Apple-app server.\n *\n * Every message is written for the person who has to fix it — a TCC denial says\n * which System Settings pane to open, not \"operation failed\".\n *\n * ## Why the surface is a required argument\n *\n * These messages name an app (\"Not authorized to control Mail\") and an\n * environment variable (`APPLE_MAIL_ALLOW_WRITES`). An earlier version made the\n * app name an *optional* parameter defaulting to \"Mail\" — and then never passed\n * it at any call site, while a second mention of Mail stayed hardcoded further\n * down the same string. That is worse than no parameter at all: it looks\n * configurable and is not.\n *\n * So `SurfaceContext` is required wherever it appears in a message. Servers\n * subclass with their own surface bound, which keeps `new MailBusyError()`\n * ergonomic at the call site without letting the context go missing.\n */\n\n/** Identity of the app a server drives, for anything user-facing. */\nexport type SurfaceContext = {\n /** How the app is named to a human, e.g. \"Mail\", \"Notes\". */\n appName: string;\n /** Environment variable prefix for this server, e.g. \"APPLE_MAIL\". */\n envPrefix: string;\n};\n\n/** Base class so `toFailure` can carry structured detail through in one branch. */\nexport class AppleAutomationError extends Error {\n override readonly name: string = \"AppleAutomationError\";\n readonly details: Record<string, unknown> | undefined;\n\n constructor(message: string, details?: Record<string, unknown>) {\n super(message);\n this.details = details;\n }\n}\n\n/** The host process may not send Apple Events to the app (osascript -1743). */\nexport class TccDeniedError extends AppleAutomationError {\n override readonly name: string = \"TccDeniedError\";\n\n constructor(surface: SurfaceContext) {\n super(\n `Not authorized to control ${surface.appName}. Grant it in System Settings > ` +\n `Privacy & Security > Automation > (the app running this server) > ${surface.appName}, ` +\n `then restart the server. If no entry appears, the first attempt was denied before the ` +\n `prompt could be answered — run \\`tccutil reset AppleEvents\\` and try again.`,\n );\n }\n}\n\n/** The app is not running and the operation refuses to launch it. */\nexport class AppNotRunningError extends AppleAutomationError {\n override readonly name: string = \"AppNotRunningError\";\n\n constructor(surface: SurfaceContext) {\n super(\n `${surface.appName} is not running. Read tools do not launch it, because launching it ` +\n `steals focus and can start a sync. Open ${surface.appName} and retry.`,\n );\n }\n}\n\n/** The app was busy and refused the Apple Event (-1712). Retried once before surfacing. */\nexport class AppBusyError extends AppleAutomationError {\n override readonly name: string = \"AppBusyError\";\n\n constructor(surface: SurfaceContext) {\n super(\n `${surface.appName} is busy (probably syncing) and did not answer in time. ` +\n `Retry in a few seconds.`,\n );\n }\n}\n\n/** osascript exceeded its budget and was killed. */\nexport class OsascriptTimeoutError extends AppleAutomationError {\n override readonly name: string = \"OsascriptTimeoutError\";\n\n constructor(timeoutMs: number, surface: SurfaceContext) {\n super(\n `${surface.appName} did not answer within ${timeoutMs}ms. It may be mid-sync, or a ` +\n `permission prompt may be waiting on screen. Raise ` +\n `${surface.envPrefix}_OSASCRIPT_TIMEOUT_MS if this is routine at your data size.`,\n );\n }\n}\n\n/**\n * A write was attempted while writes are disabled. Under the house pattern write\n * tools are not registered at all when `allowWrites` is off, so this is a\n * belt-and-braces guard for the library surface, not a path tools can reach.\n */\nexport class WritesDisabledError extends AppleAutomationError {\n override readonly name: string = \"WritesDisabledError\";\n\n constructor(surface: SurfaceContext) {\n super(\n `Writes are disabled. Set ${surface.envPrefix}_ALLOW_WRITES=1 to enable the mutating tools.`,\n );\n }\n}\n\n/** A read-only index could not be opened, so the file lane is unavailable. */\nexport class IndexUnavailableError extends AppleAutomationError {\n override readonly name: string = \"IndexUnavailableError\";\n}\n\n/** The store's schema is not the one we know how to read. */\nexport class SchemaDriftError extends AppleAutomationError {\n override readonly name: string = \"SchemaDriftError\";\n}\n\n/** The server is not running on macOS, or osascript is missing. */\nexport class PlatformError extends AppleAutomationError {\n override readonly name: string = \"PlatformError\";\n}\n\n/** osascript exited 0 but did not produce the JSON envelope we require. */\nexport class ProtocolError extends AppleAutomationError {\n override readonly name: string = \"ProtocolError\";\n}\n\n/** A local precondition failed before anything was sent to the app. */\nexport class PreconditionError extends AppleAutomationError {\n override readonly name: string = \"PreconditionError\";\n}\n","import { accessSync, constants, statSync } from \"node:fs\";\n\n/**\n * Facts about a TCC-protected file.\n *\n * The subtlety this exists to encode: `statSync` **succeeds** on a\n * TCC-protected file — you get the real size and mtime — and only `open(2)` and\n * `access(2)` are denied. Existence and readability are therefore different\n * questions, and only readability tells you whether Full Disk Access is\n * granted. \"The file is there\" is not evidence.\n */\nexport type FileFacts = {\n exists: boolean;\n readable: boolean;\n size: number | null;\n mtime: string | null;\n};\n\nexport const inspectFile = (path: string): FileFacts => {\n let size: number | null = null;\n let mtime: string | null = null;\n try {\n const st = statSync(path);\n size = st.size;\n mtime = st.mtime.toISOString();\n } catch {\n return { exists: false, readable: false, size: null, mtime: null };\n }\n let readable = false;\n try {\n accessSync(path, constants.R_OK);\n readable = true;\n } catch {\n readable = false;\n }\n return { exists: true, readable, size, mtime };\n};\n\nexport type StoreFacts = FileFacts & {\n /** Whether a `-wal` sits beside it, i.e. whether `immutable=1` COULD miss recent writes. */\n walPresent: boolean;\n walSizeBytes: number | null;\n};\n\n/**\n * Describe a SQLite store and its write-ahead log in one call.\n *\n * The WAL matters because `immutable=1` skips it, so a read can silently miss\n * whatever has not been checkpointed — which is precisely the recent data an\n * agent is usually asked about.\n */\nexport const describeStore = (path: string): StoreFacts => {\n const facts = inspectFile(path);\n const wal = inspectFile(`${path}-wal`);\n return { ...facts, walPresent: wal.exists, walSizeBytes: wal.size };\n};\n","/**\n * The only place in this family of servers that spawns a process.\n *\n * ## Why this is shared rather than copied\n *\n * Two of the guarantees below are security invariants, and an invariant that\n * exists in two copies is one refactor away from existing in one:\n *\n * * `assertStaticScript` is a shell-injection tripwire.\n * * `createQueue` serialises Apple Events, without which -1712 floods.\n *\n * ## Why this is safe\n *\n * `execFile`, never `exec` — there is no shell, so there is no quoting question\n * to get wrong.\n *\n * More importantly: **no caller input is ever interpolated into script text**.\n * The script is a static constant piped to osascript's stdin (`-`), and every\n * variable value arrives as `argv[0]` — a single JSON string that the script\n * parses. Verified against a live Mail: an account name of\n *\n * \"; do shell script \"touch /tmp/pwned\"; //\n *\n * arrives at `run(argv)` as inert data and creates no file. A mailbox named\n * after a shell metacharacter is data, not syntax, and there is no code path\n * where that changes.\n *\n * `assertStaticScript` is the tripwire that keeps it that way: a script string\n * containing `${` means someone reached for a template interpolation, which is\n * exactly the mistake this design exists to prevent.\n */\n\nimport { execFile } from \"node:child_process\";\n\nimport {\n AppBusyError,\n AppNotRunningError,\n OsascriptTimeoutError,\n PlatformError,\n ProtocolError,\n TccDeniedError,\n type SurfaceContext,\n} from \"./errors.js\";\n\nexport type Logger = {\n debug?: (...args: unknown[]) => void;\n warn?: (...args: unknown[]) => void;\n error?: (...args: unknown[]) => void;\n};\n\n/** The envelope every JXA script returns. Application failures come back on exit 0. */\nexport type JxaEnvelope<T> =\n | { ok: true; data: T }\n | { ok: false; error: { code: string; message: string } };\n\nexport type OsascriptRunner = {\n /** Run a static script with one JSON-serialisable parameter object. */\n run: <T>(script: string, params?: unknown) => Promise<T>;\n};\n\n/**\n * The process boundary, as a seam. Tests substitute this so that everything\n * above it — the queue, the static-script tripwire, argv construction and\n * envelope handling — still runs for real; mocking `run` itself would skip\n * exactly the code these guarantees live in.\n */\nexport type ExecImpl = (\n path: string,\n args: string[],\n script: string,\n timeoutMs: number,\n) => Promise<string>;\n\nexport type OsascriptOptions = {\n osascriptPath: string;\n timeoutMs: number;\n /** Named in every user-facing error this module can throw. */\n surface: SurfaceContext;\n logger?: Logger | undefined;\n exec?: ExecImpl | undefined;\n};\n\nconst MAX_BUFFER = 32 * 1024 * 1024;\n\n/**\n * Reject any script that looks like it was built by interpolation. Crude on\n * purpose: the cost of a false positive is renaming a variable, and the cost of\n * a false negative is a shell injection.\n */\nexport const assertStaticScript = (script: string): void => {\n if (script.includes(\"${\")) {\n throw new PlatformError(\n \"Refusing to run a JXA script containing `${`. Scripts must be static constants; \" +\n \"pass every value through the params object, which arrives as argv[0].\",\n );\n }\n};\n\n/** Map osascript's trailing `(-NNNN)` error code onto something actionable. */\nexport const mapOsaError = (stderr: string, timeoutMs: number, surface: SurfaceContext): Error => {\n const code = /\\((-\\d{3,4})\\)\\s*$/.exec(stderr.trim())?.[1];\n switch (code) {\n case \"-1743\":\n return new TccDeniedError(surface);\n case \"-600\":\n case \"-609\":\n return new AppNotRunningError(surface);\n case \"-1712\":\n return new AppBusyError(surface);\n default:\n break;\n }\n const thrown = /execution error:\\s*(?:Error:\\s*)?(.+?)\\s*\\(-?\\d+\\)\\s*$/m.exec(stderr.trim())?.[1];\n return new ProtocolError(\n thrown ?? stderr.trim().slice(0, 500) ?? `osascript failed (${timeoutMs}ms budget)`,\n );\n};\n\n/**\n * Serialise every invocation. Apple Event dispatch is single-threaded per app:\n * concurrent calls do not finish sooner, they just make -1712 (busy) likelier.\n * Batch within one script instead of parallelising across several.\n */\nconst createQueue = () => {\n let tail: Promise<unknown> = Promise.resolve();\n return <T>(job: () => Promise<T>): Promise<T> => {\n const next = tail.then(job, job);\n tail = next.catch(() => undefined);\n return next;\n };\n};\n\nconst defaultExec =\n (surface: SurfaceContext): ExecImpl =>\n (path, args, script, timeoutMs) =>\n new Promise((resolve, reject) => {\n const child = execFile(\n path,\n args,\n {\n timeout: timeoutMs,\n maxBuffer: MAX_BUFFER,\n killSignal: \"SIGKILL\",\n encoding: \"utf8\",\n // Inherit nothing. osascript needs no environment, and a minimal one\n // removes any question of PATH or locale influencing the run.\n env: { PATH: \"/usr/bin:/bin\" },\n },\n (err, stdout, stderr) => {\n if (!err) {\n resolve(stdout);\n return;\n }\n const killed = (err as NodeJS.ErrnoException & { killed?: boolean }).killed;\n if (killed) {\n reject(new OsascriptTimeoutError(timeoutMs, surface));\n return;\n }\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n reject(\n new PlatformError(\n `${path} not found. This server only runs on macOS with ${surface.appName} installed.`,\n ),\n );\n return;\n }\n reject(mapOsaError(stderr || String(err.message), timeoutMs, surface));\n },\n );\n child.stdin?.end(script);\n });\n\nexport const createOsascriptRunner = (opts: OsascriptOptions): OsascriptRunner => {\n const enqueue = createQueue();\n const exec = opts.exec ?? defaultExec(opts.surface);\n\n const run = async <T>(script: string, params?: unknown): Promise<T> => {\n assertStaticScript(script);\n const args = [\"-l\", \"JavaScript\", \"-\", JSON.stringify(params ?? {})];\n const stdout = await enqueue(() => exec(opts.osascriptPath, args, script, opts.timeoutMs));\n\n let envelope: JxaEnvelope<T>;\n try {\n envelope = JSON.parse(stdout) as JxaEnvelope<T>;\n } catch {\n throw new ProtocolError(`osascript returned non-JSON output: ${stdout.slice(0, 500)}`);\n }\n\n if (!envelope.ok) {\n // Application-level failures come back on exit 0 so that a non-zero exit\n // unambiguously means infrastructure. Re-inflate them into real errors.\n const { code, message } = envelope.error;\n // MAIL_NOT_RUNNING predates the generic name and is still emitted by the\n // Mail prelude; both mean the same thing.\n if (code === \"APP_NOT_RUNNING\" || code === \"MAIL_NOT_RUNNING\") {\n throw new AppNotRunningError(opts.surface);\n }\n if (code === \"NOT_AUTHORIZED\") throw new TccDeniedError(opts.surface);\n throw new ProtocolError(message, { code });\n }\n\n opts.logger?.debug?.(\"osascript ok\");\n return envelope.data;\n };\n\n return { run };\n};\n\n/** Retry a busy failure once. Apps return -1712 while mid-sync and succeed moments later. */\nexport const withBusyRetry = async <T>(fn: () => Promise<T>, delayMs = 1500): Promise<T> => {\n try {\n return await fn();\n } catch (err) {\n if (!(err instanceof AppBusyError)) throw err;\n await new Promise((r) => setTimeout(r, delayMs));\n return fn();\n }\n};\n","import { createHash } from \"node:crypto\";\nimport type { DatabaseSync } from \"node:sqlite\";\n\n/**\n * Schema introspection for stores Apple owns and can reshape in any release.\n *\n * Nothing here assumes a column exists. The failure mode being avoided is a\n * `SELECT *` that starts throwing after a system update and takes the whole\n * server down with it.\n */\n\n/** 2001-01-01T00:00:00Z in Unix seconds — the Core Data epoch. */\nexport const CORE_DATA_EPOCH_OFFSET = 978_307_200;\n\nexport const columnsOf = (db: DatabaseSync, table: string): string[] => {\n try {\n return (db.prepare(`PRAGMA table_info(\"${table}\")`).all() as { name: string }[]).map(\n (c) => c.name,\n );\n } catch {\n return [];\n }\n};\n\n/** Every table and its columns, for capability checks that name what is missing. */\nexport const tableMap = (db: DatabaseSync): Record<string, string[]> => {\n const names = (\n db.prepare(\"SELECT name FROM sqlite_master WHERE type = 'table'\").all() as { name: string }[]\n ).map((r) => r.name);\n const tables: Record<string, string[]> = {};\n for (const t of names) tables[t] = columnsOf(db, t);\n return tables;\n};\n\n/**\n * A short hash of the whole DDL. Cheap drift detection: when Apple reshapes the\n * schema this changes, which turns \"why did queries start failing after the\n * update\" into a value you can compare against what was captured.\n */\nexport const fingerprintSchema = (db: DatabaseSync): string => {\n const ddl = db\n .prepare(\"SELECT sql FROM sqlite_master WHERE sql IS NOT NULL ORDER BY type, name\")\n .all() as { sql: string }[];\n return createHash(\"sha256\")\n .update(ddl.map((r) => r.sql).join(\"\\n\"))\n .digest(\"hex\")\n .slice(0, 12);\n};\n\n/**\n * Work out whether a timestamp column is Unix seconds or Core Data seconds by\n * seeing which reading lands near today. Two independent prior-art projects\n * disagree about this for Mail, and hardcoding the wrong one puts every date 31\n * years out — a bug that looks like corruption rather than a unit mismatch.\n */\nexport const detectEpoch = (\n maxTimestamp: number | null,\n now: number = Date.now(),\n): { offset: number; reason: string } => {\n if (maxTimestamp === null || !Number.isFinite(maxTimestamp) || maxTimestamp <= 0) {\n return { offset: 0, reason: \"no dated rows; assuming unix seconds\" };\n }\n const nowSec = now / 1000;\n const tenYears = 10 * 365.25 * 24 * 3600;\n const asUnix = Math.abs(nowSec - maxTimestamp);\n const asCoreData = Math.abs(nowSec - (maxTimestamp + CORE_DATA_EPOCH_OFFSET));\n\n if (asUnix < tenYears && asUnix <= asCoreData) {\n return { offset: 0, reason: \"raw value lands within 10 years of now\" };\n }\n if (asCoreData < tenYears) {\n return {\n offset: CORE_DATA_EPOCH_OFFSET,\n reason: \"value + 978307200 lands within 10 years of now\",\n };\n }\n return {\n offset: 0,\n reason: `neither epoch lands near now (max=${maxTimestamp}); assuming unix`,\n };\n};\n","import { DatabaseSync } from \"node:sqlite\";\n\nimport { IndexUnavailableError } from \"./errors.js\";\n\n/**\n * Read-only access to a store some Apple app owns.\n *\n * Two rules, both load-bearing:\n *\n * 1. **Never write.** The app owns the database, holds it open, and reconciles\n * it against a server. `PRAGMA query_only` makes that structural rather than\n * a matter of everyone remembering.\n * 2. **Prefer `mode=ro` over `immutable=1`.** `immutable=1` tells SQLite the\n * file cannot change and to skip the `-wal` entirely — so a read silently\n * misses anything not yet checkpointed. Measured on a live Mail index: the\n * two modes reported 181427 and 181426 messages minutes after reporting the\n * same number, the difference being one newly-arrived mail. It is a race you\n * lose intermittently and without any error, which is the worst kind.\n */\nexport type ReadOnlyMode = \"auto\" | \"ro\" | \"immutable\" | \"off\";\n\nexport type OpenedStore<T = undefined> = {\n db: DatabaseSync;\n /** Which mode actually opened. `immutable` results are WAL-blind — say so. */\n mode: \"ro\" | \"immutable\";\n /** Whatever `validate` returned, so a capability probe is not run twice. */\n validated: T;\n};\n\n/**\n * SQLite URI filenames need percent-encoding, and Mail's path contains a space\n * (\"Envelope Index\"). `?` and `#` would otherwise be read as URI syntax.\n */\nexport const toFileUri = (path: string, query: string): string =>\n `file:${encodeURI(path).replaceAll(\"?\", \"%3f\").replaceAll(\"#\", \"%23\")}?${query}`;\n\n/** Escape LIKE wildcards so a value containing % or _ searches literally. */\nexport const escapeLike = (value: string): string =>\n value.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(\"%\", \"\\\\%\").replaceAll(\"_\", \"\\\\_\");\n\nexport type OpenOptions<T> = {\n /** Named in the error when `mode` is \"off\", so the message says how to re-enable. */\n envVar?: string | undefined;\n /** What the store is, for the failure message, e.g. \"Mail's search index\". */\n label?: string | undefined;\n /** Appended to the failure message — typically how to grant the missing permission. */\n hint?: string | undefined;\n /**\n * Runs on every attempt; throwing rejects that attempt and tries the next\n * mode. Validating *inside* the ladder rather than after it matters: a store\n * that opens but is unusable should fall through, not be returned.\n */\n validate?: ((db: DatabaseSync) => T) | undefined;\n /**\n * Errors that no other open mode could fix, so the ladder aborts instead of\n * masking them behind a generic \"could not open\".\n */\n fatal?: ((err: unknown) => boolean) | undefined;\n /** Called when the WAL-blind fallback is what actually opened. */\n onFallback?: (() => void) | undefined;\n};\n\nexport const openReadOnly = <T = undefined>(\n path: string,\n mode: ReadOnlyMode,\n opts: OpenOptions<T> = {},\n): OpenedStore<T> => {\n if (mode === \"off\") {\n throw new IndexUnavailableError(\n `The index lane is disabled${opts.envVar ? ` (${opts.envVar}=off)` : \"\"}.`,\n );\n }\n\n const attempts: (\"ro\" | \"immutable\")[] =\n mode === \"auto\" ? [\"ro\", \"immutable\"] : [mode === \"ro\" ? \"ro\" : \"immutable\"];\n\n let lastError: unknown = null;\n for (const attempt of attempts) {\n try {\n const uri = toFileUri(path, attempt === \"ro\" ? \"mode=ro\" : \"immutable=1\");\n const db = new DatabaseSync(uri, { readOnly: true, allowExtension: false });\n // Belt and braces: no caller can issue DML even by accident.\n db.exec(\"PRAGMA query_only = 1\");\n const validated = opts.validate?.(db) as T;\n if (attempt === \"immutable\") opts.onFallback?.();\n return { db, mode: attempt, validated };\n } catch (err) {\n if (opts.fatal?.(err)) throw err;\n lastError = err;\n }\n }\n\n const message = lastError instanceof Error ? lastError.message : String(lastError);\n throw new IndexUnavailableError(\n `Could not open ${opts.label ?? path} at ${path}: ${message}.${opts.hint ? ` ${opts.hint}` : \"\"}`,\n );\n};\n","import { z } from \"zod\";\n\nimport { AppleAutomationError } from \"./errors.js\";\n\nexport type ToolResult = {\n content: { type: \"text\"; text: string }[];\n isError?: boolean;\n};\n\nexport const ok = (data: unknown): ToolResult => ({\n content: [{ type: \"text\", text: JSON.stringify(data ?? { ok: true }, null, 2) }],\n});\n\n/**\n * Return text as-is. `ok()` JSON-stringifies, which turns a message body into\n * one escaped \"Hi,\\n\\n…\" line that no one can read.\n */\nexport const okText = (text: string): ToolResult => ({\n content: [{ type: \"text\", text }],\n});\n\nexport const fail = (message: string, extra?: unknown): ToolResult => ({\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: message, ...(extra ? { details: extra } : {}) }, null, 2),\n },\n ],\n isError: true,\n});\n\n/** Render a thrown value as a tool error, preserving whatever detail it carried. */\nexport const toFailure = (err: unknown): ToolResult => {\n if (err instanceof AppleAutomationError) {\n return fail(err.message, { kind: err.name, ...err.details });\n }\n if (err instanceof Error) {\n const details = (err as Error & { details?: unknown }).details;\n return fail(err.message, details);\n }\n return fail(\"Unknown error\", err);\n};\n\n/** Run a tool body, JSON-formatting the result and turning errors into a tool error. */\nexport const wrap = async <T>(fn: () => Promise<T>): Promise<ToolResult> => {\n try {\n return ok(await fn());\n } catch (err) {\n return toFailure(err);\n }\n};\n\n/** Like `wrap`, but the body chooses its own result shape (e.g. a raw body). */\nexport const wrapResult = async (fn: () => Promise<ToolResult>): Promise<ToolResult> => {\n try {\n return await fn();\n } catch (err) {\n return toFailure(err);\n }\n};\n\n/** Drop undefined values so we never send `{mailbox: undefined}` down a lane. */\nexport const compact = <T extends Record<string, unknown>>(obj: T): Partial<T> =>\n Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)) as Partial<T>;\n\n// ─── shared args ─────────────────────────────────────────────────────────────\n\nexport const limitArg = z\n .number()\n .int()\n .min(1)\n .max(200)\n .optional()\n .describe(\"Maximum number of results. Defaults to 25.\");\n\nexport const confirmArg = z\n .literal(true)\n .describe(\"Must be true. This action changes data and is not undoable from here.\");\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/build-info.ts","../src/cli.ts","../src/config.ts","../src/errors.ts","../src/fs.ts","../src/osascript.ts","../src/resources.ts","../src/prompts.ts","../src/schema.ts","../src/sqlite.ts","../src/tools.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\n\nexport type PackageIdentity = { name: string; version: string };\n\nexport type BuildInfo = PackageIdentity & {\n gitCommit: string;\n gitCommitDate: string;\n};\n\n/**\n * Read a package's own name and version at startup, so they are always accurate\n * rather than baked in at build time.\n *\n * Callers pass their own `new URL(\"../package.json\", import.meta.url)`: resolving\n * it here would find *this* package, not theirs. The git fields stay with the\n * caller too, because `__GIT_COMMIT__` is substituted by whichever bundler build\n * compiles the file that mentions it.\n */\nexport const readPackageIdentity = (\n packageJsonUrl: URL,\n fallback: PackageIdentity,\n): PackageIdentity => {\n try {\n return JSON.parse(readFileSync(packageJsonUrl, \"utf8\")) as PackageIdentity;\n } catch {\n return fallback;\n }\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport type { BuildInfo } from \"./build-info.js\";\nimport type { SurfaceContext } from \"./errors.js\";\nimport type { Logger } from \"./osascript.js\";\n\nexport type StdioServerOptions = {\n build: BuildInfo;\n surface: SurfaceContext;\n /** Prefix on every stderr line, e.g. \"apple-mail-mcp\". */\n logPrefix: string;\n /**\n * Build and return the server, plus a one-line summary of the settings it\n * came up with. Called only after the platform guard passes.\n */\n start: (logger: Logger) => Promise<{ server: McpServer; banner: string }>;\n};\n\n/**\n * Boot a server on stdio.\n *\n * The load-bearing rule: **everything goes to stderr**. stdout is the JSON-RPC\n * channel under stdio, and a stray `console.log` there corrupts the protocol —\n * which surfaces as an unintelligible client-side parse error rather than as\n * anything pointing at the log line that caused it.\n */\nexport const runStdioServer = async (opts: StdioServerOptions): Promise<void> => {\n const { build, surface, logPrefix } = opts;\n const debugEnabled = Boolean(process.env[`${surface.envPrefix}_DEBUG`]);\n const logger: Required<Logger> = {\n debug: (...args: unknown[]) => {\n if (debugEnabled) console.error(`[${logPrefix}]`, ...args);\n },\n warn: (...args: unknown[]) => console.error(`[${logPrefix}]`, ...args),\n error: (...args: unknown[]) => console.error(`[${logPrefix}]`, ...args),\n };\n\n logger.warn(\n `${build.name}@${build.version} (git ${build.gitCommit} ${build.gitCommitDate}, node ${process.version})`,\n );\n\n if (process.platform !== \"darwin\") {\n logger.error(\n `fatal: this server drives the macOS ${surface.appName} app and cannot run on ${process.platform}.`,\n );\n process.exit(1);\n }\n\n const { server, banner } = await opts.start(logger);\n await server.connect(new StdioServerTransport());\n logger.warn(`${logPrefix} connected (${banner})`);\n\n const shutdown = (signal: string): void => {\n logger.warn(`received ${signal}, shutting down`);\n process.exit(0);\n };\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n};\n","import { z } from \"zod\";\n\n/**\n * Environment parsing shared by every server.\n *\n * Configuration is environment-only. The sibling servers that also read a\n * `~/.config/<service>/config.json` do so because they hold a private key or an\n * OAuth token; these servers hold no secret at all — their access is the macOS\n * permission the user granted.\n */\n\nexport const trimmed = (v: string | undefined): string | undefined => {\n const t = v?.trim();\n return t ? t : undefined;\n};\n\nexport const parseBool = (v: string | undefined): boolean | undefined => {\n const t = trimmed(v)?.toLowerCase();\n if (t === undefined) return undefined;\n return t === \"1\" || t === \"true\" || t === \"yes\" || t === \"on\";\n};\n\nexport const parseIntOpt = (v: string | undefined): number | undefined => {\n const t = trimmed(v);\n if (t === undefined) return undefined;\n const n = Number(t);\n return Number.isFinite(n) ? n : undefined;\n};\n\nexport const parseList = (v: string | undefined): string[] | undefined => {\n const t = trimmed(v);\n if (t === undefined) return undefined;\n return t\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n};\n\n/**\n * Settings every Apple-app server has. Extend it rather than repeating them:\n *\n * const ConfigSchema = BaseConfigSchema.extend({ ... }).strict();\n */\nexport const BaseConfigSchema = z.object({\n allowWrites: z.boolean().default(false),\n /**\n * Register the workflow prompts and the surface resources.\n *\n * ON by default, unlike `allowWrites`, and the difference is the point: the\n * write gate is a SAFETY invariant — off means a mutation cannot be reached\n * even by name — while this is a COST knob, in the same family as\n * `maxResults`. Conflating the two would muddy the one that matters.\n *\n * What it costs, measured across all seven servers with writes on: the\n * prompt and resource listings come to ~3.4k tokens against ~18.5k for the\n * tool definitions, so roughly 18% on top of a bill that is dominated by\n * tools either way. Resource CONTENTS cost nothing until something reads\n * them. The knob exists for hosts that put every listing in the prompt and\n * for people counting bytes; if context is the problem, running fewer\n * servers is the bigger lever by far.\n *\n * One flag for both, not two, because they ship as a pair: every prompt\n * embeds its surface guide, and a prompt naming a `cupertino://…/guide` that\n * nothing serves would be a dangling reference by configuration.\n */\n exposePrompts: z.boolean().default(true),\n debug: z.boolean().default(false),\n osascriptPath: z.string().default(\"/usr/bin/osascript\"),\n osascriptTimeoutMs: z.number().int().min(1_000).max(600_000).default(30_000),\n maxResults: z.number().int().min(1).max(1_000).default(200),\n});\n\n/**\n * Parse an env-derived object against a schema.\n *\n * Undefined values are dropped so zod's defaults apply, rather than failing on\n * an explicitly-undefined key.\n */\nexport const parseConfig = <T extends z.ZodType>(\n schema: T,\n raw: Record<string, unknown>,\n): z.infer<T> => {\n const compacted = Object.fromEntries(Object.entries(raw).filter(([, v]) => v !== undefined));\n const parsed = schema.safeParse(compacted);\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`Invalid configuration: ${issues}`);\n }\n return parsed.data as z.infer<T>;\n};\n","/**\n * Error taxonomy shared by every Apple-app server.\n *\n * Every message is written for the person who has to fix it — a TCC denial says\n * which System Settings pane to open, not \"operation failed\".\n *\n * ## Why the surface is a required argument\n *\n * These messages name an app (\"Not authorized to control Mail\") and an\n * environment variable (`APPLE_MAIL_ALLOW_WRITES`). An earlier version made the\n * app name an *optional* parameter defaulting to \"Mail\" — and then never passed\n * it at any call site, while a second mention of Mail stayed hardcoded further\n * down the same string. That is worse than no parameter at all: it looks\n * configurable and is not.\n *\n * So `SurfaceContext` is required wherever it appears in a message. Servers\n * subclass with their own surface bound, which keeps `new MailBusyError()`\n * ergonomic at the call site without letting the context go missing.\n */\n\n/** Identity of the app a server drives, for anything user-facing. */\nexport type SurfaceContext = {\n /** How the app is named to a human, e.g. \"Mail\", \"Notes\". */\n appName: string;\n /** Environment variable prefix for this server, e.g. \"APPLE_MAIL\". */\n envPrefix: string;\n};\n\n/** Base class so `toFailure` can carry structured detail through in one branch. */\nexport class AppleAutomationError extends Error {\n override readonly name: string = \"AppleAutomationError\";\n readonly details: Record<string, unknown> | undefined;\n\n constructor(message: string, details?: Record<string, unknown>) {\n super(message);\n this.details = details;\n }\n}\n\n/** The host process may not send Apple Events to the app (osascript -1743). */\nexport class TccDeniedError extends AppleAutomationError {\n override readonly name: string = \"TccDeniedError\";\n\n constructor(surface: SurfaceContext) {\n super(\n `Not authorized to control ${surface.appName}. Grant it in System Settings > ` +\n `Privacy & Security > Automation > (the app running this server) > ${surface.appName}, ` +\n `then restart the server. If no entry appears, the first attempt was denied before the ` +\n `prompt could be answered — run \\`tccutil reset AppleEvents\\` and try again.`,\n );\n }\n}\n\n/** The app is not running and the operation refuses to launch it. */\nexport class AppNotRunningError extends AppleAutomationError {\n override readonly name: string = \"AppNotRunningError\";\n\n constructor(surface: SurfaceContext) {\n super(\n `${surface.appName} is not running. Read tools do not launch it, because launching it ` +\n `steals focus and can start a sync. Open ${surface.appName} and retry.`,\n );\n }\n}\n\n/** The app was busy and refused the Apple Event (-1712). Retried once before surfacing. */\nexport class AppBusyError extends AppleAutomationError {\n override readonly name: string = \"AppBusyError\";\n\n constructor(surface: SurfaceContext) {\n super(\n `${surface.appName} is busy (probably syncing) and did not answer in time. ` +\n `Retry in a few seconds.`,\n );\n }\n}\n\n/** osascript exceeded its budget and was killed. */\nexport class OsascriptTimeoutError extends AppleAutomationError {\n override readonly name: string = \"OsascriptTimeoutError\";\n\n constructor(timeoutMs: number, surface: SurfaceContext) {\n super(\n `${surface.appName} did not answer within ${timeoutMs}ms. It may be mid-sync, or a ` +\n `permission prompt may be waiting on screen. Raise ` +\n `${surface.envPrefix}_OSASCRIPT_TIMEOUT_MS if this is routine at your data size.`,\n );\n }\n}\n\n/**\n * A write was attempted while writes are disabled. Under the house pattern write\n * tools are not registered at all when `allowWrites` is off, so this is a\n * belt-and-braces guard for the library surface, not a path tools can reach.\n */\nexport class WritesDisabledError extends AppleAutomationError {\n override readonly name: string = \"WritesDisabledError\";\n\n constructor(surface: SurfaceContext) {\n super(\n `Writes are disabled. Set ${surface.envPrefix}_ALLOW_WRITES=1 to enable the mutating tools.`,\n );\n }\n}\n\n/** A read-only index could not be opened, so the file lane is unavailable. */\nexport class IndexUnavailableError extends AppleAutomationError {\n override readonly name: string = \"IndexUnavailableError\";\n}\n\n/** The store's schema is not the one we know how to read. */\nexport class SchemaDriftError extends AppleAutomationError {\n override readonly name: string = \"SchemaDriftError\";\n}\n\n/** The server is not running on macOS, or osascript is missing. */\nexport class PlatformError extends AppleAutomationError {\n override readonly name: string = \"PlatformError\";\n}\n\n/** osascript exited 0 but did not produce the JSON envelope we require. */\nexport class ProtocolError extends AppleAutomationError {\n override readonly name: string = \"ProtocolError\";\n}\n\n/** A local precondition failed before anything was sent to the app. */\nexport class PreconditionError extends AppleAutomationError {\n override readonly name: string = \"PreconditionError\";\n}\n","import { accessSync, constants, statSync } from \"node:fs\";\n\n/**\n * Facts about a TCC-protected file.\n *\n * The subtlety this exists to encode: `statSync` **succeeds** on a\n * TCC-protected file — you get the real size and mtime — and only `open(2)` and\n * `access(2)` are denied. Existence and readability are therefore different\n * questions, and only readability tells you whether Full Disk Access is\n * granted. \"The file is there\" is not evidence.\n */\nexport type FileFacts = {\n exists: boolean;\n readable: boolean;\n size: number | null;\n mtime: string | null;\n};\n\nexport const inspectFile = (path: string): FileFacts => {\n let size: number | null = null;\n let mtime: string | null = null;\n try {\n const st = statSync(path);\n size = st.size;\n mtime = st.mtime.toISOString();\n } catch {\n return { exists: false, readable: false, size: null, mtime: null };\n }\n let readable = false;\n try {\n accessSync(path, constants.R_OK);\n readable = true;\n } catch {\n readable = false;\n }\n return { exists: true, readable, size, mtime };\n};\n\nexport type StoreFacts = FileFacts & {\n /** Whether a `-wal` sits beside it, i.e. whether `immutable=1` COULD miss recent writes. */\n walPresent: boolean;\n walSizeBytes: number | null;\n};\n\n/**\n * Describe a SQLite store and its write-ahead log in one call.\n *\n * The WAL matters because `immutable=1` skips it, so a read can silently miss\n * whatever has not been checkpointed — which is precisely the recent data an\n * agent is usually asked about.\n */\nexport const describeStore = (path: string): StoreFacts => {\n const facts = inspectFile(path);\n const wal = inspectFile(`${path}-wal`);\n return { ...facts, walPresent: wal.exists, walSizeBytes: wal.size };\n};\n","/**\n * The only place in this family of servers that spawns a process.\n *\n * ## Why this is shared rather than copied\n *\n * Two of the guarantees below are security invariants, and an invariant that\n * exists in two copies is one refactor away from existing in one:\n *\n * * `assertStaticScript` is a shell-injection tripwire.\n * * `createQueue` serialises Apple Events, without which -1712 floods.\n *\n * ## Why this is safe\n *\n * `execFile`, never `exec` — there is no shell, so there is no quoting question\n * to get wrong.\n *\n * More importantly: **no caller input is ever interpolated into script text**.\n * The script is a static constant piped to osascript's stdin (`-`), and every\n * variable value arrives as `argv[0]` — a single JSON string that the script\n * parses. Verified against a live Mail: an account name of\n *\n * \"; do shell script \"touch /tmp/pwned\"; //\n *\n * arrives at `run(argv)` as inert data and creates no file. A mailbox named\n * after a shell metacharacter is data, not syntax, and there is no code path\n * where that changes.\n *\n * `assertStaticScript` is the tripwire that keeps it that way: a script string\n * containing `${` means someone reached for a template interpolation, which is\n * exactly the mistake this design exists to prevent.\n */\n\nimport { execFile } from \"node:child_process\";\n\nimport {\n AppBusyError,\n AppNotRunningError,\n OsascriptTimeoutError,\n PlatformError,\n ProtocolError,\n TccDeniedError,\n type SurfaceContext,\n} from \"./errors.js\";\n\nexport type Logger = {\n debug?: (...args: unknown[]) => void;\n warn?: (...args: unknown[]) => void;\n error?: (...args: unknown[]) => void;\n};\n\n/** The envelope every JXA script returns. Application failures come back on exit 0. */\nexport type JxaEnvelope<T> =\n | { ok: true; data: T }\n // Extra keys on `error` are carried through onto the thrown error's details.\n // Messages' send ladder uses this to report which targeting strategies were\n // tried and why each failed, which is the only diagnostic that surface has.\n | { ok: false; error: { code: string; message: string; [key: string]: unknown } };\n\nexport type OsascriptRunner = {\n /** Run a static script with one JSON-serialisable parameter object. */\n run: <T>(script: string, params?: unknown) => Promise<T>;\n};\n\n/**\n * The process boundary, as a seam. Tests substitute this so that everything\n * above it — the queue, the static-script tripwire, argv construction and\n * envelope handling — still runs for real; mocking `run` itself would skip\n * exactly the code these guarantees live in.\n */\nexport type ExecImpl = (\n path: string,\n args: string[],\n script: string,\n timeoutMs: number,\n) => Promise<string>;\n\nexport type OsascriptOptions = {\n osascriptPath: string;\n timeoutMs: number;\n /** Named in every user-facing error this module can throw. */\n surface: SurfaceContext;\n logger?: Logger | undefined;\n exec?: ExecImpl | undefined;\n};\n\nconst MAX_BUFFER = 32 * 1024 * 1024;\n\n/**\n * Reject any script that looks like it was built by interpolation. Crude on\n * purpose: the cost of a false positive is renaming a variable, and the cost of\n * a false negative is a shell injection.\n */\nexport const assertStaticScript = (script: string): void => {\n if (script.includes(\"${\")) {\n throw new PlatformError(\n \"Refusing to run a JXA script containing `${`. Scripts must be static constants; \" +\n \"pass every value through the params object, which arrives as argv[0].\",\n );\n }\n};\n\n/** Map osascript's trailing `(-NNNN)` error code onto something actionable. */\nexport const mapOsaError = (stderr: string, timeoutMs: number, surface: SurfaceContext): Error => {\n const code = /\\((-\\d{3,4})\\)\\s*$/.exec(stderr.trim())?.[1];\n switch (code) {\n case \"-1743\":\n return new TccDeniedError(surface);\n case \"-600\":\n case \"-609\":\n return new AppNotRunningError(surface);\n case \"-1712\":\n return new AppBusyError(surface);\n default:\n break;\n }\n const thrown = /execution error:\\s*(?:Error:\\s*)?(.+?)\\s*\\(-?\\d+\\)\\s*$/m.exec(stderr.trim())?.[1];\n return new ProtocolError(\n thrown ?? stderr.trim().slice(0, 500) ?? `osascript failed (${timeoutMs}ms budget)`,\n );\n};\n\n/**\n * Serialise every invocation. Apple Event dispatch is single-threaded per app:\n * concurrent calls do not finish sooner, they just make -1712 (busy) likelier.\n * Batch within one script instead of parallelising across several.\n */\nconst createQueue = () => {\n let tail: Promise<unknown> = Promise.resolve();\n return <T>(job: () => Promise<T>): Promise<T> => {\n const next = tail.then(job, job);\n tail = next.catch(() => undefined);\n return next;\n };\n};\n\nconst defaultExec =\n (surface: SurfaceContext): ExecImpl =>\n (path, args, script, timeoutMs) =>\n new Promise((resolve, reject) => {\n const child = execFile(\n path,\n args,\n {\n timeout: timeoutMs,\n maxBuffer: MAX_BUFFER,\n killSignal: \"SIGKILL\",\n encoding: \"utf8\",\n // Inherit nothing. osascript needs no environment, and a minimal one\n // removes any question of PATH or locale influencing the run.\n env: { PATH: \"/usr/bin:/bin\" },\n },\n (err, stdout, stderr) => {\n if (!err) {\n resolve(stdout);\n return;\n }\n const killed = (err as NodeJS.ErrnoException & { killed?: boolean }).killed;\n if (killed) {\n reject(new OsascriptTimeoutError(timeoutMs, surface));\n return;\n }\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n reject(\n new PlatformError(\n `${path} not found. This server only runs on macOS with ${surface.appName} installed.`,\n ),\n );\n return;\n }\n reject(mapOsaError(stderr || String(err.message), timeoutMs, surface));\n },\n );\n child.stdin?.end(script);\n });\n\nexport const createOsascriptRunner = (opts: OsascriptOptions): OsascriptRunner => {\n const enqueue = createQueue();\n const exec = opts.exec ?? defaultExec(opts.surface);\n\n const run = async <T>(script: string, params?: unknown): Promise<T> => {\n assertStaticScript(script);\n const args = [\"-l\", \"JavaScript\", \"-\", JSON.stringify(params ?? {})];\n const stdout = await enqueue(() => exec(opts.osascriptPath, args, script, opts.timeoutMs));\n\n let envelope: JxaEnvelope<T>;\n try {\n envelope = JSON.parse(stdout) as JxaEnvelope<T>;\n } catch {\n throw new ProtocolError(`osascript returned non-JSON output: ${stdout.slice(0, 500)}`);\n }\n\n if (!envelope.ok) {\n // Application-level failures come back on exit 0 so that a non-zero exit\n // unambiguously means infrastructure. Re-inflate them into real errors.\n const { code, message, ...rest } = envelope.error;\n // MAIL_NOT_RUNNING predates the generic name and is still emitted by the\n // Mail prelude; both mean the same thing.\n if (code === \"APP_NOT_RUNNING\" || code === \"MAIL_NOT_RUNNING\") {\n throw new AppNotRunningError(opts.surface);\n }\n if (code === \"NOT_AUTHORIZED\") throw new TccDeniedError(opts.surface);\n throw new ProtocolError(message, { code, ...rest });\n }\n\n opts.logger?.debug?.(\"osascript ok\");\n return envelope.data;\n };\n\n return { run };\n};\n\n/** Retry a busy failure once. Apps return -1712 while mid-sync and succeed moments later. */\nexport const withBusyRetry = async <T>(fn: () => Promise<T>, delayMs = 1500): Promise<T> => {\n try {\n return await fn();\n } catch (err) {\n if (!(err instanceof AppBusyError)) throw err;\n await new Promise((r) => setTimeout(r, delayMs));\n return fn();\n }\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\n/**\n * The surface resources.\n *\n * ## Why a resource when a tool already returns this\n *\n * `apple_mail_diagnostics` and `apple_mail_list_accounts` answer these same\n * questions, and they stay. What they cannot do is be *addressed*. A tool result\n * exists only after the model decided to spend a call on it, which means the\n * account list is re-derived every session and the diagnostics report is read\n * after the failure rather than before it. A resource is a URI: a host can\n * attach it, cache it, or let a user paste it in, and none of that costs a tool\n * call or depends on the model guessing that it should look.\n *\n * ## The scheme is `cupertino://`, not `apple://`\n *\n * The tools are named `apple_mail_*` because they say what they drive. A URI\n * scheme is a different kind of claim — it is a namespace, and taking Apple's\n * would read as affiliation this project spends a README line disclaiming. So\n * the authority is the surface id and the scheme is the project's own name, the\n * one already in the bundle identifier.\n *\n * ## Three per surface, and only one of them can fail\n *\n * - `guide` is static text. It needs no permission, touches no store and spawns\n * no process, so it is readable when every other lane is denied — which is\n * exactly the moment its contents are worth reading.\n * - `diagnostics` is the live capability report.\n * - `inventory` is the set of containers you address by name: accounts, and\n * whatever the surface calls its folders. Surfaces with no such containers\n * (Messages, Safari) register two resources rather than inventing a third.\n */\n\nexport const RESOURCE_SCHEME = \"cupertino\";\n\n/** `cupertino://mail/guide`. The one place this string is built. */\nexport const surfaceUri = (surface: string, leaf: string): string =>\n `${RESOURCE_SCHEME}://${surface}/${leaf}`;\n\nexport type ResourceReader = () => Promise<unknown>;\n\nexport type SurfaceResourceOptions = {\n /** Surface id as it appears in surfaces.json, e.g. \"mail\". */\n surface: string;\n /** Display name, e.g. \"Mail\". Used only in resource titles. */\n displayName: string;\n /** The operating manual. Static markdown — see `guide.ts` in each surface. */\n guide: string;\n /** The live capability report, normally the diagnostics tool's own payload. */\n diagnostics: ResourceReader;\n /** The addressable containers. Omitted by surfaces that have none. */\n inventory?: {\n /** What this surface calls them, e.g. \"accounts and mailboxes\". */\n describes: string;\n read: ResourceReader;\n };\n};\n\nconst jsonContents = (uri: string, data: unknown) => ({\n contents: [\n {\n uri,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n});\n\n/**\n * Read a resource without ever throwing.\n *\n * A tool that fails returns `isError` and keeps its text; a resource read that\n * throws becomes a JSON-RPC error and keeps nothing. That asymmetry is worst\n * precisely on `diagnostics`, the resource whose whole job is to explain a\n * broken machine: letting a TCC denial replace the report with \"resource read\n * failed\" would delete the answer at the only moment anyone wants it.\n *\n * So a failed read is *data*, shaped like the `degraded` results the tools\n * already return, and the caller can tell an unreadable store from an empty one.\n */\nconst guardedRead = async (surface: string, uri: string, read: ResourceReader) => {\n try {\n return jsonContents(uri, await read());\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const details = (err as Error & { details?: unknown })?.details;\n return jsonContents(uri, {\n degraded: true,\n error: message,\n ...(err instanceof Error ? { kind: err.name } : {}),\n ...(details ? { details } : {}),\n hint: `Read ${surfaceUri(surface, \"diagnostics\")} for what this server can currently reach.`,\n });\n }\n};\n\n/** Register a surface's guide, diagnostics and (where it has one) inventory. */\nexport const registerSurfaceResources = (server: McpServer, opts: SurfaceResourceOptions): void => {\n const { surface, displayName, guide, diagnostics, inventory } = opts;\n\n const guideUri = surfaceUri(surface, \"guide\");\n server.registerResource(\n `${surface}-guide`,\n guideUri,\n {\n title: `${displayName}: how to drive this server`,\n description:\n `How to use the ${displayName} tools well: what each ref means, which tool to reach for ` +\n \"under which constraint, what a degraded result does and does not say, and what the \" +\n \"write gate is currently hiding. Static text — readable even with every permission denied.\",\n mimeType: \"text/markdown\",\n },\n (uri) => ({ contents: [{ uri: uri.href, mimeType: \"text/markdown\", text: guide }] }),\n );\n\n const diagnosticsUri = surfaceUri(surface, \"diagnostics\");\n server.registerResource(\n `${surface}-diagnostics`,\n diagnosticsUri,\n {\n title: `${displayName}: capabilities and permissions`,\n description:\n `What this ${displayName} server can currently do and why — the same report as the ` +\n \"diagnostics tool, addressable without spending a tool call. Read it before trusting an \" +\n \"empty result.\",\n mimeType: \"application/json\",\n },\n () => guardedRead(surface, diagnosticsUri, diagnostics),\n );\n\n if (!inventory) return;\n\n const inventoryUri = surfaceUri(surface, \"inventory\");\n server.registerResource(\n `${surface}-inventory`,\n inventoryUri,\n {\n title: `${displayName}: ${inventory.describes}`,\n description:\n `The ${inventory.describes} this server can see, spelled exactly as ${displayName} ` +\n \"spells them. These are the names every other tool takes, so reading this first is the \" +\n \"difference between a filter that matches and one that silently matches nothing.\",\n mimeType: \"application/json\",\n },\n () => guardedRead(surface, inventoryUri, inventory.read),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { GetPromptResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\n\nimport { surfaceUri } from \"./resources.js\";\n\n/**\n * The workflow prompts.\n *\n * ## What a prompt is for here\n *\n * The same thing every tool description in this repo is for: holding a\n * constraint the model would otherwise re-derive. A tool holds the ones that\n * are about *one call* — that a body search wants a narrowing filter, that a\n * ref is opaque. A prompt holds the ones that are about the *order of calls*,\n * and those have nowhere else to live. \"Search before you list\", \"read the\n * thread before you answer it\", \"check what exists before you create a\n * duplicate\" are not properties of any single tool, so no single tool\n * description can carry them, and the model rebuilds them from scratch every\n * session — usually correctly, sometimes not, always at a cost.\n *\n * ## Every prompt embeds its surface guide\n *\n * A prompt returns messages, and one of them is the `cupertino://<surface>/guide`\n * resource. That is the coupling that makes both primitives worth more than\n * either alone: the guide is the reference, the prompt is the task, and a host\n * that expands the prompt gets both without the model having to know the guide\n * exists.\n *\n * ## Write-gated prompts follow the tools\n *\n * A prompt that ends in a mutation is registered only when writes are on, for\n * the same reason the mutating tools are: with the gate closed it must not\n * merely refuse, it must be *invisible*. A visible `draft_reply` on a\n * read-only server is an offer the server cannot keep.\n */\n\n/** MCP prompt arguments are strings on the wire. This is the only shape they take. */\nexport const promptArg = (description: string): z.ZodOptional<z.ZodString> =>\n z.string().optional().describe(description);\n\n/** Same, for an argument the prompt is useless without. */\nexport const requiredPromptArg = (description: string): z.ZodString =>\n z.string().min(1).describe(description);\n\nexport type PromptContext = {\n /** Surface id, e.g. \"mail\". */\n surface: string;\n /** The static guide, embedded ahead of every prompt's instruction. */\n guide: string;\n};\n\nexport type WorkflowPrompt<Args extends z.ZodRawShape> = {\n /** Namespaced like the tools, e.g. \"apple_mail_triage\". */\n name: string;\n title: string;\n /** What this does and when to reach for it. Shown in the host's prompt list. */\n description: string;\n argsSchema?: Args;\n /**\n * The instruction. Receives validated arguments; returns the text that does\n * the actual work of ordering the calls.\n */\n build: (args: { [K in keyof Args]: z.infer<Args[K]> }) => string;\n};\n\n/**\n * Register one workflow prompt.\n *\n * Called once per prompt rather than handed an array, because the argument\n * shape is generic and an array of prompts with differing shapes loses the\n * inference that makes `build`'s parameter typed at all.\n */\nexport const registerWorkflowPrompt = <Args extends z.ZodRawShape>(\n server: McpServer,\n ctx: PromptContext,\n prompt: WorkflowPrompt<Args>,\n): void => {\n const guideUri = surfaceUri(ctx.surface, \"guide\");\n\n const result = (instruction: string): GetPromptResult => ({\n messages: [\n {\n role: \"user\",\n content: {\n type: \"resource\",\n resource: { uri: guideUri, mimeType: \"text/markdown\", text: ctx.guide },\n },\n },\n { role: \"user\", content: { type: \"text\", text: instruction } },\n ],\n });\n\n server.registerPrompt(\n prompt.name,\n {\n title: prompt.title,\n description: prompt.description,\n ...(prompt.argsSchema ? { argsSchema: prompt.argsSchema } : {}),\n },\n // The SDK hands an args object only when a schema was declared. Both\n // callback arities are assignable here; the cast keeps one code path.\n ((args: { [K in keyof Args]: z.infer<Args[K]> }) =>\n result(prompt.build(args ?? ({} as never)))) as never,\n );\n};\n","import { createHash } from \"node:crypto\";\nimport type { DatabaseSync } from \"node:sqlite\";\n\n/**\n * Schema introspection for stores Apple owns and can reshape in any release.\n *\n * Nothing here assumes a column exists. The failure mode being avoided is a\n * `SELECT *` that starts throwing after a system update and takes the whole\n * server down with it.\n */\n\n/** 2001-01-01T00:00:00Z in Unix seconds — the Core Data epoch. */\nexport const CORE_DATA_EPOCH_OFFSET = 978_307_200;\n\nexport const columnsOf = (db: DatabaseSync, table: string): string[] => {\n try {\n return (db.prepare(`PRAGMA table_info(\"${table}\")`).all() as { name: string }[]).map(\n (c) => c.name,\n );\n } catch {\n return [];\n }\n};\n\n/** Every table and its columns, for capability checks that name what is missing. */\nexport const tableMap = (db: DatabaseSync): Record<string, string[]> => {\n const names = (\n db.prepare(\"SELECT name FROM sqlite_master WHERE type = 'table'\").all() as { name: string }[]\n ).map((r) => r.name);\n const tables: Record<string, string[]> = {};\n for (const t of names) tables[t] = columnsOf(db, t);\n return tables;\n};\n\n/**\n * A short hash of the whole DDL. Cheap drift detection: when Apple reshapes the\n * schema this changes, which turns \"why did queries start failing after the\n * update\" into a value you can compare against what was captured.\n */\nexport const fingerprintSchema = (db: DatabaseSync): string => {\n const ddl = db\n .prepare(\"SELECT sql FROM sqlite_master WHERE sql IS NOT NULL ORDER BY type, name\")\n .all() as { sql: string }[];\n return createHash(\"sha256\")\n .update(ddl.map((r) => r.sql).join(\"\\n\"))\n .digest(\"hex\")\n .slice(0, 12);\n};\n\n/**\n * Work out whether a timestamp column is Unix seconds or Core Data seconds by\n * seeing which reading lands near today. Two independent prior-art projects\n * disagree about this for Mail, and hardcoding the wrong one puts every date 31\n * years out — a bug that looks like corruption rather than a unit mismatch.\n */\nexport const detectEpoch = (\n maxTimestamp: number | null,\n now: number = Date.now(),\n): { offset: number; reason: string } => {\n if (maxTimestamp === null || !Number.isFinite(maxTimestamp) || maxTimestamp <= 0) {\n return { offset: 0, reason: \"no dated rows; assuming unix seconds\" };\n }\n const nowSec = now / 1000;\n const tenYears = 10 * 365.25 * 24 * 3600;\n const asUnix = Math.abs(nowSec - maxTimestamp);\n const asCoreData = Math.abs(nowSec - (maxTimestamp + CORE_DATA_EPOCH_OFFSET));\n\n if (asUnix < tenYears && asUnix <= asCoreData) {\n return { offset: 0, reason: \"raw value lands within 10 years of now\" };\n }\n if (asCoreData < tenYears) {\n return {\n offset: CORE_DATA_EPOCH_OFFSET,\n reason: \"value + 978307200 lands within 10 years of now\",\n };\n }\n return {\n offset: 0,\n reason: `neither epoch lands near now (max=${maxTimestamp}); assuming unix`,\n };\n};\n","import { DatabaseSync } from \"node:sqlite\";\n\nimport { IndexUnavailableError } from \"./errors.js\";\n\n/**\n * Read-only access to a store some Apple app owns.\n *\n * Two rules, both load-bearing:\n *\n * 1. **Never write.** The app owns the database, holds it open, and reconciles\n * it against a server. `PRAGMA query_only` makes that structural rather than\n * a matter of everyone remembering.\n * 2. **Prefer `mode=ro` over `immutable=1`.** `immutable=1` tells SQLite the\n * file cannot change and to skip the `-wal` entirely — so a read silently\n * misses anything not yet checkpointed. Measured on a live Mail index: the\n * two modes reported 181427 and 181426 messages minutes after reporting the\n * same number, the difference being one newly-arrived mail. It is a race you\n * lose intermittently and without any error, which is the worst kind.\n */\nexport type ReadOnlyMode = \"auto\" | \"ro\" | \"immutable\" | \"off\";\n\nexport type OpenedStore<T = undefined> = {\n db: DatabaseSync;\n /** Which mode actually opened. `immutable` results are WAL-blind — say so. */\n mode: \"ro\" | \"immutable\";\n /** Whatever `validate` returned, so a capability probe is not run twice. */\n validated: T;\n};\n\n/**\n * SQLite URI filenames need percent-encoding, and Mail's path contains a space\n * (\"Envelope Index\"). `?` and `#` would otherwise be read as URI syntax.\n */\nexport const toFileUri = (path: string, query: string): string =>\n `file:${encodeURI(path).replaceAll(\"?\", \"%3f\").replaceAll(\"#\", \"%23\")}?${query}`;\n\n/** Escape LIKE wildcards so a value containing % or _ searches literally. */\nexport const escapeLike = (value: string): string =>\n value.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(\"%\", \"\\\\%\").replaceAll(\"_\", \"\\\\_\");\n\nexport type OpenOptions<T> = {\n /** Named in the error when `mode` is \"off\", so the message says how to re-enable. */\n envVar?: string | undefined;\n /** What the store is, for the failure message, e.g. \"Mail's search index\". */\n label?: string | undefined;\n /** Appended to the failure message — typically how to grant the missing permission. */\n hint?: string | undefined;\n /**\n * Runs on every attempt; throwing rejects that attempt and tries the next\n * mode. Validating *inside* the ladder rather than after it matters: a store\n * that opens but is unusable should fall through, not be returned.\n */\n validate?: ((db: DatabaseSync) => T) | undefined;\n /**\n * Errors that no other open mode could fix, so the ladder aborts instead of\n * masking them behind a generic \"could not open\".\n */\n fatal?: ((err: unknown) => boolean) | undefined;\n /** Called when the WAL-blind fallback is what actually opened. */\n onFallback?: (() => void) | undefined;\n};\n\nexport const openReadOnly = <T = undefined>(\n path: string,\n mode: ReadOnlyMode,\n opts: OpenOptions<T> = {},\n): OpenedStore<T> => {\n if (mode === \"off\") {\n throw new IndexUnavailableError(\n `The index lane is disabled${opts.envVar ? ` (${opts.envVar}=off)` : \"\"}.`,\n );\n }\n\n const attempts: (\"ro\" | \"immutable\")[] =\n mode === \"auto\" ? [\"ro\", \"immutable\"] : [mode === \"ro\" ? \"ro\" : \"immutable\"];\n\n let lastError: unknown = null;\n for (const attempt of attempts) {\n try {\n const uri = toFileUri(path, attempt === \"ro\" ? \"mode=ro\" : \"immutable=1\");\n const db = new DatabaseSync(uri, { readOnly: true, allowExtension: false });\n // Belt and braces: no caller can issue DML even by accident.\n db.exec(\"PRAGMA query_only = 1\");\n const validated = opts.validate?.(db) as T;\n if (attempt === \"immutable\") opts.onFallback?.();\n return { db, mode: attempt, validated };\n } catch (err) {\n if (opts.fatal?.(err)) throw err;\n lastError = err;\n }\n }\n\n const message = lastError instanceof Error ? lastError.message : String(lastError);\n throw new IndexUnavailableError(\n `Could not open ${opts.label ?? path} at ${path}: ${message}.${opts.hint ? ` ${opts.hint}` : \"\"}`,\n );\n};\n","import { z } from \"zod\";\n\nimport { AppleAutomationError } from \"./errors.js\";\n\nexport type ToolResult = {\n content: { type: \"text\"; text: string }[];\n isError?: boolean;\n};\n\nexport const ok = (data: unknown): ToolResult => ({\n content: [{ type: \"text\", text: JSON.stringify(data ?? { ok: true }, null, 2) }],\n});\n\n/**\n * Return text as-is. `ok()` JSON-stringifies, which turns a message body into\n * one escaped \"Hi,\\n\\n…\" line that no one can read.\n */\nexport const okText = (text: string): ToolResult => ({\n content: [{ type: \"text\", text }],\n});\n\nexport const fail = (message: string, extra?: unknown): ToolResult => ({\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: message, ...(extra ? { details: extra } : {}) }, null, 2),\n },\n ],\n isError: true,\n});\n\n/** Render a thrown value as a tool error, preserving whatever detail it carried. */\nexport const toFailure = (err: unknown): ToolResult => {\n if (err instanceof AppleAutomationError) {\n return fail(err.message, { kind: err.name, ...err.details });\n }\n if (err instanceof Error) {\n const details = (err as Error & { details?: unknown }).details;\n return fail(err.message, details);\n }\n return fail(\"Unknown error\", err);\n};\n\n/** Run a tool body, JSON-formatting the result and turning errors into a tool error. */\nexport const wrap = async <T>(fn: () => Promise<T>): Promise<ToolResult> => {\n try {\n return ok(await fn());\n } catch (err) {\n return toFailure(err);\n }\n};\n\n/** Like `wrap`, but the body chooses its own result shape (e.g. a raw body). */\nexport const wrapResult = async (fn: () => Promise<ToolResult>): Promise<ToolResult> => {\n try {\n return await fn();\n } catch (err) {\n return toFailure(err);\n }\n};\n\n/** Drop undefined values so we never send `{mailbox: undefined}` down a lane. */\nexport const compact = <T extends Record<string, unknown>>(obj: T): Partial<T> =>\n Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)) as Partial<T>;\n\n// ─── shared args ─────────────────────────────────────────────────────────────\n\nexport const limitArg = z\n .number()\n .int()\n .min(1)\n .max(200)\n .optional()\n .describe(\"Maximum number of results. Defaults to 25.\");\n\nexport const confirmArg = z\n .literal(true)\n .describe(\"Must be true. This action changes data and is not undoable from here.\");\n"],"mappings":"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"}
|
package/package.json
CHANGED