@mgcrea/mcp-apple-core 0.0.0-bootstrap → 1.1.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 +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
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/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;;;;;;GAM3B,EAAA,KAAA;;;;;;;cAQW,cAAe,UAAU,EAAE,SAAO,QACrC,GAAC,KACJ,4BACJ,EAAE,MAAM;;;;;;;;;;;;KCjDC;EACV;EACA;EACA;EACA;;cAGW,cAAW,iBAAmB;KAoB/B,aAAa;;EAEvB;EACA;;;;;;;;;cAUW,gBAAa,iBAAmB;;;;;;;;;;;cCvChC;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
|
@@ -338,10 +338,13 @@ const createOsascriptRunner = (opts) => {
|
|
|
338
338
|
throw new ProtocolError(`osascript returned non-JSON output: ${stdout.slice(0, 500)}`);
|
|
339
339
|
}
|
|
340
340
|
if (!envelope.ok) {
|
|
341
|
-
const { code, message } = envelope.error;
|
|
341
|
+
const { code, message, ...rest } = envelope.error;
|
|
342
342
|
if (code === "APP_NOT_RUNNING" || code === "MAIL_NOT_RUNNING") throw new AppNotRunningError(opts.surface);
|
|
343
343
|
if (code === "NOT_AUTHORIZED") throw new TccDeniedError(opts.surface);
|
|
344
|
-
throw new ProtocolError(message, {
|
|
344
|
+
throw new ProtocolError(message, {
|
|
345
|
+
code,
|
|
346
|
+
...rest
|
|
347
|
+
});
|
|
345
348
|
}
|
|
346
349
|
opts.logger?.debug?.("osascript ok");
|
|
347
350
|
return envelope.data;
|
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/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 // 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 { 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