@mgcrea/mcp-apple-calendar 1.3.0 → 1.3.1
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/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as BUILD_INFO, C as CALENDAR_SURFACE, a as loadConfig, r as createServer } from "./server-
|
|
2
|
+
import { A as BUILD_INFO, C as CALENDAR_SURFACE, a as loadConfig, r as createServer } from "./server-DoSfe1y7.js";
|
|
3
3
|
import { runStdioServer } from "@mgcrea/mcp-apple-core";
|
|
4
4
|
//#region src/cli.ts
|
|
5
5
|
const LOG_PREFIX = "apple-calendar-mcp";
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as BUILD_INFO, C as CALENDAR_SURFACE, D as CalendarNotWritableError, E as CalendarNotRunningError, O as EventNotFoundError, S as CALENDAR_BUNDLE_ID, T as CalendarNotFoundError, _ as STORE_FILENAME, a as loadConfig, b as locateStore, c as introspect, d as decodeRef, f as encodeRef, g as GROUP_CONTAINER, h as EXTRAS_FILENAME, i as registerTools, k as InvalidDateError, l as openStore, m as uuidOf, n as SERVER_VERSION, o as AppleCalendarClient, p as seriesRefOf, r as createServer, s as CalendarStore, t as SERVER_NAME, u as REF_VERSION, v as defaultContainerPath, w as CalendarBusyError, x as AppleCalendarError, y as defaultStorePath } from "./server-
|
|
1
|
+
import { A as BUILD_INFO, C as CALENDAR_SURFACE, D as CalendarNotWritableError, E as CalendarNotRunningError, O as EventNotFoundError, S as CALENDAR_BUNDLE_ID, T as CalendarNotFoundError, _ as STORE_FILENAME, a as loadConfig, b as locateStore, c as introspect, d as decodeRef, f as encodeRef, g as GROUP_CONTAINER, h as EXTRAS_FILENAME, i as registerTools, k as InvalidDateError, l as openStore, m as uuidOf, n as SERVER_VERSION, o as AppleCalendarClient, p as seriesRefOf, r as createServer, s as CalendarStore, t as SERVER_NAME, u as REF_VERSION, v as defaultContainerPath, w as CalendarBusyError, x as AppleCalendarError, y as defaultStorePath } from "./server-DoSfe1y7.js";
|
|
2
2
|
export { AppleCalendarClient, AppleCalendarError, BUILD_INFO, CALENDAR_BUNDLE_ID, CALENDAR_SURFACE, CalendarBusyError, CalendarNotFoundError, CalendarNotRunningError, CalendarNotWritableError, CalendarStore, EXTRAS_FILENAME, EventNotFoundError, GROUP_CONTAINER, InvalidDateError, REF_VERSION, SERVER_NAME, SERVER_VERSION, STORE_FILENAME, createServer, decodeRef, defaultContainerPath, defaultStorePath, encodeRef, introspect, loadConfig, locateStore, openStore, registerTools, seriesRefOf, uuidOf };
|
|
@@ -12,8 +12,8 @@ const pkg = readPackageIdentity(new URL("../package.json", import.meta.url), {
|
|
|
12
12
|
const BUILD_INFO = {
|
|
13
13
|
name: pkg.name,
|
|
14
14
|
version: pkg.version,
|
|
15
|
-
gitCommit: "
|
|
16
|
-
gitCommitDate: "2026-08-
|
|
15
|
+
gitCommit: "312147f",
|
|
16
|
+
gitCommitDate: "2026-08-27T16:06:37+02:00"
|
|
17
17
|
};
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region src/client/errors.ts
|
|
@@ -2958,4 +2958,4 @@ const createServer = (opts) => {
|
|
|
2958
2958
|
//#endregion
|
|
2959
2959
|
export { BUILD_INFO as A, CALENDAR_SURFACE as C, CalendarNotWritableError as D, CalendarNotRunningError as E, EventNotFoundError as O, CALENDAR_BUNDLE_ID as S, CalendarNotFoundError as T, STORE_FILENAME as _, loadConfig as a, locateStore as b, introspect as c, decodeRef as d, encodeRef as f, GROUP_CONTAINER as g, EXTRAS_FILENAME as h, registerTools as i, InvalidDateError as k, openStore as l, uuidOf as m, SERVER_VERSION as n, AppleCalendarClient as o, seriesRefOf as p, createServer as r, CalendarStore as s, SERVER_NAME as t, REF_VERSION as u, defaultContainerPath as v, CalendarBusyError as w, AppleCalendarError as x, defaultStorePath as y };
|
|
2960
2960
|
|
|
2961
|
-
//# sourceMappingURL=server-
|
|
2961
|
+
//# sourceMappingURL=server-DoSfe1y7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-ai6cmDj4.js","names":["CLOCK","pad","addDays","pad","#col","#calendarFilter","#itemColumns","#joins","#excluded","#rowsFrom","#logger","#now","#located","#storeTried","#store","#require","#toApple","#calendarUuids","#visible","#summarise","#fromApple","#run","#writeTarget","#resolveWindow","#calendarUuidByName","#shapeWrite","#nameForRefCalendar"],"sources":["../src/build-info.ts","../src/client/errors.ts","../src/client/availability.ts","../src/client/dates.ts","../src/client/jxa/core.ts","../src/client/jxa/write.ts","../src/client/locate.ts","../src/client/recurrence.ts","../src/client/ref.ts","../src/client/store.ts","../src/client/calendar.ts","../src/config.ts","../src/guide.ts","../src/prompts.ts","../src/tools/util.ts","../src/tools/diagnostics.ts","../src/tools/actions.ts","../src/tools/availability.ts","../src/tools/calendars.ts","../src/tools/events.ts","../src/tools/index.ts","../src/server.ts"],"sourcesContent":["// Build-time / runtime identity for the running server. `name`/`version` are\n// read from package.json at startup (always accurate); `gitCommit` /\n// `gitCommitDate` are injected by tsdown's `define` substitution at build time\n// and fall back to \"unknown\" when running from source (e.g. vitest).\n\nimport { readPackageIdentity, type BuildInfo } from \"@mgcrea/mcp-apple-core\";\n\n// oxlint-disable no-underscore-dangle -- bundler-injected build-time constants.\ndeclare const __GIT_COMMIT__: string;\ndeclare const __GIT_COMMIT_DATE__: string;\n\nconst pkg = readPackageIdentity(new URL(\"../package.json\", import.meta.url), {\n name: \"@mgcrea/mcp-apple-calendar\",\n version: \"0.0.0\",\n});\n\nexport type { BuildInfo };\n\nexport const BUILD_INFO: BuildInfo = {\n name: pkg.name,\n version: pkg.version,\n gitCommit: typeof __GIT_COMMIT__ === \"string\" ? __GIT_COMMIT__ : \"unknown\",\n gitCommitDate: typeof __GIT_COMMIT_DATE__ === \"string\" ? __GIT_COMMIT_DATE__ : \"unknown\",\n};\n","/**\n * Calendar's error surface. The taxonomy lives in `@mgcrea/mcp-apple-core`;\n * what belongs here is the identity those messages are written against, and the\n * errors that are genuinely about calendars.\n */\n\nimport { AppleAutomationError, type SurfaceContext } from \"@mgcrea/mcp-apple-core\";\n\n/**\n * Named in every user-facing error and in the env vars they mention.\n *\n * `appName` is the display name, which is NOT the bundle id: Calendar.app is\n * still `com.apple.iCal` underneath. Every other surface has the two agreeing,\n * so the mismatch is written down in both places it matters.\n */\nexport const CALENDAR_SURFACE: SurfaceContext = {\n appName: \"Calendar\",\n envPrefix: \"APPLE_CALENDAR\",\n};\n\n/** Calendar's Apple Events target. Not `com.apple.Calendar`, which does not exist. */\nexport const CALENDAR_BUNDLE_ID = \"com.apple.iCal\";\n\nexport {\n AppleAutomationError as AppleCalendarError,\n AppBusyError as CalendarBusyError,\n AppNotRunningError as CalendarNotRunningError,\n IndexUnavailableError,\n OsascriptTimeoutError,\n PlatformError,\n PreconditionError,\n ProtocolError,\n SchemaDriftError,\n TccDeniedError,\n WritesDisabledError,\n} from \"@mgcrea/mcp-apple-core\";\n\n/**\n * A date argument could not be understood.\n *\n * Carries the accepted grammar rather than just rejecting, because the caller is\n * usually a model that will retry once and needs to know what shape to retry in.\n */\nexport class InvalidDateError extends AppleAutomationError {\n override readonly name = \"InvalidDateError\";\n\n constructor(field: string, raw: string, reason: string) {\n super(\n `Could not read ${field} from ${JSON.stringify(raw)}: ${reason}. ` +\n `Accepted: an ISO-8601 date \"2026-08-20\" (a whole day) or date-time ` +\n `\"2026-08-20T09:00\" (an instant), or a relative offset like \"+2d\", \"+3h\", \"+45m\", \"+1w\", ` +\n `\"today\", \"tomorrow\", \"tomorrow 09:00\", or \"next monday\".`,\n { field, raw },\n );\n }\n}\n\n/** A CalendarRef no longer resolves — deleted, or moved to another calendar. */\nexport class EventNotFoundError extends AppleAutomationError {\n override readonly name = \"EventNotFoundError\";\n\n constructor(ref: string) {\n super(\n `No event for ref \"${ref}\". It was probably deleted or moved since the search ran. ` +\n `Re-run the search to get a current ref.`,\n { ref },\n );\n }\n}\n\n/** A calendar was named that Calendar does not have. */\nexport class CalendarNotFoundError extends AppleAutomationError {\n override readonly name = \"CalendarNotFoundError\";\n\n constructor(name: string, available: readonly string[] = []) {\n super(\n `No calendar named \"${name}\".` +\n (available.length\n ? ` Available: ${available.slice(0, 20).join(\", \")}${available.length > 20 ? \", …\" : \"\"}.`\n : ` Use apple_calendar_list_calendars to see what exists.`),\n { requested: name },\n );\n }\n}\n\n/**\n * A write was aimed at a calendar that cannot accept one.\n *\n * Its own error rather than a generic failure because the cause is almost\n * always structural rather than a mistake: holiday, birthday and subscribed\n * calendars are read-only by nature, and a caller that hits one needs to pick a\n * different target, not retry.\n */\nexport class CalendarNotWritableError extends AppleAutomationError {\n override readonly name = \"CalendarNotWritableError\";\n\n constructor(name: string) {\n super(\n `The calendar \"${name}\" is read-only, so nothing can be written to it. Subscribed ` +\n `calendars — holidays, birthdays, and anything added by URL — are read-only by nature. ` +\n `Use apple_calendar_list_calendars to find one where \"writable\" is true.`,\n { calendar: name },\n );\n }\n}\n","/**\n * Free-time arithmetic: turning a set of events into the times nothing is on.\n *\n * ## Why this is its own module, and why it is pure\n *\n * `recurrence.ts` set the precedent — the piece whose correctness rests on a\n * measurement lives apart from the SQL. This is the mirror image. Nothing here\n * rests on a measurement at all; it is interval arithmetic and calendar-day\n * iteration, and it is separated for the opposite reason: it is the only part\n * of an availability answer that can be tested without a store, and the part\n * where an off-by-one books a meeting on top of another meeting.\n *\n * ## The rule that governs every decision below\n *\n * A gap reported here is an assertion that NOTHING is on the calendar then.\n * `docs/calendar.md` already names why that is dangerous: \"a short list of\n * events is indistinguishable from a free afternoon\". Every other read tool on\n * this surface can afford to return a short list and flag it. This one cannot —\n * shortening the busy set does not shorten the answer, it INVENTS free time.\n * So the caller of this module must hand it a complete busy set or refuse, and\n * everything here assumes that contract has already been checked.\n *\n * ## Local wall clock, deliberately\n *\n * Working hours are a human's, not UTC's: \"09:00 to 18:00\" means those numbers\n * on the office wall, on both sides of a daylight-saving change. So day\n * boundaries are built with local date components — the same choice, for the\n * same reason, as `at()` in `dates.ts` — and a day that loses an hour simply\n * has one fewer hour in it, which is what actually happened.\n */\n\nimport { InvalidDateError } from \"./errors.js\";\n\n/** Half-open `[from, to)`, in milliseconds since the Unix epoch. */\nexport type Interval = { from: number; to: number };\n\n/** A wall-clock time of day. `24:00` is legal and means the following midnight. */\nexport type Clock = { hours: number; minutes: number };\n\nexport const WEEKDAY_KEYS = [\"sun\", \"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\"] as const;\nexport type WeekdayKey = (typeof WEEKDAY_KEYS)[number];\n\n/** Monday to Friday, the default working week. */\nexport const DEFAULT_WEEKDAYS: readonly WeekdayKey[] = [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\"];\n\nconst CLOCK = /^(\\d{1,2}):(\\d{2})$/;\n\nconst pad = (n: number): string => String(n).padStart(2, \"0\");\n\nexport const parseClock = (field: string, raw: string): Clock => {\n const text = String(raw ?? \"\").trim();\n const m = CLOCK.exec(text);\n if (!m) throw new InvalidDateError(field, text, 'expected a time of day like \"09:00\"');\n const hours = Number(m[1]);\n const minutes = Number(m[2]);\n if (hours > 24 || minutes > 59) {\n throw new InvalidDateError(field, text, `${hours}:${m[2]} is not a time of day`);\n }\n // 24:00 names the end of the day and nothing past it; 24:30 names nothing.\n if (hours === 24 && minutes !== 0) {\n throw new InvalidDateError(\n field,\n text,\n '24:00 is the latest time of day, so \"24:30\" is not one',\n );\n }\n return { hours, minutes };\n};\n\nexport const weekdaySet = (keys: readonly WeekdayKey[]): Set<number> =>\n new Set(keys.map((k) => WEEKDAY_KEYS.indexOf(k)).filter((i) => i !== -1));\n\nconst startOfLocalDay = (d: Date): Date =>\n new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);\n\n/** Same wall-clock time, n days later. Calendar arithmetic, so DST-safe. */\nconst addDays = (d: Date, n: number): Date => {\n const out = new Date(d.getTime());\n out.setDate(out.getDate() + n);\n return out;\n};\n\n/**\n * A time of day on a given local date.\n *\n * `new Date(y, m, d, 24, 0)` rolls to the next midnight on purpose, which is\n * what makes `dayEnd: \"24:00\"` mean \"to the end of this day\" rather than an\n * error case the caller has to special-case.\n */\nconst atClock = (day: Date, c: Clock): number =>\n new Date(day.getFullYear(), day.getMonth(), day.getDate(), c.hours, c.minutes, 0, 0).getTime();\n\n/**\n * Overlapping and touching intervals collapsed into the fewest that cover the\n * same time, in order.\n *\n * Touching counts as overlapping (`<=`, not `<`): two meetings that end and\n * begin on the same minute leave no gap, and emitting a zero-length one would\n * put \"you are free from 10:00 to 10:00\" in front of a model.\n */\nexport const mergeIntervals = (input: readonly Interval[]): Interval[] => {\n const sorted = input.filter((i) => i.to > i.from).toSorted((a, b) => a.from - b.from);\n const out: Interval[] = [];\n for (const cur of sorted) {\n const last = out.at(-1);\n if (last && cur.from <= last.to) {\n if (cur.to > last.to) last.to = cur.to;\n continue;\n }\n out.push({ from: cur.from, to: cur.to });\n }\n return out;\n};\n\n/**\n * What is left of `window` once every busy interval is taken out of it.\n *\n * `busy` MUST already be merged and sorted — pass it through `mergeIntervals`\n * first. Taking unsorted input here would silently under-subtract and hand back\n * time that is booked, which is the one error this module exists to prevent, so\n * the precondition is stated rather than defended against: a defensive re-sort\n * would hide the caller's mistake instead of making it impossible.\n */\nexport const subtractBusy = (window: Interval, busy: readonly Interval[]): Interval[] => {\n const out: Interval[] = [];\n let cursor = window.from;\n for (const b of busy) {\n if (b.to <= cursor) continue;\n if (b.from >= window.to) break;\n if (b.from > cursor) out.push({ from: cursor, to: b.from });\n cursor = b.to;\n if (cursor >= window.to) return out;\n }\n if (cursor < window.to) out.push({ from: cursor, to: window.to });\n return out;\n};\n\n/**\n * The working windows inside `[from, to]`, one per admitted weekday.\n *\n * A `dayEnd` at or before `dayStart` yields no window for that day rather than\n * a negative one. Overnight working hours are not expressible, and saying so\n * with an empty result is better than wrapping around midnight and reporting\n * free time on a day the caller never asked about.\n */\nexport const dayWindows = (opts: {\n from: Date;\n to: Date;\n dayStart: Clock;\n dayEnd: Clock;\n weekdays: ReadonlySet<number>;\n}): Interval[] => {\n const out: Interval[] = [];\n const last = opts.to.getTime();\n for (let day = startOfLocalDay(opts.from); day.getTime() <= last; day = addDays(day, 1)) {\n if (!opts.weekdays.has(day.getDay())) continue;\n const open = atClock(day, opts.dayStart);\n const close = atClock(day, opts.dayEnd);\n if (close <= open) continue;\n const from = Math.max(open, opts.from.getTime());\n const to = Math.min(close, last);\n if (to > from) out.push({ from, to });\n }\n return out;\n};\n\n/**\n * Round an instant up to the next granularity boundary of its own hour.\n *\n * Anchored to the top of the hour rather than to the window, so a slot lands on\n * 09:15 and not on 09:07 because that is when the previous meeting happened to\n * end. Callers restrict granularity to a divisor of 60, which is what keeps the\n * per-hour restart invisible.\n */\nexport const alignUp = (ms: number, granularityMinutes: number): number => {\n const d = new Date(ms);\n const topOfHour = new Date(\n d.getFullYear(),\n d.getMonth(),\n d.getDate(),\n d.getHours(),\n 0,\n 0,\n 0,\n ).getTime();\n const step = granularityMinutes * 60_000;\n const past = ms - topOfHour;\n return topOfHour + Math.ceil(past / step) * step;\n};\n\n/** Local midnight at the start of the day an instant falls on. */\nexport const startOfLocalDayMs = (ms: number): number => startOfLocalDay(new Date(ms)).getTime();\n\n/** The following local midnight. Calendar arithmetic, so a DST day is 23h or 25h. */\nexport const nextLocalDayMs = (ms: number): number =>\n addDays(startOfLocalDay(new Date(ms)), 1).getTime();\n\n/** `2026-08-24`, in local components — the day a slot falls on. */\nexport const localDay = (ms: number): string => {\n const d = new Date(ms);\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n};\n","/**\n * Date handling for the Calendar tools.\n *\n * ## Two halves\n *\n * INPUT is the grammar `packages/reminders/src/client/dates.ts` established, and\n * it is deliberately identical: a caller writing `2026-08-20` names a day, one\n * writing `2026-08-20T09:00` names an instant, `+2d` names a duration, and day\n * and week offsets are calendar arithmetic while hour and minute offsets are\n * elapsed time. That split is not cosmetic — getting it backwards drifts every\n * date by an hour twice a year.\n *\n * OUTPUT is Calendar's own problem and has no analogue in Reminders. An event\n * carries a start AND an end, a timezone of its own, and an all-day flag that\n * changes what the stored number even means. Rendering that wrongly is the\n * quietest bug on this surface, so `EventInstant` makes the caller's two cases\n * two different shapes rather than one shape with a boolean to remember.\n *\n * ## The timezone rules, measured rather than assumed\n *\n * `docs/calendar.md` records 8 distinct `start_tz` values across 1,350 rows,\n * with no nulls, of which two are not IANA names and mean OPPOSITE things:\n *\n * `_float` a floating date — an instant deliberately without a zone\n * `GMT+0200` a perfectly definite fixed offset that is merely not IANA\n *\n * Collapsing the second into the first silently discards two hours, so they are\n * classified apart here. Anything matching `GMT±HHMM` is honoured as an offset;\n * only what is left over floats.\n */\n\nimport { InvalidDateError } from \"./errors.js\";\n\nexport type DateKind = \"allDay\" | \"timed\";\n\nexport type ParsedDate = {\n kind: DateKind;\n /** The absolute instant, for comparisons and for handing to JXA. */\n at: Date;\n /** ISO-8601 with an explicit offset, so the value survives leaving this process. */\n iso: string;\n /** Echoed back in tool results so a caller can see how its input was read. */\n raw: string;\n};\n\n/**\n * What a tool reports for an event's start or end.\n *\n * A union rather than one struct with an `allDay` flag: an all-day event names\n * a DAY and has no instant, and giving it an `iso` field would invite callers to\n * read one. The type makes that impossible instead of merely discouraged.\n */\nexport type EventInstant =\n | { allDay: true; day: string; timeZone: null }\n | { allDay: false; iso: string; timeZone: string | null };\n\nconst DAY_NAMES = [\n \"sunday\",\n \"monday\",\n \"tuesday\",\n \"wednesday\",\n \"thursday\",\n \"friday\",\n \"saturday\",\n] as const;\n\nconst ISO_DATE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst ISO_DATETIME =\n /^(\\d{4})-(\\d{2})-(\\d{2})[T ](\\d{2}):(\\d{2})(?::(\\d{2}))?(?:\\.\\d+)?(Z|[+-]\\d{2}:?\\d{2})?$/;\nconst OFFSET =\n /^\\+(\\d+)\\s*(m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days|w|week|weeks)$/;\nconst DAY_WORD = /^(today|tomorrow)(?:\\s+(\\d{1,2}):(\\d{2}))?$/;\nconst NEXT_DAY = /^next\\s+([a-z]+)(?:\\s+(\\d{1,2}):(\\d{2}))?$/;\nconst DURATION = /^(\\d+)\\s*(m|min|mins|minute|minutes|h|hr|hrs|hour|hours)?$/;\n\n/** `GMT+0200`, `UTC-05:00`. A definite zone that is simply not an IANA name. */\nconst FIXED_OFFSET = /^(?:GMT|UTC)([+-])(\\d{2}):?(\\d{2})$/i;\n\nconst pad = (n: number, w = 2): string => String(n).padStart(w, \"0\");\n\n/** `+02:00` / `-05:00` / `Z` for a given instant, in the system zone. */\nconst offsetOf = (d: Date): string => {\n const mins = -d.getTimezoneOffset();\n if (mins === 0) return \"Z\";\n const sign = mins < 0 ? \"-\" : \"+\";\n const abs = Math.abs(mins);\n return `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;\n};\n\n/**\n * Local wall-clock time rendered with its offset.\n *\n * Deliberately not `toISOString()`, which converts to UTC and would report a\n * 09:00 meeting as `07:00Z` — correct as an instant, unreadable in a result\n * whose purpose is confirming what the caller asked for.\n */\nexport const toLocalIso = (d: Date): string =>\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` +\n `T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${offsetOf(d)}`;\n\nconst startOfDay = (d: Date): Date =>\n new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);\n\nconst endOfDay = (d: Date): Date =>\n new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);\n\n/** Calendar-aware day arithmetic: same wall-clock time, n days later. */\nconst addDays = (d: Date, n: number): Date => {\n const out = new Date(d.getTime());\n out.setDate(out.getDate() + n);\n return out;\n};\n\nconst at = (day: Date, hours: number, minutes: number): Date =>\n new Date(day.getFullYear(), day.getMonth(), day.getDate(), hours, minutes, 0, 0);\n\nconst result = (kind: DateKind, when: Date, raw: string): ParsedDate => ({\n kind,\n at: when,\n iso: toLocalIso(when),\n raw,\n});\n\n// ─── input ───────────────────────────────────────────────────────────────────\n\n/**\n * Parse one date argument.\n *\n * @param field Named in the error, so a failure says WHICH argument was bad.\n * @param raw The caller's string.\n * @param now Injected for hermetic tests.\n */\nexport const parseDate = (field: string, raw: string, now: Date = new Date()): ParsedDate => {\n const text = String(raw ?? \"\").trim();\n if (!text) throw new InvalidDateError(field, String(raw), \"it is empty\");\n const lower = text.toLowerCase();\n\n const dt = ISO_DATETIME.exec(text);\n if (dt) {\n const [, y, mo, d, hh, mm, ss, zone] = dt;\n const when = zone\n ? new Date(text.replace(\" \", \"T\"))\n : new Date(\n Number(y),\n Number(mo) - 1,\n Number(d),\n Number(hh),\n Number(mm),\n Number(ss ?? \"0\"),\n 0,\n );\n if (Number.isNaN(when.getTime())) {\n throw new InvalidDateError(field, text, \"it is not a real date\");\n }\n return result(\"timed\", when, text);\n }\n\n const only = ISO_DATE.exec(text);\n if (only) {\n const [, y, mo, d] = only;\n const when = new Date(Number(y), Number(mo) - 1, Number(d), 0, 0, 0, 0);\n if (Number.isNaN(when.getTime())) {\n throw new InvalidDateError(field, text, \"it is not a real date\");\n }\n // Guard against JS's silent rollover: new Date(2026, 1, 30) is 2 March.\n if (when.getMonth() !== Number(mo) - 1 || when.getDate() !== Number(d)) {\n throw new InvalidDateError(field, text, `there is no day ${d} in month ${mo}`);\n }\n return result(\"allDay\", when, text);\n }\n\n const off = OFFSET.exec(lower);\n if (off) {\n const n = Number(off[1]);\n const unit = String(off[2]);\n if (!Number.isFinite(n)) throw new InvalidDateError(field, text, \"the amount is not a number\");\n if (unit.startsWith(\"d\")) return result(\"timed\", addDays(now, n), text);\n if (unit.startsWith(\"w\")) return result(\"timed\", addDays(now, n * 7), text);\n const ms = unit.startsWith(\"h\") ? n * 3_600_000 : n * 60_000;\n return result(\"timed\", new Date(now.getTime() + ms), text);\n }\n\n const word = DAY_WORD.exec(lower);\n if (word) {\n const day = word[1] === \"tomorrow\" ? addDays(now, 1) : now;\n if (word[2] === undefined) return result(\"allDay\", startOfDay(day), text);\n const [hh, mm] = [Number(word[2]), Number(word[3])];\n if (hh > 23 || mm > 59)\n throw new InvalidDateError(field, text, `${hh}:${word[3]} is not a time`);\n return result(\"timed\", at(day, hh, mm), text);\n }\n\n // \"next monday\" on a Monday is +7, never today.\n const next = NEXT_DAY.exec(lower);\n if (next) {\n const idx = DAY_NAMES.findIndex((n) => n === next[1] || n.slice(0, 3) === next[1]);\n if (idx === -1) {\n throw new InvalidDateError(field, text, `\"${next[1]}\" is not a day of the week`);\n }\n const ahead = (idx - now.getDay() + 7) % 7 || 7;\n const day = addDays(now, ahead);\n if (next[2] === undefined) return result(\"allDay\", startOfDay(day), text);\n const [hh, mm] = [Number(next[2]), Number(next[3])];\n if (hh > 23 || mm > 59)\n throw new InvalidDateError(field, text, `${hh}:${next[3]} is not a time`);\n return result(\"timed\", at(day, hh, mm), text);\n }\n\n throw new InvalidDateError(field, text, \"it matches none of the accepted forms\");\n};\n\n/**\n * Parse a bound for a range filter.\n *\n * A bare day means the WHOLE day, so which edge it resolves to depends on which\n * side of the range it is. Resolving both to midnight would make an end bound\n * quietly exclude the day the caller named.\n */\nexport const parseBound = (\n field: string,\n raw: string,\n edge: \"start\" | \"end\",\n now: Date = new Date(),\n): Date => {\n const parsed = parseDate(field, raw, now);\n if (parsed.kind !== \"allDay\") return parsed.at;\n return edge === \"end\" ? endOfDay(parsed.at) : startOfDay(parsed.at);\n};\n\nexport type Range = { from: Date; to: Date; clamped: boolean };\n\n/**\n * Resolve a query window.\n *\n * Unlike a note list, a calendar has no natural \"everything\": it stretches\n * indefinitely in both directions and `MAX(start_date)` on the probed store\n * already reads 2030. So an unbounded default would scan a decade to report\n * next Tuesday, and every window is bounded on both ends.\n */\nexport const parseRange = (\n opts: {\n from?: string | undefined;\n to?: string | undefined;\n defaultRangeDays: number;\n maxRangeDays: number;\n },\n now: Date = new Date(),\n): Range => {\n const from = opts.from ? parseBound(\"from\", opts.from, \"start\", now) : startOfDay(now);\n const to = opts.to\n ? parseBound(\"to\", opts.to, \"end\", now)\n : endOfDay(addDays(from, opts.defaultRangeDays - 1));\n\n if (to.getTime() < from.getTime()) {\n throw new InvalidDateError(\n \"to\",\n String(opts.to ?? \"\"),\n `it resolves to ${toLocalIso(to)}, which is before from (${toLocalIso(from)})`,\n );\n }\n\n const maxMs = opts.maxRangeDays * 86_400_000;\n if (to.getTime() - from.getTime() > maxMs) {\n return { from, to: new Date(from.getTime() + maxMs), clamped: true };\n }\n return { from, to, clamped: false };\n};\n\n/** `90`, `\"90\"`, `\"90m\"`, `\"2h\"` -> minutes. */\nexport const parseDuration = (field: string, raw: string | number): number => {\n const text = String(raw ?? \"\")\n .trim()\n .toLowerCase();\n if (!text) throw new InvalidDateError(field, String(raw), \"it is empty\");\n const m = DURATION.exec(text);\n if (!m)\n throw new InvalidDateError(field, text, 'expected minutes, or a value like \"90m\" or \"2h\"');\n const n = Number(m[1]);\n if (!Number.isFinite(n) || n <= 0) {\n throw new InvalidDateError(field, text, \"the amount must be a positive number\");\n }\n return m[2]?.startsWith(\"h\") ? n * 60 : n;\n};\n\n// ─── output ──────────────────────────────────────────────────────────────────\n\n/** Whether a stored `start_tz` names a zone this process can actually resolve. */\nexport const isIanaZone = (tz: string): boolean => {\n try {\n // Called, not `new`ed — it returns an instance either way, and the throw on\n // an unknown zone is the whole test.\n Intl.DateTimeFormat(\"en-US\", { timeZone: tz });\n return true;\n } catch {\n return false;\n }\n};\n\n/**\n * A stored timezone string, classified.\n *\n * `null` means floating, and floating is a real state rather than missing data:\n * the event names a wall-clock time that is correct wherever you open it.\n */\nexport const resolveZone = (tz: string | null | undefined): string | null => {\n if (!tz) return null;\n const text = String(tz).trim();\n if (!text || text === \"_float\") return null;\n if (isIanaZone(text)) return text;\n const fixed = FIXED_OFFSET.exec(text);\n // Re-expressed as an Etc/GMT name so Intl can use it. Etc/GMT signs are\n // INVERTED by the POSIX convention — Etc/GMT-2 is two hours AHEAD of UTC —\n // which is a trap worth naming rather than a detail.\n if (fixed) {\n const [, sign, hh, mm] = fixed;\n if (mm === \"00\") {\n const flipped = sign === \"+\" ? \"-\" : \"+\";\n const name = `Etc/GMT${flipped}${Number(hh)}`;\n if (isIanaZone(name)) return name;\n }\n }\n return null;\n};\n\nconst partsIn = (d: Date, tz: string): Record<string, string> => {\n const fmt = Intl.DateTimeFormat(\"en-US\", {\n timeZone: tz,\n hourCycle: \"h23\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n timeZoneName: \"longOffset\",\n });\n const out: Record<string, string> = {};\n for (const p of fmt.formatToParts(d)) out[p.type] = p.value;\n return out;\n};\n\n/**\n * Render a stored instant as an event start or end.\n *\n * ## Why all-day is derived in the event's own zone, falling back to local\n *\n * An all-day event names a DAY, and the day you get depends entirely on which\n * frame you read the stored instant in. MEASURED against a live store:\n *\n * stored 2026-08-20T22:00:00Z\n * UTC getters 2026-08-20 <- a day early\n * local getters 2026-08-21 <- what Calendar.app shows\n *\n * Calendar anchors an all-day event at midnight in the event's own zone, which\n * for a floating date is the local one. So the day comes from local components,\n * or from `start_tz` when that names a real zone.\n *\n * THIS WAS ORIGINALLY WRITTEN THE OTHER WAY ROUND, and the mistake is worth\n * keeping on the record. `docs/reminders.md` documents that REMINDERS' store\n * holds UTC midnight while its Apple Events lane holds local midnight; that was\n * generalised to Calendar without measuring, and the unit tests agreed because\n * their fixtures were built on the same wrong assumption. It survived a\n * four-timezone test matrix and was caught only by reading a real calendar,\n * where the rendered day disagreed with the ref in the same result.\n *\n * The anchor is a property of the store, not a general rule. Measure it per\n * surface. `dates.test.ts` now builds its fixtures at LOCAL midnight, which is\n * what the data actually looks like.\n *\n * @param appleSeconds Core Data seconds, straight from the column.\n * @param tz The row's `start_tz` / `end_tz`, unclassified.\n * @param allDay The row's all-day flag. Authoritative; never inferred.\n * @param epochOffset From `StoreCapabilities`, so the 31-year bug has one home.\n */\nexport const renderInstant = (\n appleSeconds: number | null,\n tz: string | null | undefined,\n allDay: boolean,\n epochOffset: number,\n): EventInstant | null => {\n if (appleSeconds === null || appleSeconds === undefined || !Number.isFinite(appleSeconds)) {\n return null;\n }\n const d = new Date((appleSeconds + epochOffset) * 1000);\n if (Number.isNaN(d.getTime())) return null;\n\n if (allDay) {\n const zone = resolveZone(tz);\n if (zone) {\n const p = partsIn(d, zone);\n return { allDay: true, day: `${p.year}-${p.month}-${p.day}`, timeZone: null };\n }\n // Floating: the stored instant is midnight where the event was written, and\n // for a floating date that is the local zone. `timeZone` stays null either\n // way — an all-day event names a day, and reporting a zone would invite a\n // caller to read it as an instant.\n return {\n allDay: true,\n day: `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`,\n timeZone: null,\n };\n }\n\n const zone = resolveZone(tz);\n if (!zone) {\n // Floating: report the wall clock the system would show, and say plainly\n // that no zone backs it by returning timeZone: null.\n return { allDay: false, iso: toLocalIso(d), timeZone: null };\n }\n\n const p = partsIn(d, zone);\n // \"GMT+02:00\" -> \"+02:00\"; bare \"GMT\" is UTC.\n const raw = p.timeZoneName ?? \"\";\n const offset = raw === \"GMT\" ? \"Z\" : raw.replace(/^GMT/, \"\");\n return {\n allDay: false,\n iso: `${p.year}-${p.month}-${p.day}T${p.hour}:${p.minute}:${p.second}${offset}`,\n timeZone: zone,\n };\n};\n","/**\n * JXA script fragments.\n *\n * Every script here is a static constant. None may contain a template\n * interpolation — `assertStaticScript` rejects any script containing a dollar\n * sign followed by a brace, including template literals written INSIDE the JXA\n * source. Use string concatenation in JXA code.\n *\n * Every script follows the same contract:\n * - it reads its parameters from `JSON.parse(argv[0])`\n * - it returns `JSON.stringify({ok: true, data})` on success\n * - it returns `JSON.stringify({ok: false, error: {code, message}})` on an\n * application-level failure, still exiting 0\n * so a non-zero exit always means infrastructure rather than \"no such event\".\n *\n * ## There is no read.ts here, and that is the design\n *\n * Mail, Notes and Reminders all carry an Apple Events read lane. Calendar does\n * not, because `docs/calendar.md` measured one: a single ±90-day range query\n * costs 3.4 s over 1,349 events, and the cost falls per ROUND TRIP rather than\n * per event — about 2 s per property, flat — so no batching rescues it. That is\n * not a slower fallback, it is no fallback. Reads go through the file lane and\n * these scripts exist only to make Calendar change something.\n *\n * `test/jxa.test.ts` asserts that `read.ts` does not exist, so this stays a\n * decision rather than an accident.\n *\n * ## Finding an event costs a bulk fetch, which is why refs carry a calendar\n *\n * Calendar has no `events.byId()`. The two options are `whose({uid})`, measured\n * at 4.5-7.3 s and — worse than slow — UNSTABLE across runs, or one bulk\n * `cal.events.uid()` fetch plus an index in JS at about 1.8 s. The second only\n * stays affordable if it is scoped to ONE calendar, so `ref.ts` carries the\n * calendar uid and every script below narrows before it scans.\n */\n\n/**\n * Shared prelude.\n *\n * THE BUNDLE IDENTIFIER IS `com.apple.iCal`. Calendar.app kept the id it\n * shipped with as iCal, and this is the only surface in the project where the\n * display name and the bundle id disagree — `Application(\"Calendar\")` is\n * correct, and `com.apple.Calendar` does not exist.\n * `runningApplicationsWithBundleIdentifier` matches exactly and does not fold\n * case, so getting this wrong makes every write report \"not running\".\n */\nexport const PRELUDE = `\nObjC.import(\"AppKit\");\n\nfunction isCalendarRunning() {\n var apps = $.NSRunningApplication.runningApplicationsWithBundleIdentifier(\"com.apple.iCal\");\n return apps.count > 0;\n}\n\nfunction ok(data) { return JSON.stringify({ ok: true, data: data }); }\nfunction err(code, message) { return JSON.stringify({ ok: false, error: { code: code, message: String(message) } }); }\n\nfunction iso(d) {\n try { return d ? d.toISOString() : null; } catch (e) { return null; }\n}\n\n/** Read one property defensively: Calendar throws on properties it cannot supply. */\nfunction prop(fn, fallback) {\n try {\n var v = fn();\n return v === undefined ? fallback : v;\n } catch (e) {\n return fallback;\n }\n}\n\n/**\n * Find a calendar BY NAME.\n *\n * MEASURED, macOS 26.6: \\`calendar.uid()\\` throws \\`AppleEvent handler failed\\`\n * (-10000) for EVERY calendar, including ones this process just created. So a\n * calendar cannot be addressed by uid across Apple Events at all, and the\n * store's \\`Calendar.UUID\\` has no counterpart on this side.\n *\n * This is NOT the event id bridge, which docs/calendar.md measured as exact\n * (\\`CalendarItem.UUID\\` matched an event's \\`uid\\` 198/198). That result was\n * assumed to carry over to calendars; it does not. Events are addressable by\n * uid, calendars are addressable by name, and the caller resolves the name\n * before it gets here — including refusing an ambiguous one, which this side\n * has no way to detect.\n */\nfunction findCalendar(C, wanted) {\n if (!wanted) {\n return prop(function () { return C.defaultCalendar(); }, null);\n }\n var cals = prop(function () { return C.calendars(); }, []);\n for (var i = 0; i < cals.length; i++) {\n var name = prop(function () { return String(cals[i].name()); }, null);\n if (name === wanted) return cals[i];\n }\n return null;\n}\n\n/**\n * Find one event inside a calendar, by uid.\n *\n * ONE bulk fetch of every uid in the calendar, then an index in JS.\n *\n * Deliberately NOT a specifier applied to the events collection, which was\n * measured slower AND unstable — 4,564 / 5,290 / 7,303 ms across three runs of\n * the same query — against a steady 3.4 s for a bulk scan. test/jxa.test.ts\n * enforces that by string match, which is why the disallowed form is described\n * here rather than written out.\n */\nfunction findEvent(cal, uid) {\n var uids = prop(function () { return cal.events.uid(); }, null);\n if (!uids) return null;\n for (var i = 0; i < uids.length; i++) {\n if (String(uids[i]) === uid) {\n return prop(function () { return cal.events[i]; }, null);\n }\n }\n return null;\n}\n\n/** Millisecond-tolerant instant comparison, for the excluded-dates path. */\nfunction sameInstant(a, b) {\n if (!a || !b) return false;\n return Math.abs(a.getTime() - b.getTime()) < 1000;\n}\n\n/**\n * Whether a calendar accepts writes.\n *\n * Subscribed calendars — holidays, birthdays, anything added by URL — are\n * read-only, and assigning to one fails deep inside Calendar with a message\n * that does not say so. Asking first turns that into an error naming the cause.\n */\nfunction isWritable(cal) {\n return prop(function () { return cal.writable(); }, true);\n}\n\nfunction readback(ev, cal) {\n return {\n uid: prop(function () { return String(ev.uid()); }, null),\n summary: prop(function () { return String(ev.summary()); }, null),\n startDate: iso(prop(function () { return ev.startDate(); }, null)),\n endDate: iso(prop(function () { return ev.endDate(); }, null)),\n alldayEvent: prop(function () { return ev.alldayEvent(); }, false),\n location: prop(function () { var v = ev.location(); return v === null ? null : String(v); }, null),\n description: prop(function () { var v = ev.description(); return v === null ? null : String(v); }, null),\n url: prop(function () { var v = ev.url(); return v === null ? null : String(v); }, null),\n stampDate: iso(prop(function () { return ev.stampDate(); }, null)),\n // No calendarUid: cal.uid() throws on every calendar (see findCalendar).\n // The caller already knows which calendar it targeted, and re-derives the\n // store uuid from this name.\n calendarName: prop(function () { return String(cal.name()); }, null)\n };\n}\n\n/**\n * Apply the optional fields a create and an update have in common.\n *\n * ## The dates go first, and in an order that is never briefly invalid\n *\n * MEASURED: moving an event later failed with\n *\n * Failed to save event [...], with error\n * [{ NSLocalizedDescription = \"The start date must be before the end date.\" }]\n *\n * because start and end are two assignments, not one. Setting start to 16:00 on\n * an event still ending at 15:30 makes the interval invalid, and EventKit\n * validates on save rather than on assignment. Which order is safe depends on\n * which way the event is moving, so the current end decides: moving later, the\n * end is pushed out first; moving earlier, the start is pulled back first.\n *\n * They also go BEFORE the text fields. Apple Events has no transaction, so a\n * failure part-way through leaves whatever already applied — and the first\n * version of this wrote the new location, then failed on the dates, leaving the\n * event half-updated. Doing the fragile part first means a date failure changes\n * nothing else.\n */\nfunction applyFields(ev, f) {\n var newStart = f.startDate !== undefined && f.startDate !== null ? new Date(f.startDate) : null;\n var newEnd = f.endDate !== undefined && f.endDate !== null ? new Date(f.endDate) : null;\n\n if (newStart !== null && newEnd !== null) {\n var currentEnd = prop(function () { return ev.endDate(); }, null);\n var movingLater = currentEnd === null || newEnd.getTime() >= currentEnd.getTime();\n if (movingLater) {\n ev.endDate = newEnd;\n ev.startDate = newStart;\n } else {\n ev.startDate = newStart;\n ev.endDate = newEnd;\n }\n } else if (newStart !== null) {\n ev.startDate = newStart;\n } else if (newEnd !== null) {\n ev.endDate = newEnd;\n }\n\n if (f.allDay !== undefined && f.allDay !== null) ev.alldayEvent = Boolean(f.allDay);\n if (f.summary !== undefined && f.summary !== null) ev.summary = String(f.summary);\n if (f.location !== undefined) ev.location = f.location === null ? \"\" : String(f.location);\n if (f.description !== undefined) ev.description = f.description === null ? \"\" : String(f.description);\n if (f.url !== undefined) ev.url = f.url === null ? \"\" : String(f.url);\n}\n`;\n\n/**\n * Wrap a script body in the prelude and the liveness gate.\n *\n * Every script in `write.ts` sets `allowLaunch`, and that is not an oversight:\n * a write is a deliberate side effect, so launching Calendar to perform one is\n * expected in a way that launching it for a read never is. There are no read\n * scripts here to keep it false for.\n */\nexport const script = (body: string, opts: { allowLaunch?: boolean } = {}): string => `\n${PRELUDE}\nfunction run(argv) {\n var p = JSON.parse(argv[0] || \"{}\");\n if (!isCalendarRunning() && !${opts.allowLaunch ? \"true\" : \"false\"}) {\n return err(\"APP_NOT_RUNNING\", \"Calendar is not running.\");\n }\n try {\n var C = Application(\"Calendar\");\n ${body}\n } catch (e) {\n var msg = String(e && e.message ? e.message : e);\n if (msg.indexOf(\"-1743\") !== -1) return err(\"NOT_AUTHORIZED\", msg);\n return err(\"SCRIPT_ERROR\", msg);\n }\n}\n`;\n","import { script } from \"./core.js\";\n\n/**\n * Mutating scripts.\n *\n * These are the only place Calendar is asked to change anything, and every one\n * re-reads the affected event afterwards: what a tool returns is what Calendar\n * STORED, never what the caller requested. The two differ more often than you\n * would expect — setting `alldayEvent` reshapes the start and end, and Calendar\n * decides the final values, not us.\n *\n * Dates arrive as ISO-8601 strings carrying an explicit offset (see `dates.ts`)\n * and are turned back into Date objects here. Passing a bare local string would\n * have Calendar resolve it in whatever zone it feels like.\n *\n * ## Real side effects, said plainly\n *\n * A create on a CalDAV or Exchange calendar syncs within seconds and other\n * people see it. There is no draft state and no undo. That is why the tools\n * that call these carry the warning in their descriptions, and why attendees\n * are not a parameter anywhere in this file: adding one emails a human.\n */\n\nexport const CREATE_EVENT = script(\n `\n var cal = findCalendar(C, p.calendar);\n if (!cal) return err(\"CALENDAR_NOT_FOUND\", p.calendar || \"(default)\");\n if (!isWritable(cal)) return err(\"CALENDAR_NOT_WRITABLE\", prop(function () { return String(cal.name()); }, \"?\"));\n\n // Build with the required dates, push, THEN apply the optional fields.\n // An object that is not yet in a container has nowhere to store a property,\n // so assignments made before the push are silently lost.\n var ev = C.Event({\n summary: String(p.summary),\n startDate: new Date(p.startDate),\n endDate: new Date(p.endDate)\n });\n cal.events.push(ev);\n applyFields(ev, p);\n\n return ok(readback(ev, cal));\n`,\n { allowLaunch: true },\n);\n\nexport const UPDATE_EVENT = script(\n `\n var cal = findCalendar(C, p.calendar);\n if (!cal) return err(\"CALENDAR_NOT_FOUND\", p.calendar || \"(default)\");\n if (!isWritable(cal)) return err(\"CALENDAR_NOT_WRITABLE\", prop(function () { return String(cal.name()); }, \"?\"));\n\n var ev = findEvent(cal, String(p.uid));\n if (!ev) return err(\"EVENT_NOT_FOUND\", String(p.uid));\n\n applyFields(ev, p);\n return ok(readback(ev, cal));\n`,\n { allowLaunch: true },\n);\n\n/**\n * Delete whole events, and VERIFY that each one actually went.\n *\n * MEASURED, macOS 26.6: `C.delete(ev)` on a RECURRING event neither throws nor\n * deletes. Event count before 1, after 1, still present — while the same call\n * removes a non-repeating event correctly. Trusting \"it did not throw\" therefore\n * reported `deleted: true` for an event that is still on the calendar, which is\n * the worst thing a delete can say.\n *\n * So the uid list is re-read afterwards and `deleted` is decided by ABSENCE.\n * The sibling script that excluded one occurrence had this check from the start\n * and it is what caught that property being broken; this one did not, and this\n * is what that omission cost.\n *\n * Per-id results rather than a bulk throw: deleting five events where the third\n * has already gone should remove four and say which one was missing.\n */\nexport const DELETE_EVENTS = script(\n `\n var cal = findCalendar(C, p.calendar);\n if (!cal) return err(\"CALENDAR_NOT_FOUND\", p.calendar || \"(default)\");\n if (!isWritable(cal)) return err(\"CALENDAR_NOT_WRITABLE\", prop(function () { return String(cal.name()); }, \"?\"));\n\n var results = [];\n for (var i = 0; i < p.uids.length; i++) {\n var uid = String(p.uids[i]);\n var ev = findEvent(cal, uid);\n if (!ev) {\n results.push({ uid: uid, found: false, deleted: false, repeats: false });\n continue;\n }\n var repeats = false;\n try {\n var r = ev.recurrence();\n repeats = r !== null && r !== undefined && String(r) !== \"\";\n } catch (e) {\n repeats = false;\n }\n var threw = null;\n try { C.delete(ev); } catch (e) { threw = String(e).slice(0, 120); }\n results.push({ uid: uid, found: true, deleted: null, repeats: repeats, error: threw });\n }\n\n // ONE bulk re-read decides the truth for every id at once.\n var after = prop(function () { return cal.events.uid(); }, null);\n for (var j = 0; j < results.length; j++) {\n if (!results[j].found) continue;\n if (after === null) {\n results[j].deleted = null;\n results[j].reason = \"could not re-read the calendar to confirm\";\n continue;\n }\n var gone = true;\n for (var k = 0; k < after.length; k++) {\n if (String(after[k]) === results[j].uid) gone = false;\n }\n results[j].deleted = gone;\n if (!gone) {\n results[j].reason = results[j].repeats\n ? \"Calendar did not delete it and reported no error, which is what it does for a repeating event. Delete it in Calendar.app.\"\n : \"Calendar reported no error but the event is still there.\";\n }\n }\n return ok({ results: results });\n`,\n { allowLaunch: true },\n);\n\n/**\n * REMOVED: excluding one occurrence, which Calendar cannot do.\n *\n * MEASURED, macOS 26.6, against a real repeating event:\n *\n * ev.excludedDates() -> [\"1903-12-31T23:50:39.000Z\"]\n * ev.excludedDates = [aDate] -> TypeError: undefined is not an object\n *\n * The read returns a sentinel rather than the empty list the event actually\n * has, and the assignment throws — while `ev.summary = \"x\"` on the very same\n * specifier works, so this is the property, not the specifier or the lane.\n *\n * A script was written here that assigned the whole array back and then read it\n * again to confirm, precisely because a silent no-op on a delete is the worst\n * lie this surface could tell. The verification worked: it caught this. But a\n * write path that can only ever report failure is not a capability, so the tool\n * no longer offers \"delete one occurrence\" at all — `delete_events` refuses an\n * occurrence ref and says why, which is the same shape as `update_event`.\n *\n * Deleting a single occurrence is possible in Calendar.app itself, so this is a\n * limit of the scripting interface rather than of the data.\n */\n","import { readdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { describeStore, type StoreFacts } from \"@mgcrea/mcp-apple-core\";\n\n/**\n * Find Calendar's store.\n *\n * ## Why this is the easy case\n *\n * Reminders keeps its database under a generated directory name, so resolving\n * it means *listing* a protected directory — which is itself the privileged\n * operation, leaving no path to even stat without the grant. Calendar does not:\n *\n * ~/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb\n *\n * is a constant. `statSync` succeeds on a TCC-protected file (only `access(2)`\n * is denied — see packages/core/src/fs.ts), so this locator can tell \"exists but\n * unreadable\" from \"not there at all\" with no permission whatsoever. Those are\n * different failures with different fixes, and saying so is most of what\n * diagnostics is for.\n *\n * ## Why it still walks the container\n *\n * `docs/calendar.md` recorded per-account stores sitting beside the main one,\n * and the probe picked between them by size. The known filename is *preferred*,\n * so the common case costs one `describeStore` and no listing at all; the walk\n * is a fallback for a machine whose layout differs, and for the day Apple moves\n * the file the way it moved Reminders' out of `~/Library/Reminders`.\n */\n\n/** `group.com.apple.calendar`, under `~/Library/Group Containers`. */\nexport const GROUP_CONTAINER = \"group.com.apple.calendar\";\n\n/** The observed filename. Preferred when present; not required. */\nexport const STORE_FILENAME = \"Calendar.sqlitedb\";\n\n/** Sits beside the store. 32 KB, and its contents are not used by this server. */\nexport const EXTRAS_FILENAME = \"Extras.db\";\n\n/** Depth cap for the fallback walk. The real store sits at depth 0. */\nconst MAX_DEPTH = 3;\n\nconst STORE_SUFFIX = /\\.(sqlitedb|sqlite)$/i;\n\nexport type StoreCandidate = StoreFacts & { path: string };\n\nexport type LocateResult = StoreFacts & {\n containerPath: string;\n /** Null when discovery found nothing at all — see `reason`. */\n storePath: string | null;\n /** Every store-shaped file found, largest first. */\n candidates: StoreCandidate[];\n /**\n * Whether the container could be listed.\n *\n * Unlike Reminders this is NOT the permission signal — the store's path is\n * known, so `readable` answers that directly. It is reported because a\n * listable container with no store in it means Calendar was never set up,\n * which is a different conversation from a denied grant.\n */\n containerListable: boolean;\n /** `Extras.db` beside the store. Recorded so it stops being an unknown. */\n extrasPresent: boolean;\n reason: string | null;\n};\n\nexport const defaultContainerPath = (home: string = homedir()): string =>\n join(home, \"Library\", \"Group Containers\", GROUP_CONTAINER);\n\nexport const defaultStorePath = (home: string = homedir()): string =>\n join(defaultContainerPath(home), STORE_FILENAME);\n\nconst listDir = (dir: string): string[] => {\n try {\n return readdirSync(dir, { withFileTypes: true }).flatMap((e) =>\n e.isDirectory() ? [] : [e.name],\n );\n } catch {\n return [];\n }\n};\n\nconst walk = (dir: string, depth: number, out: string[]): string[] => {\n if (depth > MAX_DEPTH) return out;\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return out;\n }\n for (const e of entries) {\n const p = join(dir, e.name);\n if (e.isDirectory()) walk(p, depth + 1, out);\n else if (STORE_SUFFIX.test(e.name)) out.push(p);\n }\n return out;\n};\n\nconst FDA_HINT =\n \"Grant Full Disk Access to the app running this server (System Settings > Privacy & \" +\n \"Security > Full Disk Access) and restart it. Granting it to Calendar.app does nothing — \" +\n \"the reader needs the permission.\";\n\nexport const locateStore = (\n opts: { storePath?: string | undefined; home?: string } = {},\n): LocateResult => {\n const containerPath = defaultContainerPath(opts.home);\n\n // An explicit path is a bypass, for tests and forensic copies. No discovery.\n if (opts.storePath) {\n const facts = describeStore(opts.storePath);\n return {\n ...facts,\n containerPath,\n storePath: opts.storePath,\n candidates: [{ ...facts, path: opts.storePath }],\n containerListable: true,\n extrasPresent: false,\n reason: facts.readable\n ? null\n : facts.exists\n ? `The store at ${opts.storePath} exists but cannot be read. ${FDA_HINT}`\n : `No file at ${opts.storePath}. APPLE_CALENDAR_STORE points at nothing.`,\n };\n }\n\n // The known path first. This is the whole advantage over Reminders: it needs\n // no directory listing, so it works — and reports the truth — with no grant.\n const known = defaultStorePath(opts.home);\n const knownFacts = describeStore(known);\n\n const listing = listDir(containerPath);\n const containerListable = listing.length > 0;\n const extrasPresent = listing.includes(EXTRAS_FILENAME);\n\n const paths = knownFacts.readable ? [known] : walk(containerPath, 0, []);\n const candidates: StoreCandidate[] = (paths.includes(known) ? paths : [known, ...paths])\n .map((p) => ({ ...describeStore(p), path: p }))\n .filter((c) => c.exists)\n .toSorted((a, b) => (b.size ?? 0) - (a.size ?? 0));\n\n // The known path wins when it is readable, even if some other file is larger:\n // size is a tie-breaker between unknowns, not evidence against a documented\n // filename. Otherwise fall back to the largest readable candidate.\n const chosen = knownFacts.readable\n ? ({ ...knownFacts, path: known } satisfies StoreCandidate)\n : (candidates.find((c) => c.readable) ?? candidates[0] ?? null);\n\n const reason = chosen?.readable\n ? null\n : knownFacts.exists\n ? `Found the Calendar store at ${known} but cannot read it. ${FDA_HINT}`\n : containerListable\n ? `No store file under ${containerPath}. Has Calendar ever been set up on this account?`\n : `Neither ${known} nor its container could be reached. If Calendar is set up on this ` +\n `account this is a permission problem. ${FDA_HINT}`;\n\n const facts: StoreFacts = chosen\n ? {\n exists: chosen.exists,\n readable: chosen.readable,\n size: chosen.size,\n mtime: chosen.mtime,\n walPresent: chosen.walPresent,\n walSizeBytes: chosen.walSizeBytes,\n }\n : knownFacts;\n\n return {\n ...facts,\n containerPath,\n storePath: chosen?.path ?? null,\n candidates,\n containerListable,\n extrasPresent,\n reason,\n };\n};\n","/**\n * Merging the two legs of a range query, and saying honestly what the result\n * does not cover.\n *\n * ## Why this is its own module\n *\n * It is the piece most likely to be rewritten. `store.ts` knows how to ask the\n * database questions; this file encodes what the answers MEAN, and that meaning\n * rests on measurements (`docs/calendar.md`) rather than on documentation Apple\n * publishes. Keeping it separate means a future correction swaps this file\n * rather than picking through SQL.\n *\n * ## Why a merge is needed at all\n *\n * Measured: `OccurrenceCache` holds 489 distinct parents, of which **456 carry\n * no recurrence rule**. So the cache is not \"the repeating events\" — it holds\n * plain one-shot events too, and a naive union of \"items\" and \"occurrences\"\n * double-counts most of an ordinary calendar. Dedupe here is load-bearing, not\n * defensive.\n *\n * ## Why the coverage edge is published rather than hidden\n *\n * The cache reaches about two years either side of today on the probed store,\n * which is a real expansion rather than a month-view cache. It is still an edge.\n * A query running past it gets fewer repeating events than it should, and a\n * short list of events is indistinguishable from a free afternoon — the quietest\n * possible failure on this surface. So the edge travels with the result.\n */\n\nimport { renderInstant } from \"./dates.js\";\nimport type { Coverage, EventRow } from \"./store.js\";\n\nexport type Truncation = {\n reason: string;\n /** Apple-seconds. The part of the requested window the expansion cannot cover. */\n uncoveredFromApple?: number;\n uncoveredToApple?: number;\n affects: string;\n};\n\nexport type ExpansionState = \"expanded\" | \"unavailable\";\n\nexport type MergedRange = {\n rows: EventRow[];\n expansion: ExpansionState;\n /** Present when the expansion could not run at all. */\n expansionReason?: string;\n coverage: Coverage;\n truncated?: Truncation;\n /** Rows dropped for having no usable start. Counted rather than silently lost. */\n dropped: number;\n};\n\n/**\n * Identity for deduping.\n *\n * `(uuid, rendered start)` rather than uuid alone: two occurrences of the same\n * series are the same event at different times, and collapsing them by uuid\n * would return a weekly meeting once — the exact bug the two-leg design exists\n * to avoid.\n *\n * ## Why the RENDERED start and not the raw column\n *\n * MEASURED, against a live calendar: an all-day event created through this\n * server came back TWICE — once from each leg, same uuid, both rendering as\n * 17 September. `CalendarItem.start_date` and `OccurrenceCache.occurrence_date`\n * do not hold the identical number for an all-day event, so a key built on the\n * raw value saw two different events and let both through.\n *\n * Rendering first collapses that: two all-day rows for one uuid on one DAY are\n * the same event, whatever the two columns disagree about underneath. For a\n * timed event the rendered ISO is second-precision, which also absorbs the\n * float wobble a REAL column can introduce.\n */\nconst keyOf = (r: EventRow, epochOffset: number): string => {\n const at = renderInstant(r.startApple, r.startTz, r.allDay, epochOffset);\n const when = at ? (at.allDay ? at.day : at.iso) : String(r.startApple);\n return `${String(r.uuid).toUpperCase()}|${when}`;\n};\n\n/**\n * The identity of the occurrence a detached row REPLACES.\n *\n * When someone drags one instance of a series to a new time, Calendar writes a\n * detached `CalendarItem` carrying `orig_item_id` (the series) and `orig_date`\n * (the slot it came from). The stale cache row for that slot may still exist, so\n * keying on `(uuid, start)` alone will not catch it: the instants differ, which\n * is the entire point of the move.\n */\nconst replacedKeyOf = (r: EventRow): string | null =>\n r.origItemPk !== null && r.origDateApple !== null ? `${r.origItemPk}|${r.origDateApple}` : null;\n\nconst occurrenceSlotOf = (r: EventRow): string => `${r.itemPk}|${r.startApple}`;\n\nexport const mergeRange = (opts: {\n items: EventRow[];\n occurrences: EventRow[];\n coverage: Coverage;\n hasOccurrenceCache: boolean;\n /** Why the expansion is missing, when it is. */\n unavailableReason?: string;\n fromApple: number;\n toApple: number;\n limit: number;\n /** From `StoreCapabilities`, so the dedupe key can render before comparing. */\n epochOffset: number;\n}): MergedRange => {\n const seen = new Set<string>();\n const out: EventRow[] = [];\n let dropped = 0;\n\n // Every slot a detached row has taken over. Built before anything is emitted,\n // because a cache row can be visited before the item that supersedes it.\n const replaced = new Set<string>();\n for (const r of opts.items) {\n const k = replacedKeyOf(r);\n if (k) replaced.add(k);\n }\n\n // Items first: leg 1 WINS on collision. A detached row is the user's edited\n // version, and the cache row for the same slot may predate the edit.\n for (const r of [...opts.items, ...opts.occurrences]) {\n if (r.startApple === null) {\n dropped += 1;\n continue;\n }\n if (r.source === \"occurrence\" && replaced.has(occurrenceSlotOf(r))) continue;\n const k = keyOf(r, opts.epochOffset);\n if (seen.has(k)) continue;\n seen.add(k);\n out.push(r);\n }\n\n out.sort((a, b) => (a.startApple ?? 0) - (b.startApple ?? 0));\n\n const merged: MergedRange = {\n rows: out.slice(0, opts.limit),\n expansion: opts.hasOccurrenceCache ? \"expanded\" : \"unavailable\",\n coverage: opts.coverage,\n dropped,\n };\n\n if (!opts.hasOccurrenceCache) {\n merged.expansionReason =\n opts.unavailableReason ??\n \"this store has no OccurrenceCache table, so occurrences of repeating events are not \" +\n \"expanded; each repeating event appears once, at its series start\";\n return merged;\n }\n\n const cov = opts.coverage;\n if (cov) {\n const past = opts.fromApple < cov.fromApple;\n const future = opts.toApple > cov.toApple;\n if (past || future) {\n merged.truncated = {\n reason:\n \"the requested window extends past the range the store has expanded, so repeating \" +\n \"events outside it are missing from this result\",\n ...(past ? { uncoveredFromApple: opts.fromApple } : {}),\n ...(future ? { uncoveredToApple: opts.toApple } : {}),\n affects: \"repeating events only — single events are correct at any distance\",\n };\n }\n }\n\n return merged;\n};\n","/**\n * CalendarRef — the one identifier any tool accepts or returns.\n *\n * Wire format: c1:<calendarUid>/<occurrence>/<eventUid>\n *\n * calendarUid `Calendar.UUID`. Always a UUID in this store.\n * occurrence \"-\" for a single event or a whole series, otherwise the\n * occurrence start as ISO-8601 basic with offset,\n * e.g. 20260821T090000+0200.\n * eventUid `CalendarItem.UUID`, verbatim, as the greedy tail.\n *\n * The `c1:` prefix follows the same reasoning as Notes' `n1:` and Reminders'\n * `r1:`: if the scheme ever changes, a versioned prefix makes that an additive\n * change instead of a silent reinterpretation of every ref already sitting in a\n * conversation.\n *\n * ## Why the uid is the greedy tail, and why `@` is not a separator\n *\n * `docs/calendar.md` measured the id bridge on an iCloud account, where every\n * uid is a bare UUID. That is a property of the ACCOUNT, not of Calendar: a\n * Google event's uid looks like `abc123def@google.com`, and an Exchange one is\n * a long hex blob. Requiring a UUID here would work perfectly on the machine it\n * was written on and fail completely on anyone else's — so the uid is carried\n * through verbatim and the UUID is only extracted opportunistically.\n *\n * That also rules out `@` as a field separator, which is otherwise the obvious\n * choice for pinning an occurrence to a time.\n *\n * ## Why the calendar uid rides along\n *\n * Calendar's scripting dictionary has no `events.byId()`. Finding an event over\n * Apple Events means either `whose({uid})` — measured at 4.5-7.3 s and, worse,\n * UNSTABLE across runs — or one bulk `cal.events.uid()` fetch and an index in\n * JS, at about 1.8 s. The bulk fetch is only affordable if it is scoped to ONE\n * calendar, so every write narrows by calendar before it scans.\n *\n * That is a concrete thing the file lane hands the write lane: the store knows\n * which calendar an event is in, so Apple Events never has to search for it.\n */\n\nimport { PreconditionError } from \"./errors.js\";\n\nexport const REF_VERSION = \"c1\";\n\n/** Used to FIND a uuid inside an id, never to require one. */\nconst UUID = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;\n\n/** `20260821T090000+0200` / `20260821T070000Z` — ISO-8601 basic. */\nconst BASIC = /^(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})(Z|[+-]\\d{4})$/;\n\nconst SEPARATOR = \"/\";\nconst NO_OCCURRENCE = \"-\";\n\nexport type CalendarRef = {\n /** `Calendar.UUID` — which calendar to narrow to before scanning. */\n calendarUid: string;\n /** `CalendarItem.UUID`, exactly as stored. This is what resolves. */\n eventUid: string;\n /** Null for a single event or a whole series. */\n occurrenceStart: Date | null;\n /** True when this ref names one occurrence rather than the series. */\n isOccurrence: boolean;\n};\n\nconst pad = (n: number, w = 2): string => String(n).padStart(w, \"0\");\n\n/** The inverse of BASIC: local wall clock plus its offset, no punctuation. */\nconst toBasic = (d: Date): string => {\n const mins = -d.getTimezoneOffset();\n const sign = mins < 0 ? \"-\" : \"+\";\n const abs = Math.abs(mins);\n const zone = mins === 0 ? \"Z\" : `${sign}${pad(Math.floor(abs / 60))}${pad(abs % 60)}`;\n return (\n `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}` +\n `T${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}${zone}`\n );\n};\n\nconst fromBasic = (text: string): Date | null => {\n const m = BASIC.exec(text);\n if (!m) return null;\n const [, y, mo, d, hh, mm, ss, zone] = m;\n const iso =\n `${y}-${mo}-${d}T${hh}:${mm}:${ss}` +\n (zone === \"Z\" ? \"Z\" : `${zone!.slice(0, 3)}:${zone!.slice(3)}`);\n const when = new Date(iso);\n return Number.isNaN(when.getTime()) ? null : when;\n};\n\nexport const encodeRef = (\n calendarUid: string,\n eventUid: string,\n occurrenceStart?: Date | null,\n): string =>\n [\n REF_VERSION + \":\" + String(calendarUid ?? \"\"),\n occurrenceStart ? toBasic(occurrenceStart) : NO_OCCURRENCE,\n String(eventUid ?? \"\"),\n ].join(SEPARATOR);\n\nexport const decodeRef = (raw: string): CalendarRef => {\n const text = String(raw ?? \"\");\n const colon = text.indexOf(\":\");\n const version = colon === -1 ? \"\" : text.slice(0, colon);\n\n if (version !== REF_VERSION) {\n // Distinguish a wrong-surface ref from a malformed one. A caller handing an\n // `r1:` ref to a calendar tool has made a different mistake from one that\n // invented a string, and telling them apart saves a retry.\n if (/^[a-z]\\d+$/.test(version)) {\n throw new PreconditionError(\n `That is a \"${version}:\" ref, which belongs to another surface. This server issues ` +\n `\"${REF_VERSION}:\" refs — get one from apple_calendar_list_events or ` +\n `apple_calendar_search_events.`,\n );\n }\n throw new PreconditionError(\n `Malformed calendar ref ${JSON.stringify(raw)}. Refs come from the search and list tools — ` +\n `construct them from those results rather than by hand.`,\n { expected: `${REF_VERSION}:<calendarUid>/<occurrence>/<eventUid>` },\n );\n }\n\n const body = text.slice(colon + 1);\n const first = body.indexOf(SEPARATOR);\n const second = body.indexOf(SEPARATOR, first + 1);\n if (first === -1 || second === -1) {\n throw new PreconditionError(\n `Malformed calendar ref ${JSON.stringify(raw)}: expected two \"${SEPARATOR}\" separators.`,\n { expected: `${REF_VERSION}:<calendarUid>/<occurrence>/<eventUid>` },\n );\n }\n\n const calendarUid = body.slice(0, first);\n const occurrence = body.slice(first + 1, second);\n // Greedy: everything after the second separator. A Google uid contains no\n // \"/\" but is not a UUID, and an Exchange one is neither — so the tail is\n // taken whole rather than validated into a shape.\n const eventUid = body.slice(second + 1);\n\n if (!eventUid) {\n throw new PreconditionError(\n `Malformed calendar ref ${JSON.stringify(raw)}: it names no event.`,\n { expected: `${REF_VERSION}:<calendarUid>/<occurrence>/<eventUid>` },\n );\n }\n\n const occurrenceStart = occurrence === NO_OCCURRENCE ? null : fromBasic(occurrence);\n if (occurrence !== NO_OCCURRENCE && !occurrenceStart) {\n throw new PreconditionError(\n `Malformed calendar ref ${JSON.stringify(raw)}: ${JSON.stringify(occurrence)} is not an ` +\n `occurrence start. Expected ISO-8601 basic, e.g. 20260821T090000+0200, or \"-\".`,\n );\n }\n\n return { calendarUid, eventUid, occurrenceStart, isOccurrence: occurrenceStart !== null };\n};\n\n/** The series a ref belongs to. Identity for a ref that is already a series. */\nexport const seriesRefOf = (ref: CalendarRef): string => encodeRef(ref.calendarUid, ref.eventUid);\n\n/** The bare UUID inside an id, when there is one. Null is legitimate, not an error. */\nexport const uuidOf = (id: string): string | null =>\n UUID.exec(String(id ?? \"\"))?.[1]?.toUpperCase() ?? null;\n","import type { DatabaseSync } from \"node:sqlite\";\n\nimport {\n columnsOf,\n CORE_DATA_EPOCH_OFFSET,\n fingerprintSchema,\n escapeLike,\n openReadOnly,\n SchemaDriftError,\n type Logger,\n type ReadOnlyMode,\n} from \"@mgcrea/mcp-apple-core\";\n\n/**\n * Calendar's file lane.\n *\n * ## Why there is a file lane at all\n *\n * Unlike Notes and Reminders, this one is justified on SPEED rather than\n * capability. `docs/calendar.md` measured a ±90-day range query over Apple\n * Events at 3.4 s across 1,349 events, with the cost falling per round trip\n * rather than per event (~1.8-2.2 s per property, flat), so no amount of\n * batching rescues it. Calendar's scripting dictionary is unusually complete —\n * attendees, alarms and recurrence are all in it — so the usual \"the store buys\n * you the rich stuff\" argument is much weaker here than elsewhere. The store is\n * how a range query returns before the caller gives up.\n *\n * ## What is measured, and what is not\n *\n * Measured (`docs/calendar.md`): the store is\n * `~/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb`, it is\n * plain Core Data with no `Z_PRIMARYKEY`, the main table is `CalendarItem`, and\n * the id bridge to Apple Events is exact — `CalendarItem.UUID` against the `uid`\n * Calendar hands back, 198/198 sampled.\n *\n * NOT yet measured, and the reason this file introspects but does not query:\n * `OccurrenceCache` (1,946 rows) and `OccurrenceCacheDays` (2,630) both out-row\n * `CalendarItem` (1,350), which means expanded recurrences live outside the main\n * table and a naive `SELECT ... FROM CalendarItem` returns a weekly standup\n * once. `scripts/probe-calendar.mjs` was extended to settle that with a set diff\n * against Apple Events; until it has run on a machine with Full Disk Access,\n * shipping a range query would be guessing at the one thing that fails silently.\n * A short list is indistinguishable from a free afternoon.\n */\n\n/** Tables the lane cannot work without. Anything else degrades to a null field. */\nconst REQUIRED = [\"CalendarItem\", \"Calendar\"] as const;\n\n/**\n * The schema this was written against. Named in the drift error, not enforced.\n *\n * CAUTION: this is the PROBE's fingerprint and it is NOT comparable to the one\n * `diagnostics` reports. `fingerprintSchema` in packages/core orders\n * sqlite_master by `type, name`; `dumpSchema` in scripts/lib/probe-kit.mjs\n * orders it by a CASE expression putting tables before indexes, so the same\n * schema hashes to two different values. Measured here: this store reports\n * cd2424fea732 at runtime against the 2bf4e34ff75f docs/calendar.md recorded\n * from the probe, and Reminders shows the same split (510062aad004 at runtime\n * against the 278b001e3c55 in its own drift message and in docs/verify.md).\n *\n * So compare a probe fingerprint with a probe fingerprint. Reconciling the two\n * orderings is a one-line change in packages/core, but it moves the value for\n * every surface at once and is therefore its own decision.\n */\nconst PROBED_FINGERPRINT = \"2bf4e34ff75f\";\nconst PROBED_MACOS = \"26.6\";\n\nexport type StoreCapabilities = {\n fingerprint: string;\n /** Present columns, so an Apple rename degrades one field instead of the lane. */\n itemColumns: Set<string>;\n calendarColumns: Set<string>;\n occurrenceColumns: Set<string>;\n recurrenceColumns: Set<string>;\n storeColumns: Set<string>;\n /**\n * Whether recurrences can be expanded at all.\n *\n * False is a capability downgrade, never a throw: leg 1 of a range query\n * (items carrying no recurrence rule) stays correct at any horizon, so the\n * honest degradation is to answer with it and say that repeating events were\n * not expanded — not to fail, and above all not to quietly return fewer rows.\n */\n hasOccurrenceCache: boolean;\n hasOccurrenceDays: boolean;\n hasRecurrence: boolean;\n hasExceptionDates: boolean;\n hasLocation: boolean;\n hasAttachments: boolean;\n hasParticipants: boolean;\n hasAlarms: boolean;\n epochOffset: number;\n};\n\n/** One row of the union, still in store units. Rendering happens above this. */\nexport type EventRow = {\n itemPk: number;\n uuid: string | null;\n calendarPk: number | null;\n calendarUuid: string | null;\n calendarTitle: string | null;\n summary: string | null;\n description: string | null;\n url: string | null;\n conferenceUrl: string | null;\n locationTitle: string | null;\n startApple: number | null;\n endApple: number | null;\n allDay: boolean;\n startTz: string | null;\n endTz: string | null;\n status: number | null;\n invitationStatus: number | null;\n availability: number | null;\n hasRecurrences: boolean;\n hasAttendees: boolean;\n /** Set on a detached occurrence: which series it broke away from, and when. */\n origItemPk: number | null;\n origDateApple: number | null;\n /** Which leg produced this row. Reported so a caller can see the expansion working. */\n source: \"item\" | \"occurrence\";\n};\n\nexport type RangeQuery = {\n /** Apple-seconds, inclusive lower bound. */\n fromApple: number;\n /** Apple-seconds, exclusive upper bound. */\n toApple: number;\n /** `Calendar.UUID` values. Empty means every calendar. */\n calendarUuids?: readonly string[];\n limit: number;\n};\n\nexport type SearchQuery = RangeQuery & {\n text: string;\n scope: \"summary\" | \"full\";\n};\n\n/** How far the cached expansion actually reaches, in Apple-seconds. */\nexport type Coverage = { fromApple: number; toApple: number; rows: number } | null;\n\nexport type IndexCalendar = {\n uuid: string | null;\n title: string | null;\n color: string | null;\n type: string | null;\n accountName: string | null;\n isSubscribed: boolean;\n isPublished: boolean;\n /**\n * Whether this calendar is shared with other people.\n *\n * Derived from `sharing_status`, and INFERRED rather than measured: the only\n * values observed are 0, 1 and NULL, with 1 on the two calendars known to be\n * shared. The raw value stays on the result so a caller can disagree.\n *\n * It matters because writing to a shared calendar is visible to whoever else\n * is on it, and nothing else in the output says so.\n */\n isShared: boolean;\n sharingStatus: number | null;\n};\n\nexport type IndexAccount = { name: string | null; type: number | null; calendars: number };\n\nconst bool = (v: unknown): boolean => v === 1 || v === true;\nconst num = (v: unknown): number | null => (typeof v === \"number\" ? v : null);\nconst text = (v: unknown): string | null => (typeof v === \"string\" ? v : null);\n\nexport class CalendarStore {\n readonly db: DatabaseSync;\n readonly mode: string;\n readonly caps: StoreCapabilities;\n\n constructor(db: DatabaseSync, mode: string, caps: StoreCapabilities) {\n this.db = db;\n this.mode = mode;\n this.caps = caps;\n }\n\n /**\n * Project a column, or a typed NULL when this store does not have it.\n *\n * The same guard `packages/reminders/src/client/store.ts` uses, and for the\n * same reason: the schema is reverse-engineered and unversioned, so an Apple\n * rename should cost one field rather than the whole lane.\n */\n #col(present: Set<string>, table: string, name: string, alias = name): string {\n return present.has(name) ? `${table}.\"${name}\" AS ${alias}` : `NULL AS ${alias}`;\n }\n\n #itemColumns(): string {\n const c = this.caps.itemColumns;\n return [\n `ci.\"ROWID\" AS itemPk`,\n this.#col(c, \"ci\", \"UUID\", \"uuid\"),\n this.#col(c, \"ci\", \"summary\", \"summary\"),\n this.#col(c, \"ci\", \"description\", \"description\"),\n this.#col(c, \"ci\", \"url\", \"url\"),\n this.#col(c, \"ci\", \"conference_url\", \"conferenceUrl\"),\n this.#col(c, \"ci\", \"all_day\", \"allDay\"),\n this.#col(c, \"ci\", \"start_tz\", \"startTz\"),\n this.#col(c, \"ci\", \"end_tz\", \"endTz\"),\n this.#col(c, \"ci\", \"status\", \"status\"),\n this.#col(c, \"ci\", \"invitation_status\", \"invitationStatus\"),\n this.#col(c, \"ci\", \"availability\", \"availability\"),\n this.#col(c, \"ci\", \"has_recurrences\", \"hasRecurrences\"),\n this.#col(c, \"ci\", \"has_attendees\", \"hasAttendees\"),\n this.#col(c, \"ci\", \"orig_item_id\", \"origItemPk\"),\n this.#col(c, \"ci\", \"orig_date\", \"origDateApple\"),\n this.#col(c, \"ci\", \"calendar_id\", \"calendarPk\"),\n `cal.\"UUID\" AS calendarUuid`,\n `cal.\"title\" AS calendarTitle`,\n this.caps.hasLocation ? `loc.\"title\" AS locationTitle` : `NULL AS locationTitle`,\n ].join(\",\\n \");\n }\n\n #joins(): string {\n const location =\n this.caps.hasLocation && this.caps.itemColumns.has(\"location_id\")\n ? `\\n LEFT JOIN \"Location\" loc ON loc.\"ROWID\" = ci.\"location_id\"`\n : \"\";\n return `JOIN \"Calendar\" cal ON cal.\"ROWID\" = ci.\"calendar_id\"${location}`;\n }\n\n /**\n * Rows this lane must never return, whichever leg found them.\n *\n * `entity_type` is 2 for events. The probed store holds nothing else — 0 rows\n * carry a due date or a completion date — but `CalendarItem` shares its schema\n * with Reminders, so the predicate is cheap insurance rather than dead code.\n *\n * `hidden` and `phantom_master` are INFERRED rather than measured. A phantom\n * master is the placeholder row EventKit keeps for a series whose occurrences\n * have all been detached; showing it would put an event on the calendar that\n * Calendar.app does not draw. Both are excluded conservatively, and both are\n * on the list for the next probe run to confirm.\n */\n #excluded(alias = \"ci\"): string {\n const c = this.caps.itemColumns;\n const out: string[] = [];\n if (c.has(\"entity_type\")) out.push(`${alias}.\"entity_type\" = 2`);\n if (c.has(\"hidden\")) out.push(`(${alias}.\"hidden\" IS NULL OR ${alias}.\"hidden\" = 0)`);\n if (c.has(\"phantom_master\")) {\n out.push(`(${alias}.\"phantom_master\" IS NULL OR ${alias}.\"phantom_master\" = 0)`);\n }\n return out.length ? `AND ${out.join(\"\\n AND \")}` : \"\";\n }\n\n #calendarFilter(uuids: readonly string[] | undefined): { sql: string; params: string[] } {\n if (!uuids?.length) return { sql: \"\", params: [] };\n const marks = uuids.map(() => \"?\").join(\", \");\n return { sql: `AND cal.\"UUID\" IN (${marks})`, params: [...uuids] };\n }\n\n #rowsFrom(sql: string, params: unknown[], source: EventRow[\"source\"]): EventRow[] {\n const raw = this.db.prepare(sql).all(...(params as never[])) as Record<string, unknown>[];\n return raw.map((r) => ({\n itemPk: Number(r.itemPk),\n uuid: text(r.uuid),\n calendarPk: num(r.calendarPk),\n calendarUuid: text(r.calendarUuid),\n calendarTitle: text(r.calendarTitle),\n summary: text(r.summary),\n description: text(r.description),\n url: text(r.url),\n conferenceUrl: text(r.conferenceUrl),\n locationTitle: text(r.locationTitle),\n startApple: num(r.startApple),\n endApple: num(r.endApple),\n allDay: bool(r.allDay),\n startTz: text(r.startTz),\n endTz: text(r.endTz),\n status: num(r.status),\n invitationStatus: num(r.invitationStatus),\n availability: num(r.availability),\n hasRecurrences: bool(r.hasRecurrences),\n hasAttendees: bool(r.hasAttendees),\n origItemPk: num(r.origItemPk),\n origDateApple: num(r.origDateApple),\n source,\n }));\n }\n\n /**\n * LEG 1 — items carried by the table itself.\n *\n * Overlap, not containment: `start < to AND COALESCE(end, start) > from`. A\n * naive `BETWEEN` on the start silently drops the all-hands that began at\n * 09:00 when the caller asked about 10:00 onward.\n *\n * Items whose occurrences are expanded in the cache are excluded here and\n * picked up by leg 2, so a repeating event is not also returned once at its\n * master start.\n */\n rangeItems(q: RangeQuery): EventRow[] {\n const c = this.caps.itemColumns;\n if (!c.has(\"start_date\")) return [];\n const cal = this.#calendarFilter(q.calendarUuids);\n const endExpr = c.has(\"end_date\")\n ? `COALESCE(ci.\"end_date\", ci.\"start_date\")`\n : `ci.\"start_date\"`;\n // `has_recurrences` is the table's own flag. When it is missing, fall back\n // to asking the Recurrence table directly rather than dropping the guard —\n // without one, every repeating event doubles.\n const notExpanded = c.has(\"has_recurrences\")\n ? `AND (ci.\"has_recurrences\" IS NULL OR ci.\"has_recurrences\" = 0)`\n : this.caps.hasRecurrence\n ? `AND NOT EXISTS (SELECT 1 FROM \"Recurrence\" r WHERE r.\"owner_id\" = ci.\"ROWID\")`\n : \"\";\n const sql = `\n SELECT ci.\"start_date\" AS startApple,\n ${c.has(\"end_date\") ? `ci.\"end_date\"` : `NULL`} AS endApple,\n ${this.#itemColumns()}\n FROM \"CalendarItem\" ci\n ${this.#joins()}\n WHERE ci.\"start_date\" < ?\n AND ${endExpr} > ?\n ${notExpanded}\n ${this.#excluded()}\n ${cal.sql}\n ORDER BY ci.\"start_date\" ASC\n LIMIT ?`;\n return this.#rowsFrom(sql, [q.toApple, q.fromApple, ...cal.params, q.limit], \"item\");\n }\n\n /**\n * LEG 2 — expanded occurrences.\n *\n * `OccurrenceCache.event_id` -> `CalendarItem.ROWID` was measured at a 100%\n * resolve rate. `occurrence_date` is the start; `occurrence_start_date` exists\n * too and reaches only +256 days against `occurrence_date`'s +724, so using it\n * would silently truncate the far half of the window.\n */\n rangeOccurrences(q: RangeQuery): EventRow[] {\n if (!this.caps.hasOccurrenceCache) return [];\n const o = this.caps.occurrenceColumns;\n if (!o.has(\"occurrence_date\") || !o.has(\"event_id\")) return [];\n const cal = this.#calendarFilter(q.calendarUuids);\n const endExpr = o.has(\"occurrence_end_date\")\n ? `COALESCE(oc.\"occurrence_end_date\", oc.\"occurrence_date\")`\n : `oc.\"occurrence_date\"`;\n const sql = `\n SELECT oc.\"occurrence_date\" AS startApple,\n ${o.has(\"occurrence_end_date\") ? `oc.\"occurrence_end_date\"` : `NULL`} AS endApple,\n ${this.#itemColumns()}\n FROM \"OccurrenceCache\" oc\n JOIN \"CalendarItem\" ci ON ci.\"ROWID\" = oc.\"event_id\"\n ${this.#joins()}\n WHERE oc.\"occurrence_date\" < ?\n AND ${endExpr} > ?\n ${this.#excluded()}\n ${cal.sql}\n ORDER BY oc.\"occurrence_date\" ASC\n LIMIT ?`;\n return this.#rowsFrom(sql, [q.toApple, q.fromApple, ...cal.params, q.limit], \"occurrence\");\n }\n\n /**\n * Text search, unbounded in time by default.\n *\n * Searching is the one place a caller legitimately wants all of history, so\n * the window is the caller's to set rather than a default. Runs over items\n * only: an occurrence carries no text of its own, and matching the series once\n * is what a search result should be.\n */\n searchItems(q: SearchQuery): EventRow[] {\n const c = this.caps.itemColumns;\n const cal = this.#calendarFilter(q.calendarUuids);\n const needle = `%${escapeLike(q.text)}%`;\n const fields = [\"summary\"];\n if (q.scope === \"full\") {\n if (c.has(\"description\")) fields.push(\"description\");\n if (this.caps.hasLocation) fields.push(\"__location\");\n }\n const match = fields\n .map((f) =>\n f === \"__location\" ? `loc.\"title\" LIKE ? ESCAPE '\\\\'` : `ci.\"${f}\" LIKE ? ESCAPE '\\\\'`,\n )\n .join(\" OR \");\n const sql = `\n SELECT ci.\"start_date\" AS startApple,\n ${c.has(\"end_date\") ? `ci.\"end_date\"` : `NULL`} AS endApple,\n ${this.#itemColumns()}\n FROM \"CalendarItem\" ci\n ${this.#joins()}\n WHERE (${match})\n AND ci.\"start_date\" < ?\n AND COALESCE(ci.\"end_date\", ci.\"start_date\") > ?\n ${this.#excluded()}\n ${cal.sql}\n ORDER BY ci.\"start_date\" DESC\n LIMIT ?`;\n const params = [...fields.map(() => needle), q.toApple, q.fromApple, ...cal.params, q.limit];\n return this.#rowsFrom(sql, params, \"item\");\n }\n\n /** One event by its Apple Events uid. The bridge measured 198/198 exact. */\n byUuid(uuid: string): EventRow | null {\n const c = this.caps.itemColumns;\n if (!c.has(\"UUID\")) return null;\n const sql = `\n SELECT ci.\"start_date\" AS startApple,\n ${c.has(\"end_date\") ? `ci.\"end_date\"` : `NULL`} AS endApple,\n ${this.#itemColumns()}\n FROM \"CalendarItem\" ci\n ${this.#joins()}\n WHERE UPPER(ci.\"UUID\") = ?\n ${this.#excluded()}\n LIMIT 1`;\n return this.#rowsFrom(sql, [uuid.toUpperCase()], \"item\")[0] ?? null;\n }\n\n /**\n * How far the expansion reaches.\n *\n * Published with every range result. Measured at -732 to +724 days on the\n * probed store, which is a real expansion rather than a month-view cache —\n * but it is still an edge, and nothing guarantees the next machine's is as\n * deep. A range running past it must say so rather than return a short list.\n */\n coverage(): Coverage {\n if (!this.caps.hasOccurrenceCache || !this.caps.occurrenceColumns.has(\"occurrence_date\")) {\n return null;\n }\n const row = this.db\n .prepare(\n `SELECT MIN(\"occurrence_date\") AS lo, MAX(\"occurrence_date\") AS hi, COUNT(*) AS n\n FROM \"OccurrenceCache\" WHERE \"occurrence_date\" IS NOT NULL`,\n )\n .get() as { lo: unknown; hi: unknown; n: unknown } | undefined;\n const lo = num(row?.lo);\n const hi = num(row?.hi);\n if (lo === null || hi === null) return null;\n return { fromApple: lo, toApple: hi, rows: Number(row?.n ?? 0) };\n }\n\n calendars(): IndexCalendar[] {\n const c = this.caps.calendarColumns;\n const store = this.caps.storeColumns.size > 0;\n const sql = `\n SELECT ${this.#col(c, \"cal\", \"UUID\", \"uuid\")},\n ${this.#col(c, \"cal\", \"title\", \"title\")},\n ${this.#col(c, \"cal\", \"color\", \"color\")},\n ${this.#col(c, \"cal\", \"type\", \"type\")},\n ${this.#col(c, \"cal\", \"sharing_status\", \"sharingStatus\")},\n ${this.#col(c, \"cal\", \"is_published\", \"isPublished\")},\n ${this.#col(c, \"cal\", \"subcal_url\", \"subcalUrl\")},\n ${store ? `st.\"name\"` : `NULL`} AS accountName\n FROM \"Calendar\" cal\n ${store ? `LEFT JOIN \"Store\" st ON st.\"ROWID\" = cal.\"store_id\"` : \"\"}\n ORDER BY ${c.has(\"display_order\") ? `cal.\"display_order\" ASC,` : \"\"} cal.\"title\" ASC`;\n const rows = this.db.prepare(sql).all() as Record<string, unknown>[];\n return rows.map((r) => ({\n uuid: text(r.uuid),\n title: text(r.title),\n color: text(r.color),\n type: text(r.type),\n accountName: text(r.accountName),\n // A subscribed calendar is read-only, which is what a write tool needs to\n // know. Derived from the presence of a subscription URL rather than from a\n // flags bit whose meaning has not been measured.\n isSubscribed: Boolean(text(r.subcalUrl)),\n isPublished: bool(r.isPublished),\n isShared: (num(r.sharingStatus) ?? 0) > 0,\n sharingStatus: num(r.sharingStatus),\n }));\n }\n\n accounts(): IndexAccount[] {\n if (!this.caps.storeColumns.size) return [];\n const rows = this.db\n .prepare(\n `SELECT st.\"name\" AS name, st.\"type\" AS type, COUNT(cal.\"ROWID\") AS calendars\n FROM \"Store\" st\n LEFT JOIN \"Calendar\" cal ON cal.\"store_id\" = st.\"ROWID\"\n GROUP BY st.\"ROWID\"\n ORDER BY st.\"name\" ASC`,\n )\n .all() as Record<string, unknown>[];\n return rows.map((r) => ({\n name: text(r.name),\n type: num(r.type),\n calendars: Number(r.calendars ?? 0),\n }));\n }\n\n close(): void {\n try {\n this.db.close();\n } catch {\n // Closing a database that already failed is not worth reporting.\n }\n }\n}\n\nexport const introspect = (db: DatabaseSync): StoreCapabilities => {\n const itemColumns = new Set(columnsOf(db, \"CalendarItem\"));\n const calendarColumns = new Set(columnsOf(db, \"Calendar\"));\n\n for (const t of REQUIRED) {\n const cols = t === \"CalendarItem\" ? itemColumns : calendarColumns;\n if (cols.size === 0) {\n throw new SchemaDriftError(\n `This Calendar store has no ${t} table. It was probed on macOS ${PROBED_MACOS} with ` +\n `schema fingerprint ${PROBED_FINGERPRINT} (a PROBE fingerprint — compare it against ` +\n `another probe run, not against the one diagnostics reports); re-run ` +\n `\\`pnpm probe:calendar\\` to see what changed.`,\n );\n }\n }\n\n const occurrenceColumns = new Set(columnsOf(db, \"OccurrenceCache\"));\n\n return {\n fingerprint: fingerprintSchema(db),\n itemColumns,\n calendarColumns,\n occurrenceColumns,\n recurrenceColumns: new Set(columnsOf(db, \"Recurrence\")),\n storeColumns: new Set(columnsOf(db, \"Store\")),\n hasOccurrenceCache: occurrenceColumns.size > 0,\n hasOccurrenceDays: columnsOf(db, \"OccurrenceCacheDays\").length > 0,\n hasRecurrence: columnsOf(db, \"Recurrence\").length > 0,\n hasExceptionDates: columnsOf(db, \"ExceptionDate\").length > 0,\n hasLocation: columnsOf(db, \"Location\").length > 0,\n hasAttachments: columnsOf(db, \"Attachment\").length > 0,\n hasParticipants: columnsOf(db, \"Participant\").length > 0,\n hasAlarms: columnsOf(db, \"Alarm\").length > 0,\n // Measured as apple-seconds on this store, but taken from core rather than\n // written out again: being 31 years out is the classic Core Data date bug.\n epochOffset: CORE_DATA_EPOCH_OFFSET,\n };\n};\n\nexport const openStore = (\n path: string | null,\n mode: ReadOnlyMode,\n logger?: Logger,\n): CalendarStore | null => {\n if (!path) return null;\n const {\n db,\n mode: used,\n validated,\n } = openReadOnly<StoreCapabilities>(path, mode, {\n label: \"Calendar store\",\n envVar: \"APPLE_CALENDAR_INDEX_MODE\",\n validate: introspect,\n fatal: (err) => err instanceof SchemaDriftError,\n onFallback: () =>\n logger?.debug?.(\n \"opened the Calendar store with immutable=1, which skips the write-ahead log — \" +\n \"very recent changes may be missing until Calendar checkpoints.\",\n ),\n });\n return new CalendarStore(db, used, validated);\n};\n","import {\n createOsascriptRunner,\n withBusyRetry,\n type Logger,\n type OsascriptRunner,\n} from \"@mgcrea/mcp-apple-core\";\n\nimport type { Config } from \"../config.js\";\nimport {\n alignUp,\n dayWindows,\n DEFAULT_WEEKDAYS,\n localDay,\n mergeIntervals,\n nextLocalDayMs,\n parseClock,\n startOfLocalDayMs,\n subtractBusy,\n weekdaySet,\n type Interval,\n type WeekdayKey,\n} from \"./availability.js\";\nimport {\n parseBound,\n parseDate,\n parseDuration,\n parseRange,\n renderInstant,\n toLocalIso,\n type EventInstant,\n} from \"./dates.js\";\nimport {\n CALENDAR_SURFACE,\n CalendarNotFoundError,\n CalendarNotWritableError,\n EventNotFoundError,\n IndexUnavailableError,\n PreconditionError,\n} from \"./errors.js\";\nimport { CREATE_EVENT, DELETE_EVENTS, UPDATE_EVENT } from \"./jxa/write.js\";\nimport { locateStore, type LocateResult } from \"./locate.js\";\nimport { mergeRange, type ExpansionState } from \"./recurrence.js\";\nimport { decodeRef, encodeRef, seriesRefOf } from \"./ref.js\";\nimport { CalendarStore, openStore, type EventRow, type IndexAccount } from \"./store.js\";\n\n/**\n * The lane orchestrator.\n *\n * Thinner than `packages/reminders`' by design. That surface arbitrates between\n * two read lanes and caches an expensive Apple Events bulk fetch; Calendar has\n * exactly one read lane, because `docs/distribution.md` sets the policy for new\n * surfaces — file-lane reads, Apple Events for writes and live state — and\n * `docs/calendar.md` measured why: a range query over Apple Events costs 3.4 s\n * and does not improve with batching.\n *\n * So there is nothing to arbitrate. Either the store opens or the surface says\n * plainly that it cannot answer, and never returns an empty list that reads like\n * an empty calendar.\n */\nexport type LaneStatus = {\n /**\n * Always reported, never probed here.\n *\n * Reminders probes its Apple Events lane on every `lanes()` call because that\n * lane answers reads. Calendar's does not: it exists only for writes, which\n * are not implemented yet. Probing it would fire the Automation prompt for a\n * capability the server does not currently have, which is a worse trade than\n * saying so.\n */\n applescript: \"not-used\" | \"live\" | \"unavailable\";\n index: \"live\" | \"unavailable\" | \"disabled\";\n indexMode: string | null;\n storeFingerprint: string | null;\n reason: string | null;\n};\n\n/**\n * EventKit's public status constants, which this store appears to mirror.\n *\n * Taken from the framework rather than measured: `EKEventStatus` and\n * `EKParticipantStatus` are documented API, and a store written by EventKit is\n * overwhelmingly likely to hold the same integers. \"Overwhelmingly likely\" is\n * not \"measured\", so both filters are off unless the caller asks, every result\n * reports its own raw `status`, and `diagnostics` says the mapping is inferred.\n * Confirming it is on the probe's list.\n */\nconst STATUS_CANCELLED = 3;\nconst PARTICIPANT_DECLINED = 3;\n\n/**\n * `EKEventAvailability.free`, on the same inference as the two above — and held\n * to a stricter standard, because the direction it can fail in is worse.\n *\n * Reading this number wrongly makes a busy event look free, which is the one\n * error `findAvailability` exists to avoid. So unlike `status`, it is not\n * applied by default at all: `respectFreeMarking` is opt-in, the raw value\n * travels on every busy block, and until someone measures the column against a\n * live store every event blocks time regardless of how it is marked.\n */\nconst AVAILABILITY_FREE = 1;\n\n/**\n * How many events `findAvailability` will read from one leg before refusing.\n *\n * `listEvents` over-fetches `limit * 4 + 50` and caps at 5,000 because it only\n * needs enough rows to fill a page. This tool needs EVERY row in the window, so\n * the number here is not a page size — it is the point past which the tool\n * stops claiming to know what is on the calendar. Sized against the measured\n * store in `docs/calendar.md` (1,350 items, 1,946 cached occurrences across\n * four years), so a year-long window on an ordinary calendar sits far under it\n * and a runaway one is caught rather than answered.\n */\nconst BUSY_SCAN_BOUND = 5_000;\n\nexport type EventSummary = {\n /** Opaque; feed it back to get_event or a write tool. */\n ref: string;\n summary: string | null;\n start: EventInstant | null;\n end: EventInstant | null;\n allDay: boolean;\n calendar: string | null;\n location: string | null;\n /** True when this row is one instance of a repeating event. */\n isOccurrence: boolean;\n /** Present on an occurrence: the ref naming the whole series. */\n seriesRef?: string;\n /** Raw store value, reported because the mapping above is inferred. */\n status: number | null;\n invitationStatus: number | null;\n /** Which lane produced the row. */\n source: EventRow[\"source\"];\n};\n\nexport type EventDetail = EventSummary & {\n description: string | null;\n url: string | null;\n conferenceUrl: string | null;\n hasAttendees: boolean;\n hasRecurrences: boolean;\n timeZone: string | null;\n};\n\nexport type EventPage = {\n events: EventSummary[];\n expansion: ExpansionState;\n expansionReason?: string;\n /** The window actually queried, echoed so a clamp is visible. */\n window: { from: string; to: string; clamped: boolean };\n coverage: { from: string; to: string; rows: number } | null;\n truncated?: { reason: string; affects: string; uncoveredFrom?: string; uncoveredTo?: string };\n dropped?: number;\n};\n\nexport type EventFilters = {\n from?: string | undefined;\n to?: string | undefined;\n calendar?: string | undefined;\n includeDeclined?: boolean | undefined;\n includeCancelled?: boolean | undefined;\n limit: number;\n};\n\n/**\n * A window of time nothing is on the calendar, long enough to hold the meeting\n * that was asked for.\n */\nexport type FreeSlot = {\n /** Local wall clock with its offset, ready to hand back to create_event. */\n start: string;\n end: string;\n /** The local day it falls on, so a caller can group without re-parsing. */\n day: string;\n /** How long the whole gap is — usually longer than the duration requested. */\n minutes: number;\n};\n\nexport type AvailabilityFilters = {\n from?: string | undefined;\n to?: string | undefined;\n durationMinutes: number;\n calendar?: string | undefined;\n dayStart: string;\n dayEnd: string;\n weekdays?: readonly WeekdayKey[] | undefined;\n granularityMinutes: number;\n /** Let an all-day event block its whole day. Off by default — see below. */\n allDayBusy: boolean;\n /** Trust `CalendarItem.availability`. Off by default — the mapping is inferred. */\n respectFreeMarking: boolean;\n includeDeclined?: boolean | undefined;\n includeCancelled?: boolean | undefined;\n limit: number;\n};\n\n/**\n * Why an availability answer was withheld.\n *\n * A separate shape from an empty `slots` array on purpose, and the distinction\n * is the whole point of the tool: \"nothing is free\" and \"I cannot see enough of\n * the calendar to say\" look identical to a model unless one of them says so.\n */\nexport type AvailabilityRefusal = {\n degraded: true;\n capability: string;\n reason: string;\n hint: string;\n};\n\nexport type AvailabilityResult = {\n slots: FreeSlot[];\n durationMinutes: number;\n window: {\n from: string;\n to: string;\n clamped: boolean;\n /** True when the start was pulled forward to now, dropping time already past. */\n startedAtNow: boolean;\n };\n workingHours: {\n dayStart: string;\n dayEnd: string;\n weekdays: readonly WeekdayKey[];\n granularityMinutes: number;\n /** The zone the working hours were read in — always this machine's. */\n timeZone: string;\n };\n busy: {\n /** Events that blocked time, after every filter. */\n blocking: number;\n /** Contiguous busy intervals they collapsed into. */\n intervals: number;\n };\n /**\n * All-day events overlapping the window, reported whether or not they were\n * applied. See `allDayBusy` on the tool for why this is not a default.\n */\n allDayEvents: { day: string; summary: string | null; calendar: string | null }[];\n expansion: ExpansionState;\n coverage: { from: string; to: string; rows: number } | null;\n /** Set when the window was cut back to what the expansion actually covers. */\n truncated?: { reason: string; requestedTo?: string; requestedFrom?: string };\n /** Set when the window survived every check but held no time at all. */\n note?: string;\n};\n\nexport type CreateClientOptions = {\n config: Config;\n logger?: Logger;\n /** Injected by tests so nothing spawns a process or touches a real Calendar. */\n osascript?: OsascriptRunner;\n /** Injected by tests so a relative range resolves against a frozen clock. */\n now?: () => Date;\n};\n\n/** What a write returns: what Calendar STORED, never what was requested. */\nexport type WriteResult = {\n ref: string;\n uid: string | null;\n summary: string | null;\n start: string | null;\n end: string | null;\n allDay: boolean;\n calendar: string | null;\n /** Always \"apple-events\": writes never touch the store. */\n source: \"apple-events\";\n};\n\nexport type CreateEventFields = {\n summary: string;\n calendar?: string | undefined;\n start: string;\n end?: string | undefined;\n durationMinutes?: number | undefined;\n allDay?: boolean | undefined;\n location?: string | undefined;\n description?: string | undefined;\n url?: string | undefined;\n};\n\nexport type UpdateEventFields = {\n ref: string;\n summary?: string | undefined;\n start?: string | undefined;\n end?: string | undefined;\n durationMinutes?: number | undefined;\n allDay?: boolean | undefined;\n location?: string | undefined;\n description?: string | undefined;\n url?: string | undefined;\n};\n\nexport class AppleCalendarClient {\n readonly config: Config;\n readonly runner: OsascriptRunner;\n readonly #logger: Logger | undefined;\n readonly #now: () => Date;\n\n #located: LocateResult | null = null;\n #store: CalendarStore | null = null;\n #storeTried = false;\n\n constructor(opts: CreateClientOptions) {\n this.config = opts.config;\n this.#logger = opts.logger;\n this.#now = opts.now ?? (() => new Date());\n this.runner =\n opts.osascript ??\n createOsascriptRunner({\n osascriptPath: opts.config.osascriptPath,\n timeoutMs: opts.config.osascriptTimeoutMs,\n surface: CALENDAR_SURFACE,\n logger: opts.logger,\n });\n }\n\n /** Cached: the answer cannot change without the process being restarted anyway. */\n locate(): LocateResult {\n this.#located ??= locateStore({ storePath: this.config.storePath });\n return this.#located;\n }\n\n /**\n * The store, opened lazily and at most once.\n *\n * Returns null rather than throwing, because \"no index\" is a state the caller\n * has to render, not an exception. The reason lives on the locate result.\n */\n index(): CalendarStore | null {\n if (this.#storeTried) return this.#store;\n this.#storeTried = true;\n if (this.config.indexMode === \"off\") return null;\n try {\n this.#store = openStore(this.locate().storePath, this.config.indexMode, this.#logger);\n } catch (err) {\n this.#logger?.debug?.(\"could not open the Calendar store\", err);\n this.#store = null;\n }\n return this.#store;\n }\n\n /**\n * Drop the open handle so the next read reopens.\n *\n * Called after a write: Calendar owns the store and reconciles it against a\n * server, so an event created over Apple Events lands in the file on the\n * app's schedule, not ours.\n */\n invalidate(): void {\n this.#store?.close();\n this.#store = null;\n this.#storeTried = false;\n this.#located = null;\n }\n\n // ─── reads ────────────────────────────────────────────────────────────────\n\n /**\n * The store, or a structured refusal.\n *\n * Never an empty list: Calendar has no Apple Events read lane to fall back to\n * (`docs/distribution.md`), so \"no index\" means this server cannot answer at\n * all — and an empty array would read as an empty calendar, which is the one\n * answer that must never be invented.\n */\n #require(): CalendarStore {\n const store = this.index();\n if (store) return store;\n const located = this.locate();\n throw new IndexUnavailableError(\n located.reason ??\n \"The Calendar store could not be opened, and this surface has no Apple Events read \" +\n \"lane to fall back to. Run apple_calendar_diagnostics for the details.\",\n );\n }\n\n #toApple(d: Date, store: CalendarStore): number {\n return Math.round(d.getTime() / 1000) - store.caps.epochOffset;\n }\n\n #fromApple(seconds: number, store: CalendarStore): string {\n return new Date((seconds + store.caps.epochOffset) * 1000).toISOString();\n }\n\n /** Resolve a caller's calendar name or uid to the uids the SQL filters on. */\n #calendarUuids(store: CalendarStore, named: string | undefined): string[] | undefined {\n const configured = this.config.calendars;\n const all = store.calendars();\n const pick = (want: string): string[] => {\n const lower = want.toLowerCase();\n const hits = all.filter(\n (c) => c.uuid?.toLowerCase() === lower || c.title?.toLowerCase() === lower,\n );\n if (!hits.length) {\n throw new CalendarNotFoundError(want, all.map((c) => c.title ?? \"\").filter(Boolean));\n }\n return hits.map((c) => c.uuid).filter((u): u is string => Boolean(u));\n };\n if (named) {\n const chosen = pick(named);\n // An allowlist is a boundary, not a default: naming a calendar outside it\n // must fail rather than quietly widen the scope.\n if (configured.length) {\n const allowed = new Set(configured.flatMap(pick));\n const kept = chosen.filter((u) => allowed.has(u));\n if (!kept.length) {\n throw new CalendarNotFoundError(named, configured as string[]);\n }\n return kept;\n }\n return chosen;\n }\n if (configured.length) return configured.flatMap(pick);\n return undefined;\n }\n\n #visible(row: EventRow, opts: { declined: boolean; cancelled: boolean }): boolean {\n if (!opts.cancelled && row.status === STATUS_CANCELLED) return false;\n if (!opts.declined && row.invitationStatus === PARTICIPANT_DECLINED) return false;\n return true;\n }\n\n #summarise(row: EventRow, store: CalendarStore): EventSummary {\n const start = renderInstant(row.startApple, row.startTz, row.allDay, store.caps.epochOffset);\n /**\n * OPEN: the all-day `end` convention is not settled, so it is reported RAW.\n *\n * Measured against a live calendar, creating one all-day event on\n * 21 September: Apple Events reads the end back as `2026-09-21T21:59:59`\n * (23:59:59 local — inclusive), while the store renders a day later. The two\n * legs also disagree with each other: an occurrence row reports end on the\n * same day as start, an item row a day after.\n *\n * A \"subtract a second before rendering\" normalisation was tried and turned\n * out to be a no-op on real data, which means the anchoring differs in a way\n * that has not been pinned down — `start_date` reads as local midnight while\n * `end_date` appears to be anchored differently. Guessing again on top of an\n * unverified premise is how the start-of-day bug got here in the first\n * place, so this stays raw and documented until the raw columns are read.\n * See docs/calendar.md, \"Still open\".\n */\n const end = renderInstant(\n row.endApple,\n row.endTz ?? row.startTz,\n row.allDay,\n store.caps.epochOffset,\n );\n const isOccurrence = row.source === \"occurrence\";\n const ref = encodeRef(\n row.calendarUuid ?? \"\",\n row.uuid ?? \"\",\n isOccurrence && row.startApple !== null\n ? new Date((row.startApple + store.caps.epochOffset) * 1000)\n : null,\n );\n return {\n ref,\n summary: row.summary,\n start,\n end,\n allDay: row.allDay,\n calendar: row.calendarTitle,\n location: row.locationTitle,\n isOccurrence,\n ...(isOccurrence ? { seriesRef: encodeRef(row.calendarUuid ?? \"\", row.uuid ?? \"\") } : {}),\n status: row.status,\n invitationStatus: row.invitationStatus,\n source: row.source,\n };\n }\n\n listEvents(filters: EventFilters): EventPage {\n const store = this.#require();\n const range = parseRange(\n {\n from: filters.from,\n to: filters.to,\n defaultRangeDays: this.config.defaultRangeDays,\n maxRangeDays: this.config.maxRangeDays,\n },\n this.#now(),\n );\n const fromApple = this.#toApple(range.from, store);\n const toApple = this.#toApple(range.to, store);\n const calendarUuids = this.#calendarUuids(store, filters.calendar);\n\n // Over-fetch each leg: status filters are applied in JS below, so a limit\n // pushed into SQL would cut the result before those ran.\n const legLimit = Math.min(filters.limit * 4 + 50, 5_000);\n const q = { fromApple, toApple, limit: legLimit, ...(calendarUuids ? { calendarUuids } : {}) };\n\n const merged = mergeRange({\n items: store.rangeItems(q),\n occurrences: store.rangeOccurrences(q),\n coverage: store.coverage(),\n hasOccurrenceCache: store.caps.hasOccurrenceCache,\n fromApple,\n toApple,\n limit: legLimit,\n epochOffset: store.caps.epochOffset,\n });\n\n const declined = filters.includeDeclined ?? this.config.includeDeclined;\n const cancelled = filters.includeCancelled ?? this.config.includeCancelled;\n const events = merged.rows\n .filter((r) => this.#visible(r, { declined, cancelled }))\n .slice(0, filters.limit)\n .map((r) => this.#summarise(r, store));\n\n return {\n events,\n expansion: merged.expansion,\n ...(merged.expansionReason ? { expansionReason: merged.expansionReason } : {}),\n window: {\n from: range.from.toISOString(),\n to: range.to.toISOString(),\n clamped: range.clamped,\n },\n coverage: merged.coverage\n ? {\n from: this.#fromApple(merged.coverage.fromApple, store),\n to: this.#fromApple(merged.coverage.toApple, store),\n rows: merged.coverage.rows,\n }\n : null,\n ...(merged.truncated\n ? {\n truncated: {\n reason: merged.truncated.reason,\n affects: merged.truncated.affects,\n ...(merged.truncated.uncoveredFromApple !== undefined\n ? { uncoveredFrom: this.#fromApple(merged.truncated.uncoveredFromApple, store) }\n : {}),\n ...(merged.truncated.uncoveredToApple !== undefined\n ? { uncoveredTo: this.#fromApple(merged.truncated.uncoveredToApple, store) }\n : {}),\n },\n }\n : {}),\n ...(merged.dropped ? { dropped: merged.dropped } : {}),\n };\n }\n\n /**\n * Text search over events.\n *\n * Runs on items only. An occurrence carries no text of its own, so matching\n * the series once is what a search result should be — expanding it here would\n * bury one answer under fifty identical ones.\n */\n searchEvents(\n args: EventFilters & { query: string; scope?: \"summary\" | \"full\" | undefined },\n ): EventPage {\n const store = this.#require();\n const now = this.#now();\n // Unbounded by default: search is the one place a caller legitimately wants\n // all of history, so an absent bound means \"no bound\" rather than a week.\n const fromApple = args.from\n ? this.#toApple(\n parseRange({ from: args.from, to: args.from, defaultRangeDays: 1, maxRangeDays: 1 }, now)\n .from,\n store,\n )\n : Number.MIN_SAFE_INTEGER;\n const toApple = args.to\n ? this.#toApple(\n parseRange({ from: args.to, to: args.to, defaultRangeDays: 1, maxRangeDays: 1 }, now).to,\n store,\n )\n : Number.MAX_SAFE_INTEGER;\n const calendarUuids = this.#calendarUuids(store, args.calendar);\n\n const rows = store.searchItems({\n fromApple,\n toApple,\n limit: Math.min(args.limit * 4 + 50, 5_000),\n text: args.query,\n scope: args.scope ?? \"summary\",\n ...(calendarUuids ? { calendarUuids } : {}),\n });\n\n const declined = args.includeDeclined ?? this.config.includeDeclined;\n const cancelled = args.includeCancelled ?? this.config.includeCancelled;\n return {\n events: rows\n .filter((r) => this.#visible(r, { declined, cancelled }))\n .slice(0, args.limit)\n .map((r) => this.#summarise(r, store)),\n // Search does not expand, and says so rather than implying it did.\n expansion: \"unavailable\",\n expansionReason:\n \"search matches each event once, at its series start; use apple_calendar_list_events \" +\n \"with a date range to see individual occurrences\",\n window: {\n from: args.from ? this.#fromApple(fromApple, store) : \"unbounded\",\n to: args.to ? this.#fromApple(toApple, store) : \"unbounded\",\n clamped: false,\n },\n coverage: null,\n };\n }\n\n /**\n * When nothing is on the calendar, for long enough to hold a meeting.\n *\n * ## Why this is not list_events with arithmetic bolted on\n *\n * Every other read here returns what it found and flags what it missed, and\n * a caller reads the flag or does not. This one INVERTS the events: what it\n * returns is the complement of what it read, so anything the read missed\n * comes back as free time. A page limit, an unexpanded weekly meeting, an\n * event past the coverage edge — each of those shortens a `list_events`\n * result harmlessly and each of them, here, invents a slot that is already\n * booked. `docs/calendar.md` names the failure directly: \"a short list of\n * events is indistinguishable from a free afternoon.\"\n *\n * So the busy set is either complete or the answer is withheld. Three checks\n * enforce that, and each returns a refusal rather than a short list:\n *\n * 1. the scan bound, so a saturated leg never passes for a quiet week\n * 2. the expansion, because an unexpanded series is invisible on every\n * date but one\n * 3. the coverage edge, past which repeating events simply are not there\n *\n * The third clips the window rather than refusing outright, because the part\n * inside the edge is genuinely answerable — but it says what it cut.\n */\n findAvailability(args: AvailabilityFilters): AvailabilityResult | AvailabilityRefusal {\n const store = this.#require();\n const now = this.#now();\n const range = parseRange(\n {\n from: args.from,\n to: args.to,\n defaultRangeDays: this.config.defaultRangeDays,\n maxRangeDays: this.config.maxRangeDays,\n },\n now,\n );\n\n const dayStart = parseClock(\"dayStart\", args.dayStart);\n const dayEnd = parseClock(\"dayEnd\", args.dayEnd);\n const weekdayKeys = args.weekdays?.length ? args.weekdays : DEFAULT_WEEKDAYS;\n const weekdays = weekdaySet(weekdayKeys);\n const calendarUuids = this.#calendarUuids(store, args.calendar);\n\n const fromApple = this.#toApple(range.from, store);\n const toApple = this.#toApple(range.to, store);\n const q = {\n fromApple,\n toApple,\n limit: BUSY_SCAN_BOUND,\n ...(calendarUuids ? { calendarUuids } : {}),\n };\n const items = store.rangeItems(q);\n const occurrences = store.rangeOccurrences(q);\n\n // A leg that came back exactly full was cut by SQL's LIMIT, and what it cut\n // is the tail of the window — so the later half of the answer would be\n // uniformly free. A refusal, not an empty result.\n if (items.length >= BUSY_SCAN_BOUND || occurrences.length >= BUSY_SCAN_BOUND) {\n return {\n degraded: true,\n capability: \"busy-set\",\n reason:\n `The window holds at least ${BUSY_SCAN_BOUND} events, which is the bound on what this ` +\n 'tool reads. Nothing was computed, so this is not \"you have no free time\" and not ' +\n '\"you are entirely free\" — it is no answer at all.',\n hint:\n \"Ask for a shorter window, or scope it to the calendars that matter with `calendar`. \" +\n \"apple_calendar_list_events will still page through the range as it is.\",\n };\n }\n\n const merged = mergeRange({\n items,\n occurrences,\n coverage: store.coverage(),\n hasOccurrenceCache: store.caps.hasOccurrenceCache,\n fromApple,\n toApple,\n // Nothing may be sliced off: both legs are already under the bound above.\n limit: items.length + occurrences.length,\n epochOffset: store.caps.epochOffset,\n });\n\n if (merged.expansion === \"unavailable\") {\n return {\n degraded: true,\n capability: \"occurrence-expansion\",\n reason:\n \"Repeating events are not expanded on this store, so a weekly meeting exists on the \" +\n \"date its series begins and nowhere else. Every gap computed from that would be wrong \" +\n `in the same direction — free where it is booked. ${merged.expansionReason ?? \"\"}`.trim(),\n hint:\n \"Read the schedule directly with apple_calendar_list_events, which reports the same \" +\n \"limitation rather than hiding it behind an answer.\",\n };\n }\n\n let windowFrom = range.from;\n let windowTo = range.to;\n let truncated: AvailabilityResult[\"truncated\"];\n const cov = merged.coverage;\n if (cov) {\n const covFrom = new Date((cov.fromApple + store.caps.epochOffset) * 1000);\n const covTo = new Date((cov.toApple + store.caps.epochOffset) * 1000);\n const reason =\n \"the requested window runs past the range this store has expanded, where repeating \" +\n \"events are missing entirely; it was cut back rather than reporting that time as free\";\n if (windowTo.getTime() > covTo.getTime()) {\n truncated = { reason, requestedTo: toLocalIso(range.to) };\n windowTo = covTo;\n }\n if (windowFrom.getTime() < covFrom.getTime()) {\n truncated = { ...(truncated ?? { reason }), requestedFrom: toLocalIso(range.from) };\n windowFrom = covFrom;\n }\n if (truncated && windowTo.getTime() <= windowFrom.getTime()) {\n return {\n degraded: true,\n capability: \"occurrence-coverage\",\n reason:\n \"The whole requested window lies outside the range this store has expanded, so \" +\n \"every gap in it would be invented rather than found.\",\n hint:\n `The expansion reaches ${toLocalIso(covFrom)} to ${toLocalIso(covTo)}. ` +\n \"Ask inside that, or use apple_calendar_list_events, which reports single events \" +\n \"correctly at any distance.\",\n };\n }\n }\n\n // Time already gone is not availability. Applied after the coverage cut so\n // the two reasons a window shrank stay distinguishable in the result.\n const startedAtNow = windowFrom.getTime() < now.getTime();\n if (startedAtNow) windowFrom = now;\n\n const shell = {\n durationMinutes: args.durationMinutes,\n window: {\n from: toLocalIso(windowFrom),\n to: toLocalIso(windowTo),\n clamped: range.clamped,\n startedAtNow,\n },\n workingHours: {\n dayStart: args.dayStart,\n dayEnd: args.dayEnd,\n weekdays: weekdayKeys,\n granularityMinutes: args.granularityMinutes,\n // The machine's zone, always: working hours are wall-clock hours, and\n // this server does not re-anchor a calendar day in another zone.\n timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n },\n expansion: merged.expansion,\n coverage: cov\n ? {\n from: this.#fromApple(cov.fromApple, store),\n to: this.#fromApple(cov.toApple, store),\n rows: cov.rows,\n }\n : null,\n ...(truncated ? { truncated } : {}),\n };\n\n if (windowTo.getTime() <= windowFrom.getTime()) {\n return {\n ...shell,\n slots: [],\n busy: { blocking: 0, intervals: 0 },\n allDayEvents: [],\n note:\n \"The window holds no future time — every moment in it has already passed. Nothing was \" +\n \"checked, so this is not a report that you are busy.\",\n };\n }\n\n const declined = args.includeDeclined ?? this.config.includeDeclined;\n const cancelled = args.includeCancelled ?? this.config.includeCancelled;\n const busy: Interval[] = [];\n const allDayEvents: AvailabilityResult[\"allDayEvents\"] = [];\n let blocking = 0;\n\n for (const row of merged.rows) {\n if (row.startApple === null) continue;\n if (!this.#visible(row, { declined, cancelled })) continue;\n const startMs = (row.startApple + store.caps.epochOffset) * 1000;\n\n if (row.allDay) {\n const dayOpen = startOfLocalDayMs(startMs);\n const dayShut = nextLocalDayMs(startMs);\n if (dayOpen < windowTo.getTime() && dayShut > windowFrom.getTime()) {\n const rendered = renderInstant(row.startApple, row.startTz, true, store.caps.epochOffset);\n allDayEvents.push({\n day: rendered?.allDay ? rendered.day : localDay(startMs),\n summary: row.summary,\n calendar: row.calendarTitle,\n });\n }\n if (!args.allDayBusy) continue;\n /*\n * The stored end of an all-day event is not settled — see #summarise,\n * and docs/calendar.md, \"Still open\". So the block is anchored on the\n * DAY the event renders on and closed at the later of the next midnight\n * and whatever the end column says, which errs long. Long is the safe\n * direction: an over-long block hides a slot that existed, a short one\n * offers a slot that did not.\n */\n const endMs =\n row.endApple === null ? dayShut : (row.endApple + store.caps.epochOffset) * 1000;\n busy.push({ from: dayOpen, to: Math.max(dayShut, startOfLocalDayMs(endMs)) });\n blocking += 1;\n continue;\n }\n\n if (args.respectFreeMarking && row.availability === AVAILABILITY_FREE) continue;\n const endMs =\n row.endApple === null ? startMs : (row.endApple + store.caps.epochOffset) * 1000;\n // A zero-length event is a marker, not an appointment; it blocks nothing.\n if (endMs <= startMs) continue;\n busy.push({ from: startMs, to: endMs });\n blocking += 1;\n }\n\n const blocks = mergeIntervals(busy);\n const needMs = args.durationMinutes * 60_000;\n const slots: FreeSlot[] = [];\n outer: for (const w of dayWindows({\n from: windowFrom,\n to: windowTo,\n dayStart,\n dayEnd,\n weekdays,\n })) {\n for (const gap of subtractBusy(w, blocks)) {\n // Align first, then re-measure: a gap opening at 09:07 offers a 09:15\n // start, and the seven minutes in between are not bookable time.\n const start = alignUp(gap.from, args.granularityMinutes);\n if (gap.to - start < needMs) continue;\n slots.push({\n start: toLocalIso(new Date(start)),\n end: toLocalIso(new Date(gap.to)),\n day: localDay(start),\n minutes: Math.round((gap.to - start) / 60_000),\n });\n if (slots.length >= args.limit) break outer;\n }\n }\n\n return {\n ...shell,\n slots,\n busy: { blocking, intervals: blocks.length },\n allDayEvents,\n };\n }\n\n getEvent(ref: string): EventDetail {\n const store = this.#require();\n const decoded = decodeRef(ref);\n const row = store.byUuid(decoded.eventUid);\n if (!row) throw new EventNotFoundError(ref);\n\n // An occurrence ref names an instant the master row does not carry, so the\n // start is taken from the ref rather than from the row it resolved to.\n const shaped: EventRow = decoded.occurrenceStart\n ? {\n ...row,\n startApple: this.#toApple(decoded.occurrenceStart, store),\n endApple:\n row.startApple !== null && row.endApple !== null\n ? this.#toApple(decoded.occurrenceStart, store) + (row.endApple - row.startApple)\n : row.endApple,\n source: \"occurrence\",\n }\n : row;\n\n const summary = this.#summarise(shaped, store);\n return {\n ...summary,\n ref,\n ...(decoded.isOccurrence ? { seriesRef: seriesRefOf(decoded) } : {}),\n description: row.description,\n url: row.url,\n conferenceUrl: row.conferenceUrl,\n hasAttendees: row.hasAttendees,\n hasRecurrences: row.hasRecurrences,\n timeZone: summary.start && !summary.start.allDay ? summary.start.timeZone : null,\n };\n }\n\n calendars() {\n return this.#require().calendars();\n }\n\n accounts(): IndexAccount[] {\n return this.#require().accounts();\n }\n\n // ─── writes ───────────────────────────────────────────────────────────────\n\n /**\n * Resolve the calendar a write targets, and refuse a read-only one up front.\n *\n * Asking the store first means the refusal names the cause. Letting the Apple\n * Event fail instead produces a message from deep inside Calendar that does\n * not mention writability at all.\n */\n #writeTarget(named: string | undefined): { name: string | undefined; uuid: string | null } {\n const store = this.index();\n const wanted = named ?? this.config.defaultCalendar;\n if (!store) {\n // No store to check against. The JXA script asks Calendar itself, so the\n // guard is not lost — only the earlier, better-worded error is.\n return { name: wanted, uuid: null };\n }\n const all = store.calendars();\n if (!wanted) return { name: undefined, uuid: null };\n\n const lower = wanted.toLowerCase();\n const hits = all.filter(\n (c) => c.uuid?.toLowerCase() === lower || c.title?.toLowerCase() === lower,\n );\n if (!hits.length) {\n throw new CalendarNotFoundError(wanted, all.map((c) => c.title ?? \"\").filter(Boolean));\n }\n\n /**\n * Duplicate titles are real: the machine this was measured on has two\n * calendars both named `olouvignes@me.com`. A read can legitimately span\n * both, but a write has to land in exactly one — and only a NAME crosses to\n * Apple Events, so an ambiguous one is refused rather than resolved by coin\n * flip.\n */\n if (hits.length > 1) {\n throw new PreconditionError(\n `\"${wanted}\" matches ${hits.length} calendars, and a write has to name exactly one. ` +\n `Calendars are addressed by name when writing — Apple Events cannot resolve a ` +\n `calendar uid at all — so duplicates cannot be told apart. Rename one in Calendar.app, ` +\n `or write to a calendar whose name is unique.`,\n { requested: wanted, matches: hits.length },\n );\n }\n const hit = hits[0]!;\n if (hit.isSubscribed) throw new CalendarNotWritableError(hit.title ?? wanted);\n // The NAME is what crosses the boundary; the uuid is kept only for the ref.\n return { name: hit.title ?? wanted, uuid: hit.uuid };\n }\n\n /** Map a calendar name reported by Apple Events back to its store uuid. */\n #calendarUuidByName(name: string | null): string | null {\n if (!name) return null;\n const store = this.index();\n if (!store) return null;\n return store.calendars().find((c) => c.title === name)?.uuid ?? null;\n }\n\n /**\n * The calendar NAME a ref points at, for a write.\n *\n * A ref carries the store uuid, which Apple Events cannot resolve, so it has\n * to be turned back into a name before it crosses.\n */\n #nameForRefCalendar(calendarUid: string): string | undefined {\n if (!calendarUid) return undefined;\n const store = this.index();\n if (!store) return undefined;\n const lower = calendarUid.toLowerCase();\n return store.calendars().find((c) => c.uuid?.toLowerCase() === lower)?.title ?? undefined;\n }\n\n /**\n * Run a write script, re-inflating its application-level failures.\n *\n * Core turns a `{ok:false, error:{code, message}}` envelope into a generic\n * `ProtocolError` carrying the code, which surfaces as a bare `\"Birthdays\"`.\n * That matters more here than it looks: the store-side writability check is\n * derived from `subcal_url` and MISSES calendars that are read-only for other\n * reasons — Birthdays and Siri Suggestions both report `writable() === false`\n * while carrying no subscription URL. The JXA lane catches them correctly, so\n * the only thing lost was the explanation. This puts it back.\n */\n async #run<T>(scriptText: string, params: unknown): Promise<T> {\n try {\n return await withBusyRetry(() => this.runner.run<T>(scriptText, params));\n } catch (err) {\n const code = (err as { details?: { code?: string } })?.details?.code;\n const message = err instanceof Error ? err.message : String(err);\n if (code === \"CALENDAR_NOT_WRITABLE\") throw new CalendarNotWritableError(message);\n if (code === \"CALENDAR_NOT_FOUND\") throw new CalendarNotFoundError(message);\n if (code === \"EVENT_NOT_FOUND\") throw new EventNotFoundError(message);\n throw err;\n }\n }\n\n #shapeWrite(data: Record<string, unknown>, calendarUid: string | null): WriteResult {\n const uid = typeof data.uid === \"string\" ? data.uid : null;\n return {\n ref: encodeRef(calendarUid ?? \"\", uid ?? \"\"),\n uid,\n summary: typeof data.summary === \"string\" ? data.summary : null,\n start: typeof data.startDate === \"string\" ? data.startDate : null,\n end: typeof data.endDate === \"string\" ? data.endDate : null,\n allDay: data.alldayEvent === true,\n calendar: typeof data.calendarName === \"string\" ? data.calendarName : null,\n source: \"apple-events\",\n };\n }\n\n /**\n * Work out an event's end from whichever of the three forms the caller used.\n *\n * `end` wins over `durationMinutes`; with neither, the configured default\n * length applies. A zero-length event is refused rather than created, because\n * Calendar renders one as a point in time that is almost impossible to click.\n */\n #resolveWindow(\n start: string,\n end: string | undefined,\n durationMinutes: number | undefined,\n ): { startIso: string; endIso: string; allDayHint: boolean } {\n const now = this.#now();\n const from = parseDate(\"start\", start, now);\n if (end !== undefined) {\n /**\n * A bare day as `end` means THROUGH that day, not midnight at its start.\n *\n * `parseBound` already encodes this for range queries, and list_events\n * documents it — \"naming the same day for both gives that whole day\". Using\n * plain `parseDate` here made create disagree with list about the same\n * word: `{start: \"2026-09-21\", end: \"2026-09-21\"}`, the natural way to say\n * \"a one-day event\", was refused as ending before it began.\n */\n const to = parseBound(\"end\", end, \"end\", now);\n if (to.getTime() <= from.at.getTime()) {\n throw new PreconditionError(\n `end (${toLocalIso(to)}) is not after start (${from.iso}). An event cannot finish ` +\n `before it begins.`,\n );\n }\n return { startIso: from.iso, endIso: toLocalIso(to), allDayHint: from.kind === \"allDay\" };\n }\n const minutes =\n durationMinutes === undefined\n ? this.config.defaultEventDurationMinutes\n : parseDuration(\"durationMinutes\", durationMinutes);\n const to = new Date(from.at.getTime() + minutes * 60_000);\n return {\n startIso: from.iso,\n endIso: toLocalIso(to),\n allDayHint: from.kind === \"allDay\",\n };\n }\n\n async createEvent(fields: CreateEventFields): Promise<WriteResult> {\n const target = this.#writeTarget(fields.calendar);\n const win = this.#resolveWindow(fields.start, fields.end, fields.durationMinutes);\n const data = await this.#run<Record<string, unknown>>(CREATE_EVENT, {\n calendar: target.name ?? null,\n summary: fields.summary,\n startDate: win.startIso,\n endDate: win.endIso,\n // A bare day in `start` already means all-day; an explicit flag overrides.\n allDay: fields.allDay ?? win.allDayHint,\n ...(fields.location !== undefined ? { location: fields.location } : {}),\n ...(fields.description !== undefined ? { description: fields.description } : {}),\n ...(fields.url !== undefined ? { url: fields.url } : {}),\n });\n this.invalidate();\n // Prefer the uuid we resolved; fall back to looking up whichever calendar\n // Calendar actually used, which is the default-calendar case.\n const uuid =\n target.uuid ??\n this.#calendarUuidByName(typeof data.calendarName === \"string\" ? data.calendarName : null);\n return this.#shapeWrite(data, uuid);\n }\n\n async updateEvent(fields: UpdateEventFields): Promise<WriteResult> {\n const ref = decodeRef(fields.ref);\n /**\n * An occurrence ref is REFUSED rather than quietly applied to the series.\n *\n * Calendar's scripting dictionary has no way to detach a single occurrence —\n * the \"This Event\" edit in the UI has no scripting equivalent. Applying the\n * change to the series would move every future standup because someone\n * asked to move one lunch, which is a data-loss bug wearing a success\n * message. \"All future\" is absent for a related reason: it needs a rule\n * split, which is two writes with no transaction between them.\n */\n if (ref.isOccurrence) {\n throw new PreconditionError(\n `That ref names one occurrence of a repeating event, and Calendar's scripting interface ` +\n `cannot edit a single occurrence — only the whole series. Applying your change to the ` +\n `series would move every other occurrence too, so this is refused rather than done ` +\n `silently. To change just this one: delete it with apple_calendar_delete_events ` +\n `(scope \"occurrence\"), then create a replacement. To change them all, pass the ` +\n `seriesRef from apple_calendar_get_event instead.`,\n { ref: fields.ref, seriesRef: seriesRefOf(ref) },\n );\n }\n\n const window =\n fields.start !== undefined\n ? this.#resolveWindow(fields.start, fields.end, fields.durationMinutes)\n : null;\n\n const data = await this.#run<Record<string, unknown>>(UPDATE_EVENT, {\n calendar: this.#nameForRefCalendar(ref.calendarUid) ?? null,\n uid: ref.eventUid,\n ...(fields.summary !== undefined ? { summary: fields.summary } : {}),\n ...(window ? { startDate: window.startIso, endDate: window.endIso } : {}),\n ...(fields.allDay !== undefined ? { allDay: fields.allDay } : {}),\n ...(fields.location !== undefined ? { location: fields.location } : {}),\n ...(fields.description !== undefined ? { description: fields.description } : {}),\n ...(fields.url !== undefined ? { url: fields.url } : {}),\n });\n this.invalidate();\n return this.#shapeWrite(data, ref.calendarUid);\n }\n\n /**\n * Delete whole events.\n *\n * Only whole events: Calendar's scripting interface cannot remove a single\n * occurrence of a repeating one. `excludedDates` — the property Calendar.app\n * itself uses for \"Delete This Event\" — reads back a 1903 sentinel and throws\n * on assignment, measured on macOS 26.6. So an occurrence ref is refused\n * rather than silently deleting the whole series, which is the same shape of\n * refusal `updateEvent` makes and for the same underlying reason.\n */\n async deleteEvents(refs: readonly string[]): Promise<{ results: unknown[]; scope: string }> {\n const decoded = refs.map((r, i) => ({ ref: refs[i]!, parsed: decodeRef(r) }));\n\n const occurrence = decoded.find((d) => d.parsed.isOccurrence);\n if (occurrence) {\n throw new PreconditionError(\n `That ref names one occurrence of a repeating event, and Calendar's scripting interface ` +\n `cannot delete a single occurrence — the excluded-dates property it would need is not ` +\n `writable. Deleting the series instead would remove every other occurrence too, so ` +\n `this is refused rather than done silently. Delete this one in Calendar.app, or pass ` +\n `the seriesRef from apple_calendar_get_event to delete the whole series.`,\n { ref: occurrence.ref, seriesRef: seriesRefOf(occurrence.parsed) },\n );\n }\n\n // Grouped by calendar: findEvent costs one bulk uid fetch per calendar, so\n // deleting five events from one calendar should pay that once, not five times.\n const byCalendar = new Map<string, string[]>();\n for (const { parsed } of decoded) {\n const key = parsed.calendarUid || \"\";\n byCalendar.set(key, [...(byCalendar.get(key) ?? []), parsed.eventUid]);\n }\n const results: unknown[] = [];\n for (const [calendar, uids] of byCalendar) {\n const data = await this.#run<{ results: unknown[] }>(DELETE_EVENTS, {\n calendar: this.#nameForRefCalendar(calendar) ?? null,\n uids,\n });\n results.push(...(data.results ?? []));\n }\n this.invalidate();\n return { results, scope: \"series\" };\n }\n\n lanes(): LaneStatus {\n const located = this.locate();\n const store = this.index();\n return {\n applescript: \"not-used\",\n index: this.config.indexMode === \"off\" ? \"disabled\" : store ? \"live\" : \"unavailable\",\n indexMode: store?.mode ?? null,\n storeFingerprint: store?.caps.fingerprint ?? null,\n reason: store ? null : located.reason,\n };\n }\n}\n","import {\n BaseConfigSchema,\n parseBool,\n parseConfig,\n parseIntOpt,\n parseList,\n trimmed,\n} from \"@mgcrea/mcp-apple-core\";\nimport { z } from \"zod\";\n\nimport { DEFAULT_WEEKDAYS, WEEKDAY_KEYS } from \"./client/availability.js\";\n\nconst CLOCK = /^\\d{1,2}:\\d{2}$/;\n\n/**\n * `Mon`, `MONDAY` and `mon` all mean the same day.\n *\n * Normalised here rather than in the schema so the schema's error message stays\n * the useful one — it lists the seven keys — instead of rejecting a spelling\n * nobody would think was wrong.\n */\nconst parseWorkdays = (raw: string[] | undefined): string[] | undefined =>\n raw?.map((d) => d.trim().toLowerCase().slice(0, 3));\n\n/**\n * Configuration is environment-only — this server holds no secret at all, its\n * access is the macOS permission the user granted.\n *\n * `allowWrites`, `debug`, `osascriptPath`, `osascriptTimeoutMs` and `maxResults`\n * come from `BaseConfigSchema`.\n *\n * Note what is deliberately ABSENT relative to `packages/reminders`: there is no\n * `searchCacheTtlMs` and no degraded-listing cap, because both exist there to\n * manage an Apple Events READ lane. Calendar has none by design\n * (`docs/distribution.md`), and config for a lane that does not exist would\n * advertise a fallback this server cannot provide.\n */\nconst ConfigSchema = BaseConfigSchema.extend({\n /** Account allowlist (names or ids). Empty means every account. */\n accounts: z.array(z.string().min(1)).default([]),\n /**\n * Calendar allowlist (names or uids). Empty means every calendar.\n *\n * The important one for this surface: a work calendar and a personal one\n * routinely live in the same account, so the account is the wrong unit to\n * scope by whenever scoping is the point.\n */\n calendars: z.array(z.string().min(1)).default([]),\n /** Explicit store path. Bypasses discovery — for tests and forensic copies. */\n storePath: z.string().optional(),\n indexMode: z.enum([\"auto\", \"ro\", \"immutable\", \"off\"]).default(\"auto\"),\n /** Calendar a new event goes to when the caller names none. Empty = Calendar's default. */\n defaultCalendar: z.string().optional(),\n /**\n * Window for a range query that names only a start.\n *\n * A calendar has no natural \"everything\" answer the way a note list does, and\n * an unbounded default would scan a decade to report next Tuesday.\n */\n defaultRangeDays: z.number().int().min(1).max(366).default(7),\n /** Hard clamp, so one query cannot ask for a decade. */\n maxRangeDays: z.number().int().min(1).max(3_660).default(366),\n /**\n * The working day `find_availability` assumes when the caller names no hours.\n *\n * Local wall clock, both of them: working hours are a person's, so they hold\n * their numbers across a daylight-saving change rather than drifting an hour.\n * `24:00` is a legal end and means midnight at the close of that day.\n */\n workdayStart: z.string().regex(CLOCK, 'expected a time of day like \"09:00\"').default(\"09:00\"),\n workdayEnd: z.string().regex(CLOCK, 'expected a time of day like \"18:00\"').default(\"18:00\"),\n /** The days that working day applies to. */\n workdays: z\n .array(z.enum(WEEKDAY_KEYS))\n .min(1)\n .default([...DEFAULT_WEEKDAYS]),\n /** Length of a created event when the caller gives neither an end nor a duration. */\n defaultEventDurationMinutes: z.number().int().min(1).max(1_440).default(60),\n /** Whether events the user declined are included when the caller does not say. */\n includeDeclined: z.boolean().default(false),\n /** Whether events an organiser cancelled are included when the caller does not say. */\n includeCancelled: z.boolean().default(false),\n /**\n * Render override for timed events. Empty means the system zone.\n *\n * Validated as a real IANA name here rather than at render time: a bad zone\n * should fail at startup with the variable named, not once per event.\n */\n timeZone: z\n .string()\n .refine(\n (v) => {\n try {\n Intl.DateTimeFormat(\"en-US\", { timeZone: v });\n return true;\n } catch {\n return false;\n }\n },\n { message: 'not an IANA time zone name, e.g. \"Europe/Paris\"' },\n )\n .optional(),\n}).strict();\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\n/**\n * `env` is a parameter with a default so tests are hermetic — they pass their\n * own object rather than mutating (and having to restore) process.env.\n */\nexport const loadConfig = (env: NodeJS.ProcessEnv = process.env): Config =>\n parseConfig(ConfigSchema, {\n allowWrites: parseBool(env.APPLE_CALENDAR_ALLOW_WRITES),\n exposePrompts: parseBool(env.APPLE_CALENDAR_EXPOSE_PROMPTS),\n debug: parseBool(env.APPLE_CALENDAR_DEBUG),\n accounts: parseList(env.APPLE_CALENDAR_ACCOUNTS),\n calendars: parseList(env.APPLE_CALENDAR_CALENDARS),\n storePath: trimmed(env.APPLE_CALENDAR_STORE),\n indexMode: trimmed(env.APPLE_CALENDAR_INDEX_MODE),\n defaultCalendar: trimmed(env.APPLE_CALENDAR_DEFAULT_CALENDAR),\n defaultRangeDays: parseIntOpt(env.APPLE_CALENDAR_DEFAULT_RANGE_DAYS),\n maxRangeDays: parseIntOpt(env.APPLE_CALENDAR_MAX_RANGE_DAYS),\n defaultEventDurationMinutes: parseIntOpt(env.APPLE_CALENDAR_DEFAULT_EVENT_DURATION_MINUTES),\n workdayStart: trimmed(env.APPLE_CALENDAR_WORKDAY_START),\n workdayEnd: trimmed(env.APPLE_CALENDAR_WORKDAY_END),\n workdays: parseWorkdays(parseList(env.APPLE_CALENDAR_WORKDAYS)),\n includeDeclined: parseBool(env.APPLE_CALENDAR_INCLUDE_DECLINED),\n includeCancelled: parseBool(env.APPLE_CALENDAR_INCLUDE_CANCELLED),\n osascriptPath: trimmed(env.APPLE_CALENDAR_OSASCRIPT_PATH),\n osascriptTimeoutMs: parseIntOpt(env.APPLE_CALENDAR_OSASCRIPT_TIMEOUT_MS),\n maxResults: parseIntOpt(env.APPLE_CALENDAR_MAX_RESULTS),\n timeZone: trimmed(env.APPLE_CALENDAR_TIMEZONE),\n });\n","/**\n * The Calendar operating manual, served as `cupertino://calendar/guide` and\n * embedded ahead of every Calendar prompt. Static by design — see the note in\n * the Mail guide.\n */\nexport const CALENDAR_GUIDE = `# Apple Calendar — how to drive this server\n\n## Refs are opaque, and they name an occurrence\n\nEvents come back with a \\`ref\\` like \\`c1:<calendar>/<occurrence>/<uid>\\`. Pass it\nback verbatim. The part that matters: a ref names **one occurrence**, not the\nseries. Reading it reports that occurrence's times; acting on it acts on that\noccurrence. Do not assume changing one changes the repeat.\n\n## Which tool, under which constraint\n\n- **\"When am I free\", \"find me 45 minutes\", \"what does Thursday look like\"** is\n \\`apple_calendar_find_availability\\`. Reach for it instead of listing events and\n looking for gaps by hand — that is where an occurrence of a repeating meeting\n gets missed. It works in working hours (09:00–18:00 on weekdays by default),\n in this machine's timezone, and never offers time already past.\n- **What is on** is \\`apple_calendar_list_events\\` over a bounded range.\n- **Finding a specific event** is \\`apple_calendar_search_events\\`.\n- **Calendar names** must match what Calendar calls them — read the inventory\n resource first. A mistyped name matches nothing rather than erroring.\n\n## Empty is booked; degraded is unknown\n\nOn availability these are two different answers and must never be reported the\nsame way. An empty \\`slots\\` list means **the time is booked**. \\`degraded: true\\`\nmeans the question could not be answered — too many events to read, a store\nthat does not expand repeats, or a window past the expansion range — and the\nright reply is \"I could not check\", never \"you are free\".\n\nAll-day events do not block time by default, because an all-day event is as\noften a birthday as a holiday. They come back in \\`allDayEvents\\`: read that\nbefore booking over one rather than turning \\`allDayBusy\\` on blindly. Declined\nand cancelled events never block.\n\n## Writes, and the thing this server will not do\n\nMutating tools exist only when writes are enabled. If you cannot see\n\\`apple_calendar_create_event\\`, writes are off.\n\n**There is no \\`attendees\\` parameter, anywhere, on purpose.** Adding an attendee\nsends mail to a person. This server creates events on calendars; it does not\ninvite anyone. If the user wants someone invited, create the event and tell them\nto invite from Calendar — do not silently create an event they believe was sent.\n\nRead-only calendars are refused rather than silently skipped.\n`;\n","import {\n promptArg,\n registerWorkflowPrompt,\n requiredPromptArg,\n type PromptContext,\n} from \"@mgcrea/mcp-apple-core\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport { CALENDAR_GUIDE } from \"./guide.js\";\n\nconst CTX: PromptContext = { surface: \"calendar\", guide: CALENDAR_GUIDE };\n\n/**\n * Calendar's workflow prompts.\n *\n * The scheduling one carries the constraint that costs the most when it is\n * missed: this server can create an event but cannot invite anyone to it, so a\n * model that treats \"schedule a meeting with Ana\" as done has told the user\n * something false about a message that was never sent.\n */\nexport const registerPrompts = (server: McpServer, allowWrites: boolean): void => {\n registerWorkflowPrompt(server, CTX, {\n name: \"apple_calendar_whats_my_day\",\n title: \"What my day looks like\",\n description:\n \"Read back a day or a range as something you can act on — what is fixed, what is \" +\n \"movable, and where the real free time is. Read-only.\",\n argsSchema: {\n when: promptArg(\n 'Which day or range, e.g. \"today\", \"tomorrow\", \"next week\". Defaults to today.',\n ),\n calendar: promptArg(\"Restrict to one calendar, exactly as Calendar spells it.\"),\n },\n build: ({ when, calendar }) => `Tell me what ${when ?? \"today\"} looks like${\n calendar ? `, on the ${calendar} calendar` : \"\"\n }.\n\n1. \\`apple_calendar_list_events\\` over that range${\n calendar ? \" for that calendar\" : \"\"\n }. Bound the range explicitly — do not list open-endedly and trim afterwards.\n2. Then \\`apple_calendar_find_availability\\` over the same range, so you can say\n where the usable gaps are rather than leaving the user to subtract meetings\n from a day in their head. Report each gap's real length.\n3. Call out anything in \\`allDayEvents\\` — a holiday or someone's day off changes\n what the rest of the day means, and it does not block time by default.\n4. Flag the pressure points: back-to-back blocks with no gap, anything starting\n before or ending after normal hours, and any two events that overlap.\n\nIf a call comes back \\`degraded: true\\`, say which part of the range you could not\nread. An empty result means booked; degraded means unknown, and they must not be\nreported the same way.`,\n });\n\n if (!allowWrites) return;\n\n registerWorkflowPrompt(server, CTX, {\n name: \"apple_calendar_schedule\",\n title: \"Schedule something\",\n description:\n \"Find a time that genuinely works and put an event on the calendar. Requires writes. \" +\n \"Note that this cannot invite anyone — it creates the event only.\",\n argsSchema: {\n what: requiredPromptArg('What the event is, e.g. \"45 minutes with Ana about pricing\".'),\n when: promptArg('Constraint on when, e.g. \"next week\", \"Thursday afternoon\".'),\n calendar: promptArg(\"Which calendar to create it on, exactly as Calendar spells it.\"),\n },\n build: ({ what, when, calendar }) => `Schedule: ${what}${when ? `\\n\\nWhen: ${when}` : \"\"}\n\n1. Work out the duration from the description; ask if it is genuinely unclear\n rather than defaulting to an hour.\n2. \\`apple_calendar_find_availability\\` over ${\n when ? `the window implied by \"${when}\"` : \"the coming week\"\n }. Do not list events and pick a gap yourself — a repeating meeting's\n occurrence is exactly what that misses.\n3. Offer the user two or three real options before creating anything, with the\n full length of each gap so they can see whether it is tight. If availability\n comes back \\`degraded: true\\`, stop and say you could not check — do not offer\n a slot you could not verify.\n4. On their choice, \\`apple_calendar_create_event\\`${\n calendar\n ? ` on the ${calendar} calendar`\n : \" on a calendar from the inventory, not an invented name\"\n }.\n\n**Then be explicit about what did not happen: nobody was invited.** This server\nhas no \\`attendees\\` parameter on any tool — adding one would send mail, and it\ndeliberately cannot. If someone else needs to be at this, the event is on the\ncalendar and the invitation still has to be sent from Calendar by hand. Say so\nplainly; do not let \"scheduled with Ana\" stand when Ana was never told.`,\n });\n};\n","/**\n * Calendar's tool helpers. The generic half lives in `@mgcrea/mcp-apple-core`;\n * re-exported here so tools import from one place.\n *\n * Calendar's own zod args (event refs, the date grammar, the range block) land\n * with the tools that take them.\n */\nexport {\n compact,\n confirmArg,\n fail,\n limitArg,\n ok,\n okText,\n toFailure,\n wrap,\n wrapResult,\n type ToolResult,\n} from \"@mgcrea/mcp-apple-core\";\n\nimport { z } from \"zod\";\n\n// ─── Calendar's own args ─────────────────────────────────────────────────────\n\nexport const eventRefArg = z\n .string()\n .min(1)\n .describe(\n 'An opaque event ref from a list or search result (looks like \"c1:<calendar>/<occurrence>/<uid>\"). ' +\n \"Do not construct one by hand.\",\n );\n\nexport const calendarArg = z\n .string()\n .optional()\n .describe(\n 'A calendar name or uid, e.g. \"Work\". Use apple_calendar_list_calendars to see what exists.',\n );\n\n/**\n * The date grammar, written out in full.\n *\n * Load-bearing: the caller is usually a model that gets one retry, and the\n * difference between naming a day and naming an instant is the difference\n * between a whole-day range and a one-minute one.\n */\nexport const fromArg = z\n .string()\n .optional()\n .describe(\n 'Start of the window. ISO-8601 \"2026-08-21\" or \"2026-08-21T09:00\", or \"today\", \"tomorrow\", ' +\n '\"next monday\", \"+2d\". A bare day starts at that morning. Defaults to today.',\n );\n\nexport const toArg = z\n .string()\n .optional()\n .describe(\n \"End of the window. A bare day runs through that EVENING, so naming the same day for both \" +\n \"gives that whole day. Defaults to a week after the start.\",\n );\n\nexport const includeDeclinedArg = z\n .boolean()\n .optional()\n .describe(\"Include events you have declined. Off by default; they are still on the calendar.\");\n\nexport const includeCancelledArg = z\n .boolean()\n .optional()\n .describe(\n \"Include events an organiser cancelled but that are still in the store. Off by default.\",\n );\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport { ok, wrap } from \"./util.js\";\n\n/**\n * Build the report.\n *\n * Split out of the tool registration so the `cupertino://calendar/diagnostics`\n * resource can serve the same bytes. Two renderings of one probe: duplicated,\n * the resource and the tool would drift, and the disagreement would surface as\n * \"the diagnostics lied\" — the one thing this file must never do.\n */\nexport const buildDiagnostics = async (\n client: AppleCalendarClient,\n ctx: { allowWrites: boolean },\n): Promise<Record<string, unknown>> => {\n const lanes = client.lanes();\n const located = client.locate();\n const store = client.index();\n\n /**\n * Three-valued, and the middle value is the useful one.\n *\n * Calendar's store has a constant filename, so `stat` answers \"is it\n * there\" even when `access(2)` is denied. That lets this separate \"the\n * grant is missing\" from \"Calendar was never set up on this account\",\n * which Reminders cannot do — its filename carries a generated UUID, so\n * without the grant there is no path to test at all.\n */\n const fullDiskAccess = located.readable\n ? \"granted\"\n : located.exists\n ? \"denied (found the store, cannot read it)\"\n : \"unknown (no store file at the expected path)\";\n\n return {\n server: { lanes },\n permissions: {\n fullDiskAccess,\n automation: ctx.allowWrites\n ? \"needed for writes only. Reads never send an Apple Event, so a read-only setup \" +\n \"prompts for nothing.\"\n : \"not needed — writes are off, and reads never send an Apple Event. Turning \" +\n \"writes on will prompt for Automation the first time one runs.\",\n ...(located.readable\n ? {}\n : {\n howToGrant: [\n \"System Settings > Privacy & Security > Full Disk Access\",\n \"Add the app that launches this server (Terminal, iTerm, VS Code, Claude...), then restart it.\",\n \"Granting it to Calendar.app does nothing — the reader needs the permission, not Calendar.\",\n ],\n }),\n },\n store: {\n containerPath: located.containerPath,\n path: located.storePath,\n containerListable: located.containerListable,\n extrasPresent: located.extrasPresent,\n candidates: located.candidates.length,\n exists: located.exists,\n readable: located.readable,\n sizeBytes: located.size,\n walPresent: located.walPresent,\n walSizeBytes: located.walSizeBytes,\n fingerprint: lanes.storeFingerprint,\n reason: located.reason,\n },\n capabilities: store\n ? {\n hasOccurrenceCache: store.caps.hasOccurrenceCache,\n hasOccurrenceDays: store.caps.hasOccurrenceDays,\n hasRecurrence: store.caps.hasRecurrence,\n hasExceptionDates: store.caps.hasExceptionDates,\n hasLocation: store.caps.hasLocation,\n hasAttachments: store.caps.hasAttachments,\n hasParticipants: store.caps.hasParticipants,\n hasAlarms: store.caps.hasAlarms,\n itemColumns: store.caps.itemColumns.size,\n calendarColumns: store.caps.calendarColumns.size,\n }\n : null,\n settings: {\n allowWrites: ctx.allowWrites,\n // Off means the prompts and the cupertino:// resources are not registered\n // at all. Reported here because this tool still is, so it stays the one\n // place that explains a capability the client cannot see.\n exposePrompts: client.config.exposePrompts,\n accountAllowlist: client.config.accounts,\n calendarAllowlist: client.config.calendars,\n defaultCalendar: client.config.defaultCalendar ?? null,\n defaultRangeDays: client.config.defaultRangeDays,\n maxRangeDays: client.config.maxRangeDays,\n includeDeclined: client.config.includeDeclined,\n includeCancelled: client.config.includeCancelled,\n indexMode: client.config.indexMode,\n maxResults: client.config.maxResults,\n timeZone: client.config.timeZone ?? null,\n },\n caveats: [\n \"Writes go through Apple Events and are real side effects: on a shared, CalDAV or \" +\n \"Exchange calendar a created event syncs within seconds and other people see it. \" +\n \"There is no draft state and no undo.\",\n \"Single occurrences of a repeating event can be neither edited nor deleted through \" +\n \"this server, and that is a limit of Calendar's scripting interface rather than a \" +\n \"choice: there is no way to detach one occurrence, and the excluded-dates property \" +\n \"reads back a 1903 sentinel and throws on assignment (measured, macOS 26.6). Both \" +\n \"are refused rather than applied to the whole series. Calendar.app can still do them.\",\n 'Attendees cannot be set, because that emails a person. There is no \"all future ' +\n 'occurrences\" either, which would need two writes with no transaction between them.',\n \"Calendar reads through the file lane only. Apple Events was measured at 3.4s for \" +\n \"a single 90-day range query, with the cost falling per round trip rather than \" +\n \"per event, so there is no slower-but-working fallback to offer when Full Disk \" +\n \"Access is missing. Reads need the grant.\",\n \"Repeating events are expanded from OccurrenceCache, which was measured to reach \" +\n \"about two years either side of today on the probed store. That is an edge: a \" +\n \"range running past it returns fewer repeating events than exist, so every \" +\n \"list_events result carries `coverage`, and sets `truncated` rather than \" +\n \"returning a short list silently.\",\n \"The `status` and `invitationStatus` numbers are EventKit's documented constants, \" +\n \"and this store is assumed to mirror them — likely, but not measured. That is why \" +\n \"cancelled and declined events are only hidden when you ask, and why the raw \" +\n \"value is on every result.\",\n \"Writes will go through Apple Events, targeting com.apple.iCal. Note the bundle id \" +\n \"is iCal, not Calendar, which is the one place this surface differs from the \" +\n \"other three.\",\n ],\n };\n};\n\n/**\n * One tool that answers \"why is this not working\".\n *\n * Unlike the Reminders equivalent this probes no Apple Event before reading:\n * Calendar has no Apple Events read lane to probe (`docs/distribution.md`), so\n * firing one here would trigger the Automation prompt for a capability the\n * server does not yet have.\n */\nexport const registerDiagnosticsTools = (\n server: McpServer,\n client: AppleCalendarClient,\n ctx: { allowWrites: boolean },\n): void => {\n server.registerTool(\n \"apple_calendar_diagnostics\",\n {\n description:\n \"Report which lanes are live, which macOS permissions are granted, and what each \" +\n \"missing one is blocking. Start here when a tool fails or reports nothing — it names \" +\n \"the exact System Settings pane to open.\",\n inputSchema: {},\n annotations: { readOnlyHint: true },\n },\n async () => wrap(() => buildDiagnostics(client, ctx)).then((r) => r ?? ok({})),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport { calendarArg, confirmArg, eventRefArg, wrap } from \"./util.js\";\n\n/**\n * The mutating tools.\n *\n * Registered only when `allowWrites` is on — not merely refused, absent, so a\n * host is never told they exist.\n *\n * ## No `attendees` parameter, anywhere\n *\n * Adding an attendee sends an email to a person. That is not something to do\n * behind a tool call, and leaving it out of the schema is a stronger guarantee\n * than validating it away. `test/jxa.test.ts` asserts the scripts cannot set it\n * either, so there are two places to remove rather than one to forget.\n */\n\nconst startArg = z\n .string()\n .describe(\n 'When it starts. ISO-8601 \"2026-08-21\" for an all-day event or \"2026-08-21T09:00\" for a ' +\n 'timed one, or \"tomorrow 09:00\", \"next monday\", \"+2d\". Naming a bare day makes it all-day.',\n );\n\nconst endArg = z\n .string()\n .optional()\n .describe(\n \"When it ends. Give this or durationMinutes; with neither, the default length applies.\",\n );\n\nconst durationArg = z\n .number()\n .int()\n .min(1)\n .max(60 * 24 * 366)\n .optional()\n .describe(\"Length in minutes, as an alternative to `end`.\");\n\nexport const registerActionTools = (server: McpServer, client: AppleCalendarClient): void => {\n server.registerTool(\n \"apple_calendar_create_event\",\n {\n description:\n \"Create an event. THIS IS A REAL EVENT ON A REAL CALENDAR: on an iCloud, CalDAV or \" +\n \"Exchange calendar it syncs within seconds and there is no draft state and no undo. \" +\n \"Check apple_calendar_list_calendars first — writing to one where `isShared` is true is \" +\n \"visible to everyone else on that calendar, so prefer a personal one unless the user \" +\n \"meant to share it. Read-only calendars are refused. This tool cannot add attendees, \" +\n \"which would email a person. The result reports what Calendar actually stored, which is \" +\n \"not always what was asked for.\",\n inputSchema: {\n summary: z.string().min(1).describe(\"The event's title.\"),\n calendar: calendarArg,\n start: startArg,\n end: endArg,\n durationMinutes: durationArg,\n allDay: z\n .boolean()\n .optional()\n .describe(\n \"Force an all-day event. Usually unnecessary: a bare day in `start` means one.\",\n ),\n location: z.string().optional(),\n description: z.string().optional().describe(\"The event's notes.\"),\n url: z.string().optional(),\n },\n },\n async (args) => wrap(() => client.createEvent(args)),\n );\n\n server.registerTool(\n \"apple_calendar_update_event\",\n {\n description:\n \"Change an existing event. On a shared calendar the change is visible to everyone else \" +\n \"on it. Note that Apple Events has no transaction: if a change is refused part-way \" +\n \"through, earlier fields may already have been written, so the result is the truth \" +\n \"about what the event now looks like. Only whole events can be edited: a ref naming \" +\n \"ONE occurrence \" +\n \"of a repeating event is refused, because Calendar's scripting interface cannot detach a \" +\n \"single occurrence and applying the change to the series would move every other one too. \" +\n 'To change a single occurrence, delete it with scope \"occurrence\" and create a ' +\n \"replacement.\",\n inputSchema: {\n ref: eventRefArg,\n summary: z.string().min(1).optional(),\n start: startArg.optional(),\n end: endArg,\n durationMinutes: durationArg,\n allDay: z.boolean().optional(),\n location: z.string().optional(),\n description: z.string().optional(),\n url: z.string().optional(),\n },\n },\n async (args) => wrap(() => client.updateEvent(args)),\n );\n\n server.registerTool(\n \"apple_calendar_delete_events\",\n {\n description:\n \"Delete whole events. Each result says whether the event ACTUALLY went — Calendar \" +\n \"silently declines to delete a repeating event, reporting no error, so `deleted` is \" +\n \"decided by re-reading the calendar rather than by the call succeeding. \" +\n \"A ref naming ONE occurrence of a repeating event is refused: \" +\n \"Calendar's scripting interface cannot remove a single occurrence, and deleting the \" +\n \"series instead would take every other one with it. Delete a single occurrence in \" +\n \"Calendar.app, or pass the `seriesRef` from get_event to remove the whole series. \" +\n '\"All future occurrences\" is not offered either — it needs two writes with no ' +\n \"transaction between them.\",\n inputSchema: {\n refs: z\n .array(z.string().min(1))\n .min(1)\n .max(100)\n .describe(\n \"Event refs from a list or search result. Each must name a whole event, not one \" +\n \"occurrence of a repeating one.\",\n ),\n confirm: confirmArg,\n },\n },\n async ({ refs }) => wrap(() => client.deleteEvents(refs)),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nimport { WEEKDAY_KEYS } from \"../client/availability.js\";\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport {\n calendarArg,\n includeCancelledArg,\n includeDeclinedArg,\n limitArg,\n toArg,\n wrap,\n} from \"./util.js\";\n\n/**\n * The one read on this surface that answers a question instead of returning\n * rows, and the only one whose result is the COMPLEMENT of what it read.\n *\n * It lives apart from `events.ts` for that reason. The three tools there share\n * a failure mode — return fewer events than exist, flag it, and the caller is\n * merely under-informed. This one turns the same shortfall into a positive\n * claim that a time is free, so its refusal paths are load-bearing rather than\n * defensive, and keeping them next to a `list_events` that legitimately pages\n * would invite someone to make the two consistent.\n */\nexport const registerAvailabilityTools = (server: McpServer, client: AppleCalendarClient): void => {\n server.registerTool(\n \"apple_calendar_find_availability\",\n {\n description:\n \"Find the times nothing is on the calendar, long enough to hold a meeting of a given \" +\n 'length. This is the tool for \"when am I free\", \"find me 45 minutes next week\" or ' +\n '\"what does Thursday look like\" — reach for it instead of listing events and looking ' +\n \"for gaps yourself, which is where an occurrence of a repeating meeting gets missed. \" +\n \"Working hours default to 09:00-18:00 on weekdays, in THIS MACHINE'S timezone, and only \" +\n \"time inside them is offered. Slots start on a 15-minute boundary and each one reports \" +\n \"how long the whole gap is, which is usually longer than you asked for. \" +\n \"Time already past today is never offered. \" +\n \"It refuses rather than guesses: if the window holds too many events to read, if this \" +\n \"store does not expand repeating events, or if the window runs past the range the \" +\n \"expansion covers, you get `degraded: true` and a reason instead of free time that is \" +\n \"not free. An empty `slots` list means BOOKED; `degraded` means UNKNOWN. \" +\n \"Declined and cancelled events do not block time; all-day events do not either, but \" +\n \"they are listed in `allDayEvents` so you can see the holiday you are about to book over.\",\n inputSchema: {\n durationMinutes: z\n .number()\n .int()\n .min(1)\n .max(1_440)\n .describe(\"How long the meeting needs to be. A gap shorter than this is not returned.\"),\n from: z\n .string()\n .optional()\n .describe(\n 'Start of the window. ISO-8601 \"2026-08-21\" or \"2026-08-21T09:00\", or \"today\", ' +\n '\"tomorrow\", \"next monday\", \"+2d\". Defaults to today. A start already in the past ' +\n \"is pulled forward to now.\",\n ),\n to: toArg,\n calendar: calendarArg,\n dayStart: z\n .string()\n .optional()\n .describe(\n 'Earliest time of day to offer, local wall clock, e.g. \"09:00\". Defaults to ' +\n \"APPLE_CALENDAR_WORKDAY_START (09:00).\",\n ),\n dayEnd: z\n .string()\n .optional()\n .describe(\n 'Latest time of day to offer, e.g. \"18:00\" — or \"24:00\" for the end of the day. ' +\n \"Defaults to APPLE_CALENDAR_WORKDAY_END (18:00).\",\n ),\n weekdays: z\n .array(z.enum(WEEKDAY_KEYS))\n .min(1)\n .optional()\n .describe(\n 'Which days to offer, e.g. [\"mon\",\"tue\",\"wed\",\"thu\",\"fri\"]. Defaults to the ' +\n \"configured working week. Pass all seven to include the weekend.\",\n ),\n granularityMinutes: z\n .number()\n .int()\n .min(1)\n .max(60)\n .refine((n) => 60 % n === 0, {\n message: \"must divide 60 evenly — 5, 10, 15, 20, 30 or 60\",\n })\n .optional()\n .describe(\"Slot starts are rounded up to this boundary. Default 15.\"),\n allDayBusy: z\n .boolean()\n .optional()\n .describe(\n \"Let an all-day event block its whole day. Off by default, because an all-day event \" +\n 'is as often a birthday as a holiday, and blocking on \"Ana\\'s birthday\" would hide ' +\n \"a working day. They are reported in `allDayEvents` either way — read that before \" +\n \"booking rather than turning this on blindly.\",\n ),\n respectFreeMarking: z\n .boolean()\n .optional()\n .describe(\n 'Skip events the calendar marks as \"free\" rather than \"busy\". Off by default: the ' +\n \"stored constant is inferred from EventKit and has not been measured against a \" +\n \"real store, and reading it wrongly would offer a slot that is booked. Every busy \" +\n \"block reports its raw value so you can check before turning this on.\",\n ),\n includeDeclined: includeDeclinedArg,\n includeCancelled: includeCancelledArg,\n limit: limitArg,\n },\n annotations: { readOnlyHint: true },\n },\n async (args) =>\n wrap(async () =>\n client.findAvailability({\n ...args,\n dayStart: args.dayStart ?? client.config.workdayStart,\n dayEnd: args.dayEnd ?? client.config.workdayEnd,\n weekdays: args.weekdays ?? client.config.workdays,\n granularityMinutes: args.granularityMinutes ?? 15,\n allDayBusy: args.allDayBusy ?? false,\n respectFreeMarking: args.respectFreeMarking ?? false,\n limit: args.limit ?? Math.min(25, client.config.maxResults),\n }),\n ),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport { wrap } from \"./util.js\";\n\n/**\n * NOTE ON `async` BELOW: core's `wrap` is typed `() => Promise<T>` because every\n * other surface reaches Apple Events. Calendar reads synchronous SQLite, so the\n * thunks are marked async here rather than widening a shared signature for all\n * four surfaces to accommodate one.\n */\n\nexport const registerCalendarTools = (server: McpServer, client: AppleCalendarClient): void => {\n server.registerTool(\n \"apple_calendar_list_calendars\",\n {\n description:\n \"List every calendar with the account it belongs to, whether it is SHARED with other \" +\n \"people, and whether it looks writable. Two cautions. `isShared` means anything you \" +\n \"write there is visible to the others on it. And `isSubscribed` catches URL-subscribed \" +\n \"calendars but not every read-only one — Birthdays and Siri Suggestions are also \" +\n \"read-only — so a write can still be refused by Calendar itself.\",\n inputSchema: {},\n annotations: { readOnlyHint: true },\n },\n async () => wrap(async () => client.calendars()),\n );\n\n server.registerTool(\n \"apple_calendar_list_accounts\",\n {\n description:\n \"List the accounts Calendar syncs, with how many calendars each holds. Use this to scope \" +\n \"a search when the same calendar name exists in more than one account.\",\n inputSchema: {},\n annotations: { readOnlyHint: true },\n },\n async () => wrap(async () => client.accounts()),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport {\n calendarArg,\n eventRefArg,\n fromArg,\n includeCancelledArg,\n includeDeclinedArg,\n limitArg,\n toArg,\n wrap,\n} from \"./util.js\";\n\n/** Declared once and spread into both tools, so the two cannot drift apart. */\nconst filterSchema = {\n calendar: calendarArg,\n includeDeclined: includeDeclinedArg,\n includeCancelled: includeCancelledArg,\n limit: limitArg,\n};\n\nexport const registerEventTools = (server: McpServer, client: AppleCalendarClient): void => {\n server.registerTool(\n \"apple_calendar_list_events\",\n {\n description:\n \"List events in a date range, earliest first. Occurrences of repeating events are \" +\n \"expanded, so a weekly standup appears once per week rather than once. The result \" +\n \"carries a `coverage` block naming the window the expansion is known to cover; if your \" +\n \"range runs past it, `truncated` is set and says what is missing rather than returning a \" +\n \"short list silently. Returns a `ref` per event for get_event.\",\n inputSchema: { from: fromArg, to: toArg, ...filterSchema },\n annotations: { readOnlyHint: true },\n },\n async (args) =>\n wrap(async () =>\n client.listEvents({\n ...args,\n limit: args.limit ?? Math.min(50, client.config.maxResults),\n }),\n ),\n );\n\n server.registerTool(\n \"apple_calendar_search_events\",\n {\n description:\n \"Search events by text, most recent first. Unbounded in time unless you give `from` or \" +\n \"`to`. Matches each event ONCE at its series start — it does not expand occurrences, so \" +\n \"use list_events with a range to see individual instances of a repeating event.\",\n inputSchema: {\n query: z.string().min(1).describe(\"Text to look for. Matched case-insensitively.\"),\n scope: z\n .enum([\"summary\", \"full\"])\n .optional()\n .describe('\"summary\" (default) searches titles; \"full\" adds notes and location.'),\n from: fromArg,\n to: toArg,\n ...filterSchema,\n },\n annotations: { readOnlyHint: true },\n },\n async (args) =>\n wrap(async () =>\n client.searchEvents({\n ...args,\n limit: args.limit ?? Math.min(50, client.config.maxResults),\n }),\n ),\n );\n\n server.registerTool(\n \"apple_calendar_get_event\",\n {\n description:\n \"Full detail for one event: notes, location, URL, conference link, and whether it has \" +\n \"attendees or repeats. A ref naming one occurrence reports that occurrence's times and \" +\n \"carries a `seriesRef` pointing at the whole series.\",\n inputSchema: { ref: eventRefArg },\n annotations: { readOnlyHint: true },\n },\n async ({ ref }) => wrap(async () => client.getEvent(ref)),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport { registerActionTools } from \"./actions.js\";\nimport { registerAvailabilityTools } from \"./availability.js\";\nimport { registerCalendarTools } from \"./calendars.js\";\nimport { registerDiagnosticsTools } from \"./diagnostics.js\";\nimport { registerEventTools } from \"./events.js\";\n\nexport type ToolContext = {\n /**\n * Register the mutating tools too. Off by default — with the flag off they are\n * not merely refused, they are invisible and cannot be called at all.\n */\n allowWrites: boolean;\n};\n\n/**\n * Register the Apple Calendar tools.\n *\n * The registered set is a pure function of `allowWrites` and nothing else. In\n * particular it does NOT vary with whether Full Disk Access is granted: that is\n * a runtime condition which can change while the process lives, and MCP clients\n * cache the tool list, so a tool that appears and disappears would leave clients\n * calling names the server no longer has. Tools that need the store instead\n * report their source, or explain what is missing.\n *\n * Writes go through Apple Events, always. Not a preference: `PRAGMA query_only`\n * is set on the store because Calendar owns it, holds it open and reconciles it\n * against a server, so writing to it would corrupt sync state.\n */\nexport const registerTools = (\n server: McpServer,\n client: AppleCalendarClient,\n ctx: ToolContext,\n): void => {\n registerDiagnosticsTools(server, client, ctx);\n registerCalendarTools(server, client);\n registerEventTools(server, client);\n registerAvailabilityTools(server, client);\n\n if (!ctx.allowWrites) return;\n registerActionTools(server, client);\n};\n","import {\n registerSurfaceResources,\n type Logger,\n type OsascriptRunner,\n} from \"@mgcrea/mcp-apple-core\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport { BUILD_INFO } from \"./build-info.js\";\nimport { AppleCalendarClient } from \"./client/calendar.js\";\nimport type { Config } from \"./config.js\";\nimport { CALENDAR_GUIDE } from \"./guide.js\";\nimport { registerPrompts } from \"./prompts.js\";\nimport { buildDiagnostics } from \"./tools/diagnostics.js\";\nimport { registerTools } from \"./tools/index.js\";\n\nexport const SERVER_NAME = BUILD_INFO.name;\nexport const SERVER_VERSION = BUILD_INFO.version;\n\nexport type CreateServerOptions = {\n config: Config;\n logger?: Logger;\n /** Injected by tests so nothing spawns a process or touches a real Calendar. */\n osascript?: OsascriptRunner;\n /** Injected by tests so a relative range resolves against a frozen clock. */\n now?: () => Date;\n};\n\nexport type CreatedServer = {\n server: McpServer;\n client: AppleCalendarClient;\n};\n\n/**\n * Build the server. Side-effect free: it opens no connection, spawns no\n * process and reads no file, so a test can construct it freely and every\n * external dependency arrives through an option.\n *\n */\nexport const createServer = (opts: CreateServerOptions): CreatedServer => {\n const { config } = opts;\n const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });\n\n const client = new AppleCalendarClient({\n config,\n ...(opts.logger ? { logger: opts.logger } : {}),\n ...(opts.osascript ? { osascript: opts.osascript } : {}),\n ...(opts.now ? { now: opts.now } : {}),\n });\n\n registerTools(server, client, { allowWrites: config.allowWrites });\n /*\n * One flag, both primitives — see `exposePrompts` in core's config. A prompt\n * embeds its surface guide, so registering prompts without the resources\n * would leave every expansion naming a `cupertino://…/guide` that this\n * server does not serve.\n */\n if (config.exposePrompts) {\n registerPrompts(server, config.allowWrites);\n registerSurfaceResources(server, {\n surface: \"calendar\",\n displayName: \"Calendar\",\n guide: CALENDAR_GUIDE,\n diagnostics: () => buildDiagnostics(client, { allowWrites: config.allowWrites }),\n inventory: {\n describes: \"accounts and calendars\",\n read: async () => ({\n accounts: await client.accounts(),\n calendars: await client.calendars(),\n }),\n },\n });\n }\n\n return { server, client };\n};\n"],"mappings":";;;;;;;AAWA,MAAM,MAAM,oBAAoB,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG;CAC3E,MAAM;CACN,SAAS;AACX,CAAC;AAID,MAAa,aAAwB;CACnC,MAAM,IAAI;CACV,SAAS,IAAI;CACb,WAAA;CACA,eAAA;AACF;;;;;;;;;;;;;;;ACRA,MAAa,mBAAmC;CAC9C,SAAS;CACT,WAAW;AACb;;AAGA,MAAa,qBAAqB;;;;;;;AAsBlC,IAAa,mBAAb,cAAsC,qBAAqB;CACzD,OAAyB;CAEzB,YAAY,OAAe,KAAa,QAAgB;EACtD,MACE,kBAAkB,MAAM,QAAQ,KAAK,UAAU,GAAG,EAAE,IAAI,OAAO,wNAI/D;GAAE;GAAO;EAAI,CACf;CACF;AACF;;AAGA,IAAa,qBAAb,cAAwC,qBAAqB;CAC3D,OAAyB;CAEzB,YAAY,KAAa;EACvB,MACE,qBAAqB,IAAI,oGAEzB,EAAE,IAAI,CACR;CACF;AACF;;AAGA,IAAa,wBAAb,cAA2C,qBAAqB;CAC9D,OAAyB;CAEzB,YAAY,MAAc,YAA+B,CAAC,GAAG;EAC3D,MACE,sBAAsB,KAAK,OACxB,UAAU,SACP,eAAe,UAAU,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,UAAU,SAAS,KAAK,QAAQ,GAAG,KACtF,2DACN,EAAE,WAAW,KAAK,CACpB;CACF;AACF;;;;;;;;;AAUA,IAAa,2BAAb,cAA8C,qBAAqB;CACjE,OAAyB;CAEzB,YAAY,MAAc;EACxB,MACE,iBAAiB,KAAK,4NAGtB,EAAE,UAAU,KAAK,CACnB;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjEA,MAAa,eAAe;CAAC;CAAO;CAAO;CAAO;CAAO;CAAO;CAAO;AAAK;;AAI5E,MAAa,mBAA0C;CAAC;CAAO;CAAO;CAAO;CAAO;AAAK;AAEzF,MAAMA,UAAQ;AAEd,MAAMC,SAAO,MAAsB,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;AAE5D,MAAa,cAAc,OAAe,QAAuB;CAC/D,MAAM,OAAO,OAAO,OAAO,EAAE,CAAC,CAAC,KAAK;CACpC,MAAM,IAAID,QAAM,KAAK,IAAI;CACzB,IAAI,CAAC,GAAG,MAAM,IAAI,iBAAiB,OAAO,MAAM,uCAAqC;CACrF,MAAM,QAAQ,OAAO,EAAE,EAAE;CACzB,MAAM,UAAU,OAAO,EAAE,EAAE;CAC3B,IAAI,QAAQ,MAAM,UAAU,IAC1B,MAAM,IAAI,iBAAiB,OAAO,MAAM,GAAG,MAAM,GAAG,EAAE,GAAG,sBAAsB;CAGjF,IAAI,UAAU,MAAM,YAAY,GAC9B,MAAM,IAAI,iBACR,OACA,MACA,0DACF;CAEF,OAAO;EAAE;EAAO;CAAQ;AAC1B;AAEA,MAAa,cAAc,SACzB,IAAI,IAAI,KAAK,KAAK,MAAM,aAAa,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,EAAE,CAAC;AAE1E,MAAM,mBAAmB,MACvB,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,GAAG,GAAG,GAAG,GAAG,CAAC;;AAGjE,MAAME,aAAW,GAAS,MAAoB;CAC5C,MAAM,MAAM,IAAI,KAAK,EAAE,QAAQ,CAAC;CAChC,IAAI,QAAQ,IAAI,QAAQ,IAAI,CAAC;CAC7B,OAAO;AACT;;;;;;;;AASA,MAAM,WAAW,KAAW,MAC1B,IAAI,KAAK,IAAI,YAAY,GAAG,IAAI,SAAS,GAAG,IAAI,QAAQ,GAAG,EAAE,OAAO,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,QAAQ;;;;;;;;;AAU/F,MAAa,kBAAkB,UAA2C;CACxE,MAAM,SAAS,MAAM,QAAQ,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,UAAU,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;CACpF,MAAM,MAAkB,CAAC;CACzB,KAAK,MAAM,OAAO,QAAQ;EACxB,MAAM,OAAO,IAAI,GAAG,EAAE;EACtB,IAAI,QAAQ,IAAI,QAAQ,KAAK,IAAI;GAC/B,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI;GACpC;EACF;EACA,IAAI,KAAK;GAAE,MAAM,IAAI;GAAM,IAAI,IAAI;EAAG,CAAC;CACzC;CACA,OAAO;AACT;;;;;;;;;;AAWA,MAAa,gBAAgB,QAAkB,SAA0C;CACvF,MAAM,MAAkB,CAAC;CACzB,IAAI,SAAS,OAAO;CACpB,KAAK,MAAM,KAAK,MAAM;EACpB,IAAI,EAAE,MAAM,QAAQ;EACpB,IAAI,EAAE,QAAQ,OAAO,IAAI;EACzB,IAAI,EAAE,OAAO,QAAQ,IAAI,KAAK;GAAE,MAAM;GAAQ,IAAI,EAAE;EAAK,CAAC;EAC1D,SAAS,EAAE;EACX,IAAI,UAAU,OAAO,IAAI,OAAO;CAClC;CACA,IAAI,SAAS,OAAO,IAAI,IAAI,KAAK;EAAE,MAAM;EAAQ,IAAI,OAAO;CAAG,CAAC;CAChE,OAAO;AACT;;;;;;;;;AAUA,MAAa,cAAc,SAMT;CAChB,MAAM,MAAkB,CAAC;CACzB,MAAM,OAAO,KAAK,GAAG,QAAQ;CAC7B,KAAK,IAAI,MAAM,gBAAgB,KAAK,IAAI,GAAG,IAAI,QAAQ,KAAK,MAAM,MAAMA,UAAQ,KAAK,CAAC,GAAG;EACvF,IAAI,CAAC,KAAK,SAAS,IAAI,IAAI,OAAO,CAAC,GAAG;EACtC,MAAM,OAAO,QAAQ,KAAK,KAAK,QAAQ;EACvC,MAAM,QAAQ,QAAQ,KAAK,KAAK,MAAM;EACtC,IAAI,SAAS,MAAM;EACnB,MAAM,OAAO,KAAK,IAAI,MAAM,KAAK,KAAK,QAAQ,CAAC;EAC/C,MAAM,KAAK,KAAK,IAAI,OAAO,IAAI;EAC/B,IAAI,KAAK,MAAM,IAAI,KAAK;GAAE;GAAM;EAAG,CAAC;CACtC;CACA,OAAO;AACT;;;;;;;;;AAUA,MAAa,WAAW,IAAY,uBAAuC;CACzE,MAAM,IAAI,IAAI,KAAK,EAAE;CACrB,MAAM,YAAY,IAAI,KACpB,EAAE,YAAY,GACd,EAAE,SAAS,GACX,EAAE,QAAQ,GACV,EAAE,SAAS,GACX,GACA,GACA,CACF,CAAC,CAAC,QAAQ;CACV,MAAM,OAAO,qBAAqB;CAClC,MAAM,OAAO,KAAK;CAClB,OAAO,YAAY,KAAK,KAAK,OAAO,IAAI,IAAI;AAC9C;;AAGA,MAAa,qBAAqB,OAAuB,gBAAgB,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ;;AAG/F,MAAa,kBAAkB,OAC7BA,UAAQ,gBAAgB,IAAI,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ;;AAGpD,MAAa,YAAY,OAAuB;CAC9C,MAAM,IAAI,IAAI,KAAK,EAAE;CACrB,OAAO,GAAG,EAAE,YAAY,EAAE,GAAGD,MAAI,EAAE,SAAS,IAAI,CAAC,EAAE,GAAGA,MAAI,EAAE,QAAQ,CAAC;AACvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjJA,MAAM,YAAY;CAChB;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,WAAW;AACjB,MAAM,eACJ;AACF,MAAM,SACJ;AACF,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,WAAW;;AAGjB,MAAM,eAAe;AAErB,MAAME,SAAO,GAAW,IAAI,MAAc,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;;AAGnE,MAAM,YAAY,MAAoB;CACpC,MAAM,OAAO,CAAC,EAAE,kBAAkB;CAClC,IAAI,SAAS,GAAG,OAAO;CACvB,MAAM,OAAO,OAAO,IAAI,MAAM;CAC9B,MAAM,MAAM,KAAK,IAAI,IAAI;CACzB,OAAO,GAAG,OAAOA,MAAI,KAAK,MAAM,MAAM,EAAE,CAAC,EAAE,GAAGA,MAAI,MAAM,EAAE;AAC5D;;;;;;;;AASA,MAAa,cAAc,MACzB,GAAG,EAAE,YAAY,EAAE,GAAGA,MAAI,EAAE,SAAS,IAAI,CAAC,EAAE,GAAGA,MAAI,EAAE,QAAQ,CAAC,EAAA,GAC1DA,MAAI,EAAE,SAAS,CAAC,EAAE,GAAGA,MAAI,EAAE,WAAW,CAAC,EAAE,GAAGA,MAAI,EAAE,WAAW,CAAC,IAAI,SAAS,CAAC;AAElF,MAAM,cAAc,MAClB,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,GAAG,GAAG,GAAG,GAAG,CAAC;AAEjE,MAAM,YAAY,MAChB,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,GAAG,IAAI,IAAI,IAAI,GAAG;;AAGtE,MAAM,WAAW,GAAS,MAAoB;CAC5C,MAAM,MAAM,IAAI,KAAK,EAAE,QAAQ,CAAC;CAChC,IAAI,QAAQ,IAAI,QAAQ,IAAI,CAAC;CAC7B,OAAO;AACT;AAEA,MAAM,MAAM,KAAW,OAAe,YACpC,IAAI,KAAK,IAAI,YAAY,GAAG,IAAI,SAAS,GAAG,IAAI,QAAQ,GAAG,OAAO,SAAS,GAAG,CAAC;AAEjF,MAAM,UAAU,MAAgB,MAAY,SAA6B;CACvE;CACA,IAAI;CACJ,KAAK,WAAW,IAAI;CACpB;AACF;;;;;;;;AAWA,MAAa,aAAa,OAAe,KAAa,sBAAY,IAAI,KAAK,MAAkB;CAC3F,MAAM,OAAO,OAAO,OAAO,EAAE,CAAC,CAAC,KAAK;CACpC,IAAI,CAAC,MAAM,MAAM,IAAI,iBAAiB,OAAO,OAAO,GAAG,GAAG,aAAa;CACvE,MAAM,QAAQ,KAAK,YAAY;CAE/B,MAAM,KAAK,aAAa,KAAK,IAAI;CACjC,IAAI,IAAI;EACN,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,QAAQ;EACvC,MAAM,OAAO,OACT,IAAI,KAAK,KAAK,QAAQ,KAAK,GAAG,CAAC,IAC/B,IAAI,KACF,OAAO,CAAC,GACR,OAAO,EAAE,IAAI,GACb,OAAO,CAAC,GACR,OAAO,EAAE,GACT,OAAO,EAAE,GACT,OAAO,MAAM,GAAG,GAChB,CACF;EACJ,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAC7B,MAAM,IAAI,iBAAiB,OAAO,MAAM,uBAAuB;EAEjE,OAAO,OAAO,SAAS,MAAM,IAAI;CACnC;CAEA,MAAM,OAAO,SAAS,KAAK,IAAI;CAC/B,IAAI,MAAM;EACR,MAAM,GAAG,GAAG,IAAI,KAAK;EACrB,MAAM,OAAO,IAAI,KAAK,OAAO,CAAC,GAAG,OAAO,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;EACtE,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAC7B,MAAM,IAAI,iBAAiB,OAAO,MAAM,uBAAuB;EAGjE,IAAI,KAAK,SAAS,MAAM,OAAO,EAAE,IAAI,KAAK,KAAK,QAAQ,MAAM,OAAO,CAAC,GACnE,MAAM,IAAI,iBAAiB,OAAO,MAAM,mBAAmB,EAAE,YAAY,IAAI;EAE/E,OAAO,OAAO,UAAU,MAAM,IAAI;CACpC;CAEA,MAAM,MAAM,OAAO,KAAK,KAAK;CAC7B,IAAI,KAAK;EACP,MAAM,IAAI,OAAO,IAAI,EAAE;EACvB,MAAM,OAAO,OAAO,IAAI,EAAE;EAC1B,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,MAAM,IAAI,iBAAiB,OAAO,MAAM,4BAA4B;EAC7F,IAAI,KAAK,WAAW,GAAG,GAAG,OAAO,OAAO,SAAS,QAAQ,KAAK,CAAC,GAAG,IAAI;EACtE,IAAI,KAAK,WAAW,GAAG,GAAG,OAAO,OAAO,SAAS,QAAQ,KAAK,IAAI,CAAC,GAAG,IAAI;EAC1E,MAAM,KAAK,KAAK,WAAW,GAAG,IAAI,IAAI,OAAY,IAAI;EACtD,OAAO,OAAO,SAAS,IAAI,KAAK,IAAI,QAAQ,IAAI,EAAE,GAAG,IAAI;CAC3D;CAEA,MAAM,OAAO,SAAS,KAAK,KAAK;CAChC,IAAI,MAAM;EACR,MAAM,MAAM,KAAK,OAAO,aAAa,QAAQ,KAAK,CAAC,IAAI;EACvD,IAAI,KAAK,OAAO,KAAA,GAAW,OAAO,OAAO,UAAU,WAAW,GAAG,GAAG,IAAI;EACxE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,EAAE,GAAG,OAAO,KAAK,EAAE,CAAC;EAClD,IAAI,KAAK,MAAM,KAAK,IAClB,MAAM,IAAI,iBAAiB,OAAO,MAAM,GAAG,GAAG,GAAG,KAAK,GAAG,eAAe;EAC1E,OAAO,OAAO,SAAS,GAAG,KAAK,IAAI,EAAE,GAAG,IAAI;CAC9C;CAGA,MAAM,OAAO,SAAS,KAAK,KAAK;CAChC,IAAI,MAAM;EACR,MAAM,MAAM,UAAU,WAAW,MAAM,MAAM,KAAK,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,KAAK,EAAE;EACjF,IAAI,QAAQ,IACV,MAAM,IAAI,iBAAiB,OAAO,MAAM,IAAI,KAAK,GAAG,2BAA2B;EAEjF,MAAM,SAAS,MAAM,IAAI,OAAO,IAAI,KAAK,KAAK;EAC9C,MAAM,MAAM,QAAQ,KAAK,KAAK;EAC9B,IAAI,KAAK,OAAO,KAAA,GAAW,OAAO,OAAO,UAAU,WAAW,GAAG,GAAG,IAAI;EACxE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,EAAE,GAAG,OAAO,KAAK,EAAE,CAAC;EAClD,IAAI,KAAK,MAAM,KAAK,IAClB,MAAM,IAAI,iBAAiB,OAAO,MAAM,GAAG,GAAG,GAAG,KAAK,GAAG,eAAe;EAC1E,OAAO,OAAO,SAAS,GAAG,KAAK,IAAI,EAAE,GAAG,IAAI;CAC9C;CAEA,MAAM,IAAI,iBAAiB,OAAO,MAAM,uCAAuC;AACjF;;;;;;;;AASA,MAAa,cACX,OACA,KACA,MACA,sBAAY,IAAI,KAAK,MACZ;CACT,MAAM,SAAS,UAAU,OAAO,KAAK,GAAG;CACxC,IAAI,OAAO,SAAS,UAAU,OAAO,OAAO;CAC5C,OAAO,SAAS,QAAQ,SAAS,OAAO,EAAE,IAAI,WAAW,OAAO,EAAE;AACpE;;;;;;;;;AAYA,MAAa,cACX,MAMA,sBAAY,IAAI,KAAK,MACX;CACV,MAAM,OAAO,KAAK,OAAO,WAAW,QAAQ,KAAK,MAAM,SAAS,GAAG,IAAI,WAAW,GAAG;CACrF,MAAM,KAAK,KAAK,KACZ,WAAW,MAAM,KAAK,IAAI,OAAO,GAAG,IACpC,SAAS,QAAQ,MAAM,KAAK,mBAAmB,CAAC,CAAC;CAErD,IAAI,GAAG,QAAQ,IAAI,KAAK,QAAQ,GAC9B,MAAM,IAAI,iBACR,MACA,OAAO,KAAK,MAAM,EAAE,GACpB,kBAAkB,WAAW,EAAE,EAAE,0BAA0B,WAAW,IAAI,EAAE,EAC9E;CAGF,MAAM,QAAQ,KAAK,eAAe;CAClC,IAAI,GAAG,QAAQ,IAAI,KAAK,QAAQ,IAAI,OAClC,OAAO;EAAE;EAAM,IAAI,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK;EAAG,SAAS;CAAK;CAErE,OAAO;EAAE;EAAM;EAAI,SAAS;CAAM;AACpC;;AAGA,MAAa,iBAAiB,OAAe,QAAiC;CAC5E,MAAM,OAAO,OAAO,OAAO,EAAE,CAAC,CAC3B,KAAK,CAAC,CACN,YAAY;CACf,IAAI,CAAC,MAAM,MAAM,IAAI,iBAAiB,OAAO,OAAO,GAAG,GAAG,aAAa;CACvE,MAAM,IAAI,SAAS,KAAK,IAAI;CAC5B,IAAI,CAAC,GACH,MAAM,IAAI,iBAAiB,OAAO,MAAM,qDAAiD;CAC3F,MAAM,IAAI,OAAO,EAAE,EAAE;CACrB,IAAI,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,GAC9B,MAAM,IAAI,iBAAiB,OAAO,MAAM,sCAAsC;CAEhF,OAAO,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,IAAI,KAAK;AAC1C;;AAKA,MAAa,cAAc,OAAwB;CACjD,IAAI;EAGF,KAAK,eAAe,SAAS,EAAE,UAAU,GAAG,CAAC;EAC7C,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;AAQA,MAAa,eAAe,OAAiD;CAC3E,IAAI,CAAC,IAAI,OAAO;CAChB,MAAM,OAAO,OAAO,EAAE,CAAC,CAAC,KAAK;CAC7B,IAAI,CAAC,QAAQ,SAAS,UAAU,OAAO;CACvC,IAAI,WAAW,IAAI,GAAG,OAAO;CAC7B,MAAM,QAAQ,aAAa,KAAK,IAAI;CAIpC,IAAI,OAAO;EACT,MAAM,GAAG,MAAM,IAAI,MAAM;EACzB,IAAI,OAAO,MAAM;GAEf,MAAM,OAAO,UADG,SAAS,MAAM,MAAM,MACJ,OAAO,EAAE;GAC1C,IAAI,WAAW,IAAI,GAAG,OAAO;EAC/B;CACF;CACA,OAAO;AACT;AAEA,MAAM,WAAW,GAAS,OAAuC;CAC/D,MAAM,MAAM,KAAK,eAAe,SAAS;EACvC,UAAU;EACV,WAAW;EACX,MAAM;EACN,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,cAAc;CAChB,CAAC;CACD,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,KAAK,IAAI,cAAc,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE;CACtD,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,iBACX,cACA,IACA,QACA,gBACwB;CACxB,IAAI,iBAAiB,QAAQ,iBAAiB,KAAA,KAAa,CAAC,OAAO,SAAS,YAAY,GACtF,OAAO;CAET,MAAM,oBAAI,IAAI,MAAM,eAAe,eAAe,GAAI;CACtD,IAAI,OAAO,MAAM,EAAE,QAAQ,CAAC,GAAG,OAAO;CAEtC,IAAI,QAAQ;EACV,MAAM,OAAO,YAAY,EAAE;EAC3B,IAAI,MAAM;GACR,MAAM,IAAI,QAAQ,GAAG,IAAI;GACzB,OAAO;IAAE,QAAQ;IAAM,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE;IAAO,UAAU;GAAK;EAC9E;EAKA,OAAO;GACL,QAAQ;GACR,KAAK,GAAG,EAAE,YAAY,EAAE,GAAGA,MAAI,EAAE,SAAS,IAAI,CAAC,EAAE,GAAGA,MAAI,EAAE,QAAQ,CAAC;GACnE,UAAU;EACZ;CACF;CAEA,MAAM,OAAO,YAAY,EAAE;CAC3B,IAAI,CAAC,MAGH,OAAO;EAAE,QAAQ;EAAO,KAAK,WAAW,CAAC;EAAG,UAAU;CAAK;CAG7D,MAAM,IAAI,QAAQ,GAAG,IAAI;CAEzB,MAAM,MAAM,EAAE,gBAAgB;CAC9B,MAAM,SAAS,QAAQ,QAAQ,MAAM,IAAI,QAAQ,QAAQ,EAAE;CAC3D,OAAO;EACL,QAAQ;EACR,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,IAAI,GAAG,EAAE,KAAK,GAAG,EAAE,OAAO,GAAG,EAAE,SAAS;EACvE,UAAU;CACZ;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrXA,MAAa,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuKvB,MAAa,UAAU,MAAc,OAAkC,CAAC,MAAc;EACpF,QAAQ;;;iCAGuB,KAAK,cAAc,SAAS,QAAQ;;;;;MAK/D,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvMX,MAAa,eAAe,OAC1B;;;;;;;;;;;;;;;;;GAkBA,EAAE,aAAa,KAAK,CACtB;AAEA,MAAa,eAAe,OAC1B;;;;;;;;;;GAWA,EAAE,aAAa,KAAK,CACtB;;;;;;;;;;;;;;;;;;AAmBA,MAAa,gBAAgB,OAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CA,EAAE,aAAa,KAAK,CACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7FA,MAAa,kBAAkB;;AAG/B,MAAa,iBAAiB;;AAG9B,MAAa,kBAAkB;;AAG/B,MAAM,YAAY;AAElB,MAAM,eAAe;AAwBrB,MAAa,wBAAwB,OAAe,QAAQ,MAC1D,KAAK,MAAM,WAAW,oBAAoB,eAAe;AAE3D,MAAa,oBAAoB,OAAe,QAAQ,MACtD,KAAK,qBAAqB,IAAI,GAAG,cAAc;AAEjD,MAAM,WAAW,QAA0B;CACzC,IAAI;EACF,OAAO,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC,CAAC,CAAC,SAAS,MACxD,EAAE,YAAY,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAChC;CACF,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,MAAM,QAAQ,KAAa,OAAe,QAA4B;CACpE,IAAI,QAAQ,WAAW,OAAO;CAC9B,IAAI;CACJ,IAAI;EACF,UAAU,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;CACpD,QAAQ;EACN,OAAO;CACT;CACA,KAAK,MAAM,KAAK,SAAS;EACvB,MAAM,IAAI,KAAK,KAAK,EAAE,IAAI;EAC1B,IAAI,EAAE,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,GAAG;OACtC,IAAI,aAAa,KAAK,EAAE,IAAI,GAAG,IAAI,KAAK,CAAC;CAChD;CACA,OAAO;AACT;AAEA,MAAM,WACJ;AAIF,MAAa,eACX,OAA0D,CAAC,MAC1C;CACjB,MAAM,gBAAgB,qBAAqB,KAAK,IAAI;CAGpD,IAAI,KAAK,WAAW;EAClB,MAAM,QAAQ,cAAc,KAAK,SAAS;EAC1C,OAAO;GACL,GAAG;GACH;GACA,WAAW,KAAK;GAChB,YAAY,CAAC;IAAE,GAAG;IAAO,MAAM,KAAK;GAAU,CAAC;GAC/C,mBAAmB;GACnB,eAAe;GACf,QAAQ,MAAM,WACV,OACA,MAAM,SACJ,gBAAgB,KAAK,UAAU,8BAA8B,aAC7D,cAAc,KAAK,UAAU;EACrC;CACF;CAIA,MAAM,QAAQ,iBAAiB,KAAK,IAAI;CACxC,MAAM,aAAa,cAAc,KAAK;CAEtC,MAAM,UAAU,QAAQ,aAAa;CACrC,MAAM,oBAAoB,QAAQ,SAAS;CAC3C,MAAM,gBAAgB,QAAQ,SAAS,eAAe;CAEtD,MAAM,QAAQ,WAAW,WAAW,CAAC,KAAK,IAAI,KAAK,eAAe,GAAG,CAAC,CAAC;CACvE,MAAM,cAAgC,MAAM,SAAS,KAAK,IAAI,QAAQ,CAAC,OAAO,GAAG,KAAK,EAAA,CACnF,KAAK,OAAO;EAAE,GAAG,cAAc,CAAC;EAAG,MAAM;CAAE,EAAE,CAAC,CAC9C,QAAQ,MAAM,EAAE,MAAM,CAAC,CACvB,UAAU,GAAG,OAAO,EAAE,QAAQ,MAAM,EAAE,QAAQ,EAAE;CAKnD,MAAM,SAAS,WAAW,WACrB;EAAE,GAAG;EAAY,MAAM;CAAM,IAC7B,WAAW,MAAM,MAAM,EAAE,QAAQ,KAAK,WAAW,MAAM;CAE5D,MAAM,SAAS,QAAQ,WACnB,OACA,WAAW,SACT,+BAA+B,MAAM,uBAAuB,aAC5D,oBACE,uBAAuB,cAAc,oDACrC,WAAW,MAAM,2GACwB;CAajD,OAAO;EACL,GAZwB,SACtB;GACE,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,MAAM,OAAO;GACb,OAAO,OAAO;GACd,YAAY,OAAO;GACnB,cAAc,OAAO;EACvB,IACA;EAIF;EACA,WAAW,QAAQ,QAAQ;EAC3B;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzGA,MAAM,SAAS,GAAa,gBAAgC;CAC1D,MAAM,KAAK,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,WAAW;CACvE,MAAM,OAAO,KAAM,GAAG,SAAS,GAAG,MAAM,GAAG,MAAO,OAAO,EAAE,UAAU;CACrE,OAAO,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,YAAY,EAAE,GAAG;AAC5C;;;;;;;;;;AAWA,MAAM,iBAAiB,MACrB,EAAE,eAAe,QAAQ,EAAE,kBAAkB,OAAO,GAAG,EAAE,WAAW,GAAG,EAAE,kBAAkB;AAE7F,MAAM,oBAAoB,MAAwB,GAAG,EAAE,OAAO,GAAG,EAAE;AAEnE,MAAa,cAAc,SAYR;CACjB,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,MAAkB,CAAC;CACzB,IAAI,UAAU;CAId,MAAM,2BAAW,IAAI,IAAY;CACjC,KAAK,MAAM,KAAK,KAAK,OAAO;EAC1B,MAAM,IAAI,cAAc,CAAC;EACzB,IAAI,GAAG,SAAS,IAAI,CAAC;CACvB;CAIA,KAAK,MAAM,KAAK,CAAC,GAAG,KAAK,OAAO,GAAG,KAAK,WAAW,GAAG;EACpD,IAAI,EAAE,eAAe,MAAM;GACzB,WAAW;GACX;EACF;EACA,IAAI,EAAE,WAAW,gBAAgB,SAAS,IAAI,iBAAiB,CAAC,CAAC,GAAG;EACpE,MAAM,IAAI,MAAM,GAAG,KAAK,WAAW;EACnC,IAAI,KAAK,IAAI,CAAC,GAAG;EACjB,KAAK,IAAI,CAAC;EACV,IAAI,KAAK,CAAC;CACZ;CAEA,IAAI,MAAM,GAAG,OAAO,EAAE,cAAc,MAAM,EAAE,cAAc,EAAE;CAE5D,MAAM,SAAsB;EAC1B,MAAM,IAAI,MAAM,GAAG,KAAK,KAAK;EAC7B,WAAW,KAAK,qBAAqB,aAAa;EAClD,UAAU,KAAK;EACf;CACF;CAEA,IAAI,CAAC,KAAK,oBAAoB;EAC5B,OAAO,kBACL,KAAK,qBACL;EAEF,OAAO;CACT;CAEA,MAAM,MAAM,KAAK;CACjB,IAAI,KAAK;EACP,MAAM,OAAO,KAAK,YAAY,IAAI;EAClC,MAAM,SAAS,KAAK,UAAU,IAAI;EAClC,IAAI,QAAQ,QACV,OAAO,YAAY;GACjB,QACE;GAEF,GAAI,OAAO,EAAE,oBAAoB,KAAK,UAAU,IAAI,CAAC;GACrD,GAAI,SAAS,EAAE,kBAAkB,KAAK,QAAQ,IAAI,CAAC;GACnD,SAAS;EACX;CAEJ;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7HA,MAAa,cAAc;;AAG3B,MAAM,OAAO;;AAGb,MAAM,QAAQ;AAEd,MAAM,YAAY;AAClB,MAAM,gBAAgB;AAatB,MAAM,OAAO,GAAW,IAAI,MAAc,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;;AAGnE,MAAM,WAAW,MAAoB;CACnC,MAAM,OAAO,CAAC,EAAE,kBAAkB;CAClC,MAAM,OAAO,OAAO,IAAI,MAAM;CAC9B,MAAM,MAAM,KAAK,IAAI,IAAI;CACzB,MAAM,OAAO,SAAS,IAAI,MAAM,GAAG,OAAO,IAAI,KAAK,MAAM,MAAM,EAAE,CAAC,IAAI,IAAI,MAAM,EAAE;CAClF,OACE,GAAG,EAAE,YAAY,IAAI,IAAI,EAAE,SAAS,IAAI,CAAC,IAAI,IAAI,EAAE,QAAQ,CAAC,EAAA,GACxD,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,EAAE,WAAW,CAAC,IAAI,IAAI,EAAE,WAAW,CAAC,IAAI;AAExE;AAEA,MAAM,aAAa,SAA8B;CAC/C,MAAM,IAAI,MAAM,KAAK,IAAI;CACzB,IAAI,CAAC,GAAG,OAAO;CACf,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,QAAQ;CACvC,MAAM,MACJ,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,QAC9B,SAAS,MAAM,MAAM,GAAG,KAAM,MAAM,GAAG,CAAC,EAAE,GAAG,KAAM,MAAM,CAAC;CAC7D,MAAM,OAAO,IAAI,KAAK,GAAG;CACzB,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO;AAC/C;AAEA,MAAa,aACX,aACA,UACA,oBAEA;CACE,QAAoB,OAAO,eAAe,EAAE;CAC5C,kBAAkB,QAAQ,eAAe,IAAI;CAC7C,OAAO,YAAY,EAAE;AACvB,CAAC,CAAC,KAAK,SAAS;AAElB,MAAa,aAAa,QAA6B;CACrD,MAAM,OAAO,OAAO,OAAO,EAAE;CAC7B,MAAM,QAAQ,KAAK,QAAQ,GAAG;CAC9B,MAAM,UAAU,UAAU,KAAK,KAAK,KAAK,MAAM,GAAG,KAAK;CAEvD,IAAI,YAAA,MAAyB;EAI3B,IAAI,aAAa,KAAK,OAAO,GAC3B,MAAM,IAAI,kBACR,cAAc,QAAQ,mJAGxB;EAEF,MAAM,IAAI,kBACR,0BAA0B,KAAK,UAAU,GAAG,EAAE,sGAE9C,EAAE,UAAU,2CAAuD,CACrE;CACF;CAEA,MAAM,OAAO,KAAK,MAAM,QAAQ,CAAC;CACjC,MAAM,QAAQ,KAAK,QAAQ,SAAS;CACpC,MAAM,SAAS,KAAK,QAAQ,WAAW,QAAQ,CAAC;CAChD,IAAI,UAAU,MAAM,WAAW,IAC7B,MAAM,IAAI,kBACR,0BAA0B,KAAK,UAAU,GAAG,EAAE,kBAAkB,UAAU,gBAC1E,EAAE,UAAU,2CAAuD,CACrE;CAGF,MAAM,cAAc,KAAK,MAAM,GAAG,KAAK;CACvC,MAAM,aAAa,KAAK,MAAM,QAAQ,GAAG,MAAM;CAI/C,MAAM,WAAW,KAAK,MAAM,SAAS,CAAC;CAEtC,IAAI,CAAC,UACH,MAAM,IAAI,kBACR,0BAA0B,KAAK,UAAU,GAAG,EAAE,uBAC9C,EAAE,UAAU,2CAAuD,CACrE;CAGF,MAAM,kBAAkB,eAAe,gBAAgB,OAAO,UAAU,UAAU;CAClF,IAAI,eAAe,iBAAiB,CAAC,iBACnC,MAAM,IAAI,kBACR,0BAA0B,KAAK,UAAU,GAAG,EAAE,IAAI,KAAK,UAAU,UAAU,EAAE,yFAE/E;CAGF,OAAO;EAAE;EAAa;EAAU;EAAiB,cAAc,oBAAoB;CAAK;AAC1F;;AAGA,MAAa,eAAe,QAA6B,UAAU,IAAI,aAAa,IAAI,QAAQ;;AAGhG,MAAa,UAAU,OACrB,KAAK,KAAK,OAAO,MAAM,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,YAAY,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrHrD,MAAM,WAAW,CAAC,gBAAgB,UAAU;;;;;;;;;;;;;;;;;AAkB5C,MAAM,qBAAqB;AAC3B,MAAM,eAAe;AAoGrB,MAAM,QAAQ,MAAwB,MAAM,KAAK,MAAM;AACvD,MAAM,OAAO,MAA+B,OAAO,MAAM,WAAW,IAAI;AACxE,MAAM,QAAQ,MAA+B,OAAO,MAAM,WAAW,IAAI;AAEzE,IAAa,gBAAb,MAA2B;CACzB;CACA;CACA;CAEA,YAAY,IAAkB,MAAc,MAAyB;EACnE,KAAK,KAAK;EACV,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;;;;;;;;CASA,KAAK,SAAsB,OAAe,MAAc,QAAQ,MAAc;EAC5E,OAAO,QAAQ,IAAI,IAAI,IAAI,GAAG,MAAM,IAAI,KAAK,OAAO,UAAU,WAAW;CAC3E;CAEA,eAAuB;EACrB,MAAM,IAAI,KAAK,KAAK;EACpB,OAAO;GACL;GACA,KAAKC,KAAK,GAAG,MAAM,QAAQ,MAAM;GACjC,KAAKA,KAAK,GAAG,MAAM,WAAW,SAAS;GACvC,KAAKA,KAAK,GAAG,MAAM,eAAe,aAAa;GAC/C,KAAKA,KAAK,GAAG,MAAM,OAAO,KAAK;GAC/B,KAAKA,KAAK,GAAG,MAAM,kBAAkB,eAAe;GACpD,KAAKA,KAAK,GAAG,MAAM,WAAW,QAAQ;GACtC,KAAKA,KAAK,GAAG,MAAM,YAAY,SAAS;GACxC,KAAKA,KAAK,GAAG,MAAM,UAAU,OAAO;GACpC,KAAKA,KAAK,GAAG,MAAM,UAAU,QAAQ;GACrC,KAAKA,KAAK,GAAG,MAAM,qBAAqB,kBAAkB;GAC1D,KAAKA,KAAK,GAAG,MAAM,gBAAgB,cAAc;GACjD,KAAKA,KAAK,GAAG,MAAM,mBAAmB,gBAAgB;GACtD,KAAKA,KAAK,GAAG,MAAM,iBAAiB,cAAc;GAClD,KAAKA,KAAK,GAAG,MAAM,gBAAgB,YAAY;GAC/C,KAAKA,KAAK,GAAG,MAAM,aAAa,eAAe;GAC/C,KAAKA,KAAK,GAAG,MAAM,eAAe,YAAY;GAC9C;GACA;GACA,KAAK,KAAK,cAAc,iCAAiC;EAC3D,CAAC,CAAC,KAAK,cAAc;CACvB;CAEA,SAAiB;EAKf,OAAO,wDAHL,KAAK,KAAK,eAAe,KAAK,KAAK,YAAY,IAAI,aAAa,IAC5D,qEACA;CAER;;;;;;;;;;;;;;CAeA,UAAU,QAAQ,MAAc;EAC9B,MAAM,IAAI,KAAK,KAAK;EACpB,MAAM,MAAgB,CAAC;EACvB,IAAI,EAAE,IAAI,aAAa,GAAG,IAAI,KAAK,GAAG,MAAM,mBAAmB;EAC/D,IAAI,EAAE,IAAI,QAAQ,GAAG,IAAI,KAAK,IAAI,MAAM,uBAAuB,MAAM,eAAe;EACpF,IAAI,EAAE,IAAI,gBAAgB,GACxB,IAAI,KAAK,IAAI,MAAM,+BAA+B,MAAM,uBAAuB;EAEjF,OAAO,IAAI,SAAS,OAAO,IAAI,KAAK,oBAAoB,MAAM;CAChE;CAEA,gBAAgB,OAAyE;EACvF,IAAI,CAAC,OAAO,QAAQ,OAAO;GAAE,KAAK;GAAI,QAAQ,CAAC;EAAE;EAEjD,OAAO;GAAE,KAAK,sBADA,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,IACA,EAAE;GAAI,QAAQ,CAAC,GAAG,KAAK;EAAE;CACnE;CAEA,UAAU,KAAa,QAAmB,QAAwC;EAEhF,OADY,KAAK,GAAG,QAAQ,GAAG,CAAC,CAAC,IAAI,GAAI,MAChC,CAAC,CAAC,KAAK,OAAO;GACrB,QAAQ,OAAO,EAAE,MAAM;GACvB,MAAM,KAAK,EAAE,IAAI;GACjB,YAAY,IAAI,EAAE,UAAU;GAC5B,cAAc,KAAK,EAAE,YAAY;GACjC,eAAe,KAAK,EAAE,aAAa;GACnC,SAAS,KAAK,EAAE,OAAO;GACvB,aAAa,KAAK,EAAE,WAAW;GAC/B,KAAK,KAAK,EAAE,GAAG;GACf,eAAe,KAAK,EAAE,aAAa;GACnC,eAAe,KAAK,EAAE,aAAa;GACnC,YAAY,IAAI,EAAE,UAAU;GAC5B,UAAU,IAAI,EAAE,QAAQ;GACxB,QAAQ,KAAK,EAAE,MAAM;GACrB,SAAS,KAAK,EAAE,OAAO;GACvB,OAAO,KAAK,EAAE,KAAK;GACnB,QAAQ,IAAI,EAAE,MAAM;GACpB,kBAAkB,IAAI,EAAE,gBAAgB;GACxC,cAAc,IAAI,EAAE,YAAY;GAChC,gBAAgB,KAAK,EAAE,cAAc;GACrC,cAAc,KAAK,EAAE,YAAY;GACjC,YAAY,IAAI,EAAE,UAAU;GAC5B,eAAe,IAAI,EAAE,aAAa;GAClC;EACF,EAAE;CACJ;;;;;;;;;;;;CAaA,WAAW,GAA2B;EACpC,MAAM,IAAI,KAAK,KAAK;EACpB,IAAI,CAAC,EAAE,IAAI,YAAY,GAAG,OAAO,CAAC;EAClC,MAAM,MAAM,KAAKC,gBAAgB,EAAE,aAAa;EAChD,MAAM,UAAU,EAAE,IAAI,UAAU,IAC5B,6CACA;EAIJ,MAAM,cAAc,EAAE,IAAI,iBAAiB,IACvC,mEACA,KAAK,KAAK,gBACR,kFACA;EACN,MAAM,MAAM;;eAED,EAAE,IAAI,UAAU,IAAI,kBAAkB,OAAO;eAC7C,KAAKC,aAAa,EAAE;;UAEzB,KAAKC,OAAO,EAAE;;eAET,QAAQ;WACZ,YAAY;WACZ,KAAKC,UAAU,EAAE;WACjB,IAAI,IAAI;;;EAGf,OAAO,KAAKC,UAAU,KAAK;GAAC,EAAE;GAAS,EAAE;GAAW,GAAG,IAAI;GAAQ,EAAE;EAAK,GAAG,MAAM;CACrF;;;;;;;;;CAUA,iBAAiB,GAA2B;EAC1C,IAAI,CAAC,KAAK,KAAK,oBAAoB,OAAO,CAAC;EAC3C,MAAM,IAAI,KAAK,KAAK;EACpB,IAAI,CAAC,EAAE,IAAI,iBAAiB,KAAK,CAAC,EAAE,IAAI,UAAU,GAAG,OAAO,CAAC;EAC7D,MAAM,MAAM,KAAKJ,gBAAgB,EAAE,aAAa;EAChD,MAAM,UAAU,EAAE,IAAI,qBAAqB,IACvC,6DACA;EACJ,MAAM,MAAM;;eAED,EAAE,IAAI,qBAAqB,IAAI,6BAA6B,OAAO;eACnE,KAAKC,aAAa,EAAE;;;UAGzB,KAAKC,OAAO,EAAE;;eAET,QAAQ;WACZ,KAAKC,UAAU,EAAE;WACjB,IAAI,IAAI;;;EAGf,OAAO,KAAKC,UAAU,KAAK;GAAC,EAAE;GAAS,EAAE;GAAW,GAAG,IAAI;GAAQ,EAAE;EAAK,GAAG,YAAY;CAC3F;;;;;;;;;CAUA,YAAY,GAA4B;EACtC,MAAM,IAAI,KAAK,KAAK;EACpB,MAAM,MAAM,KAAKJ,gBAAgB,EAAE,aAAa;EAChD,MAAM,SAAS,IAAI,WAAW,EAAE,IAAI,EAAE;EACtC,MAAM,SAAS,CAAC,SAAS;EACzB,IAAI,EAAE,UAAU,QAAQ;GACtB,IAAI,EAAE,IAAI,aAAa,GAAG,OAAO,KAAK,aAAa;GACnD,IAAI,KAAK,KAAK,aAAa,OAAO,KAAK,YAAY;EACrD;EACA,MAAM,QAAQ,OACX,KAAK,MACJ,MAAM,eAAe,mCAAmC,OAAO,EAAE,qBACnE,CAAC,CACA,KAAK,MAAM;EACd,MAAM,MAAM;;eAED,EAAE,IAAI,UAAU,IAAI,kBAAkB,OAAO;eAC7C,KAAKC,aAAa,EAAE;;UAEzB,KAAKC,OAAO,EAAE;gBACR,MAAM;;;WAGX,KAAKC,UAAU,EAAE;WACjB,IAAI,IAAI;;;EAGf,MAAM,SAAS;GAAC,GAAG,OAAO,UAAU,MAAM;GAAG,EAAE;GAAS,EAAE;GAAW,GAAG,IAAI;GAAQ,EAAE;EAAK;EAC3F,OAAO,KAAKC,UAAU,KAAK,QAAQ,MAAM;CAC3C;;CAGA,OAAO,MAA+B;EACpC,MAAM,IAAI,KAAK,KAAK;EACpB,IAAI,CAAC,EAAE,IAAI,MAAM,GAAG,OAAO;EAC3B,MAAM,MAAM;;eAED,EAAE,IAAI,UAAU,IAAI,kBAAkB,OAAO;eAC7C,KAAKH,aAAa,EAAE;;UAEzB,KAAKC,OAAO,EAAE;;WAEb,KAAKC,UAAU,EAAE;;EAExB,OAAO,KAAKC,UAAU,KAAK,CAAC,KAAK,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM;CACjE;;;;;;;;;CAUA,WAAqB;EACnB,IAAI,CAAC,KAAK,KAAK,sBAAsB,CAAC,KAAK,KAAK,kBAAkB,IAAI,iBAAiB,GACrF,OAAO;EAET,MAAM,MAAM,KAAK,GACd,QACC;sEAEF,CAAC,CACA,IAAI;EACP,MAAM,KAAK,IAAI,KAAK,EAAE;EACtB,MAAM,KAAK,IAAI,KAAK,EAAE;EACtB,IAAI,OAAO,QAAQ,OAAO,MAAM,OAAO;EACvC,OAAO;GAAE,WAAW;GAAI,SAAS;GAAI,MAAM,OAAO,KAAK,KAAK,CAAC;EAAE;CACjE;CAEA,YAA6B;EAC3B,MAAM,IAAI,KAAK,KAAK;EACpB,MAAM,QAAQ,KAAK,KAAK,aAAa,OAAO;EAC5C,MAAM,MAAM;eACD,KAAKL,KAAK,GAAG,OAAO,QAAQ,MAAM,EAAE;eACpC,KAAKA,KAAK,GAAG,OAAO,SAAS,OAAO,EAAE;eACtC,KAAKA,KAAK,GAAG,OAAO,SAAS,OAAO,EAAE;eACtC,KAAKA,KAAK,GAAG,OAAO,QAAQ,MAAM,EAAE;eACpC,KAAKA,KAAK,GAAG,OAAO,kBAAkB,eAAe,EAAE;eACvD,KAAKA,KAAK,GAAG,OAAO,gBAAgB,aAAa,EAAE;eACnD,KAAKA,KAAK,GAAG,OAAO,cAAc,WAAW,EAAE;eAC/C,QAAQ,cAAc,OAAO;;UAElC,QAAQ,wDAAwD,GAAG;kBAC3D,EAAE,IAAI,eAAe,IAAI,6BAA6B,GAAG;EAEvE,OADa,KAAK,GAAG,QAAQ,GAAG,CAAC,CAAC,IACxB,CAAC,CAAC,KAAK,OAAO;GACtB,MAAM,KAAK,EAAE,IAAI;GACjB,OAAO,KAAK,EAAE,KAAK;GACnB,OAAO,KAAK,EAAE,KAAK;GACnB,MAAM,KAAK,EAAE,IAAI;GACjB,aAAa,KAAK,EAAE,WAAW;GAI/B,cAAc,QAAQ,KAAK,EAAE,SAAS,CAAC;GACvC,aAAa,KAAK,EAAE,WAAW;GAC/B,WAAW,IAAI,EAAE,aAAa,KAAK,KAAK;GACxC,eAAe,IAAI,EAAE,aAAa;EACpC,EAAE;CACJ;CAEA,WAA2B;EACzB,IAAI,CAAC,KAAK,KAAK,aAAa,MAAM,OAAO,CAAC;EAU1C,OATa,KAAK,GACf,QACC;;;;iCAKF,CAAC,CACA,IACO,CAAC,CAAC,KAAK,OAAO;GACtB,MAAM,KAAK,EAAE,IAAI;GACjB,MAAM,IAAI,EAAE,IAAI;GAChB,WAAW,OAAO,EAAE,aAAa,CAAC;EACpC,EAAE;CACJ;CAEA,QAAc;EACZ,IAAI;GACF,KAAK,GAAG,MAAM;EAChB,QAAQ,CAER;CACF;AACF;AAEA,MAAa,cAAc,OAAwC;CACjE,MAAM,cAAc,IAAI,IAAI,UAAU,IAAI,cAAc,CAAC;CACzD,MAAM,kBAAkB,IAAI,IAAI,UAAU,IAAI,UAAU,CAAC;CAEzD,KAAK,MAAM,KAAK,UAEd,KADa,MAAM,iBAAiB,cAAc,gBAAA,CACzC,SAAS,GAChB,MAAM,IAAI,iBACR,8BAA8B,EAAE,iCAAiC,aAAa,2BACtD,mBAAmB,4JAG7C;CAIJ,MAAM,oBAAoB,IAAI,IAAI,UAAU,IAAI,iBAAiB,CAAC;CAElE,OAAO;EACL,aAAa,kBAAkB,EAAE;EACjC;EACA;EACA;EACA,mBAAmB,IAAI,IAAI,UAAU,IAAI,YAAY,CAAC;EACtD,cAAc,IAAI,IAAI,UAAU,IAAI,OAAO,CAAC;EAC5C,oBAAoB,kBAAkB,OAAO;EAC7C,mBAAmB,UAAU,IAAI,qBAAqB,CAAC,CAAC,SAAS;EACjE,eAAe,UAAU,IAAI,YAAY,CAAC,CAAC,SAAS;EACpD,mBAAmB,UAAU,IAAI,eAAe,CAAC,CAAC,SAAS;EAC3D,aAAa,UAAU,IAAI,UAAU,CAAC,CAAC,SAAS;EAChD,gBAAgB,UAAU,IAAI,YAAY,CAAC,CAAC,SAAS;EACrD,iBAAiB,UAAU,IAAI,aAAa,CAAC,CAAC,SAAS;EACvD,WAAW,UAAU,IAAI,OAAO,CAAC,CAAC,SAAS;EAG3C,aAAa;CACf;AACF;AAEA,MAAa,aACX,MACA,MACA,WACyB;CACzB,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,EACJ,IACA,MAAM,MACN,cACE,aAAgC,MAAM,MAAM;EAC9C,OAAO;EACP,QAAQ;EACR,UAAU;EACV,QAAQ,QAAQ,eAAe;EAC/B,kBACE,QAAQ,QACN,8IAEF;CACJ,CAAC;CACD,OAAO,IAAI,cAAc,IAAI,MAAM,SAAS;AAC9C;;;;;;;;;;;;;ACvdA,MAAM,mBAAmB;AACzB,MAAM,uBAAuB;;;;;;;;;;;AAY7B,MAAM,oBAAoB;;;;;;;;;;;;AAa1B,MAAM,kBAAkB;AAoLxB,IAAa,sBAAb,MAAiC;CAC/B;CACA;CACA;CACA;CAEA,WAAgC;CAChC,SAA+B;CAC/B,cAAc;CAEd,YAAY,MAA2B;EACrC,KAAK,SAAS,KAAK;EACnB,KAAKM,UAAU,KAAK;EACpB,KAAKC,OAAO,KAAK,8BAAc,IAAI,KAAK;EACxC,KAAK,SACH,KAAK,aACL,sBAAsB;GACpB,eAAe,KAAK,OAAO;GAC3B,WAAW,KAAK,OAAO;GACvB,SAAS;GACT,QAAQ,KAAK;EACf,CAAC;CACL;;CAGA,SAAuB;EACrB,KAAKC,aAAa,YAAY,EAAE,WAAW,KAAK,OAAO,UAAU,CAAC;EAClE,OAAO,KAAKA;CACd;;;;;;;CAQA,QAA8B;EAC5B,IAAI,KAAKC,aAAa,OAAO,KAAKC;EAClC,KAAKD,cAAc;EACnB,IAAI,KAAK,OAAO,cAAc,OAAO,OAAO;EAC5C,IAAI;GACF,KAAKC,SAAS,UAAU,KAAK,OAAO,CAAC,CAAC,WAAW,KAAK,OAAO,WAAW,KAAKJ,OAAO;EACtF,SAAS,KAAK;GACZ,KAAKA,SAAS,QAAQ,qCAAqC,GAAG;GAC9D,KAAKI,SAAS;EAChB;EACA,OAAO,KAAKA;CACd;;;;;;;;CASA,aAAmB;EACjB,KAAKA,QAAQ,MAAM;EACnB,KAAKA,SAAS;EACd,KAAKD,cAAc;EACnB,KAAKD,WAAW;CAClB;;;;;;;;;CAYA,WAA0B;EACxB,MAAM,QAAQ,KAAK,MAAM;EACzB,IAAI,OAAO,OAAO;EAClB,MAAM,UAAU,KAAK,OAAO;EAC5B,MAAM,IAAI,sBACR,QAAQ,UACN,yJAEJ;CACF;CAEA,SAAS,GAAS,OAA8B;EAC9C,OAAO,KAAK,MAAM,EAAE,QAAQ,IAAI,GAAI,IAAI,MAAM,KAAK;CACrD;CAEA,WAAW,SAAiB,OAA8B;EACxD,wBAAO,IAAI,MAAM,UAAU,MAAM,KAAK,eAAe,GAAI,EAAA,CAAE,YAAY;CACzE;;CAGA,eAAe,OAAsB,OAAiD;EACpF,MAAM,aAAa,KAAK,OAAO;EAC/B,MAAM,MAAM,MAAM,UAAU;EAC5B,MAAM,QAAQ,SAA2B;GACvC,MAAM,QAAQ,KAAK,YAAY;GAC/B,MAAM,OAAO,IAAI,QACd,MAAM,EAAE,MAAM,YAAY,MAAM,SAAS,EAAE,OAAO,YAAY,MAAM,KACvE;GACA,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,sBAAsB,MAAM,IAAI,KAAK,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,OAAO,OAAO,CAAC;GAErF,OAAO,KAAK,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,QAAQ,MAAmB,QAAQ,CAAC,CAAC;EACtE;EACA,IAAI,OAAO;GACT,MAAM,SAAS,KAAK,KAAK;GAGzB,IAAI,WAAW,QAAQ;IACrB,MAAM,UAAU,IAAI,IAAI,WAAW,QAAQ,IAAI,CAAC;IAChD,MAAM,OAAO,OAAO,QAAQ,MAAM,QAAQ,IAAI,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,sBAAsB,OAAO,UAAsB;IAE/D,OAAO;GACT;GACA,OAAO;EACT;EACA,IAAI,WAAW,QAAQ,OAAO,WAAW,QAAQ,IAAI;CAEvD;CAEA,SAAS,KAAe,MAA0D;EAChF,IAAI,CAAC,KAAK,aAAa,IAAI,WAAW,kBAAkB,OAAO;EAC/D,IAAI,CAAC,KAAK,YAAY,IAAI,qBAAqB,sBAAsB,OAAO;EAC5E,OAAO;CACT;CAEA,WAAW,KAAe,OAAoC;EAC5D,MAAM,QAAQ,cAAc,IAAI,YAAY,IAAI,SAAS,IAAI,QAAQ,MAAM,KAAK,WAAW;;;;;;;;;;;;;;;;;;EAkB3F,MAAM,MAAM,cACV,IAAI,UACJ,IAAI,SAAS,IAAI,SACjB,IAAI,QACJ,MAAM,KAAK,WACb;EACA,MAAM,eAAe,IAAI,WAAW;EAQpC,OAAO;GACL,KARU,UACV,IAAI,gBAAgB,IACpB,IAAI,QAAQ,IACZ,gBAAgB,IAAI,eAAe,uBAC/B,IAAI,MAAM,IAAI,aAAa,MAAM,KAAK,eAAe,GAAI,IACzD,IAGF;GACF,SAAS,IAAI;GACb;GACA;GACA,QAAQ,IAAI;GACZ,UAAU,IAAI;GACd,UAAU,IAAI;GACd;GACA,GAAI,eAAe,EAAE,WAAW,UAAU,IAAI,gBAAgB,IAAI,IAAI,QAAQ,EAAE,EAAE,IAAI,CAAC;GACvF,QAAQ,IAAI;GACZ,kBAAkB,IAAI;GACtB,QAAQ,IAAI;EACd;CACF;CAEA,WAAW,SAAkC;EAC3C,MAAM,QAAQ,KAAKG,SAAS;EAC5B,MAAM,QAAQ,WACZ;GACE,MAAM,QAAQ;GACd,IAAI,QAAQ;GACZ,kBAAkB,KAAK,OAAO;GAC9B,cAAc,KAAK,OAAO;EAC5B,GACA,KAAKJ,KAAK,CACZ;EACA,MAAM,YAAY,KAAKK,SAAS,MAAM,MAAM,KAAK;EACjD,MAAM,UAAU,KAAKA,SAAS,MAAM,IAAI,KAAK;EAC7C,MAAM,gBAAgB,KAAKC,eAAe,OAAO,QAAQ,QAAQ;EAIjE,MAAM,WAAW,KAAK,IAAI,QAAQ,QAAQ,IAAI,IAAI,GAAK;EACvD,MAAM,IAAI;GAAE;GAAW;GAAS,OAAO;GAAU,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;EAAG;EAE7F,MAAM,SAAS,WAAW;GACxB,OAAO,MAAM,WAAW,CAAC;GACzB,aAAa,MAAM,iBAAiB,CAAC;GACrC,UAAU,MAAM,SAAS;GACzB,oBAAoB,MAAM,KAAK;GAC/B;GACA;GACA,OAAO;GACP,aAAa,MAAM,KAAK;EAC1B,CAAC;EAED,MAAM,WAAW,QAAQ,mBAAmB,KAAK,OAAO;EACxD,MAAM,YAAY,QAAQ,oBAAoB,KAAK,OAAO;EAM1D,OAAO;GACL,QANa,OAAO,KACnB,QAAQ,MAAM,KAAKC,SAAS,GAAG;IAAE;IAAU;GAAU,CAAC,CAAC,CAAC,CACxD,MAAM,GAAG,QAAQ,KAAK,CAAC,CACvB,KAAK,MAAM,KAAKC,WAAW,GAAG,KAAK,CAG/B;GACL,WAAW,OAAO;GAClB,GAAI,OAAO,kBAAkB,EAAE,iBAAiB,OAAO,gBAAgB,IAAI,CAAC;GAC5E,QAAQ;IACN,MAAM,MAAM,KAAK,YAAY;IAC7B,IAAI,MAAM,GAAG,YAAY;IACzB,SAAS,MAAM;GACjB;GACA,UAAU,OAAO,WACb;IACE,MAAM,KAAKC,WAAW,OAAO,SAAS,WAAW,KAAK;IACtD,IAAI,KAAKA,WAAW,OAAO,SAAS,SAAS,KAAK;IAClD,MAAM,OAAO,SAAS;GACxB,IACA;GACJ,GAAI,OAAO,YACP,EACE,WAAW;IACT,QAAQ,OAAO,UAAU;IACzB,SAAS,OAAO,UAAU;IAC1B,GAAI,OAAO,UAAU,uBAAuB,KAAA,IACxC,EAAE,eAAe,KAAKA,WAAW,OAAO,UAAU,oBAAoB,KAAK,EAAE,IAC7E,CAAC;IACL,GAAI,OAAO,UAAU,qBAAqB,KAAA,IACtC,EAAE,aAAa,KAAKA,WAAW,OAAO,UAAU,kBAAkB,KAAK,EAAE,IACzE,CAAC;GACP,EACF,IACA,CAAC;GACL,GAAI,OAAO,UAAU,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;EACtD;CACF;;;;;;;;CASA,aACE,MACW;EACX,MAAM,QAAQ,KAAKL,SAAS;EAC5B,MAAM,MAAM,KAAKJ,KAAK;EAGtB,MAAM,YAAY,KAAK,OACnB,KAAKK,SACH,WAAW;GAAE,MAAM,KAAK;GAAM,IAAI,KAAK;GAAM,kBAAkB;GAAG,cAAc;EAAE,GAAG,GAAG,CAAC,CACtF,MACH,KACF,IACA,OAAO;EACX,MAAM,UAAU,KAAK,KACjB,KAAKA,SACH,WAAW;GAAE,MAAM,KAAK;GAAI,IAAI,KAAK;GAAI,kBAAkB;GAAG,cAAc;EAAE,GAAG,GAAG,CAAC,CAAC,IACtF,KACF,IACA,OAAO;EACX,MAAM,gBAAgB,KAAKC,eAAe,OAAO,KAAK,QAAQ;EAE9D,MAAM,OAAO,MAAM,YAAY;GAC7B;GACA;GACA,OAAO,KAAK,IAAI,KAAK,QAAQ,IAAI,IAAI,GAAK;GAC1C,MAAM,KAAK;GACX,OAAO,KAAK,SAAS;GACrB,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;EAC3C,CAAC;EAED,MAAM,WAAW,KAAK,mBAAmB,KAAK,OAAO;EACrD,MAAM,YAAY,KAAK,oBAAoB,KAAK,OAAO;EACvD,OAAO;GACL,QAAQ,KACL,QAAQ,MAAM,KAAKC,SAAS,GAAG;IAAE;IAAU;GAAU,CAAC,CAAC,CAAC,CACxD,MAAM,GAAG,KAAK,KAAK,CAAC,CACpB,KAAK,MAAM,KAAKC,WAAW,GAAG,KAAK,CAAC;GAEvC,WAAW;GACX,iBACE;GAEF,QAAQ;IACN,MAAM,KAAK,OAAO,KAAKC,WAAW,WAAW,KAAK,IAAI;IACtD,IAAI,KAAK,KAAK,KAAKA,WAAW,SAAS,KAAK,IAAI;IAChD,SAAS;GACX;GACA,UAAU;EACZ;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BA,iBAAiB,MAAqE;EACpF,MAAM,QAAQ,KAAKL,SAAS;EAC5B,MAAM,MAAM,KAAKJ,KAAK;EACtB,MAAM,QAAQ,WACZ;GACE,MAAM,KAAK;GACX,IAAI,KAAK;GACT,kBAAkB,KAAK,OAAO;GAC9B,cAAc,KAAK,OAAO;EAC5B,GACA,GACF;EAEA,MAAM,WAAW,WAAW,YAAY,KAAK,QAAQ;EACrD,MAAM,SAAS,WAAW,UAAU,KAAK,MAAM;EAC/C,MAAM,cAAc,KAAK,UAAU,SAAS,KAAK,WAAW;EAC5D,MAAM,WAAW,WAAW,WAAW;EACvC,MAAM,gBAAgB,KAAKM,eAAe,OAAO,KAAK,QAAQ;EAE9D,MAAM,YAAY,KAAKD,SAAS,MAAM,MAAM,KAAK;EACjD,MAAM,UAAU,KAAKA,SAAS,MAAM,IAAI,KAAK;EAC7C,MAAM,IAAI;GACR;GACA;GACA,OAAO;GACP,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;EAC3C;EACA,MAAM,QAAQ,MAAM,WAAW,CAAC;EAChC,MAAM,cAAc,MAAM,iBAAiB,CAAC;EAK5C,IAAI,MAAM,UAAU,mBAAmB,YAAY,UAAU,iBAC3D,OAAO;GACL,UAAU;GACV,YAAY;GACZ,QACE,6BAA6B,gBAAgB;GAG/C,MACE;EAEJ;EAGF,MAAM,SAAS,WAAW;GACxB;GACA;GACA,UAAU,MAAM,SAAS;GACzB,oBAAoB,MAAM,KAAK;GAC/B;GACA;GAEA,OAAO,MAAM,SAAS,YAAY;GAClC,aAAa,MAAM,KAAK;EAC1B,CAAC;EAED,IAAI,OAAO,cAAc,eACvB,OAAO;GACL,UAAU;GACV,YAAY;GACZ,QACE,6KAEA,oDAAoD,OAAO,mBAAmB,KAAK,KAAK;GAC1F,MACE;EAEJ;EAGF,IAAI,aAAa,MAAM;EACvB,IAAI,WAAW,MAAM;EACrB,IAAI;EACJ,MAAM,MAAM,OAAO;EACnB,IAAI,KAAK;GACP,MAAM,0BAAU,IAAI,MAAM,IAAI,YAAY,MAAM,KAAK,eAAe,GAAI;GACxE,MAAM,wBAAQ,IAAI,MAAM,IAAI,UAAU,MAAM,KAAK,eAAe,GAAI;GACpE,MAAM,SACJ;GAEF,IAAI,SAAS,QAAQ,IAAI,MAAM,QAAQ,GAAG;IACxC,YAAY;KAAE;KAAQ,aAAa,WAAW,MAAM,EAAE;IAAE;IACxD,WAAW;GACb;GACA,IAAI,WAAW,QAAQ,IAAI,QAAQ,QAAQ,GAAG;IAC5C,YAAY;KAAE,GAAI,aAAa,EAAE,OAAO;KAAI,eAAe,WAAW,MAAM,IAAI;IAAE;IAClF,aAAa;GACf;GACA,IAAI,aAAa,SAAS,QAAQ,KAAK,WAAW,QAAQ,GACxD,OAAO;IACL,UAAU;IACV,YAAY;IACZ,QACE;IAEF,MACE,yBAAyB,WAAW,OAAO,EAAE,MAAM,WAAW,KAAK,EAAE;GAGzE;EAEJ;EAIA,MAAM,eAAe,WAAW,QAAQ,IAAI,IAAI,QAAQ;EACxD,IAAI,cAAc,aAAa;EAE/B,MAAM,QAAQ;GACZ,iBAAiB,KAAK;GACtB,QAAQ;IACN,MAAM,WAAW,UAAU;IAC3B,IAAI,WAAW,QAAQ;IACvB,SAAS,MAAM;IACf;GACF;GACA,cAAc;IACZ,UAAU,KAAK;IACf,QAAQ,KAAK;IACb,UAAU;IACV,oBAAoB,KAAK;IAGzB,UAAU,KAAK,eAAe,CAAC,CAAC,gBAAgB,CAAC,CAAC;GACpD;GACA,WAAW,OAAO;GAClB,UAAU,MACN;IACE,MAAM,KAAKI,WAAW,IAAI,WAAW,KAAK;IAC1C,IAAI,KAAKA,WAAW,IAAI,SAAS,KAAK;IACtC,MAAM,IAAI;GACZ,IACA;GACJ,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EACnC;EAEA,IAAI,SAAS,QAAQ,KAAK,WAAW,QAAQ,GAC3C,OAAO;GACL,GAAG;GACH,OAAO,CAAC;GACR,MAAM;IAAE,UAAU;IAAG,WAAW;GAAE;GAClC,cAAc,CAAC;GACf,MACE;EAEJ;EAGF,MAAM,WAAW,KAAK,mBAAmB,KAAK,OAAO;EACrD,MAAM,YAAY,KAAK,oBAAoB,KAAK,OAAO;EACvD,MAAM,OAAmB,CAAC;EAC1B,MAAM,eAAmD,CAAC;EAC1D,IAAI,WAAW;EAEf,KAAK,MAAM,OAAO,OAAO,MAAM;GAC7B,IAAI,IAAI,eAAe,MAAM;GAC7B,IAAI,CAAC,KAAKF,SAAS,KAAK;IAAE;IAAU;GAAU,CAAC,GAAG;GAClD,MAAM,WAAW,IAAI,aAAa,MAAM,KAAK,eAAe;GAE5D,IAAI,IAAI,QAAQ;IACd,MAAM,UAAU,kBAAkB,OAAO;IACzC,MAAM,UAAU,eAAe,OAAO;IACtC,IAAI,UAAU,SAAS,QAAQ,KAAK,UAAU,WAAW,QAAQ,GAAG;KAClE,MAAM,WAAW,cAAc,IAAI,YAAY,IAAI,SAAS,MAAM,MAAM,KAAK,WAAW;KACxF,aAAa,KAAK;MAChB,KAAK,UAAU,SAAS,SAAS,MAAM,SAAS,OAAO;MACvD,SAAS,IAAI;MACb,UAAU,IAAI;KAChB,CAAC;IACH;IACA,IAAI,CAAC,KAAK,YAAY;IAStB,MAAM,QACJ,IAAI,aAAa,OAAO,WAAW,IAAI,WAAW,MAAM,KAAK,eAAe;IAC9E,KAAK,KAAK;KAAE,MAAM;KAAS,IAAI,KAAK,IAAI,SAAS,kBAAkB,KAAK,CAAC;IAAE,CAAC;IAC5E,YAAY;IACZ;GACF;GAEA,IAAI,KAAK,sBAAsB,IAAI,iBAAiB,mBAAmB;GACvE,MAAM,QACJ,IAAI,aAAa,OAAO,WAAW,IAAI,WAAW,MAAM,KAAK,eAAe;GAE9E,IAAI,SAAS,SAAS;GACtB,KAAK,KAAK;IAAE,MAAM;IAAS,IAAI;GAAM,CAAC;GACtC,YAAY;EACd;EAEA,MAAM,SAAS,eAAe,IAAI;EAClC,MAAM,SAAS,KAAK,kBAAkB;EACtC,MAAM,QAAoB,CAAC;EAC3B,OAAO,KAAK,MAAM,KAAK,WAAW;GAChC,MAAM;GACN,IAAI;GACJ;GACA;GACA;EACF,CAAC,GACC,KAAK,MAAM,OAAO,aAAa,GAAG,MAAM,GAAG;GAGzC,MAAM,QAAQ,QAAQ,IAAI,MAAM,KAAK,kBAAkB;GACvD,IAAI,IAAI,KAAK,QAAQ,QAAQ;GAC7B,MAAM,KAAK;IACT,OAAO,WAAW,IAAI,KAAK,KAAK,CAAC;IACjC,KAAK,WAAW,IAAI,KAAK,IAAI,EAAE,CAAC;IAChC,KAAK,SAAS,KAAK;IACnB,SAAS,KAAK,OAAO,IAAI,KAAK,SAAS,GAAM;GAC/C,CAAC;GACD,IAAI,MAAM,UAAU,KAAK,OAAO,MAAM;EACxC;EAGF,OAAO;GACL,GAAG;GACH;GACA,MAAM;IAAE;IAAU,WAAW,OAAO;GAAO;GAC3C;EACF;CACF;CAEA,SAAS,KAA0B;EACjC,MAAM,QAAQ,KAAKH,SAAS;EAC5B,MAAM,UAAU,UAAU,GAAG;EAC7B,MAAM,MAAM,MAAM,OAAO,QAAQ,QAAQ;EACzC,IAAI,CAAC,KAAK,MAAM,IAAI,mBAAmB,GAAG;EAI1C,MAAM,SAAmB,QAAQ,kBAC7B;GACE,GAAG;GACH,YAAY,KAAKC,SAAS,QAAQ,iBAAiB,KAAK;GACxD,UACE,IAAI,eAAe,QAAQ,IAAI,aAAa,OACxC,KAAKA,SAAS,QAAQ,iBAAiB,KAAK,KAAK,IAAI,WAAW,IAAI,cACpE,IAAI;GACV,QAAQ;EACV,IACA;EAEJ,MAAM,UAAU,KAAKG,WAAW,QAAQ,KAAK;EAC7C,OAAO;GACL,GAAG;GACH;GACA,GAAI,QAAQ,eAAe,EAAE,WAAW,YAAY,OAAO,EAAE,IAAI,CAAC;GAClE,aAAa,IAAI;GACjB,KAAK,IAAI;GACT,eAAe,IAAI;GACnB,cAAc,IAAI;GAClB,gBAAgB,IAAI;GACpB,UAAU,QAAQ,SAAS,CAAC,QAAQ,MAAM,SAAS,QAAQ,MAAM,WAAW;EAC9E;CACF;CAEA,YAAY;EACV,OAAO,KAAKJ,SAAS,CAAC,CAAC,UAAU;CACnC;CAEA,WAA2B;EACzB,OAAO,KAAKA,SAAS,CAAC,CAAC,SAAS;CAClC;;;;;;;;CAWA,aAAa,OAA8E;EACzF,MAAM,QAAQ,KAAK,MAAM;EACzB,MAAM,SAAS,SAAS,KAAK,OAAO;EACpC,IAAI,CAAC,OAGH,OAAO;GAAE,MAAM;GAAQ,MAAM;EAAK;EAEpC,MAAM,MAAM,MAAM,UAAU;EAC5B,IAAI,CAAC,QAAQ,OAAO;GAAE,MAAM,KAAA;GAAW,MAAM;EAAK;EAElD,MAAM,QAAQ,OAAO,YAAY;EACjC,MAAM,OAAO,IAAI,QACd,MAAM,EAAE,MAAM,YAAY,MAAM,SAAS,EAAE,OAAO,YAAY,MAAM,KACvE;EACA,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,sBAAsB,QAAQ,IAAI,KAAK,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,OAAO,OAAO,CAAC;;;;;;;;EAUvF,IAAI,KAAK,SAAS,GAChB,MAAM,IAAI,kBACR,IAAI,OAAO,YAAY,KAAK,OAAO,mQAInC;GAAE,WAAW;GAAQ,SAAS,KAAK;EAAO,CAC5C;EAEF,MAAM,MAAM,KAAK;EACjB,IAAI,IAAI,cAAc,MAAM,IAAI,yBAAyB,IAAI,SAAS,MAAM;EAE5E,OAAO;GAAE,MAAM,IAAI,SAAS;GAAQ,MAAM,IAAI;EAAK;CACrD;;CAGA,oBAAoB,MAAoC;EACtD,IAAI,CAAC,MAAM,OAAO;EAClB,MAAM,QAAQ,KAAK,MAAM;EACzB,IAAI,CAAC,OAAO,OAAO;EACnB,OAAO,MAAM,UAAU,CAAC,CAAC,MAAM,MAAM,EAAE,UAAU,IAAI,CAAC,EAAE,QAAQ;CAClE;;;;;;;CAQA,oBAAoB,aAAyC;EAC3D,IAAI,CAAC,aAAa,OAAO,KAAA;EACzB,MAAM,QAAQ,KAAK,MAAM;EACzB,IAAI,CAAC,OAAO,OAAO,KAAA;EACnB,MAAM,QAAQ,YAAY,YAAY;EACtC,OAAO,MAAM,UAAU,CAAC,CAAC,MAAM,MAAM,EAAE,MAAM,YAAY,MAAM,KAAK,CAAC,EAAE,SAAS,KAAA;CAClF;;;;;;;;;;;;CAaA,MAAMM,KAAQ,YAAoB,QAA6B;EAC7D,IAAI;GACF,OAAO,MAAM,oBAAoB,KAAK,OAAO,IAAO,YAAY,MAAM,CAAC;EACzE,SAAS,KAAK;GACZ,MAAM,OAAQ,KAAyC,SAAS;GAChE,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAC/D,IAAI,SAAS,yBAAyB,MAAM,IAAI,yBAAyB,OAAO;GAChF,IAAI,SAAS,sBAAsB,MAAM,IAAI,sBAAsB,OAAO;GAC1E,IAAI,SAAS,mBAAmB,MAAM,IAAI,mBAAmB,OAAO;GACpE,MAAM;EACR;CACF;CAEA,YAAY,MAA+B,aAAyC;EAClF,MAAM,MAAM,OAAO,KAAK,QAAQ,WAAW,KAAK,MAAM;EACtD,OAAO;GACL,KAAK,UAAU,eAAe,IAAI,OAAO,EAAE;GAC3C;GACA,SAAS,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;GAC3D,OAAO,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY;GAC7D,KAAK,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;GACvD,QAAQ,KAAK,gBAAgB;GAC7B,UAAU,OAAO,KAAK,iBAAiB,WAAW,KAAK,eAAe;GACtE,QAAQ;EACV;CACF;;;;;;;;CASA,eACE,OACA,KACA,iBAC2D;EAC3D,MAAM,MAAM,KAAKV,KAAK;EACtB,MAAM,OAAO,UAAU,SAAS,OAAO,GAAG;EAC1C,IAAI,QAAQ,KAAA,GAAW;;;;;;;;;;GAUrB,MAAM,KAAK,WAAW,OAAO,KAAK,OAAO,GAAG;GAC5C,IAAI,GAAG,QAAQ,KAAK,KAAK,GAAG,QAAQ,GAClC,MAAM,IAAI,kBACR,QAAQ,WAAW,EAAE,EAAE,wBAAwB,KAAK,IAAI,4CAE1D;GAEF,OAAO;IAAE,UAAU,KAAK;IAAK,QAAQ,WAAW,EAAE;IAAG,YAAY,KAAK,SAAS;GAAS;EAC1F;EACA,MAAM,UACJ,oBAAoB,KAAA,IAChB,KAAK,OAAO,8BACZ,cAAc,mBAAmB,eAAe;EACtD,MAAM,KAAK,IAAI,KAAK,KAAK,GAAG,QAAQ,IAAI,UAAU,GAAM;EACxD,OAAO;GACL,UAAU,KAAK;GACf,QAAQ,WAAW,EAAE;GACrB,YAAY,KAAK,SAAS;EAC5B;CACF;CAEA,MAAM,YAAY,QAAiD;EACjE,MAAM,SAAS,KAAKW,aAAa,OAAO,QAAQ;EAChD,MAAM,MAAM,KAAKC,eAAe,OAAO,OAAO,OAAO,KAAK,OAAO,eAAe;EAChF,MAAM,OAAO,MAAM,KAAKF,KAA8B,cAAc;GAClE,UAAU,OAAO,QAAQ;GACzB,SAAS,OAAO;GAChB,WAAW,IAAI;GACf,SAAS,IAAI;GAEb,QAAQ,OAAO,UAAU,IAAI;GAC7B,GAAI,OAAO,aAAa,KAAA,IAAY,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;GACrE,GAAI,OAAO,gBAAgB,KAAA,IAAY,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;GAC9E,GAAI,OAAO,QAAQ,KAAA,IAAY,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC;EACxD,CAAC;EACD,KAAK,WAAW;EAGhB,MAAM,OACJ,OAAO,QACP,KAAKG,oBAAoB,OAAO,KAAK,iBAAiB,WAAW,KAAK,eAAe,IAAI;EAC3F,OAAO,KAAKC,YAAY,MAAM,IAAI;CACpC;CAEA,MAAM,YAAY,QAAiD;EACjE,MAAM,MAAM,UAAU,OAAO,GAAG;;;;;;;;;;;EAWhC,IAAI,IAAI,cACN,MAAM,IAAI,kBACR,idAMA;GAAE,KAAK,OAAO;GAAK,WAAW,YAAY,GAAG;EAAE,CACjD;EAGF,MAAM,SACJ,OAAO,UAAU,KAAA,IACb,KAAKF,eAAe,OAAO,OAAO,OAAO,KAAK,OAAO,eAAe,IACpE;EAEN,MAAM,OAAO,MAAM,KAAKF,KAA8B,cAAc;GAClE,UAAU,KAAKK,oBAAoB,IAAI,WAAW,KAAK;GACvD,KAAK,IAAI;GACT,GAAI,OAAO,YAAY,KAAA,IAAY,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;GAClE,GAAI,SAAS;IAAE,WAAW,OAAO;IAAU,SAAS,OAAO;GAAO,IAAI,CAAC;GACvE,GAAI,OAAO,WAAW,KAAA,IAAY,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;GAC/D,GAAI,OAAO,aAAa,KAAA,IAAY,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;GACrE,GAAI,OAAO,gBAAgB,KAAA,IAAY,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;GAC9E,GAAI,OAAO,QAAQ,KAAA,IAAY,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC;EACxD,CAAC;EACD,KAAK,WAAW;EAChB,OAAO,KAAKD,YAAY,MAAM,IAAI,WAAW;CAC/C;;;;;;;;;;;CAYA,MAAM,aAAa,MAAyE;EAC1F,MAAM,UAAU,KAAK,KAAK,GAAG,OAAO;GAAE,KAAK,KAAK;GAAK,QAAQ,UAAU,CAAC;EAAE,EAAE;EAE5E,MAAM,aAAa,QAAQ,MAAM,MAAM,EAAE,OAAO,YAAY;EAC5D,IAAI,YACF,MAAM,IAAI,kBACR,6ZAKA;GAAE,KAAK,WAAW;GAAK,WAAW,YAAY,WAAW,MAAM;EAAE,CACnE;EAKF,MAAM,6BAAa,IAAI,IAAsB;EAC7C,KAAK,MAAM,EAAE,YAAY,SAAS;GAChC,MAAM,MAAM,OAAO,eAAe;GAClC,WAAW,IAAI,KAAK,CAAC,GAAI,WAAW,IAAI,GAAG,KAAK,CAAC,GAAI,OAAO,QAAQ,CAAC;EACvE;EACA,MAAM,UAAqB,CAAC;EAC5B,KAAK,MAAM,CAAC,UAAU,SAAS,YAAY;GACzC,MAAM,OAAO,MAAM,KAAKJ,KAA6B,eAAe;IAClE,UAAU,KAAKK,oBAAoB,QAAQ,KAAK;IAChD;GACF,CAAC;GACD,QAAQ,KAAK,GAAI,KAAK,WAAW,CAAC,CAAE;EACtC;EACA,KAAK,WAAW;EAChB,OAAO;GAAE;GAAS,OAAO;EAAS;CACpC;CAEA,QAAoB;EAClB,MAAM,UAAU,KAAK,OAAO;EAC5B,MAAM,QAAQ,KAAK,MAAM;EACzB,OAAO;GACL,aAAa;GACb,OAAO,KAAK,OAAO,cAAc,QAAQ,aAAa,QAAQ,SAAS;GACvE,WAAW,OAAO,QAAQ;GAC1B,kBAAkB,OAAO,KAAK,eAAe;GAC7C,QAAQ,QAAQ,OAAO,QAAQ;EACjC;CACF;AACF;;;AC5oCA,MAAM,QAAQ;;;;;;;;AASd,MAAM,iBAAiB,QACrB,KAAK,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;;;;;;;;;;;;;;AAepD,MAAM,eAAe,iBAAiB,OAAO;;CAE3C,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;;;;;;;;CAQ/C,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;;CAEhD,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,WAAW,EAAE,KAAK;EAAC;EAAQ;EAAM;EAAa;CAAK,CAAC,CAAC,CAAC,QAAQ,MAAM;;CAEpE,iBAAiB,EAAE,OAAO,CAAC,CAAC,SAAS;;;;;;;CAOrC,kBAAkB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC;;CAE5D,cAAc,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAK,CAAC,CAAC,QAAQ,GAAG;;;;;;;;CAQ5D,cAAc,EAAE,OAAO,CAAC,CAAC,MAAM,OAAO,uCAAqC,CAAC,CAAC,QAAQ,OAAO;CAC5F,YAAY,EAAE,OAAO,CAAC,CAAC,MAAM,OAAO,uCAAqC,CAAC,CAAC,QAAQ,OAAO;;CAE1F,UAAU,EACP,MAAM,EAAE,KAAK,YAAY,CAAC,CAAC,CAC3B,IAAI,CAAC,CAAC,CACN,QAAQ,CAAC,GAAG,gBAAgB,CAAC;;CAEhC,6BAA6B,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAK,CAAC,CAAC,QAAQ,EAAE;;CAE1E,iBAAiB,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;;CAE1C,kBAAkB,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;;;;;;;CAO3C,UAAU,EACP,OAAO,CAAC,CACR,QACE,MAAM;EACL,IAAI;GACF,KAAK,eAAe,SAAS,EAAE,UAAU,EAAE,CAAC;GAC5C,OAAO;EACT,QAAQ;GACN,OAAO;EACT;CACF,GACA,EAAE,SAAS,oDAAkD,CAC/D,CAAC,CACA,SAAS;AACd,CAAC,CAAC,CAAC,OAAO;;;;;AAQV,MAAa,cAAc,MAAyB,QAAQ,QAC1D,YAAY,cAAc;CACxB,aAAa,UAAU,IAAI,2BAA2B;CACtD,eAAe,UAAU,IAAI,6BAA6B;CAC1D,OAAO,UAAU,IAAI,oBAAoB;CACzC,UAAU,UAAU,IAAI,uBAAuB;CAC/C,WAAW,UAAU,IAAI,wBAAwB;CACjD,WAAW,QAAQ,IAAI,oBAAoB;CAC3C,WAAW,QAAQ,IAAI,yBAAyB;CAChD,iBAAiB,QAAQ,IAAI,+BAA+B;CAC5D,kBAAkB,YAAY,IAAI,iCAAiC;CACnE,cAAc,YAAY,IAAI,6BAA6B;CAC3D,6BAA6B,YAAY,IAAI,6CAA6C;CAC1F,cAAc,QAAQ,IAAI,4BAA4B;CACtD,YAAY,QAAQ,IAAI,0BAA0B;CAClD,UAAU,cAAc,UAAU,IAAI,uBAAuB,CAAC;CAC9D,iBAAiB,UAAU,IAAI,+BAA+B;CAC9D,kBAAkB,UAAU,IAAI,gCAAgC;CAChE,eAAe,QAAQ,IAAI,6BAA6B;CACxD,oBAAoB,YAAY,IAAI,mCAAmC;CACvE,YAAY,YAAY,IAAI,0BAA0B;CACtD,UAAU,QAAQ,IAAI,uBAAuB;AAC/C,CAAC;;;;;;;;AC/HH,MAAa,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACK9B,MAAM,MAAqB;CAAE,SAAS;CAAY,OAAO;AAAe;;;;;;;;;AAUxE,MAAa,mBAAmB,QAAmB,gBAA+B;CAChF,uBAAuB,QAAQ,KAAK;EAClC,MAAM;EACN,OAAO;EACP,aACE;EAEF,YAAY;GACV,MAAM,UACJ,qFACF;GACA,UAAU,UAAU,0DAA0D;EAChF;EACA,QAAQ,EAAE,MAAM,eAAe,gBAAgB,QAAQ,QAAQ,aAC7D,WAAW,YAAY,SAAS,aAAa,GAC9C;;mDAGC,WAAW,uBAAuB,GACnC;;;;;;;;;;;;CAYH,CAAC;CAED,IAAI,CAAC,aAAa;CAElB,uBAAuB,QAAQ,KAAK;EAClC,MAAM;EACN,OAAO;EACP,aACE;EAEF,YAAY;GACV,MAAM,kBAAkB,gEAA8D;GACtF,MAAM,UAAU,iEAA6D;GAC7E,UAAU,UAAU,gEAAgE;EACtF;EACA,QAAQ,EAAE,MAAM,MAAM,eAAe,aAAa,OAAO,OAAO,aAAa,SAAS,GAAG;;;;+CAKvF,OAAO,0BAA0B,KAAK,KAAK,kBAC5C;;;;;;qDAOC,WACI,WAAW,SAAS,aACpB,0DACL;;;;;;;CAOH,CAAC;AACH;;;AClEA,MAAa,cAAc,EACxB,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SACC,mIAEF;AAEF,MAAa,cAAc,EACxB,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,8FACF;;;;;;;;AASF,MAAa,UAAU,EACpB,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,mLAEF;AAEF,MAAa,QAAQ,EAClB,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,oJAEF;AAEF,MAAa,qBAAqB,EAC/B,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,mFAAmF;AAE/F,MAAa,sBAAsB,EAChC,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SACC,wFACF;;;;;;;;;;;AC3DF,MAAa,mBAAmB,OAC9B,QACA,QACqC;CACrC,MAAM,QAAQ,OAAO,MAAM;CAC3B,MAAM,UAAU,OAAO,OAAO;CAC9B,MAAM,QAAQ,OAAO,MAAM;;;;;;;;;;CAW3B,MAAM,iBAAiB,QAAQ,WAC3B,YACA,QAAQ,SACN,6CACA;CAEN,OAAO;EACL,QAAQ,EAAE,MAAM;EAChB,aAAa;GACX;GACA,YAAY,IAAI,cACZ,uGAEA;GAEJ,GAAI,QAAQ,WACR,CAAC,IACD,EACE,YAAY;IACV;IACA;IACA;GACF,EACF;EACN;EACA,OAAO;GACL,eAAe,QAAQ;GACvB,MAAM,QAAQ;GACd,mBAAmB,QAAQ;GAC3B,eAAe,QAAQ;GACvB,YAAY,QAAQ,WAAW;GAC/B,QAAQ,QAAQ;GAChB,UAAU,QAAQ;GAClB,WAAW,QAAQ;GACnB,YAAY,QAAQ;GACpB,cAAc,QAAQ;GACtB,aAAa,MAAM;GACnB,QAAQ,QAAQ;EAClB;EACA,cAAc,QACV;GACE,oBAAoB,MAAM,KAAK;GAC/B,mBAAmB,MAAM,KAAK;GAC9B,eAAe,MAAM,KAAK;GAC1B,mBAAmB,MAAM,KAAK;GAC9B,aAAa,MAAM,KAAK;GACxB,gBAAgB,MAAM,KAAK;GAC3B,iBAAiB,MAAM,KAAK;GAC5B,WAAW,MAAM,KAAK;GACtB,aAAa,MAAM,KAAK,YAAY;GACpC,iBAAiB,MAAM,KAAK,gBAAgB;EAC9C,IACA;EACJ,UAAU;GACR,aAAa,IAAI;GAIjB,eAAe,OAAO,OAAO;GAC7B,kBAAkB,OAAO,OAAO;GAChC,mBAAmB,OAAO,OAAO;GACjC,iBAAiB,OAAO,OAAO,mBAAmB;GAClD,kBAAkB,OAAO,OAAO;GAChC,cAAc,OAAO,OAAO;GAC5B,iBAAiB,OAAO,OAAO;GAC/B,kBAAkB,OAAO,OAAO;GAChC,WAAW,OAAO,OAAO;GACzB,YAAY,OAAO,OAAO;GAC1B,UAAU,OAAO,OAAO,YAAY;EACtC;EACA,SAAS;GACP;GAGA;GAKA;GAEA;GAIA;GAKA;GAIA;EAGF;CACF;AACF;;;;;;;;;AAUA,MAAa,4BACX,QACA,QACA,QACS;CACT,OAAO,aACL,8BACA;EACE,aACE;EAGF,aAAa,CAAC;EACd,aAAa,EAAE,cAAc,KAAK;CACpC,GACA,YAAY,WAAW,iBAAiB,QAAQ,GAAG,CAAC,CAAC,CAAC,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAC/E;AACF;;;;;;;;;;;;;;;;ACxIA,MAAM,WAAW,EACd,OAAO,CAAC,CACR,SACC,4LAEF;AAEF,MAAM,SAAS,EACZ,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,uFACF;AAEF,MAAM,cAAc,EACjB,OAAO,CAAC,CACR,IAAI,CAAC,CACL,IAAI,CAAC,CAAC,CACN,IAAI,MAAa,CAAC,CAClB,SAAS,CAAC,CACV,SAAS,gDAAgD;AAE5D,MAAa,uBAAuB,QAAmB,WAAsC;CAC3F,OAAO,aACL,+BACA;EACE,aACE;EAOF,aAAa;GACX,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,oBAAoB;GACxD,UAAU;GACV,OAAO;GACP,KAAK;GACL,iBAAiB;GACjB,QAAQ,EACL,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SACC,+EACF;GACF,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;GAC9B,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,oBAAoB;GAChE,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS;EAC3B;CACF,GACA,OAAO,SAAS,WAAW,OAAO,YAAY,IAAI,CAAC,CACrD;CAEA,OAAO,aACL,+BACA;EACE,aACE;EASF,aAAa;GACX,KAAK;GACL,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;GACpC,OAAO,SAAS,SAAS;GACzB,KAAK;GACL,iBAAiB;GACjB,QAAQ,EAAE,QAAQ,CAAC,CAAC,SAAS;GAC7B,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;GAC9B,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS;GACjC,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS;EAC3B;CACF,GACA,OAAO,SAAS,WAAW,OAAO,YAAY,IAAI,CAAC,CACrD;CAEA,OAAO,aACL,gCACA;EACE,aACE;EASF,aAAa;GACX,MAAM,EACH,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACxB,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,SACC,+GAEF;GACF,SAAS;EACX;CACF,GACA,OAAO,EAAE,WAAW,WAAW,OAAO,aAAa,IAAI,CAAC,CAC1D;AACF;;;;;;;;;;;;;;ACxGA,MAAa,6BAA6B,QAAmB,WAAsC;CACjG,OAAO,aACL,oCACA;EACE,aACE;EAcF,aAAa;GACX,iBAAiB,EACd,OAAO,CAAC,CACR,IAAI,CAAC,CACL,IAAI,CAAC,CAAC,CACN,IAAI,IAAK,CAAC,CACV,SAAS,4EAA4E;GACxF,MAAM,EACH,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,sMAGF;GACF,IAAI;GACJ,UAAU;GACV,UAAU,EACP,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,oHAEF;GACF,QAAQ,EACL,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,oIAEF;GACF,UAAU,EACP,MAAM,EAAE,KAAK,YAAY,CAAC,CAAC,CAC3B,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SACC,sJAEF;GACF,oBAAoB,EACjB,OAAO,CAAC,CACR,IAAI,CAAC,CACL,IAAI,CAAC,CAAC,CACN,IAAI,EAAE,CAAC,CACP,QAAQ,MAAM,KAAK,MAAM,GAAG,EAC3B,SAAS,kDACX,CAAC,CAAC,CACD,SAAS,CAAC,CACV,SAAS,0DAA0D;GACtE,YAAY,EACT,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SACC,qSAIF;GACF,oBAAoB,EACjB,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SACC,0TAIF;GACF,iBAAiB;GACjB,kBAAkB;GAClB,OAAO;EACT;EACA,aAAa,EAAE,cAAc,KAAK;CACpC,GACA,OAAO,SACL,KAAK,YACH,OAAO,iBAAiB;EACtB,GAAG;EACH,UAAU,KAAK,YAAY,OAAO,OAAO;EACzC,QAAQ,KAAK,UAAU,OAAO,OAAO;EACrC,UAAU,KAAK,YAAY,OAAO,OAAO;EACzC,oBAAoB,KAAK,sBAAsB;EAC/C,YAAY,KAAK,cAAc;EAC/B,oBAAoB,KAAK,sBAAsB;EAC/C,OAAO,KAAK,SAAS,KAAK,IAAI,IAAI,OAAO,OAAO,UAAU;CAC5D,CAAC,CACH,CACJ;AACF;;;;;;;;;ACvHA,MAAa,yBAAyB,QAAmB,WAAsC;CAC7F,OAAO,aACL,iCACA;EACE,aACE;EAKF,aAAa,CAAC;EACd,aAAa,EAAE,cAAc,KAAK;CACpC,GACA,YAAY,KAAK,YAAY,OAAO,UAAU,CAAC,CACjD;CAEA,OAAO,aACL,gCACA;EACE,aACE;EAEF,aAAa,CAAC;EACd,aAAa,EAAE,cAAc,KAAK;CACpC,GACA,YAAY,KAAK,YAAY,OAAO,SAAS,CAAC,CAChD;AACF;;;;ACvBA,MAAM,eAAe;CACnB,UAAU;CACV,iBAAiB;CACjB,kBAAkB;CAClB,OAAO;AACT;AAEA,MAAa,sBAAsB,QAAmB,WAAsC;CAC1F,OAAO,aACL,8BACA;EACE,aACE;EAKF,aAAa;GAAE,MAAM;GAAS,IAAI;GAAO,GAAG;EAAa;EACzD,aAAa,EAAE,cAAc,KAAK;CACpC,GACA,OAAO,SACL,KAAK,YACH,OAAO,WAAW;EAChB,GAAG;EACH,OAAO,KAAK,SAAS,KAAK,IAAI,IAAI,OAAO,OAAO,UAAU;CAC5D,CAAC,CACH,CACJ;CAEA,OAAO,aACL,gCACA;EACE,aACE;EAGF,aAAa;GACX,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,+CAA+C;GACjF,OAAO,EACJ,KAAK,CAAC,WAAW,MAAM,CAAC,CAAC,CACzB,SAAS,CAAC,CACV,SAAS,0EAAsE;GAClF,MAAM;GACN,IAAI;GACJ,GAAG;EACL;EACA,aAAa,EAAE,cAAc,KAAK;CACpC,GACA,OAAO,SACL,KAAK,YACH,OAAO,aAAa;EAClB,GAAG;EACH,OAAO,KAAK,SAAS,KAAK,IAAI,IAAI,OAAO,OAAO,UAAU;CAC5D,CAAC,CACH,CACJ;CAEA,OAAO,aACL,4BACA;EACE,aACE;EAGF,aAAa,EAAE,KAAK,YAAY;EAChC,aAAa,EAAE,cAAc,KAAK;CACpC,GACA,OAAO,EAAE,UAAU,KAAK,YAAY,OAAO,SAAS,GAAG,CAAC,CAC1D;AACF;;;;;;;;;;;;;;;;;ACtDA,MAAa,iBACX,QACA,QACA,QACS;CACT,yBAAyB,QAAQ,QAAQ,GAAG;CAC5C,sBAAsB,QAAQ,MAAM;CACpC,mBAAmB,QAAQ,MAAM;CACjC,0BAA0B,QAAQ,MAAM;CAExC,IAAI,CAAC,IAAI,aAAa;CACtB,oBAAoB,QAAQ,MAAM;AACpC;;;AC5BA,MAAa,cAAc,WAAW;AACtC,MAAa,iBAAiB,WAAW;;;;;;;AAsBzC,MAAa,gBAAgB,SAA6C;CACxE,MAAM,EAAE,WAAW;CACnB,MAAM,SAAS,IAAI,UAAU;EAAE,MAAM;EAAa,SAAS;CAAe,CAAC;CAE3E,MAAM,SAAS,IAAI,oBAAoB;EACrC;EACA,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;EAC7C,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;EACtD,GAAI,KAAK,MAAM,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;CACtC,CAAC;CAED,cAAc,QAAQ,QAAQ,EAAE,aAAa,OAAO,YAAY,CAAC;CAOjE,IAAI,OAAO,eAAe;EACxB,gBAAgB,QAAQ,OAAO,WAAW;EAC1C,yBAAyB,QAAQ;GAC/B,SAAS;GACT,aAAa;GACb,OAAO;GACP,mBAAmB,iBAAiB,QAAQ,EAAE,aAAa,OAAO,YAAY,CAAC;GAC/E,WAAW;IACT,WAAW;IACX,MAAM,aAAa;KACjB,UAAU,MAAM,OAAO,SAAS;KAChC,WAAW,MAAM,OAAO,UAAU;IACpC;GACF;EACF,CAAC;CACH;CAEA,OAAO;EAAE;EAAQ;CAAO;AAC1B"}
|
|
1
|
+
{"version":3,"file":"server-DoSfe1y7.js","names":["CLOCK","pad","addDays","pad","#col","#calendarFilter","#itemColumns","#joins","#excluded","#rowsFrom","#logger","#now","#located","#storeTried","#store","#require","#toApple","#calendarUuids","#visible","#summarise","#fromApple","#run","#writeTarget","#resolveWindow","#calendarUuidByName","#shapeWrite","#nameForRefCalendar"],"sources":["../src/build-info.ts","../src/client/errors.ts","../src/client/availability.ts","../src/client/dates.ts","../src/client/jxa/core.ts","../src/client/jxa/write.ts","../src/client/locate.ts","../src/client/recurrence.ts","../src/client/ref.ts","../src/client/store.ts","../src/client/calendar.ts","../src/config.ts","../src/guide.ts","../src/prompts.ts","../src/tools/util.ts","../src/tools/diagnostics.ts","../src/tools/actions.ts","../src/tools/availability.ts","../src/tools/calendars.ts","../src/tools/events.ts","../src/tools/index.ts","../src/server.ts"],"sourcesContent":["// Build-time / runtime identity for the running server. `name`/`version` are\n// read from package.json at startup (always accurate); `gitCommit` /\n// `gitCommitDate` are injected by tsdown's `define` substitution at build time\n// and fall back to \"unknown\" when running from source (e.g. vitest).\n\nimport { readPackageIdentity, type BuildInfo } from \"@mgcrea/mcp-apple-core\";\n\n// oxlint-disable no-underscore-dangle -- bundler-injected build-time constants.\ndeclare const __GIT_COMMIT__: string;\ndeclare const __GIT_COMMIT_DATE__: string;\n\nconst pkg = readPackageIdentity(new URL(\"../package.json\", import.meta.url), {\n name: \"@mgcrea/mcp-apple-calendar\",\n version: \"0.0.0\",\n});\n\nexport type { BuildInfo };\n\nexport const BUILD_INFO: BuildInfo = {\n name: pkg.name,\n version: pkg.version,\n gitCommit: typeof __GIT_COMMIT__ === \"string\" ? __GIT_COMMIT__ : \"unknown\",\n gitCommitDate: typeof __GIT_COMMIT_DATE__ === \"string\" ? __GIT_COMMIT_DATE__ : \"unknown\",\n};\n","/**\n * Calendar's error surface. The taxonomy lives in `@mgcrea/mcp-apple-core`;\n * what belongs here is the identity those messages are written against, and the\n * errors that are genuinely about calendars.\n */\n\nimport { AppleAutomationError, type SurfaceContext } from \"@mgcrea/mcp-apple-core\";\n\n/**\n * Named in every user-facing error and in the env vars they mention.\n *\n * `appName` is the display name, which is NOT the bundle id: Calendar.app is\n * still `com.apple.iCal` underneath. Every other surface has the two agreeing,\n * so the mismatch is written down in both places it matters.\n */\nexport const CALENDAR_SURFACE: SurfaceContext = {\n appName: \"Calendar\",\n envPrefix: \"APPLE_CALENDAR\",\n};\n\n/** Calendar's Apple Events target. Not `com.apple.Calendar`, which does not exist. */\nexport const CALENDAR_BUNDLE_ID = \"com.apple.iCal\";\n\nexport {\n AppleAutomationError as AppleCalendarError,\n AppBusyError as CalendarBusyError,\n AppNotRunningError as CalendarNotRunningError,\n IndexUnavailableError,\n OsascriptTimeoutError,\n PlatformError,\n PreconditionError,\n ProtocolError,\n SchemaDriftError,\n TccDeniedError,\n WritesDisabledError,\n} from \"@mgcrea/mcp-apple-core\";\n\n/**\n * A date argument could not be understood.\n *\n * Carries the accepted grammar rather than just rejecting, because the caller is\n * usually a model that will retry once and needs to know what shape to retry in.\n */\nexport class InvalidDateError extends AppleAutomationError {\n override readonly name = \"InvalidDateError\";\n\n constructor(field: string, raw: string, reason: string) {\n super(\n `Could not read ${field} from ${JSON.stringify(raw)}: ${reason}. ` +\n `Accepted: an ISO-8601 date \"2026-08-20\" (a whole day) or date-time ` +\n `\"2026-08-20T09:00\" (an instant), or a relative offset like \"+2d\", \"+3h\", \"+45m\", \"+1w\", ` +\n `\"today\", \"tomorrow\", \"tomorrow 09:00\", or \"next monday\".`,\n { field, raw },\n );\n }\n}\n\n/** A CalendarRef no longer resolves — deleted, or moved to another calendar. */\nexport class EventNotFoundError extends AppleAutomationError {\n override readonly name = \"EventNotFoundError\";\n\n constructor(ref: string) {\n super(\n `No event for ref \"${ref}\". It was probably deleted or moved since the search ran. ` +\n `Re-run the search to get a current ref.`,\n { ref },\n );\n }\n}\n\n/** A calendar was named that Calendar does not have. */\nexport class CalendarNotFoundError extends AppleAutomationError {\n override readonly name = \"CalendarNotFoundError\";\n\n constructor(name: string, available: readonly string[] = []) {\n super(\n `No calendar named \"${name}\".` +\n (available.length\n ? ` Available: ${available.slice(0, 20).join(\", \")}${available.length > 20 ? \", …\" : \"\"}.`\n : ` Use apple_calendar_list_calendars to see what exists.`),\n { requested: name },\n );\n }\n}\n\n/**\n * A write was aimed at a calendar that cannot accept one.\n *\n * Its own error rather than a generic failure because the cause is almost\n * always structural rather than a mistake: holiday, birthday and subscribed\n * calendars are read-only by nature, and a caller that hits one needs to pick a\n * different target, not retry.\n */\nexport class CalendarNotWritableError extends AppleAutomationError {\n override readonly name = \"CalendarNotWritableError\";\n\n constructor(name: string) {\n super(\n `The calendar \"${name}\" is read-only, so nothing can be written to it. Subscribed ` +\n `calendars — holidays, birthdays, and anything added by URL — are read-only by nature. ` +\n `Use apple_calendar_list_calendars to find one where \"writable\" is true.`,\n { calendar: name },\n );\n }\n}\n","/**\n * Free-time arithmetic: turning a set of events into the times nothing is on.\n *\n * ## Why this is its own module, and why it is pure\n *\n * `recurrence.ts` set the precedent — the piece whose correctness rests on a\n * measurement lives apart from the SQL. This is the mirror image. Nothing here\n * rests on a measurement at all; it is interval arithmetic and calendar-day\n * iteration, and it is separated for the opposite reason: it is the only part\n * of an availability answer that can be tested without a store, and the part\n * where an off-by-one books a meeting on top of another meeting.\n *\n * ## The rule that governs every decision below\n *\n * A gap reported here is an assertion that NOTHING is on the calendar then.\n * `docs/calendar.md` already names why that is dangerous: \"a short list of\n * events is indistinguishable from a free afternoon\". Every other read tool on\n * this surface can afford to return a short list and flag it. This one cannot —\n * shortening the busy set does not shorten the answer, it INVENTS free time.\n * So the caller of this module must hand it a complete busy set or refuse, and\n * everything here assumes that contract has already been checked.\n *\n * ## Local wall clock, deliberately\n *\n * Working hours are a human's, not UTC's: \"09:00 to 18:00\" means those numbers\n * on the office wall, on both sides of a daylight-saving change. So day\n * boundaries are built with local date components — the same choice, for the\n * same reason, as `at()` in `dates.ts` — and a day that loses an hour simply\n * has one fewer hour in it, which is what actually happened.\n */\n\nimport { InvalidDateError } from \"./errors.js\";\n\n/** Half-open `[from, to)`, in milliseconds since the Unix epoch. */\nexport type Interval = { from: number; to: number };\n\n/** A wall-clock time of day. `24:00` is legal and means the following midnight. */\nexport type Clock = { hours: number; minutes: number };\n\nexport const WEEKDAY_KEYS = [\"sun\", \"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\"] as const;\nexport type WeekdayKey = (typeof WEEKDAY_KEYS)[number];\n\n/** Monday to Friday, the default working week. */\nexport const DEFAULT_WEEKDAYS: readonly WeekdayKey[] = [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\"];\n\nconst CLOCK = /^(\\d{1,2}):(\\d{2})$/;\n\nconst pad = (n: number): string => String(n).padStart(2, \"0\");\n\nexport const parseClock = (field: string, raw: string): Clock => {\n const text = String(raw ?? \"\").trim();\n const m = CLOCK.exec(text);\n if (!m) throw new InvalidDateError(field, text, 'expected a time of day like \"09:00\"');\n const hours = Number(m[1]);\n const minutes = Number(m[2]);\n if (hours > 24 || minutes > 59) {\n throw new InvalidDateError(field, text, `${hours}:${m[2]} is not a time of day`);\n }\n // 24:00 names the end of the day and nothing past it; 24:30 names nothing.\n if (hours === 24 && minutes !== 0) {\n throw new InvalidDateError(\n field,\n text,\n '24:00 is the latest time of day, so \"24:30\" is not one',\n );\n }\n return { hours, minutes };\n};\n\nexport const weekdaySet = (keys: readonly WeekdayKey[]): Set<number> =>\n new Set(keys.map((k) => WEEKDAY_KEYS.indexOf(k)).filter((i) => i !== -1));\n\nconst startOfLocalDay = (d: Date): Date =>\n new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);\n\n/** Same wall-clock time, n days later. Calendar arithmetic, so DST-safe. */\nconst addDays = (d: Date, n: number): Date => {\n const out = new Date(d.getTime());\n out.setDate(out.getDate() + n);\n return out;\n};\n\n/**\n * A time of day on a given local date.\n *\n * `new Date(y, m, d, 24, 0)` rolls to the next midnight on purpose, which is\n * what makes `dayEnd: \"24:00\"` mean \"to the end of this day\" rather than an\n * error case the caller has to special-case.\n */\nconst atClock = (day: Date, c: Clock): number =>\n new Date(day.getFullYear(), day.getMonth(), day.getDate(), c.hours, c.minutes, 0, 0).getTime();\n\n/**\n * Overlapping and touching intervals collapsed into the fewest that cover the\n * same time, in order.\n *\n * Touching counts as overlapping (`<=`, not `<`): two meetings that end and\n * begin on the same minute leave no gap, and emitting a zero-length one would\n * put \"you are free from 10:00 to 10:00\" in front of a model.\n */\nexport const mergeIntervals = (input: readonly Interval[]): Interval[] => {\n const sorted = input.filter((i) => i.to > i.from).toSorted((a, b) => a.from - b.from);\n const out: Interval[] = [];\n for (const cur of sorted) {\n const last = out.at(-1);\n if (last && cur.from <= last.to) {\n if (cur.to > last.to) last.to = cur.to;\n continue;\n }\n out.push({ from: cur.from, to: cur.to });\n }\n return out;\n};\n\n/**\n * What is left of `window` once every busy interval is taken out of it.\n *\n * `busy` MUST already be merged and sorted — pass it through `mergeIntervals`\n * first. Taking unsorted input here would silently under-subtract and hand back\n * time that is booked, which is the one error this module exists to prevent, so\n * the precondition is stated rather than defended against: a defensive re-sort\n * would hide the caller's mistake instead of making it impossible.\n */\nexport const subtractBusy = (window: Interval, busy: readonly Interval[]): Interval[] => {\n const out: Interval[] = [];\n let cursor = window.from;\n for (const b of busy) {\n if (b.to <= cursor) continue;\n if (b.from >= window.to) break;\n if (b.from > cursor) out.push({ from: cursor, to: b.from });\n cursor = b.to;\n if (cursor >= window.to) return out;\n }\n if (cursor < window.to) out.push({ from: cursor, to: window.to });\n return out;\n};\n\n/**\n * The working windows inside `[from, to]`, one per admitted weekday.\n *\n * A `dayEnd` at or before `dayStart` yields no window for that day rather than\n * a negative one. Overnight working hours are not expressible, and saying so\n * with an empty result is better than wrapping around midnight and reporting\n * free time on a day the caller never asked about.\n */\nexport const dayWindows = (opts: {\n from: Date;\n to: Date;\n dayStart: Clock;\n dayEnd: Clock;\n weekdays: ReadonlySet<number>;\n}): Interval[] => {\n const out: Interval[] = [];\n const last = opts.to.getTime();\n for (let day = startOfLocalDay(opts.from); day.getTime() <= last; day = addDays(day, 1)) {\n if (!opts.weekdays.has(day.getDay())) continue;\n const open = atClock(day, opts.dayStart);\n const close = atClock(day, opts.dayEnd);\n if (close <= open) continue;\n const from = Math.max(open, opts.from.getTime());\n const to = Math.min(close, last);\n if (to > from) out.push({ from, to });\n }\n return out;\n};\n\n/**\n * Round an instant up to the next granularity boundary of its own hour.\n *\n * Anchored to the top of the hour rather than to the window, so a slot lands on\n * 09:15 and not on 09:07 because that is when the previous meeting happened to\n * end. Callers restrict granularity to a divisor of 60, which is what keeps the\n * per-hour restart invisible.\n */\nexport const alignUp = (ms: number, granularityMinutes: number): number => {\n const d = new Date(ms);\n const topOfHour = new Date(\n d.getFullYear(),\n d.getMonth(),\n d.getDate(),\n d.getHours(),\n 0,\n 0,\n 0,\n ).getTime();\n const step = granularityMinutes * 60_000;\n const past = ms - topOfHour;\n return topOfHour + Math.ceil(past / step) * step;\n};\n\n/** Local midnight at the start of the day an instant falls on. */\nexport const startOfLocalDayMs = (ms: number): number => startOfLocalDay(new Date(ms)).getTime();\n\n/** The following local midnight. Calendar arithmetic, so a DST day is 23h or 25h. */\nexport const nextLocalDayMs = (ms: number): number =>\n addDays(startOfLocalDay(new Date(ms)), 1).getTime();\n\n/** `2026-08-24`, in local components — the day a slot falls on. */\nexport const localDay = (ms: number): string => {\n const d = new Date(ms);\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n};\n","/**\n * Date handling for the Calendar tools.\n *\n * ## Two halves\n *\n * INPUT is the grammar `packages/reminders/src/client/dates.ts` established, and\n * it is deliberately identical: a caller writing `2026-08-20` names a day, one\n * writing `2026-08-20T09:00` names an instant, `+2d` names a duration, and day\n * and week offsets are calendar arithmetic while hour and minute offsets are\n * elapsed time. That split is not cosmetic — getting it backwards drifts every\n * date by an hour twice a year.\n *\n * OUTPUT is Calendar's own problem and has no analogue in Reminders. An event\n * carries a start AND an end, a timezone of its own, and an all-day flag that\n * changes what the stored number even means. Rendering that wrongly is the\n * quietest bug on this surface, so `EventInstant` makes the caller's two cases\n * two different shapes rather than one shape with a boolean to remember.\n *\n * ## The timezone rules, measured rather than assumed\n *\n * `docs/calendar.md` records 8 distinct `start_tz` values across 1,350 rows,\n * with no nulls, of which two are not IANA names and mean OPPOSITE things:\n *\n * `_float` a floating date — an instant deliberately without a zone\n * `GMT+0200` a perfectly definite fixed offset that is merely not IANA\n *\n * Collapsing the second into the first silently discards two hours, so they are\n * classified apart here. Anything matching `GMT±HHMM` is honoured as an offset;\n * only what is left over floats.\n */\n\nimport { InvalidDateError } from \"./errors.js\";\n\nexport type DateKind = \"allDay\" | \"timed\";\n\nexport type ParsedDate = {\n kind: DateKind;\n /** The absolute instant, for comparisons and for handing to JXA. */\n at: Date;\n /** ISO-8601 with an explicit offset, so the value survives leaving this process. */\n iso: string;\n /** Echoed back in tool results so a caller can see how its input was read. */\n raw: string;\n};\n\n/**\n * What a tool reports for an event's start or end.\n *\n * A union rather than one struct with an `allDay` flag: an all-day event names\n * a DAY and has no instant, and giving it an `iso` field would invite callers to\n * read one. The type makes that impossible instead of merely discouraged.\n */\nexport type EventInstant =\n | { allDay: true; day: string; timeZone: null }\n | { allDay: false; iso: string; timeZone: string | null };\n\nconst DAY_NAMES = [\n \"sunday\",\n \"monday\",\n \"tuesday\",\n \"wednesday\",\n \"thursday\",\n \"friday\",\n \"saturday\",\n] as const;\n\nconst ISO_DATE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst ISO_DATETIME =\n /^(\\d{4})-(\\d{2})-(\\d{2})[T ](\\d{2}):(\\d{2})(?::(\\d{2}))?(?:\\.\\d+)?(Z|[+-]\\d{2}:?\\d{2})?$/;\nconst OFFSET =\n /^\\+(\\d+)\\s*(m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days|w|week|weeks)$/;\nconst DAY_WORD = /^(today|tomorrow)(?:\\s+(\\d{1,2}):(\\d{2}))?$/;\nconst NEXT_DAY = /^next\\s+([a-z]+)(?:\\s+(\\d{1,2}):(\\d{2}))?$/;\nconst DURATION = /^(\\d+)\\s*(m|min|mins|minute|minutes|h|hr|hrs|hour|hours)?$/;\n\n/** `GMT+0200`, `UTC-05:00`. A definite zone that is simply not an IANA name. */\nconst FIXED_OFFSET = /^(?:GMT|UTC)([+-])(\\d{2}):?(\\d{2})$/i;\n\nconst pad = (n: number, w = 2): string => String(n).padStart(w, \"0\");\n\n/** `+02:00` / `-05:00` / `Z` for a given instant, in the system zone. */\nconst offsetOf = (d: Date): string => {\n const mins = -d.getTimezoneOffset();\n if (mins === 0) return \"Z\";\n const sign = mins < 0 ? \"-\" : \"+\";\n const abs = Math.abs(mins);\n return `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;\n};\n\n/**\n * Local wall-clock time rendered with its offset.\n *\n * Deliberately not `toISOString()`, which converts to UTC and would report a\n * 09:00 meeting as `07:00Z` — correct as an instant, unreadable in a result\n * whose purpose is confirming what the caller asked for.\n */\nexport const toLocalIso = (d: Date): string =>\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` +\n `T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${offsetOf(d)}`;\n\nconst startOfDay = (d: Date): Date =>\n new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);\n\nconst endOfDay = (d: Date): Date =>\n new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);\n\n/** Calendar-aware day arithmetic: same wall-clock time, n days later. */\nconst addDays = (d: Date, n: number): Date => {\n const out = new Date(d.getTime());\n out.setDate(out.getDate() + n);\n return out;\n};\n\nconst at = (day: Date, hours: number, minutes: number): Date =>\n new Date(day.getFullYear(), day.getMonth(), day.getDate(), hours, minutes, 0, 0);\n\nconst result = (kind: DateKind, when: Date, raw: string): ParsedDate => ({\n kind,\n at: when,\n iso: toLocalIso(when),\n raw,\n});\n\n// ─── input ───────────────────────────────────────────────────────────────────\n\n/**\n * Parse one date argument.\n *\n * @param field Named in the error, so a failure says WHICH argument was bad.\n * @param raw The caller's string.\n * @param now Injected for hermetic tests.\n */\nexport const parseDate = (field: string, raw: string, now: Date = new Date()): ParsedDate => {\n const text = String(raw ?? \"\").trim();\n if (!text) throw new InvalidDateError(field, String(raw), \"it is empty\");\n const lower = text.toLowerCase();\n\n const dt = ISO_DATETIME.exec(text);\n if (dt) {\n const [, y, mo, d, hh, mm, ss, zone] = dt;\n const when = zone\n ? new Date(text.replace(\" \", \"T\"))\n : new Date(\n Number(y),\n Number(mo) - 1,\n Number(d),\n Number(hh),\n Number(mm),\n Number(ss ?? \"0\"),\n 0,\n );\n if (Number.isNaN(when.getTime())) {\n throw new InvalidDateError(field, text, \"it is not a real date\");\n }\n return result(\"timed\", when, text);\n }\n\n const only = ISO_DATE.exec(text);\n if (only) {\n const [, y, mo, d] = only;\n const when = new Date(Number(y), Number(mo) - 1, Number(d), 0, 0, 0, 0);\n if (Number.isNaN(when.getTime())) {\n throw new InvalidDateError(field, text, \"it is not a real date\");\n }\n // Guard against JS's silent rollover: new Date(2026, 1, 30) is 2 March.\n if (when.getMonth() !== Number(mo) - 1 || when.getDate() !== Number(d)) {\n throw new InvalidDateError(field, text, `there is no day ${d} in month ${mo}`);\n }\n return result(\"allDay\", when, text);\n }\n\n const off = OFFSET.exec(lower);\n if (off) {\n const n = Number(off[1]);\n const unit = String(off[2]);\n if (!Number.isFinite(n)) throw new InvalidDateError(field, text, \"the amount is not a number\");\n if (unit.startsWith(\"d\")) return result(\"timed\", addDays(now, n), text);\n if (unit.startsWith(\"w\")) return result(\"timed\", addDays(now, n * 7), text);\n const ms = unit.startsWith(\"h\") ? n * 3_600_000 : n * 60_000;\n return result(\"timed\", new Date(now.getTime() + ms), text);\n }\n\n const word = DAY_WORD.exec(lower);\n if (word) {\n const day = word[1] === \"tomorrow\" ? addDays(now, 1) : now;\n if (word[2] === undefined) return result(\"allDay\", startOfDay(day), text);\n const [hh, mm] = [Number(word[2]), Number(word[3])];\n if (hh > 23 || mm > 59)\n throw new InvalidDateError(field, text, `${hh}:${word[3]} is not a time`);\n return result(\"timed\", at(day, hh, mm), text);\n }\n\n // \"next monday\" on a Monday is +7, never today.\n const next = NEXT_DAY.exec(lower);\n if (next) {\n const idx = DAY_NAMES.findIndex((n) => n === next[1] || n.slice(0, 3) === next[1]);\n if (idx === -1) {\n throw new InvalidDateError(field, text, `\"${next[1]}\" is not a day of the week`);\n }\n const ahead = (idx - now.getDay() + 7) % 7 || 7;\n const day = addDays(now, ahead);\n if (next[2] === undefined) return result(\"allDay\", startOfDay(day), text);\n const [hh, mm] = [Number(next[2]), Number(next[3])];\n if (hh > 23 || mm > 59)\n throw new InvalidDateError(field, text, `${hh}:${next[3]} is not a time`);\n return result(\"timed\", at(day, hh, mm), text);\n }\n\n throw new InvalidDateError(field, text, \"it matches none of the accepted forms\");\n};\n\n/**\n * Parse a bound for a range filter.\n *\n * A bare day means the WHOLE day, so which edge it resolves to depends on which\n * side of the range it is. Resolving both to midnight would make an end bound\n * quietly exclude the day the caller named.\n */\nexport const parseBound = (\n field: string,\n raw: string,\n edge: \"start\" | \"end\",\n now: Date = new Date(),\n): Date => {\n const parsed = parseDate(field, raw, now);\n if (parsed.kind !== \"allDay\") return parsed.at;\n return edge === \"end\" ? endOfDay(parsed.at) : startOfDay(parsed.at);\n};\n\nexport type Range = { from: Date; to: Date; clamped: boolean };\n\n/**\n * Resolve a query window.\n *\n * Unlike a note list, a calendar has no natural \"everything\": it stretches\n * indefinitely in both directions and `MAX(start_date)` on the probed store\n * already reads 2030. So an unbounded default would scan a decade to report\n * next Tuesday, and every window is bounded on both ends.\n */\nexport const parseRange = (\n opts: {\n from?: string | undefined;\n to?: string | undefined;\n defaultRangeDays: number;\n maxRangeDays: number;\n },\n now: Date = new Date(),\n): Range => {\n const from = opts.from ? parseBound(\"from\", opts.from, \"start\", now) : startOfDay(now);\n const to = opts.to\n ? parseBound(\"to\", opts.to, \"end\", now)\n : endOfDay(addDays(from, opts.defaultRangeDays - 1));\n\n if (to.getTime() < from.getTime()) {\n throw new InvalidDateError(\n \"to\",\n String(opts.to ?? \"\"),\n `it resolves to ${toLocalIso(to)}, which is before from (${toLocalIso(from)})`,\n );\n }\n\n const maxMs = opts.maxRangeDays * 86_400_000;\n if (to.getTime() - from.getTime() > maxMs) {\n return { from, to: new Date(from.getTime() + maxMs), clamped: true };\n }\n return { from, to, clamped: false };\n};\n\n/** `90`, `\"90\"`, `\"90m\"`, `\"2h\"` -> minutes. */\nexport const parseDuration = (field: string, raw: string | number): number => {\n const text = String(raw ?? \"\")\n .trim()\n .toLowerCase();\n if (!text) throw new InvalidDateError(field, String(raw), \"it is empty\");\n const m = DURATION.exec(text);\n if (!m)\n throw new InvalidDateError(field, text, 'expected minutes, or a value like \"90m\" or \"2h\"');\n const n = Number(m[1]);\n if (!Number.isFinite(n) || n <= 0) {\n throw new InvalidDateError(field, text, \"the amount must be a positive number\");\n }\n return m[2]?.startsWith(\"h\") ? n * 60 : n;\n};\n\n// ─── output ──────────────────────────────────────────────────────────────────\n\n/** Whether a stored `start_tz` names a zone this process can actually resolve. */\nexport const isIanaZone = (tz: string): boolean => {\n try {\n // Called, not `new`ed — it returns an instance either way, and the throw on\n // an unknown zone is the whole test.\n Intl.DateTimeFormat(\"en-US\", { timeZone: tz });\n return true;\n } catch {\n return false;\n }\n};\n\n/**\n * A stored timezone string, classified.\n *\n * `null` means floating, and floating is a real state rather than missing data:\n * the event names a wall-clock time that is correct wherever you open it.\n */\nexport const resolveZone = (tz: string | null | undefined): string | null => {\n if (!tz) return null;\n const text = String(tz).trim();\n if (!text || text === \"_float\") return null;\n if (isIanaZone(text)) return text;\n const fixed = FIXED_OFFSET.exec(text);\n // Re-expressed as an Etc/GMT name so Intl can use it. Etc/GMT signs are\n // INVERTED by the POSIX convention — Etc/GMT-2 is two hours AHEAD of UTC —\n // which is a trap worth naming rather than a detail.\n if (fixed) {\n const [, sign, hh, mm] = fixed;\n if (mm === \"00\") {\n const flipped = sign === \"+\" ? \"-\" : \"+\";\n const name = `Etc/GMT${flipped}${Number(hh)}`;\n if (isIanaZone(name)) return name;\n }\n }\n return null;\n};\n\nconst partsIn = (d: Date, tz: string): Record<string, string> => {\n const fmt = Intl.DateTimeFormat(\"en-US\", {\n timeZone: tz,\n hourCycle: \"h23\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n timeZoneName: \"longOffset\",\n });\n const out: Record<string, string> = {};\n for (const p of fmt.formatToParts(d)) out[p.type] = p.value;\n return out;\n};\n\n/**\n * Render a stored instant as an event start or end.\n *\n * ## Why all-day is derived in the event's own zone, falling back to local\n *\n * An all-day event names a DAY, and the day you get depends entirely on which\n * frame you read the stored instant in. MEASURED against a live store:\n *\n * stored 2026-08-20T22:00:00Z\n * UTC getters 2026-08-20 <- a day early\n * local getters 2026-08-21 <- what Calendar.app shows\n *\n * Calendar anchors an all-day event at midnight in the event's own zone, which\n * for a floating date is the local one. So the day comes from local components,\n * or from `start_tz` when that names a real zone.\n *\n * THIS WAS ORIGINALLY WRITTEN THE OTHER WAY ROUND, and the mistake is worth\n * keeping on the record. `docs/reminders.md` documents that REMINDERS' store\n * holds UTC midnight while its Apple Events lane holds local midnight; that was\n * generalised to Calendar without measuring, and the unit tests agreed because\n * their fixtures were built on the same wrong assumption. It survived a\n * four-timezone test matrix and was caught only by reading a real calendar,\n * where the rendered day disagreed with the ref in the same result.\n *\n * The anchor is a property of the store, not a general rule. Measure it per\n * surface. `dates.test.ts` now builds its fixtures at LOCAL midnight, which is\n * what the data actually looks like.\n *\n * @param appleSeconds Core Data seconds, straight from the column.\n * @param tz The row's `start_tz` / `end_tz`, unclassified.\n * @param allDay The row's all-day flag. Authoritative; never inferred.\n * @param epochOffset From `StoreCapabilities`, so the 31-year bug has one home.\n */\nexport const renderInstant = (\n appleSeconds: number | null,\n tz: string | null | undefined,\n allDay: boolean,\n epochOffset: number,\n): EventInstant | null => {\n if (appleSeconds === null || appleSeconds === undefined || !Number.isFinite(appleSeconds)) {\n return null;\n }\n const d = new Date((appleSeconds + epochOffset) * 1000);\n if (Number.isNaN(d.getTime())) return null;\n\n if (allDay) {\n const zone = resolveZone(tz);\n if (zone) {\n const p = partsIn(d, zone);\n return { allDay: true, day: `${p.year}-${p.month}-${p.day}`, timeZone: null };\n }\n // Floating: the stored instant is midnight where the event was written, and\n // for a floating date that is the local zone. `timeZone` stays null either\n // way — an all-day event names a day, and reporting a zone would invite a\n // caller to read it as an instant.\n return {\n allDay: true,\n day: `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`,\n timeZone: null,\n };\n }\n\n const zone = resolveZone(tz);\n if (!zone) {\n // Floating: report the wall clock the system would show, and say plainly\n // that no zone backs it by returning timeZone: null.\n return { allDay: false, iso: toLocalIso(d), timeZone: null };\n }\n\n const p = partsIn(d, zone);\n // \"GMT+02:00\" -> \"+02:00\"; bare \"GMT\" is UTC.\n const raw = p.timeZoneName ?? \"\";\n const offset = raw === \"GMT\" ? \"Z\" : raw.replace(/^GMT/, \"\");\n return {\n allDay: false,\n iso: `${p.year}-${p.month}-${p.day}T${p.hour}:${p.minute}:${p.second}${offset}`,\n timeZone: zone,\n };\n};\n","/**\n * JXA script fragments.\n *\n * Every script here is a static constant. None may contain a template\n * interpolation — `assertStaticScript` rejects any script containing a dollar\n * sign followed by a brace, including template literals written INSIDE the JXA\n * source. Use string concatenation in JXA code.\n *\n * Every script follows the same contract:\n * - it reads its parameters from `JSON.parse(argv[0])`\n * - it returns `JSON.stringify({ok: true, data})` on success\n * - it returns `JSON.stringify({ok: false, error: {code, message}})` on an\n * application-level failure, still exiting 0\n * so a non-zero exit always means infrastructure rather than \"no such event\".\n *\n * ## There is no read.ts here, and that is the design\n *\n * Mail, Notes and Reminders all carry an Apple Events read lane. Calendar does\n * not, because `docs/calendar.md` measured one: a single ±90-day range query\n * costs 3.4 s over 1,349 events, and the cost falls per ROUND TRIP rather than\n * per event — about 2 s per property, flat — so no batching rescues it. That is\n * not a slower fallback, it is no fallback. Reads go through the file lane and\n * these scripts exist only to make Calendar change something.\n *\n * `test/jxa.test.ts` asserts that `read.ts` does not exist, so this stays a\n * decision rather than an accident.\n *\n * ## Finding an event costs a bulk fetch, which is why refs carry a calendar\n *\n * Calendar has no `events.byId()`. The two options are `whose({uid})`, measured\n * at 4.5-7.3 s and — worse than slow — UNSTABLE across runs, or one bulk\n * `cal.events.uid()` fetch plus an index in JS at about 1.8 s. The second only\n * stays affordable if it is scoped to ONE calendar, so `ref.ts` carries the\n * calendar uid and every script below narrows before it scans.\n */\n\n/**\n * Shared prelude.\n *\n * THE BUNDLE IDENTIFIER IS `com.apple.iCal`. Calendar.app kept the id it\n * shipped with as iCal, and this is the only surface in the project where the\n * display name and the bundle id disagree — `Application(\"Calendar\")` is\n * correct, and `com.apple.Calendar` does not exist.\n * `runningApplicationsWithBundleIdentifier` matches exactly and does not fold\n * case, so getting this wrong makes every write report \"not running\".\n */\nexport const PRELUDE = `\nObjC.import(\"AppKit\");\n\nfunction isCalendarRunning() {\n var apps = $.NSRunningApplication.runningApplicationsWithBundleIdentifier(\"com.apple.iCal\");\n return apps.count > 0;\n}\n\nfunction ok(data) { return JSON.stringify({ ok: true, data: data }); }\nfunction err(code, message) { return JSON.stringify({ ok: false, error: { code: code, message: String(message) } }); }\n\nfunction iso(d) {\n try { return d ? d.toISOString() : null; } catch (e) { return null; }\n}\n\n/** Read one property defensively: Calendar throws on properties it cannot supply. */\nfunction prop(fn, fallback) {\n try {\n var v = fn();\n return v === undefined ? fallback : v;\n } catch (e) {\n return fallback;\n }\n}\n\n/**\n * Find a calendar BY NAME.\n *\n * MEASURED, macOS 26.6: \\`calendar.uid()\\` throws \\`AppleEvent handler failed\\`\n * (-10000) for EVERY calendar, including ones this process just created. So a\n * calendar cannot be addressed by uid across Apple Events at all, and the\n * store's \\`Calendar.UUID\\` has no counterpart on this side.\n *\n * This is NOT the event id bridge, which docs/calendar.md measured as exact\n * (\\`CalendarItem.UUID\\` matched an event's \\`uid\\` 198/198). That result was\n * assumed to carry over to calendars; it does not. Events are addressable by\n * uid, calendars are addressable by name, and the caller resolves the name\n * before it gets here — including refusing an ambiguous one, which this side\n * has no way to detect.\n */\nfunction findCalendar(C, wanted) {\n if (!wanted) {\n return prop(function () { return C.defaultCalendar(); }, null);\n }\n var cals = prop(function () { return C.calendars(); }, []);\n for (var i = 0; i < cals.length; i++) {\n var name = prop(function () { return String(cals[i].name()); }, null);\n if (name === wanted) return cals[i];\n }\n return null;\n}\n\n/**\n * Find one event inside a calendar, by uid.\n *\n * ONE bulk fetch of every uid in the calendar, then an index in JS.\n *\n * Deliberately NOT a specifier applied to the events collection, which was\n * measured slower AND unstable — 4,564 / 5,290 / 7,303 ms across three runs of\n * the same query — against a steady 3.4 s for a bulk scan. test/jxa.test.ts\n * enforces that by string match, which is why the disallowed form is described\n * here rather than written out.\n */\nfunction findEvent(cal, uid) {\n var uids = prop(function () { return cal.events.uid(); }, null);\n if (!uids) return null;\n for (var i = 0; i < uids.length; i++) {\n if (String(uids[i]) === uid) {\n return prop(function () { return cal.events[i]; }, null);\n }\n }\n return null;\n}\n\n/** Millisecond-tolerant instant comparison, for the excluded-dates path. */\nfunction sameInstant(a, b) {\n if (!a || !b) return false;\n return Math.abs(a.getTime() - b.getTime()) < 1000;\n}\n\n/**\n * Whether a calendar accepts writes.\n *\n * Subscribed calendars — holidays, birthdays, anything added by URL — are\n * read-only, and assigning to one fails deep inside Calendar with a message\n * that does not say so. Asking first turns that into an error naming the cause.\n */\nfunction isWritable(cal) {\n return prop(function () { return cal.writable(); }, true);\n}\n\nfunction readback(ev, cal) {\n return {\n uid: prop(function () { return String(ev.uid()); }, null),\n summary: prop(function () { return String(ev.summary()); }, null),\n startDate: iso(prop(function () { return ev.startDate(); }, null)),\n endDate: iso(prop(function () { return ev.endDate(); }, null)),\n alldayEvent: prop(function () { return ev.alldayEvent(); }, false),\n location: prop(function () { var v = ev.location(); return v === null ? null : String(v); }, null),\n description: prop(function () { var v = ev.description(); return v === null ? null : String(v); }, null),\n url: prop(function () { var v = ev.url(); return v === null ? null : String(v); }, null),\n stampDate: iso(prop(function () { return ev.stampDate(); }, null)),\n // No calendarUid: cal.uid() throws on every calendar (see findCalendar).\n // The caller already knows which calendar it targeted, and re-derives the\n // store uuid from this name.\n calendarName: prop(function () { return String(cal.name()); }, null)\n };\n}\n\n/**\n * Apply the optional fields a create and an update have in common.\n *\n * ## The dates go first, and in an order that is never briefly invalid\n *\n * MEASURED: moving an event later failed with\n *\n * Failed to save event [...], with error\n * [{ NSLocalizedDescription = \"The start date must be before the end date.\" }]\n *\n * because start and end are two assignments, not one. Setting start to 16:00 on\n * an event still ending at 15:30 makes the interval invalid, and EventKit\n * validates on save rather than on assignment. Which order is safe depends on\n * which way the event is moving, so the current end decides: moving later, the\n * end is pushed out first; moving earlier, the start is pulled back first.\n *\n * They also go BEFORE the text fields. Apple Events has no transaction, so a\n * failure part-way through leaves whatever already applied — and the first\n * version of this wrote the new location, then failed on the dates, leaving the\n * event half-updated. Doing the fragile part first means a date failure changes\n * nothing else.\n */\nfunction applyFields(ev, f) {\n var newStart = f.startDate !== undefined && f.startDate !== null ? new Date(f.startDate) : null;\n var newEnd = f.endDate !== undefined && f.endDate !== null ? new Date(f.endDate) : null;\n\n if (newStart !== null && newEnd !== null) {\n var currentEnd = prop(function () { return ev.endDate(); }, null);\n var movingLater = currentEnd === null || newEnd.getTime() >= currentEnd.getTime();\n if (movingLater) {\n ev.endDate = newEnd;\n ev.startDate = newStart;\n } else {\n ev.startDate = newStart;\n ev.endDate = newEnd;\n }\n } else if (newStart !== null) {\n ev.startDate = newStart;\n } else if (newEnd !== null) {\n ev.endDate = newEnd;\n }\n\n if (f.allDay !== undefined && f.allDay !== null) ev.alldayEvent = Boolean(f.allDay);\n if (f.summary !== undefined && f.summary !== null) ev.summary = String(f.summary);\n if (f.location !== undefined) ev.location = f.location === null ? \"\" : String(f.location);\n if (f.description !== undefined) ev.description = f.description === null ? \"\" : String(f.description);\n if (f.url !== undefined) ev.url = f.url === null ? \"\" : String(f.url);\n}\n`;\n\n/**\n * Wrap a script body in the prelude and the liveness gate.\n *\n * Every script in `write.ts` sets `allowLaunch`, and that is not an oversight:\n * a write is a deliberate side effect, so launching Calendar to perform one is\n * expected in a way that launching it for a read never is. There are no read\n * scripts here to keep it false for.\n */\nexport const script = (body: string, opts: { allowLaunch?: boolean } = {}): string => `\n${PRELUDE}\nfunction run(argv) {\n var p = JSON.parse(argv[0] || \"{}\");\n if (!isCalendarRunning() && !${opts.allowLaunch ? \"true\" : \"false\"}) {\n return err(\"APP_NOT_RUNNING\", \"Calendar is not running.\");\n }\n try {\n var C = Application(\"Calendar\");\n ${body}\n } catch (e) {\n var msg = String(e && e.message ? e.message : e);\n if (msg.indexOf(\"-1743\") !== -1) return err(\"NOT_AUTHORIZED\", msg);\n return err(\"SCRIPT_ERROR\", msg);\n }\n}\n`;\n","import { script } from \"./core.js\";\n\n/**\n * Mutating scripts.\n *\n * These are the only place Calendar is asked to change anything, and every one\n * re-reads the affected event afterwards: what a tool returns is what Calendar\n * STORED, never what the caller requested. The two differ more often than you\n * would expect — setting `alldayEvent` reshapes the start and end, and Calendar\n * decides the final values, not us.\n *\n * Dates arrive as ISO-8601 strings carrying an explicit offset (see `dates.ts`)\n * and are turned back into Date objects here. Passing a bare local string would\n * have Calendar resolve it in whatever zone it feels like.\n *\n * ## Real side effects, said plainly\n *\n * A create on a CalDAV or Exchange calendar syncs within seconds and other\n * people see it. There is no draft state and no undo. That is why the tools\n * that call these carry the warning in their descriptions, and why attendees\n * are not a parameter anywhere in this file: adding one emails a human.\n */\n\nexport const CREATE_EVENT = script(\n `\n var cal = findCalendar(C, p.calendar);\n if (!cal) return err(\"CALENDAR_NOT_FOUND\", p.calendar || \"(default)\");\n if (!isWritable(cal)) return err(\"CALENDAR_NOT_WRITABLE\", prop(function () { return String(cal.name()); }, \"?\"));\n\n // Build with the required dates, push, THEN apply the optional fields.\n // An object that is not yet in a container has nowhere to store a property,\n // so assignments made before the push are silently lost.\n var ev = C.Event({\n summary: String(p.summary),\n startDate: new Date(p.startDate),\n endDate: new Date(p.endDate)\n });\n cal.events.push(ev);\n applyFields(ev, p);\n\n return ok(readback(ev, cal));\n`,\n { allowLaunch: true },\n);\n\nexport const UPDATE_EVENT = script(\n `\n var cal = findCalendar(C, p.calendar);\n if (!cal) return err(\"CALENDAR_NOT_FOUND\", p.calendar || \"(default)\");\n if (!isWritable(cal)) return err(\"CALENDAR_NOT_WRITABLE\", prop(function () { return String(cal.name()); }, \"?\"));\n\n var ev = findEvent(cal, String(p.uid));\n if (!ev) return err(\"EVENT_NOT_FOUND\", String(p.uid));\n\n applyFields(ev, p);\n return ok(readback(ev, cal));\n`,\n { allowLaunch: true },\n);\n\n/**\n * Delete whole events, and VERIFY that each one actually went.\n *\n * MEASURED, macOS 26.6: `C.delete(ev)` on a RECURRING event neither throws nor\n * deletes. Event count before 1, after 1, still present — while the same call\n * removes a non-repeating event correctly. Trusting \"it did not throw\" therefore\n * reported `deleted: true` for an event that is still on the calendar, which is\n * the worst thing a delete can say.\n *\n * So the uid list is re-read afterwards and `deleted` is decided by ABSENCE.\n * The sibling script that excluded one occurrence had this check from the start\n * and it is what caught that property being broken; this one did not, and this\n * is what that omission cost.\n *\n * Per-id results rather than a bulk throw: deleting five events where the third\n * has already gone should remove four and say which one was missing.\n */\nexport const DELETE_EVENTS = script(\n `\n var cal = findCalendar(C, p.calendar);\n if (!cal) return err(\"CALENDAR_NOT_FOUND\", p.calendar || \"(default)\");\n if (!isWritable(cal)) return err(\"CALENDAR_NOT_WRITABLE\", prop(function () { return String(cal.name()); }, \"?\"));\n\n var results = [];\n for (var i = 0; i < p.uids.length; i++) {\n var uid = String(p.uids[i]);\n var ev = findEvent(cal, uid);\n if (!ev) {\n results.push({ uid: uid, found: false, deleted: false, repeats: false });\n continue;\n }\n var repeats = false;\n try {\n var r = ev.recurrence();\n repeats = r !== null && r !== undefined && String(r) !== \"\";\n } catch (e) {\n repeats = false;\n }\n var threw = null;\n try { C.delete(ev); } catch (e) { threw = String(e).slice(0, 120); }\n results.push({ uid: uid, found: true, deleted: null, repeats: repeats, error: threw });\n }\n\n // ONE bulk re-read decides the truth for every id at once.\n var after = prop(function () { return cal.events.uid(); }, null);\n for (var j = 0; j < results.length; j++) {\n if (!results[j].found) continue;\n if (after === null) {\n results[j].deleted = null;\n results[j].reason = \"could not re-read the calendar to confirm\";\n continue;\n }\n var gone = true;\n for (var k = 0; k < after.length; k++) {\n if (String(after[k]) === results[j].uid) gone = false;\n }\n results[j].deleted = gone;\n if (!gone) {\n results[j].reason = results[j].repeats\n ? \"Calendar did not delete it and reported no error, which is what it does for a repeating event. Delete it in Calendar.app.\"\n : \"Calendar reported no error but the event is still there.\";\n }\n }\n return ok({ results: results });\n`,\n { allowLaunch: true },\n);\n\n/**\n * REMOVED: excluding one occurrence, which Calendar cannot do.\n *\n * MEASURED, macOS 26.6, against a real repeating event:\n *\n * ev.excludedDates() -> [\"1903-12-31T23:50:39.000Z\"]\n * ev.excludedDates = [aDate] -> TypeError: undefined is not an object\n *\n * The read returns a sentinel rather than the empty list the event actually\n * has, and the assignment throws — while `ev.summary = \"x\"` on the very same\n * specifier works, so this is the property, not the specifier or the lane.\n *\n * A script was written here that assigned the whole array back and then read it\n * again to confirm, precisely because a silent no-op on a delete is the worst\n * lie this surface could tell. The verification worked: it caught this. But a\n * write path that can only ever report failure is not a capability, so the tool\n * no longer offers \"delete one occurrence\" at all — `delete_events` refuses an\n * occurrence ref and says why, which is the same shape as `update_event`.\n *\n * Deleting a single occurrence is possible in Calendar.app itself, so this is a\n * limit of the scripting interface rather than of the data.\n */\n","import { readdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { describeStore, type StoreFacts } from \"@mgcrea/mcp-apple-core\";\n\n/**\n * Find Calendar's store.\n *\n * ## Why this is the easy case\n *\n * Reminders keeps its database under a generated directory name, so resolving\n * it means *listing* a protected directory — which is itself the privileged\n * operation, leaving no path to even stat without the grant. Calendar does not:\n *\n * ~/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb\n *\n * is a constant. `statSync` succeeds on a TCC-protected file (only `access(2)`\n * is denied — see packages/core/src/fs.ts), so this locator can tell \"exists but\n * unreadable\" from \"not there at all\" with no permission whatsoever. Those are\n * different failures with different fixes, and saying so is most of what\n * diagnostics is for.\n *\n * ## Why it still walks the container\n *\n * `docs/calendar.md` recorded per-account stores sitting beside the main one,\n * and the probe picked between them by size. The known filename is *preferred*,\n * so the common case costs one `describeStore` and no listing at all; the walk\n * is a fallback for a machine whose layout differs, and for the day Apple moves\n * the file the way it moved Reminders' out of `~/Library/Reminders`.\n */\n\n/** `group.com.apple.calendar`, under `~/Library/Group Containers`. */\nexport const GROUP_CONTAINER = \"group.com.apple.calendar\";\n\n/** The observed filename. Preferred when present; not required. */\nexport const STORE_FILENAME = \"Calendar.sqlitedb\";\n\n/** Sits beside the store. 32 KB, and its contents are not used by this server. */\nexport const EXTRAS_FILENAME = \"Extras.db\";\n\n/** Depth cap for the fallback walk. The real store sits at depth 0. */\nconst MAX_DEPTH = 3;\n\nconst STORE_SUFFIX = /\\.(sqlitedb|sqlite)$/i;\n\nexport type StoreCandidate = StoreFacts & { path: string };\n\nexport type LocateResult = StoreFacts & {\n containerPath: string;\n /** Null when discovery found nothing at all — see `reason`. */\n storePath: string | null;\n /** Every store-shaped file found, largest first. */\n candidates: StoreCandidate[];\n /**\n * Whether the container could be listed.\n *\n * Unlike Reminders this is NOT the permission signal — the store's path is\n * known, so `readable` answers that directly. It is reported because a\n * listable container with no store in it means Calendar was never set up,\n * which is a different conversation from a denied grant.\n */\n containerListable: boolean;\n /** `Extras.db` beside the store. Recorded so it stops being an unknown. */\n extrasPresent: boolean;\n reason: string | null;\n};\n\nexport const defaultContainerPath = (home: string = homedir()): string =>\n join(home, \"Library\", \"Group Containers\", GROUP_CONTAINER);\n\nexport const defaultStorePath = (home: string = homedir()): string =>\n join(defaultContainerPath(home), STORE_FILENAME);\n\nconst listDir = (dir: string): string[] => {\n try {\n return readdirSync(dir, { withFileTypes: true }).flatMap((e) =>\n e.isDirectory() ? [] : [e.name],\n );\n } catch {\n return [];\n }\n};\n\nconst walk = (dir: string, depth: number, out: string[]): string[] => {\n if (depth > MAX_DEPTH) return out;\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return out;\n }\n for (const e of entries) {\n const p = join(dir, e.name);\n if (e.isDirectory()) walk(p, depth + 1, out);\n else if (STORE_SUFFIX.test(e.name)) out.push(p);\n }\n return out;\n};\n\nconst FDA_HINT =\n \"Grant Full Disk Access to the app running this server (System Settings > Privacy & \" +\n \"Security > Full Disk Access) and restart it. Granting it to Calendar.app does nothing — \" +\n \"the reader needs the permission.\";\n\nexport const locateStore = (\n opts: { storePath?: string | undefined; home?: string } = {},\n): LocateResult => {\n const containerPath = defaultContainerPath(opts.home);\n\n // An explicit path is a bypass, for tests and forensic copies. No discovery.\n if (opts.storePath) {\n const facts = describeStore(opts.storePath);\n return {\n ...facts,\n containerPath,\n storePath: opts.storePath,\n candidates: [{ ...facts, path: opts.storePath }],\n containerListable: true,\n extrasPresent: false,\n reason: facts.readable\n ? null\n : facts.exists\n ? `The store at ${opts.storePath} exists but cannot be read. ${FDA_HINT}`\n : `No file at ${opts.storePath}. APPLE_CALENDAR_STORE points at nothing.`,\n };\n }\n\n // The known path first. This is the whole advantage over Reminders: it needs\n // no directory listing, so it works — and reports the truth — with no grant.\n const known = defaultStorePath(opts.home);\n const knownFacts = describeStore(known);\n\n const listing = listDir(containerPath);\n const containerListable = listing.length > 0;\n const extrasPresent = listing.includes(EXTRAS_FILENAME);\n\n const paths = knownFacts.readable ? [known] : walk(containerPath, 0, []);\n const candidates: StoreCandidate[] = (paths.includes(known) ? paths : [known, ...paths])\n .map((p) => ({ ...describeStore(p), path: p }))\n .filter((c) => c.exists)\n .toSorted((a, b) => (b.size ?? 0) - (a.size ?? 0));\n\n // The known path wins when it is readable, even if some other file is larger:\n // size is a tie-breaker between unknowns, not evidence against a documented\n // filename. Otherwise fall back to the largest readable candidate.\n const chosen = knownFacts.readable\n ? ({ ...knownFacts, path: known } satisfies StoreCandidate)\n : (candidates.find((c) => c.readable) ?? candidates[0] ?? null);\n\n const reason = chosen?.readable\n ? null\n : knownFacts.exists\n ? `Found the Calendar store at ${known} but cannot read it. ${FDA_HINT}`\n : containerListable\n ? `No store file under ${containerPath}. Has Calendar ever been set up on this account?`\n : `Neither ${known} nor its container could be reached. If Calendar is set up on this ` +\n `account this is a permission problem. ${FDA_HINT}`;\n\n const facts: StoreFacts = chosen\n ? {\n exists: chosen.exists,\n readable: chosen.readable,\n size: chosen.size,\n mtime: chosen.mtime,\n walPresent: chosen.walPresent,\n walSizeBytes: chosen.walSizeBytes,\n }\n : knownFacts;\n\n return {\n ...facts,\n containerPath,\n storePath: chosen?.path ?? null,\n candidates,\n containerListable,\n extrasPresent,\n reason,\n };\n};\n","/**\n * Merging the two legs of a range query, and saying honestly what the result\n * does not cover.\n *\n * ## Why this is its own module\n *\n * It is the piece most likely to be rewritten. `store.ts` knows how to ask the\n * database questions; this file encodes what the answers MEAN, and that meaning\n * rests on measurements (`docs/calendar.md`) rather than on documentation Apple\n * publishes. Keeping it separate means a future correction swaps this file\n * rather than picking through SQL.\n *\n * ## Why a merge is needed at all\n *\n * Measured: `OccurrenceCache` holds 489 distinct parents, of which **456 carry\n * no recurrence rule**. So the cache is not \"the repeating events\" — it holds\n * plain one-shot events too, and a naive union of \"items\" and \"occurrences\"\n * double-counts most of an ordinary calendar. Dedupe here is load-bearing, not\n * defensive.\n *\n * ## Why the coverage edge is published rather than hidden\n *\n * The cache reaches about two years either side of today on the probed store,\n * which is a real expansion rather than a month-view cache. It is still an edge.\n * A query running past it gets fewer repeating events than it should, and a\n * short list of events is indistinguishable from a free afternoon — the quietest\n * possible failure on this surface. So the edge travels with the result.\n */\n\nimport { renderInstant } from \"./dates.js\";\nimport type { Coverage, EventRow } from \"./store.js\";\n\nexport type Truncation = {\n reason: string;\n /** Apple-seconds. The part of the requested window the expansion cannot cover. */\n uncoveredFromApple?: number;\n uncoveredToApple?: number;\n affects: string;\n};\n\nexport type ExpansionState = \"expanded\" | \"unavailable\";\n\nexport type MergedRange = {\n rows: EventRow[];\n expansion: ExpansionState;\n /** Present when the expansion could not run at all. */\n expansionReason?: string;\n coverage: Coverage;\n truncated?: Truncation;\n /** Rows dropped for having no usable start. Counted rather than silently lost. */\n dropped: number;\n};\n\n/**\n * Identity for deduping.\n *\n * `(uuid, rendered start)` rather than uuid alone: two occurrences of the same\n * series are the same event at different times, and collapsing them by uuid\n * would return a weekly meeting once — the exact bug the two-leg design exists\n * to avoid.\n *\n * ## Why the RENDERED start and not the raw column\n *\n * MEASURED, against a live calendar: an all-day event created through this\n * server came back TWICE — once from each leg, same uuid, both rendering as\n * 17 September. `CalendarItem.start_date` and `OccurrenceCache.occurrence_date`\n * do not hold the identical number for an all-day event, so a key built on the\n * raw value saw two different events and let both through.\n *\n * Rendering first collapses that: two all-day rows for one uuid on one DAY are\n * the same event, whatever the two columns disagree about underneath. For a\n * timed event the rendered ISO is second-precision, which also absorbs the\n * float wobble a REAL column can introduce.\n */\nconst keyOf = (r: EventRow, epochOffset: number): string => {\n const at = renderInstant(r.startApple, r.startTz, r.allDay, epochOffset);\n const when = at ? (at.allDay ? at.day : at.iso) : String(r.startApple);\n return `${String(r.uuid).toUpperCase()}|${when}`;\n};\n\n/**\n * The identity of the occurrence a detached row REPLACES.\n *\n * When someone drags one instance of a series to a new time, Calendar writes a\n * detached `CalendarItem` carrying `orig_item_id` (the series) and `orig_date`\n * (the slot it came from). The stale cache row for that slot may still exist, so\n * keying on `(uuid, start)` alone will not catch it: the instants differ, which\n * is the entire point of the move.\n */\nconst replacedKeyOf = (r: EventRow): string | null =>\n r.origItemPk !== null && r.origDateApple !== null ? `${r.origItemPk}|${r.origDateApple}` : null;\n\nconst occurrenceSlotOf = (r: EventRow): string => `${r.itemPk}|${r.startApple}`;\n\nexport const mergeRange = (opts: {\n items: EventRow[];\n occurrences: EventRow[];\n coverage: Coverage;\n hasOccurrenceCache: boolean;\n /** Why the expansion is missing, when it is. */\n unavailableReason?: string;\n fromApple: number;\n toApple: number;\n limit: number;\n /** From `StoreCapabilities`, so the dedupe key can render before comparing. */\n epochOffset: number;\n}): MergedRange => {\n const seen = new Set<string>();\n const out: EventRow[] = [];\n let dropped = 0;\n\n // Every slot a detached row has taken over. Built before anything is emitted,\n // because a cache row can be visited before the item that supersedes it.\n const replaced = new Set<string>();\n for (const r of opts.items) {\n const k = replacedKeyOf(r);\n if (k) replaced.add(k);\n }\n\n // Items first: leg 1 WINS on collision. A detached row is the user's edited\n // version, and the cache row for the same slot may predate the edit.\n for (const r of [...opts.items, ...opts.occurrences]) {\n if (r.startApple === null) {\n dropped += 1;\n continue;\n }\n if (r.source === \"occurrence\" && replaced.has(occurrenceSlotOf(r))) continue;\n const k = keyOf(r, opts.epochOffset);\n if (seen.has(k)) continue;\n seen.add(k);\n out.push(r);\n }\n\n out.sort((a, b) => (a.startApple ?? 0) - (b.startApple ?? 0));\n\n const merged: MergedRange = {\n rows: out.slice(0, opts.limit),\n expansion: opts.hasOccurrenceCache ? \"expanded\" : \"unavailable\",\n coverage: opts.coverage,\n dropped,\n };\n\n if (!opts.hasOccurrenceCache) {\n merged.expansionReason =\n opts.unavailableReason ??\n \"this store has no OccurrenceCache table, so occurrences of repeating events are not \" +\n \"expanded; each repeating event appears once, at its series start\";\n return merged;\n }\n\n const cov = opts.coverage;\n if (cov) {\n const past = opts.fromApple < cov.fromApple;\n const future = opts.toApple > cov.toApple;\n if (past || future) {\n merged.truncated = {\n reason:\n \"the requested window extends past the range the store has expanded, so repeating \" +\n \"events outside it are missing from this result\",\n ...(past ? { uncoveredFromApple: opts.fromApple } : {}),\n ...(future ? { uncoveredToApple: opts.toApple } : {}),\n affects: \"repeating events only — single events are correct at any distance\",\n };\n }\n }\n\n return merged;\n};\n","/**\n * CalendarRef — the one identifier any tool accepts or returns.\n *\n * Wire format: c1:<calendarUid>/<occurrence>/<eventUid>\n *\n * calendarUid `Calendar.UUID`. Always a UUID in this store.\n * occurrence \"-\" for a single event or a whole series, otherwise the\n * occurrence start as ISO-8601 basic with offset,\n * e.g. 20260821T090000+0200.\n * eventUid `CalendarItem.UUID`, verbatim, as the greedy tail.\n *\n * The `c1:` prefix follows the same reasoning as Notes' `n1:` and Reminders'\n * `r1:`: if the scheme ever changes, a versioned prefix makes that an additive\n * change instead of a silent reinterpretation of every ref already sitting in a\n * conversation.\n *\n * ## Why the uid is the greedy tail, and why `@` is not a separator\n *\n * `docs/calendar.md` measured the id bridge on an iCloud account, where every\n * uid is a bare UUID. That is a property of the ACCOUNT, not of Calendar: a\n * Google event's uid looks like `abc123def@google.com`, and an Exchange one is\n * a long hex blob. Requiring a UUID here would work perfectly on the machine it\n * was written on and fail completely on anyone else's — so the uid is carried\n * through verbatim and the UUID is only extracted opportunistically.\n *\n * That also rules out `@` as a field separator, which is otherwise the obvious\n * choice for pinning an occurrence to a time.\n *\n * ## Why the calendar uid rides along\n *\n * Calendar's scripting dictionary has no `events.byId()`. Finding an event over\n * Apple Events means either `whose({uid})` — measured at 4.5-7.3 s and, worse,\n * UNSTABLE across runs — or one bulk `cal.events.uid()` fetch and an index in\n * JS, at about 1.8 s. The bulk fetch is only affordable if it is scoped to ONE\n * calendar, so every write narrows by calendar before it scans.\n *\n * That is a concrete thing the file lane hands the write lane: the store knows\n * which calendar an event is in, so Apple Events never has to search for it.\n */\n\nimport { PreconditionError } from \"./errors.js\";\n\nexport const REF_VERSION = \"c1\";\n\n/** Used to FIND a uuid inside an id, never to require one. */\nconst UUID = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;\n\n/** `20260821T090000+0200` / `20260821T070000Z` — ISO-8601 basic. */\nconst BASIC = /^(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})(Z|[+-]\\d{4})$/;\n\nconst SEPARATOR = \"/\";\nconst NO_OCCURRENCE = \"-\";\n\nexport type CalendarRef = {\n /** `Calendar.UUID` — which calendar to narrow to before scanning. */\n calendarUid: string;\n /** `CalendarItem.UUID`, exactly as stored. This is what resolves. */\n eventUid: string;\n /** Null for a single event or a whole series. */\n occurrenceStart: Date | null;\n /** True when this ref names one occurrence rather than the series. */\n isOccurrence: boolean;\n};\n\nconst pad = (n: number, w = 2): string => String(n).padStart(w, \"0\");\n\n/** The inverse of BASIC: local wall clock plus its offset, no punctuation. */\nconst toBasic = (d: Date): string => {\n const mins = -d.getTimezoneOffset();\n const sign = mins < 0 ? \"-\" : \"+\";\n const abs = Math.abs(mins);\n const zone = mins === 0 ? \"Z\" : `${sign}${pad(Math.floor(abs / 60))}${pad(abs % 60)}`;\n return (\n `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}` +\n `T${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}${zone}`\n );\n};\n\nconst fromBasic = (text: string): Date | null => {\n const m = BASIC.exec(text);\n if (!m) return null;\n const [, y, mo, d, hh, mm, ss, zone] = m;\n const iso =\n `${y}-${mo}-${d}T${hh}:${mm}:${ss}` +\n (zone === \"Z\" ? \"Z\" : `${zone!.slice(0, 3)}:${zone!.slice(3)}`);\n const when = new Date(iso);\n return Number.isNaN(when.getTime()) ? null : when;\n};\n\nexport const encodeRef = (\n calendarUid: string,\n eventUid: string,\n occurrenceStart?: Date | null,\n): string =>\n [\n REF_VERSION + \":\" + String(calendarUid ?? \"\"),\n occurrenceStart ? toBasic(occurrenceStart) : NO_OCCURRENCE,\n String(eventUid ?? \"\"),\n ].join(SEPARATOR);\n\nexport const decodeRef = (raw: string): CalendarRef => {\n const text = String(raw ?? \"\");\n const colon = text.indexOf(\":\");\n const version = colon === -1 ? \"\" : text.slice(0, colon);\n\n if (version !== REF_VERSION) {\n // Distinguish a wrong-surface ref from a malformed one. A caller handing an\n // `r1:` ref to a calendar tool has made a different mistake from one that\n // invented a string, and telling them apart saves a retry.\n if (/^[a-z]\\d+$/.test(version)) {\n throw new PreconditionError(\n `That is a \"${version}:\" ref, which belongs to another surface. This server issues ` +\n `\"${REF_VERSION}:\" refs — get one from apple_calendar_list_events or ` +\n `apple_calendar_search_events.`,\n );\n }\n throw new PreconditionError(\n `Malformed calendar ref ${JSON.stringify(raw)}. Refs come from the search and list tools — ` +\n `construct them from those results rather than by hand.`,\n { expected: `${REF_VERSION}:<calendarUid>/<occurrence>/<eventUid>` },\n );\n }\n\n const body = text.slice(colon + 1);\n const first = body.indexOf(SEPARATOR);\n const second = body.indexOf(SEPARATOR, first + 1);\n if (first === -1 || second === -1) {\n throw new PreconditionError(\n `Malformed calendar ref ${JSON.stringify(raw)}: expected two \"${SEPARATOR}\" separators.`,\n { expected: `${REF_VERSION}:<calendarUid>/<occurrence>/<eventUid>` },\n );\n }\n\n const calendarUid = body.slice(0, first);\n const occurrence = body.slice(first + 1, second);\n // Greedy: everything after the second separator. A Google uid contains no\n // \"/\" but is not a UUID, and an Exchange one is neither — so the tail is\n // taken whole rather than validated into a shape.\n const eventUid = body.slice(second + 1);\n\n if (!eventUid) {\n throw new PreconditionError(\n `Malformed calendar ref ${JSON.stringify(raw)}: it names no event.`,\n { expected: `${REF_VERSION}:<calendarUid>/<occurrence>/<eventUid>` },\n );\n }\n\n const occurrenceStart = occurrence === NO_OCCURRENCE ? null : fromBasic(occurrence);\n if (occurrence !== NO_OCCURRENCE && !occurrenceStart) {\n throw new PreconditionError(\n `Malformed calendar ref ${JSON.stringify(raw)}: ${JSON.stringify(occurrence)} is not an ` +\n `occurrence start. Expected ISO-8601 basic, e.g. 20260821T090000+0200, or \"-\".`,\n );\n }\n\n return { calendarUid, eventUid, occurrenceStart, isOccurrence: occurrenceStart !== null };\n};\n\n/** The series a ref belongs to. Identity for a ref that is already a series. */\nexport const seriesRefOf = (ref: CalendarRef): string => encodeRef(ref.calendarUid, ref.eventUid);\n\n/** The bare UUID inside an id, when there is one. Null is legitimate, not an error. */\nexport const uuidOf = (id: string): string | null =>\n UUID.exec(String(id ?? \"\"))?.[1]?.toUpperCase() ?? null;\n","import type { DatabaseSync } from \"node:sqlite\";\n\nimport {\n columnsOf,\n CORE_DATA_EPOCH_OFFSET,\n fingerprintSchema,\n escapeLike,\n openReadOnly,\n SchemaDriftError,\n type Logger,\n type ReadOnlyMode,\n} from \"@mgcrea/mcp-apple-core\";\n\n/**\n * Calendar's file lane.\n *\n * ## Why there is a file lane at all\n *\n * Unlike Notes and Reminders, this one is justified on SPEED rather than\n * capability. `docs/calendar.md` measured a ±90-day range query over Apple\n * Events at 3.4 s across 1,349 events, with the cost falling per round trip\n * rather than per event (~1.8-2.2 s per property, flat), so no amount of\n * batching rescues it. Calendar's scripting dictionary is unusually complete —\n * attendees, alarms and recurrence are all in it — so the usual \"the store buys\n * you the rich stuff\" argument is much weaker here than elsewhere. The store is\n * how a range query returns before the caller gives up.\n *\n * ## What is measured, and what is not\n *\n * Measured (`docs/calendar.md`): the store is\n * `~/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb`, it is\n * plain Core Data with no `Z_PRIMARYKEY`, the main table is `CalendarItem`, and\n * the id bridge to Apple Events is exact — `CalendarItem.UUID` against the `uid`\n * Calendar hands back, 198/198 sampled.\n *\n * NOT yet measured, and the reason this file introspects but does not query:\n * `OccurrenceCache` (1,946 rows) and `OccurrenceCacheDays` (2,630) both out-row\n * `CalendarItem` (1,350), which means expanded recurrences live outside the main\n * table and a naive `SELECT ... FROM CalendarItem` returns a weekly standup\n * once. `scripts/probe-calendar.mjs` was extended to settle that with a set diff\n * against Apple Events; until it has run on a machine with Full Disk Access,\n * shipping a range query would be guessing at the one thing that fails silently.\n * A short list is indistinguishable from a free afternoon.\n */\n\n/** Tables the lane cannot work without. Anything else degrades to a null field. */\nconst REQUIRED = [\"CalendarItem\", \"Calendar\"] as const;\n\n/**\n * The schema this was written against. Named in the drift error, not enforced.\n *\n * CAUTION: this is the PROBE's fingerprint and it is NOT comparable to the one\n * `diagnostics` reports. `fingerprintSchema` in packages/core orders\n * sqlite_master by `type, name`; `dumpSchema` in scripts/lib/probe-kit.mjs\n * orders it by a CASE expression putting tables before indexes, so the same\n * schema hashes to two different values. Measured here: this store reports\n * cd2424fea732 at runtime against the 2bf4e34ff75f docs/calendar.md recorded\n * from the probe, and Reminders shows the same split (510062aad004 at runtime\n * against the 278b001e3c55 in its own drift message and in docs/verify.md).\n *\n * So compare a probe fingerprint with a probe fingerprint. Reconciling the two\n * orderings is a one-line change in packages/core, but it moves the value for\n * every surface at once and is therefore its own decision.\n */\nconst PROBED_FINGERPRINT = \"2bf4e34ff75f\";\nconst PROBED_MACOS = \"26.6\";\n\nexport type StoreCapabilities = {\n fingerprint: string;\n /** Present columns, so an Apple rename degrades one field instead of the lane. */\n itemColumns: Set<string>;\n calendarColumns: Set<string>;\n occurrenceColumns: Set<string>;\n recurrenceColumns: Set<string>;\n storeColumns: Set<string>;\n /**\n * Whether recurrences can be expanded at all.\n *\n * False is a capability downgrade, never a throw: leg 1 of a range query\n * (items carrying no recurrence rule) stays correct at any horizon, so the\n * honest degradation is to answer with it and say that repeating events were\n * not expanded — not to fail, and above all not to quietly return fewer rows.\n */\n hasOccurrenceCache: boolean;\n hasOccurrenceDays: boolean;\n hasRecurrence: boolean;\n hasExceptionDates: boolean;\n hasLocation: boolean;\n hasAttachments: boolean;\n hasParticipants: boolean;\n hasAlarms: boolean;\n epochOffset: number;\n};\n\n/** One row of the union, still in store units. Rendering happens above this. */\nexport type EventRow = {\n itemPk: number;\n uuid: string | null;\n calendarPk: number | null;\n calendarUuid: string | null;\n calendarTitle: string | null;\n summary: string | null;\n description: string | null;\n url: string | null;\n conferenceUrl: string | null;\n locationTitle: string | null;\n startApple: number | null;\n endApple: number | null;\n allDay: boolean;\n startTz: string | null;\n endTz: string | null;\n status: number | null;\n invitationStatus: number | null;\n availability: number | null;\n hasRecurrences: boolean;\n hasAttendees: boolean;\n /** Set on a detached occurrence: which series it broke away from, and when. */\n origItemPk: number | null;\n origDateApple: number | null;\n /** Which leg produced this row. Reported so a caller can see the expansion working. */\n source: \"item\" | \"occurrence\";\n};\n\nexport type RangeQuery = {\n /** Apple-seconds, inclusive lower bound. */\n fromApple: number;\n /** Apple-seconds, exclusive upper bound. */\n toApple: number;\n /** `Calendar.UUID` values. Empty means every calendar. */\n calendarUuids?: readonly string[];\n limit: number;\n};\n\nexport type SearchQuery = RangeQuery & {\n text: string;\n scope: \"summary\" | \"full\";\n};\n\n/** How far the cached expansion actually reaches, in Apple-seconds. */\nexport type Coverage = { fromApple: number; toApple: number; rows: number } | null;\n\nexport type IndexCalendar = {\n uuid: string | null;\n title: string | null;\n color: string | null;\n type: string | null;\n accountName: string | null;\n isSubscribed: boolean;\n isPublished: boolean;\n /**\n * Whether this calendar is shared with other people.\n *\n * Derived from `sharing_status`, and INFERRED rather than measured: the only\n * values observed are 0, 1 and NULL, with 1 on the two calendars known to be\n * shared. The raw value stays on the result so a caller can disagree.\n *\n * It matters because writing to a shared calendar is visible to whoever else\n * is on it, and nothing else in the output says so.\n */\n isShared: boolean;\n sharingStatus: number | null;\n};\n\nexport type IndexAccount = { name: string | null; type: number | null; calendars: number };\n\nconst bool = (v: unknown): boolean => v === 1 || v === true;\nconst num = (v: unknown): number | null => (typeof v === \"number\" ? v : null);\nconst text = (v: unknown): string | null => (typeof v === \"string\" ? v : null);\n\nexport class CalendarStore {\n readonly db: DatabaseSync;\n readonly mode: string;\n readonly caps: StoreCapabilities;\n\n constructor(db: DatabaseSync, mode: string, caps: StoreCapabilities) {\n this.db = db;\n this.mode = mode;\n this.caps = caps;\n }\n\n /**\n * Project a column, or a typed NULL when this store does not have it.\n *\n * The same guard `packages/reminders/src/client/store.ts` uses, and for the\n * same reason: the schema is reverse-engineered and unversioned, so an Apple\n * rename should cost one field rather than the whole lane.\n */\n #col(present: Set<string>, table: string, name: string, alias = name): string {\n return present.has(name) ? `${table}.\"${name}\" AS ${alias}` : `NULL AS ${alias}`;\n }\n\n #itemColumns(): string {\n const c = this.caps.itemColumns;\n return [\n `ci.\"ROWID\" AS itemPk`,\n this.#col(c, \"ci\", \"UUID\", \"uuid\"),\n this.#col(c, \"ci\", \"summary\", \"summary\"),\n this.#col(c, \"ci\", \"description\", \"description\"),\n this.#col(c, \"ci\", \"url\", \"url\"),\n this.#col(c, \"ci\", \"conference_url\", \"conferenceUrl\"),\n this.#col(c, \"ci\", \"all_day\", \"allDay\"),\n this.#col(c, \"ci\", \"start_tz\", \"startTz\"),\n this.#col(c, \"ci\", \"end_tz\", \"endTz\"),\n this.#col(c, \"ci\", \"status\", \"status\"),\n this.#col(c, \"ci\", \"invitation_status\", \"invitationStatus\"),\n this.#col(c, \"ci\", \"availability\", \"availability\"),\n this.#col(c, \"ci\", \"has_recurrences\", \"hasRecurrences\"),\n this.#col(c, \"ci\", \"has_attendees\", \"hasAttendees\"),\n this.#col(c, \"ci\", \"orig_item_id\", \"origItemPk\"),\n this.#col(c, \"ci\", \"orig_date\", \"origDateApple\"),\n this.#col(c, \"ci\", \"calendar_id\", \"calendarPk\"),\n `cal.\"UUID\" AS calendarUuid`,\n `cal.\"title\" AS calendarTitle`,\n this.caps.hasLocation ? `loc.\"title\" AS locationTitle` : `NULL AS locationTitle`,\n ].join(\",\\n \");\n }\n\n #joins(): string {\n const location =\n this.caps.hasLocation && this.caps.itemColumns.has(\"location_id\")\n ? `\\n LEFT JOIN \"Location\" loc ON loc.\"ROWID\" = ci.\"location_id\"`\n : \"\";\n return `JOIN \"Calendar\" cal ON cal.\"ROWID\" = ci.\"calendar_id\"${location}`;\n }\n\n /**\n * Rows this lane must never return, whichever leg found them.\n *\n * `entity_type` is 2 for events. The probed store holds nothing else — 0 rows\n * carry a due date or a completion date — but `CalendarItem` shares its schema\n * with Reminders, so the predicate is cheap insurance rather than dead code.\n *\n * `hidden` and `phantom_master` are INFERRED rather than measured. A phantom\n * master is the placeholder row EventKit keeps for a series whose occurrences\n * have all been detached; showing it would put an event on the calendar that\n * Calendar.app does not draw. Both are excluded conservatively, and both are\n * on the list for the next probe run to confirm.\n */\n #excluded(alias = \"ci\"): string {\n const c = this.caps.itemColumns;\n const out: string[] = [];\n if (c.has(\"entity_type\")) out.push(`${alias}.\"entity_type\" = 2`);\n if (c.has(\"hidden\")) out.push(`(${alias}.\"hidden\" IS NULL OR ${alias}.\"hidden\" = 0)`);\n if (c.has(\"phantom_master\")) {\n out.push(`(${alias}.\"phantom_master\" IS NULL OR ${alias}.\"phantom_master\" = 0)`);\n }\n return out.length ? `AND ${out.join(\"\\n AND \")}` : \"\";\n }\n\n #calendarFilter(uuids: readonly string[] | undefined): { sql: string; params: string[] } {\n if (!uuids?.length) return { sql: \"\", params: [] };\n const marks = uuids.map(() => \"?\").join(\", \");\n return { sql: `AND cal.\"UUID\" IN (${marks})`, params: [...uuids] };\n }\n\n #rowsFrom(sql: string, params: unknown[], source: EventRow[\"source\"]): EventRow[] {\n const raw = this.db.prepare(sql).all(...(params as never[])) as Record<string, unknown>[];\n return raw.map((r) => ({\n itemPk: Number(r.itemPk),\n uuid: text(r.uuid),\n calendarPk: num(r.calendarPk),\n calendarUuid: text(r.calendarUuid),\n calendarTitle: text(r.calendarTitle),\n summary: text(r.summary),\n description: text(r.description),\n url: text(r.url),\n conferenceUrl: text(r.conferenceUrl),\n locationTitle: text(r.locationTitle),\n startApple: num(r.startApple),\n endApple: num(r.endApple),\n allDay: bool(r.allDay),\n startTz: text(r.startTz),\n endTz: text(r.endTz),\n status: num(r.status),\n invitationStatus: num(r.invitationStatus),\n availability: num(r.availability),\n hasRecurrences: bool(r.hasRecurrences),\n hasAttendees: bool(r.hasAttendees),\n origItemPk: num(r.origItemPk),\n origDateApple: num(r.origDateApple),\n source,\n }));\n }\n\n /**\n * LEG 1 — items carried by the table itself.\n *\n * Overlap, not containment: `start < to AND COALESCE(end, start) > from`. A\n * naive `BETWEEN` on the start silently drops the all-hands that began at\n * 09:00 when the caller asked about 10:00 onward.\n *\n * Items whose occurrences are expanded in the cache are excluded here and\n * picked up by leg 2, so a repeating event is not also returned once at its\n * master start.\n */\n rangeItems(q: RangeQuery): EventRow[] {\n const c = this.caps.itemColumns;\n if (!c.has(\"start_date\")) return [];\n const cal = this.#calendarFilter(q.calendarUuids);\n const endExpr = c.has(\"end_date\")\n ? `COALESCE(ci.\"end_date\", ci.\"start_date\")`\n : `ci.\"start_date\"`;\n // `has_recurrences` is the table's own flag. When it is missing, fall back\n // to asking the Recurrence table directly rather than dropping the guard —\n // without one, every repeating event doubles.\n const notExpanded = c.has(\"has_recurrences\")\n ? `AND (ci.\"has_recurrences\" IS NULL OR ci.\"has_recurrences\" = 0)`\n : this.caps.hasRecurrence\n ? `AND NOT EXISTS (SELECT 1 FROM \"Recurrence\" r WHERE r.\"owner_id\" = ci.\"ROWID\")`\n : \"\";\n const sql = `\n SELECT ci.\"start_date\" AS startApple,\n ${c.has(\"end_date\") ? `ci.\"end_date\"` : `NULL`} AS endApple,\n ${this.#itemColumns()}\n FROM \"CalendarItem\" ci\n ${this.#joins()}\n WHERE ci.\"start_date\" < ?\n AND ${endExpr} > ?\n ${notExpanded}\n ${this.#excluded()}\n ${cal.sql}\n ORDER BY ci.\"start_date\" ASC\n LIMIT ?`;\n return this.#rowsFrom(sql, [q.toApple, q.fromApple, ...cal.params, q.limit], \"item\");\n }\n\n /**\n * LEG 2 — expanded occurrences.\n *\n * `OccurrenceCache.event_id` -> `CalendarItem.ROWID` was measured at a 100%\n * resolve rate. `occurrence_date` is the start; `occurrence_start_date` exists\n * too and reaches only +256 days against `occurrence_date`'s +724, so using it\n * would silently truncate the far half of the window.\n */\n rangeOccurrences(q: RangeQuery): EventRow[] {\n if (!this.caps.hasOccurrenceCache) return [];\n const o = this.caps.occurrenceColumns;\n if (!o.has(\"occurrence_date\") || !o.has(\"event_id\")) return [];\n const cal = this.#calendarFilter(q.calendarUuids);\n const endExpr = o.has(\"occurrence_end_date\")\n ? `COALESCE(oc.\"occurrence_end_date\", oc.\"occurrence_date\")`\n : `oc.\"occurrence_date\"`;\n const sql = `\n SELECT oc.\"occurrence_date\" AS startApple,\n ${o.has(\"occurrence_end_date\") ? `oc.\"occurrence_end_date\"` : `NULL`} AS endApple,\n ${this.#itemColumns()}\n FROM \"OccurrenceCache\" oc\n JOIN \"CalendarItem\" ci ON ci.\"ROWID\" = oc.\"event_id\"\n ${this.#joins()}\n WHERE oc.\"occurrence_date\" < ?\n AND ${endExpr} > ?\n ${this.#excluded()}\n ${cal.sql}\n ORDER BY oc.\"occurrence_date\" ASC\n LIMIT ?`;\n return this.#rowsFrom(sql, [q.toApple, q.fromApple, ...cal.params, q.limit], \"occurrence\");\n }\n\n /**\n * Text search, unbounded in time by default.\n *\n * Searching is the one place a caller legitimately wants all of history, so\n * the window is the caller's to set rather than a default. Runs over items\n * only: an occurrence carries no text of its own, and matching the series once\n * is what a search result should be.\n */\n searchItems(q: SearchQuery): EventRow[] {\n const c = this.caps.itemColumns;\n const cal = this.#calendarFilter(q.calendarUuids);\n const needle = `%${escapeLike(q.text)}%`;\n const fields = [\"summary\"];\n if (q.scope === \"full\") {\n if (c.has(\"description\")) fields.push(\"description\");\n if (this.caps.hasLocation) fields.push(\"__location\");\n }\n const match = fields\n .map((f) =>\n f === \"__location\" ? `loc.\"title\" LIKE ? ESCAPE '\\\\'` : `ci.\"${f}\" LIKE ? ESCAPE '\\\\'`,\n )\n .join(\" OR \");\n const sql = `\n SELECT ci.\"start_date\" AS startApple,\n ${c.has(\"end_date\") ? `ci.\"end_date\"` : `NULL`} AS endApple,\n ${this.#itemColumns()}\n FROM \"CalendarItem\" ci\n ${this.#joins()}\n WHERE (${match})\n AND ci.\"start_date\" < ?\n AND COALESCE(ci.\"end_date\", ci.\"start_date\") > ?\n ${this.#excluded()}\n ${cal.sql}\n ORDER BY ci.\"start_date\" DESC\n LIMIT ?`;\n const params = [...fields.map(() => needle), q.toApple, q.fromApple, ...cal.params, q.limit];\n return this.#rowsFrom(sql, params, \"item\");\n }\n\n /** One event by its Apple Events uid. The bridge measured 198/198 exact. */\n byUuid(uuid: string): EventRow | null {\n const c = this.caps.itemColumns;\n if (!c.has(\"UUID\")) return null;\n const sql = `\n SELECT ci.\"start_date\" AS startApple,\n ${c.has(\"end_date\") ? `ci.\"end_date\"` : `NULL`} AS endApple,\n ${this.#itemColumns()}\n FROM \"CalendarItem\" ci\n ${this.#joins()}\n WHERE UPPER(ci.\"UUID\") = ?\n ${this.#excluded()}\n LIMIT 1`;\n return this.#rowsFrom(sql, [uuid.toUpperCase()], \"item\")[0] ?? null;\n }\n\n /**\n * How far the expansion reaches.\n *\n * Published with every range result. Measured at -732 to +724 days on the\n * probed store, which is a real expansion rather than a month-view cache —\n * but it is still an edge, and nothing guarantees the next machine's is as\n * deep. A range running past it must say so rather than return a short list.\n */\n coverage(): Coverage {\n if (!this.caps.hasOccurrenceCache || !this.caps.occurrenceColumns.has(\"occurrence_date\")) {\n return null;\n }\n const row = this.db\n .prepare(\n `SELECT MIN(\"occurrence_date\") AS lo, MAX(\"occurrence_date\") AS hi, COUNT(*) AS n\n FROM \"OccurrenceCache\" WHERE \"occurrence_date\" IS NOT NULL`,\n )\n .get() as { lo: unknown; hi: unknown; n: unknown } | undefined;\n const lo = num(row?.lo);\n const hi = num(row?.hi);\n if (lo === null || hi === null) return null;\n return { fromApple: lo, toApple: hi, rows: Number(row?.n ?? 0) };\n }\n\n calendars(): IndexCalendar[] {\n const c = this.caps.calendarColumns;\n const store = this.caps.storeColumns.size > 0;\n const sql = `\n SELECT ${this.#col(c, \"cal\", \"UUID\", \"uuid\")},\n ${this.#col(c, \"cal\", \"title\", \"title\")},\n ${this.#col(c, \"cal\", \"color\", \"color\")},\n ${this.#col(c, \"cal\", \"type\", \"type\")},\n ${this.#col(c, \"cal\", \"sharing_status\", \"sharingStatus\")},\n ${this.#col(c, \"cal\", \"is_published\", \"isPublished\")},\n ${this.#col(c, \"cal\", \"subcal_url\", \"subcalUrl\")},\n ${store ? `st.\"name\"` : `NULL`} AS accountName\n FROM \"Calendar\" cal\n ${store ? `LEFT JOIN \"Store\" st ON st.\"ROWID\" = cal.\"store_id\"` : \"\"}\n ORDER BY ${c.has(\"display_order\") ? `cal.\"display_order\" ASC,` : \"\"} cal.\"title\" ASC`;\n const rows = this.db.prepare(sql).all() as Record<string, unknown>[];\n return rows.map((r) => ({\n uuid: text(r.uuid),\n title: text(r.title),\n color: text(r.color),\n type: text(r.type),\n accountName: text(r.accountName),\n // A subscribed calendar is read-only, which is what a write tool needs to\n // know. Derived from the presence of a subscription URL rather than from a\n // flags bit whose meaning has not been measured.\n isSubscribed: Boolean(text(r.subcalUrl)),\n isPublished: bool(r.isPublished),\n isShared: (num(r.sharingStatus) ?? 0) > 0,\n sharingStatus: num(r.sharingStatus),\n }));\n }\n\n accounts(): IndexAccount[] {\n if (!this.caps.storeColumns.size) return [];\n const rows = this.db\n .prepare(\n `SELECT st.\"name\" AS name, st.\"type\" AS type, COUNT(cal.\"ROWID\") AS calendars\n FROM \"Store\" st\n LEFT JOIN \"Calendar\" cal ON cal.\"store_id\" = st.\"ROWID\"\n GROUP BY st.\"ROWID\"\n ORDER BY st.\"name\" ASC`,\n )\n .all() as Record<string, unknown>[];\n return rows.map((r) => ({\n name: text(r.name),\n type: num(r.type),\n calendars: Number(r.calendars ?? 0),\n }));\n }\n\n close(): void {\n try {\n this.db.close();\n } catch {\n // Closing a database that already failed is not worth reporting.\n }\n }\n}\n\nexport const introspect = (db: DatabaseSync): StoreCapabilities => {\n const itemColumns = new Set(columnsOf(db, \"CalendarItem\"));\n const calendarColumns = new Set(columnsOf(db, \"Calendar\"));\n\n for (const t of REQUIRED) {\n const cols = t === \"CalendarItem\" ? itemColumns : calendarColumns;\n if (cols.size === 0) {\n throw new SchemaDriftError(\n `This Calendar store has no ${t} table. It was probed on macOS ${PROBED_MACOS} with ` +\n `schema fingerprint ${PROBED_FINGERPRINT} (a PROBE fingerprint — compare it against ` +\n `another probe run, not against the one diagnostics reports); re-run ` +\n `\\`pnpm probe:calendar\\` to see what changed.`,\n );\n }\n }\n\n const occurrenceColumns = new Set(columnsOf(db, \"OccurrenceCache\"));\n\n return {\n fingerprint: fingerprintSchema(db),\n itemColumns,\n calendarColumns,\n occurrenceColumns,\n recurrenceColumns: new Set(columnsOf(db, \"Recurrence\")),\n storeColumns: new Set(columnsOf(db, \"Store\")),\n hasOccurrenceCache: occurrenceColumns.size > 0,\n hasOccurrenceDays: columnsOf(db, \"OccurrenceCacheDays\").length > 0,\n hasRecurrence: columnsOf(db, \"Recurrence\").length > 0,\n hasExceptionDates: columnsOf(db, \"ExceptionDate\").length > 0,\n hasLocation: columnsOf(db, \"Location\").length > 0,\n hasAttachments: columnsOf(db, \"Attachment\").length > 0,\n hasParticipants: columnsOf(db, \"Participant\").length > 0,\n hasAlarms: columnsOf(db, \"Alarm\").length > 0,\n // Measured as apple-seconds on this store, but taken from core rather than\n // written out again: being 31 years out is the classic Core Data date bug.\n epochOffset: CORE_DATA_EPOCH_OFFSET,\n };\n};\n\nexport const openStore = (\n path: string | null,\n mode: ReadOnlyMode,\n logger?: Logger,\n): CalendarStore | null => {\n if (!path) return null;\n const {\n db,\n mode: used,\n validated,\n } = openReadOnly<StoreCapabilities>(path, mode, {\n label: \"Calendar store\",\n envVar: \"APPLE_CALENDAR_INDEX_MODE\",\n validate: introspect,\n fatal: (err) => err instanceof SchemaDriftError,\n onFallback: () =>\n logger?.debug?.(\n \"opened the Calendar store with immutable=1, which skips the write-ahead log — \" +\n \"very recent changes may be missing until Calendar checkpoints.\",\n ),\n });\n return new CalendarStore(db, used, validated);\n};\n","import {\n createOsascriptRunner,\n withBusyRetry,\n type Logger,\n type OsascriptRunner,\n} from \"@mgcrea/mcp-apple-core\";\n\nimport type { Config } from \"../config.js\";\nimport {\n alignUp,\n dayWindows,\n DEFAULT_WEEKDAYS,\n localDay,\n mergeIntervals,\n nextLocalDayMs,\n parseClock,\n startOfLocalDayMs,\n subtractBusy,\n weekdaySet,\n type Interval,\n type WeekdayKey,\n} from \"./availability.js\";\nimport {\n parseBound,\n parseDate,\n parseDuration,\n parseRange,\n renderInstant,\n toLocalIso,\n type EventInstant,\n} from \"./dates.js\";\nimport {\n CALENDAR_SURFACE,\n CalendarNotFoundError,\n CalendarNotWritableError,\n EventNotFoundError,\n IndexUnavailableError,\n PreconditionError,\n} from \"./errors.js\";\nimport { CREATE_EVENT, DELETE_EVENTS, UPDATE_EVENT } from \"./jxa/write.js\";\nimport { locateStore, type LocateResult } from \"./locate.js\";\nimport { mergeRange, type ExpansionState } from \"./recurrence.js\";\nimport { decodeRef, encodeRef, seriesRefOf } from \"./ref.js\";\nimport { CalendarStore, openStore, type EventRow, type IndexAccount } from \"./store.js\";\n\n/**\n * The lane orchestrator.\n *\n * Thinner than `packages/reminders`' by design. That surface arbitrates between\n * two read lanes and caches an expensive Apple Events bulk fetch; Calendar has\n * exactly one read lane, because `docs/distribution.md` sets the policy for new\n * surfaces — file-lane reads, Apple Events for writes and live state — and\n * `docs/calendar.md` measured why: a range query over Apple Events costs 3.4 s\n * and does not improve with batching.\n *\n * So there is nothing to arbitrate. Either the store opens or the surface says\n * plainly that it cannot answer, and never returns an empty list that reads like\n * an empty calendar.\n */\nexport type LaneStatus = {\n /**\n * Always reported, never probed here.\n *\n * Reminders probes its Apple Events lane on every `lanes()` call because that\n * lane answers reads. Calendar's does not: it exists only for writes, which\n * are not implemented yet. Probing it would fire the Automation prompt for a\n * capability the server does not currently have, which is a worse trade than\n * saying so.\n */\n applescript: \"not-used\" | \"live\" | \"unavailable\";\n index: \"live\" | \"unavailable\" | \"disabled\";\n indexMode: string | null;\n storeFingerprint: string | null;\n reason: string | null;\n};\n\n/**\n * EventKit's public status constants, which this store appears to mirror.\n *\n * Taken from the framework rather than measured: `EKEventStatus` and\n * `EKParticipantStatus` are documented API, and a store written by EventKit is\n * overwhelmingly likely to hold the same integers. \"Overwhelmingly likely\" is\n * not \"measured\", so both filters are off unless the caller asks, every result\n * reports its own raw `status`, and `diagnostics` says the mapping is inferred.\n * Confirming it is on the probe's list.\n */\nconst STATUS_CANCELLED = 3;\nconst PARTICIPANT_DECLINED = 3;\n\n/**\n * `EKEventAvailability.free`, on the same inference as the two above — and held\n * to a stricter standard, because the direction it can fail in is worse.\n *\n * Reading this number wrongly makes a busy event look free, which is the one\n * error `findAvailability` exists to avoid. So unlike `status`, it is not\n * applied by default at all: `respectFreeMarking` is opt-in, the raw value\n * travels on every busy block, and until someone measures the column against a\n * live store every event blocks time regardless of how it is marked.\n */\nconst AVAILABILITY_FREE = 1;\n\n/**\n * How many events `findAvailability` will read from one leg before refusing.\n *\n * `listEvents` over-fetches `limit * 4 + 50` and caps at 5,000 because it only\n * needs enough rows to fill a page. This tool needs EVERY row in the window, so\n * the number here is not a page size — it is the point past which the tool\n * stops claiming to know what is on the calendar. Sized against the measured\n * store in `docs/calendar.md` (1,350 items, 1,946 cached occurrences across\n * four years), so a year-long window on an ordinary calendar sits far under it\n * and a runaway one is caught rather than answered.\n */\nconst BUSY_SCAN_BOUND = 5_000;\n\nexport type EventSummary = {\n /** Opaque; feed it back to get_event or a write tool. */\n ref: string;\n summary: string | null;\n start: EventInstant | null;\n end: EventInstant | null;\n allDay: boolean;\n calendar: string | null;\n location: string | null;\n /** True when this row is one instance of a repeating event. */\n isOccurrence: boolean;\n /** Present on an occurrence: the ref naming the whole series. */\n seriesRef?: string;\n /** Raw store value, reported because the mapping above is inferred. */\n status: number | null;\n invitationStatus: number | null;\n /** Which lane produced the row. */\n source: EventRow[\"source\"];\n};\n\nexport type EventDetail = EventSummary & {\n description: string | null;\n url: string | null;\n conferenceUrl: string | null;\n hasAttendees: boolean;\n hasRecurrences: boolean;\n timeZone: string | null;\n};\n\nexport type EventPage = {\n events: EventSummary[];\n expansion: ExpansionState;\n expansionReason?: string;\n /** The window actually queried, echoed so a clamp is visible. */\n window: { from: string; to: string; clamped: boolean };\n coverage: { from: string; to: string; rows: number } | null;\n truncated?: { reason: string; affects: string; uncoveredFrom?: string; uncoveredTo?: string };\n dropped?: number;\n};\n\nexport type EventFilters = {\n from?: string | undefined;\n to?: string | undefined;\n calendar?: string | undefined;\n includeDeclined?: boolean | undefined;\n includeCancelled?: boolean | undefined;\n limit: number;\n};\n\n/**\n * A window of time nothing is on the calendar, long enough to hold the meeting\n * that was asked for.\n */\nexport type FreeSlot = {\n /** Local wall clock with its offset, ready to hand back to create_event. */\n start: string;\n end: string;\n /** The local day it falls on, so a caller can group without re-parsing. */\n day: string;\n /** How long the whole gap is — usually longer than the duration requested. */\n minutes: number;\n};\n\nexport type AvailabilityFilters = {\n from?: string | undefined;\n to?: string | undefined;\n durationMinutes: number;\n calendar?: string | undefined;\n dayStart: string;\n dayEnd: string;\n weekdays?: readonly WeekdayKey[] | undefined;\n granularityMinutes: number;\n /** Let an all-day event block its whole day. Off by default — see below. */\n allDayBusy: boolean;\n /** Trust `CalendarItem.availability`. Off by default — the mapping is inferred. */\n respectFreeMarking: boolean;\n includeDeclined?: boolean | undefined;\n includeCancelled?: boolean | undefined;\n limit: number;\n};\n\n/**\n * Why an availability answer was withheld.\n *\n * A separate shape from an empty `slots` array on purpose, and the distinction\n * is the whole point of the tool: \"nothing is free\" and \"I cannot see enough of\n * the calendar to say\" look identical to a model unless one of them says so.\n */\nexport type AvailabilityRefusal = {\n degraded: true;\n capability: string;\n reason: string;\n hint: string;\n};\n\nexport type AvailabilityResult = {\n slots: FreeSlot[];\n durationMinutes: number;\n window: {\n from: string;\n to: string;\n clamped: boolean;\n /** True when the start was pulled forward to now, dropping time already past. */\n startedAtNow: boolean;\n };\n workingHours: {\n dayStart: string;\n dayEnd: string;\n weekdays: readonly WeekdayKey[];\n granularityMinutes: number;\n /** The zone the working hours were read in — always this machine's. */\n timeZone: string;\n };\n busy: {\n /** Events that blocked time, after every filter. */\n blocking: number;\n /** Contiguous busy intervals they collapsed into. */\n intervals: number;\n };\n /**\n * All-day events overlapping the window, reported whether or not they were\n * applied. See `allDayBusy` on the tool for why this is not a default.\n */\n allDayEvents: { day: string; summary: string | null; calendar: string | null }[];\n expansion: ExpansionState;\n coverage: { from: string; to: string; rows: number } | null;\n /** Set when the window was cut back to what the expansion actually covers. */\n truncated?: { reason: string; requestedTo?: string; requestedFrom?: string };\n /** Set when the window survived every check but held no time at all. */\n note?: string;\n};\n\nexport type CreateClientOptions = {\n config: Config;\n logger?: Logger;\n /** Injected by tests so nothing spawns a process or touches a real Calendar. */\n osascript?: OsascriptRunner;\n /** Injected by tests so a relative range resolves against a frozen clock. */\n now?: () => Date;\n};\n\n/** What a write returns: what Calendar STORED, never what was requested. */\nexport type WriteResult = {\n ref: string;\n uid: string | null;\n summary: string | null;\n start: string | null;\n end: string | null;\n allDay: boolean;\n calendar: string | null;\n /** Always \"apple-events\": writes never touch the store. */\n source: \"apple-events\";\n};\n\nexport type CreateEventFields = {\n summary: string;\n calendar?: string | undefined;\n start: string;\n end?: string | undefined;\n durationMinutes?: number | undefined;\n allDay?: boolean | undefined;\n location?: string | undefined;\n description?: string | undefined;\n url?: string | undefined;\n};\n\nexport type UpdateEventFields = {\n ref: string;\n summary?: string | undefined;\n start?: string | undefined;\n end?: string | undefined;\n durationMinutes?: number | undefined;\n allDay?: boolean | undefined;\n location?: string | undefined;\n description?: string | undefined;\n url?: string | undefined;\n};\n\nexport class AppleCalendarClient {\n readonly config: Config;\n readonly runner: OsascriptRunner;\n readonly #logger: Logger | undefined;\n readonly #now: () => Date;\n\n #located: LocateResult | null = null;\n #store: CalendarStore | null = null;\n #storeTried = false;\n\n constructor(opts: CreateClientOptions) {\n this.config = opts.config;\n this.#logger = opts.logger;\n this.#now = opts.now ?? (() => new Date());\n this.runner =\n opts.osascript ??\n createOsascriptRunner({\n osascriptPath: opts.config.osascriptPath,\n timeoutMs: opts.config.osascriptTimeoutMs,\n surface: CALENDAR_SURFACE,\n logger: opts.logger,\n });\n }\n\n /** Cached: the answer cannot change without the process being restarted anyway. */\n locate(): LocateResult {\n this.#located ??= locateStore({ storePath: this.config.storePath });\n return this.#located;\n }\n\n /**\n * The store, opened lazily and at most once.\n *\n * Returns null rather than throwing, because \"no index\" is a state the caller\n * has to render, not an exception. The reason lives on the locate result.\n */\n index(): CalendarStore | null {\n if (this.#storeTried) return this.#store;\n this.#storeTried = true;\n if (this.config.indexMode === \"off\") return null;\n try {\n this.#store = openStore(this.locate().storePath, this.config.indexMode, this.#logger);\n } catch (err) {\n this.#logger?.debug?.(\"could not open the Calendar store\", err);\n this.#store = null;\n }\n return this.#store;\n }\n\n /**\n * Drop the open handle so the next read reopens.\n *\n * Called after a write: Calendar owns the store and reconciles it against a\n * server, so an event created over Apple Events lands in the file on the\n * app's schedule, not ours.\n */\n invalidate(): void {\n this.#store?.close();\n this.#store = null;\n this.#storeTried = false;\n this.#located = null;\n }\n\n // ─── reads ────────────────────────────────────────────────────────────────\n\n /**\n * The store, or a structured refusal.\n *\n * Never an empty list: Calendar has no Apple Events read lane to fall back to\n * (`docs/distribution.md`), so \"no index\" means this server cannot answer at\n * all — and an empty array would read as an empty calendar, which is the one\n * answer that must never be invented.\n */\n #require(): CalendarStore {\n const store = this.index();\n if (store) return store;\n const located = this.locate();\n throw new IndexUnavailableError(\n located.reason ??\n \"The Calendar store could not be opened, and this surface has no Apple Events read \" +\n \"lane to fall back to. Run apple_calendar_diagnostics for the details.\",\n );\n }\n\n #toApple(d: Date, store: CalendarStore): number {\n return Math.round(d.getTime() / 1000) - store.caps.epochOffset;\n }\n\n #fromApple(seconds: number, store: CalendarStore): string {\n return new Date((seconds + store.caps.epochOffset) * 1000).toISOString();\n }\n\n /** Resolve a caller's calendar name or uid to the uids the SQL filters on. */\n #calendarUuids(store: CalendarStore, named: string | undefined): string[] | undefined {\n const configured = this.config.calendars;\n const all = store.calendars();\n const pick = (want: string): string[] => {\n const lower = want.toLowerCase();\n const hits = all.filter(\n (c) => c.uuid?.toLowerCase() === lower || c.title?.toLowerCase() === lower,\n );\n if (!hits.length) {\n throw new CalendarNotFoundError(want, all.map((c) => c.title ?? \"\").filter(Boolean));\n }\n return hits.map((c) => c.uuid).filter((u): u is string => Boolean(u));\n };\n if (named) {\n const chosen = pick(named);\n // An allowlist is a boundary, not a default: naming a calendar outside it\n // must fail rather than quietly widen the scope.\n if (configured.length) {\n const allowed = new Set(configured.flatMap(pick));\n const kept = chosen.filter((u) => allowed.has(u));\n if (!kept.length) {\n throw new CalendarNotFoundError(named, configured as string[]);\n }\n return kept;\n }\n return chosen;\n }\n if (configured.length) return configured.flatMap(pick);\n return undefined;\n }\n\n #visible(row: EventRow, opts: { declined: boolean; cancelled: boolean }): boolean {\n if (!opts.cancelled && row.status === STATUS_CANCELLED) return false;\n if (!opts.declined && row.invitationStatus === PARTICIPANT_DECLINED) return false;\n return true;\n }\n\n #summarise(row: EventRow, store: CalendarStore): EventSummary {\n const start = renderInstant(row.startApple, row.startTz, row.allDay, store.caps.epochOffset);\n /**\n * OPEN: the all-day `end` convention is not settled, so it is reported RAW.\n *\n * Measured against a live calendar, creating one all-day event on\n * 21 September: Apple Events reads the end back as `2026-09-21T21:59:59`\n * (23:59:59 local — inclusive), while the store renders a day later. The two\n * legs also disagree with each other: an occurrence row reports end on the\n * same day as start, an item row a day after.\n *\n * A \"subtract a second before rendering\" normalisation was tried and turned\n * out to be a no-op on real data, which means the anchoring differs in a way\n * that has not been pinned down — `start_date` reads as local midnight while\n * `end_date` appears to be anchored differently. Guessing again on top of an\n * unverified premise is how the start-of-day bug got here in the first\n * place, so this stays raw and documented until the raw columns are read.\n * See docs/calendar.md, \"Still open\".\n */\n const end = renderInstant(\n row.endApple,\n row.endTz ?? row.startTz,\n row.allDay,\n store.caps.epochOffset,\n );\n const isOccurrence = row.source === \"occurrence\";\n const ref = encodeRef(\n row.calendarUuid ?? \"\",\n row.uuid ?? \"\",\n isOccurrence && row.startApple !== null\n ? new Date((row.startApple + store.caps.epochOffset) * 1000)\n : null,\n );\n return {\n ref,\n summary: row.summary,\n start,\n end,\n allDay: row.allDay,\n calendar: row.calendarTitle,\n location: row.locationTitle,\n isOccurrence,\n ...(isOccurrence ? { seriesRef: encodeRef(row.calendarUuid ?? \"\", row.uuid ?? \"\") } : {}),\n status: row.status,\n invitationStatus: row.invitationStatus,\n source: row.source,\n };\n }\n\n listEvents(filters: EventFilters): EventPage {\n const store = this.#require();\n const range = parseRange(\n {\n from: filters.from,\n to: filters.to,\n defaultRangeDays: this.config.defaultRangeDays,\n maxRangeDays: this.config.maxRangeDays,\n },\n this.#now(),\n );\n const fromApple = this.#toApple(range.from, store);\n const toApple = this.#toApple(range.to, store);\n const calendarUuids = this.#calendarUuids(store, filters.calendar);\n\n // Over-fetch each leg: status filters are applied in JS below, so a limit\n // pushed into SQL would cut the result before those ran.\n const legLimit = Math.min(filters.limit * 4 + 50, 5_000);\n const q = { fromApple, toApple, limit: legLimit, ...(calendarUuids ? { calendarUuids } : {}) };\n\n const merged = mergeRange({\n items: store.rangeItems(q),\n occurrences: store.rangeOccurrences(q),\n coverage: store.coverage(),\n hasOccurrenceCache: store.caps.hasOccurrenceCache,\n fromApple,\n toApple,\n limit: legLimit,\n epochOffset: store.caps.epochOffset,\n });\n\n const declined = filters.includeDeclined ?? this.config.includeDeclined;\n const cancelled = filters.includeCancelled ?? this.config.includeCancelled;\n const events = merged.rows\n .filter((r) => this.#visible(r, { declined, cancelled }))\n .slice(0, filters.limit)\n .map((r) => this.#summarise(r, store));\n\n return {\n events,\n expansion: merged.expansion,\n ...(merged.expansionReason ? { expansionReason: merged.expansionReason } : {}),\n window: {\n from: range.from.toISOString(),\n to: range.to.toISOString(),\n clamped: range.clamped,\n },\n coverage: merged.coverage\n ? {\n from: this.#fromApple(merged.coverage.fromApple, store),\n to: this.#fromApple(merged.coverage.toApple, store),\n rows: merged.coverage.rows,\n }\n : null,\n ...(merged.truncated\n ? {\n truncated: {\n reason: merged.truncated.reason,\n affects: merged.truncated.affects,\n ...(merged.truncated.uncoveredFromApple !== undefined\n ? { uncoveredFrom: this.#fromApple(merged.truncated.uncoveredFromApple, store) }\n : {}),\n ...(merged.truncated.uncoveredToApple !== undefined\n ? { uncoveredTo: this.#fromApple(merged.truncated.uncoveredToApple, store) }\n : {}),\n },\n }\n : {}),\n ...(merged.dropped ? { dropped: merged.dropped } : {}),\n };\n }\n\n /**\n * Text search over events.\n *\n * Runs on items only. An occurrence carries no text of its own, so matching\n * the series once is what a search result should be — expanding it here would\n * bury one answer under fifty identical ones.\n */\n searchEvents(\n args: EventFilters & { query: string; scope?: \"summary\" | \"full\" | undefined },\n ): EventPage {\n const store = this.#require();\n const now = this.#now();\n // Unbounded by default: search is the one place a caller legitimately wants\n // all of history, so an absent bound means \"no bound\" rather than a week.\n const fromApple = args.from\n ? this.#toApple(\n parseRange({ from: args.from, to: args.from, defaultRangeDays: 1, maxRangeDays: 1 }, now)\n .from,\n store,\n )\n : Number.MIN_SAFE_INTEGER;\n const toApple = args.to\n ? this.#toApple(\n parseRange({ from: args.to, to: args.to, defaultRangeDays: 1, maxRangeDays: 1 }, now).to,\n store,\n )\n : Number.MAX_SAFE_INTEGER;\n const calendarUuids = this.#calendarUuids(store, args.calendar);\n\n const rows = store.searchItems({\n fromApple,\n toApple,\n limit: Math.min(args.limit * 4 + 50, 5_000),\n text: args.query,\n scope: args.scope ?? \"summary\",\n ...(calendarUuids ? { calendarUuids } : {}),\n });\n\n const declined = args.includeDeclined ?? this.config.includeDeclined;\n const cancelled = args.includeCancelled ?? this.config.includeCancelled;\n return {\n events: rows\n .filter((r) => this.#visible(r, { declined, cancelled }))\n .slice(0, args.limit)\n .map((r) => this.#summarise(r, store)),\n // Search does not expand, and says so rather than implying it did.\n expansion: \"unavailable\",\n expansionReason:\n \"search matches each event once, at its series start; use apple_calendar_list_events \" +\n \"with a date range to see individual occurrences\",\n window: {\n from: args.from ? this.#fromApple(fromApple, store) : \"unbounded\",\n to: args.to ? this.#fromApple(toApple, store) : \"unbounded\",\n clamped: false,\n },\n coverage: null,\n };\n }\n\n /**\n * When nothing is on the calendar, for long enough to hold a meeting.\n *\n * ## Why this is not list_events with arithmetic bolted on\n *\n * Every other read here returns what it found and flags what it missed, and\n * a caller reads the flag or does not. This one INVERTS the events: what it\n * returns is the complement of what it read, so anything the read missed\n * comes back as free time. A page limit, an unexpanded weekly meeting, an\n * event past the coverage edge — each of those shortens a `list_events`\n * result harmlessly and each of them, here, invents a slot that is already\n * booked. `docs/calendar.md` names the failure directly: \"a short list of\n * events is indistinguishable from a free afternoon.\"\n *\n * So the busy set is either complete or the answer is withheld. Three checks\n * enforce that, and each returns a refusal rather than a short list:\n *\n * 1. the scan bound, so a saturated leg never passes for a quiet week\n * 2. the expansion, because an unexpanded series is invisible on every\n * date but one\n * 3. the coverage edge, past which repeating events simply are not there\n *\n * The third clips the window rather than refusing outright, because the part\n * inside the edge is genuinely answerable — but it says what it cut.\n */\n findAvailability(args: AvailabilityFilters): AvailabilityResult | AvailabilityRefusal {\n const store = this.#require();\n const now = this.#now();\n const range = parseRange(\n {\n from: args.from,\n to: args.to,\n defaultRangeDays: this.config.defaultRangeDays,\n maxRangeDays: this.config.maxRangeDays,\n },\n now,\n );\n\n const dayStart = parseClock(\"dayStart\", args.dayStart);\n const dayEnd = parseClock(\"dayEnd\", args.dayEnd);\n const weekdayKeys = args.weekdays?.length ? args.weekdays : DEFAULT_WEEKDAYS;\n const weekdays = weekdaySet(weekdayKeys);\n const calendarUuids = this.#calendarUuids(store, args.calendar);\n\n const fromApple = this.#toApple(range.from, store);\n const toApple = this.#toApple(range.to, store);\n const q = {\n fromApple,\n toApple,\n limit: BUSY_SCAN_BOUND,\n ...(calendarUuids ? { calendarUuids } : {}),\n };\n const items = store.rangeItems(q);\n const occurrences = store.rangeOccurrences(q);\n\n // A leg that came back exactly full was cut by SQL's LIMIT, and what it cut\n // is the tail of the window — so the later half of the answer would be\n // uniformly free. A refusal, not an empty result.\n if (items.length >= BUSY_SCAN_BOUND || occurrences.length >= BUSY_SCAN_BOUND) {\n return {\n degraded: true,\n capability: \"busy-set\",\n reason:\n `The window holds at least ${BUSY_SCAN_BOUND} events, which is the bound on what this ` +\n 'tool reads. Nothing was computed, so this is not \"you have no free time\" and not ' +\n '\"you are entirely free\" — it is no answer at all.',\n hint:\n \"Ask for a shorter window, or scope it to the calendars that matter with `calendar`. \" +\n \"apple_calendar_list_events will still page through the range as it is.\",\n };\n }\n\n const merged = mergeRange({\n items,\n occurrences,\n coverage: store.coverage(),\n hasOccurrenceCache: store.caps.hasOccurrenceCache,\n fromApple,\n toApple,\n // Nothing may be sliced off: both legs are already under the bound above.\n limit: items.length + occurrences.length,\n epochOffset: store.caps.epochOffset,\n });\n\n if (merged.expansion === \"unavailable\") {\n return {\n degraded: true,\n capability: \"occurrence-expansion\",\n reason:\n \"Repeating events are not expanded on this store, so a weekly meeting exists on the \" +\n \"date its series begins and nowhere else. Every gap computed from that would be wrong \" +\n `in the same direction — free where it is booked. ${merged.expansionReason ?? \"\"}`.trim(),\n hint:\n \"Read the schedule directly with apple_calendar_list_events, which reports the same \" +\n \"limitation rather than hiding it behind an answer.\",\n };\n }\n\n let windowFrom = range.from;\n let windowTo = range.to;\n let truncated: AvailabilityResult[\"truncated\"];\n const cov = merged.coverage;\n if (cov) {\n const covFrom = new Date((cov.fromApple + store.caps.epochOffset) * 1000);\n const covTo = new Date((cov.toApple + store.caps.epochOffset) * 1000);\n const reason =\n \"the requested window runs past the range this store has expanded, where repeating \" +\n \"events are missing entirely; it was cut back rather than reporting that time as free\";\n if (windowTo.getTime() > covTo.getTime()) {\n truncated = { reason, requestedTo: toLocalIso(range.to) };\n windowTo = covTo;\n }\n if (windowFrom.getTime() < covFrom.getTime()) {\n truncated = { ...(truncated ?? { reason }), requestedFrom: toLocalIso(range.from) };\n windowFrom = covFrom;\n }\n if (truncated && windowTo.getTime() <= windowFrom.getTime()) {\n return {\n degraded: true,\n capability: \"occurrence-coverage\",\n reason:\n \"The whole requested window lies outside the range this store has expanded, so \" +\n \"every gap in it would be invented rather than found.\",\n hint:\n `The expansion reaches ${toLocalIso(covFrom)} to ${toLocalIso(covTo)}. ` +\n \"Ask inside that, or use apple_calendar_list_events, which reports single events \" +\n \"correctly at any distance.\",\n };\n }\n }\n\n // Time already gone is not availability. Applied after the coverage cut so\n // the two reasons a window shrank stay distinguishable in the result.\n const startedAtNow = windowFrom.getTime() < now.getTime();\n if (startedAtNow) windowFrom = now;\n\n const shell = {\n durationMinutes: args.durationMinutes,\n window: {\n from: toLocalIso(windowFrom),\n to: toLocalIso(windowTo),\n clamped: range.clamped,\n startedAtNow,\n },\n workingHours: {\n dayStart: args.dayStart,\n dayEnd: args.dayEnd,\n weekdays: weekdayKeys,\n granularityMinutes: args.granularityMinutes,\n // The machine's zone, always: working hours are wall-clock hours, and\n // this server does not re-anchor a calendar day in another zone.\n timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n },\n expansion: merged.expansion,\n coverage: cov\n ? {\n from: this.#fromApple(cov.fromApple, store),\n to: this.#fromApple(cov.toApple, store),\n rows: cov.rows,\n }\n : null,\n ...(truncated ? { truncated } : {}),\n };\n\n if (windowTo.getTime() <= windowFrom.getTime()) {\n return {\n ...shell,\n slots: [],\n busy: { blocking: 0, intervals: 0 },\n allDayEvents: [],\n note:\n \"The window holds no future time — every moment in it has already passed. Nothing was \" +\n \"checked, so this is not a report that you are busy.\",\n };\n }\n\n const declined = args.includeDeclined ?? this.config.includeDeclined;\n const cancelled = args.includeCancelled ?? this.config.includeCancelled;\n const busy: Interval[] = [];\n const allDayEvents: AvailabilityResult[\"allDayEvents\"] = [];\n let blocking = 0;\n\n for (const row of merged.rows) {\n if (row.startApple === null) continue;\n if (!this.#visible(row, { declined, cancelled })) continue;\n const startMs = (row.startApple + store.caps.epochOffset) * 1000;\n\n if (row.allDay) {\n const dayOpen = startOfLocalDayMs(startMs);\n const dayShut = nextLocalDayMs(startMs);\n if (dayOpen < windowTo.getTime() && dayShut > windowFrom.getTime()) {\n const rendered = renderInstant(row.startApple, row.startTz, true, store.caps.epochOffset);\n allDayEvents.push({\n day: rendered?.allDay ? rendered.day : localDay(startMs),\n summary: row.summary,\n calendar: row.calendarTitle,\n });\n }\n if (!args.allDayBusy) continue;\n /*\n * The stored end of an all-day event is not settled — see #summarise,\n * and docs/calendar.md, \"Still open\". So the block is anchored on the\n * DAY the event renders on and closed at the later of the next midnight\n * and whatever the end column says, which errs long. Long is the safe\n * direction: an over-long block hides a slot that existed, a short one\n * offers a slot that did not.\n */\n const endMs =\n row.endApple === null ? dayShut : (row.endApple + store.caps.epochOffset) * 1000;\n busy.push({ from: dayOpen, to: Math.max(dayShut, startOfLocalDayMs(endMs)) });\n blocking += 1;\n continue;\n }\n\n if (args.respectFreeMarking && row.availability === AVAILABILITY_FREE) continue;\n const endMs =\n row.endApple === null ? startMs : (row.endApple + store.caps.epochOffset) * 1000;\n // A zero-length event is a marker, not an appointment; it blocks nothing.\n if (endMs <= startMs) continue;\n busy.push({ from: startMs, to: endMs });\n blocking += 1;\n }\n\n const blocks = mergeIntervals(busy);\n const needMs = args.durationMinutes * 60_000;\n const slots: FreeSlot[] = [];\n outer: for (const w of dayWindows({\n from: windowFrom,\n to: windowTo,\n dayStart,\n dayEnd,\n weekdays,\n })) {\n for (const gap of subtractBusy(w, blocks)) {\n // Align first, then re-measure: a gap opening at 09:07 offers a 09:15\n // start, and the seven minutes in between are not bookable time.\n const start = alignUp(gap.from, args.granularityMinutes);\n if (gap.to - start < needMs) continue;\n slots.push({\n start: toLocalIso(new Date(start)),\n end: toLocalIso(new Date(gap.to)),\n day: localDay(start),\n minutes: Math.round((gap.to - start) / 60_000),\n });\n if (slots.length >= args.limit) break outer;\n }\n }\n\n return {\n ...shell,\n slots,\n busy: { blocking, intervals: blocks.length },\n allDayEvents,\n };\n }\n\n getEvent(ref: string): EventDetail {\n const store = this.#require();\n const decoded = decodeRef(ref);\n const row = store.byUuid(decoded.eventUid);\n if (!row) throw new EventNotFoundError(ref);\n\n // An occurrence ref names an instant the master row does not carry, so the\n // start is taken from the ref rather than from the row it resolved to.\n const shaped: EventRow = decoded.occurrenceStart\n ? {\n ...row,\n startApple: this.#toApple(decoded.occurrenceStart, store),\n endApple:\n row.startApple !== null && row.endApple !== null\n ? this.#toApple(decoded.occurrenceStart, store) + (row.endApple - row.startApple)\n : row.endApple,\n source: \"occurrence\",\n }\n : row;\n\n const summary = this.#summarise(shaped, store);\n return {\n ...summary,\n ref,\n ...(decoded.isOccurrence ? { seriesRef: seriesRefOf(decoded) } : {}),\n description: row.description,\n url: row.url,\n conferenceUrl: row.conferenceUrl,\n hasAttendees: row.hasAttendees,\n hasRecurrences: row.hasRecurrences,\n timeZone: summary.start && !summary.start.allDay ? summary.start.timeZone : null,\n };\n }\n\n calendars() {\n return this.#require().calendars();\n }\n\n accounts(): IndexAccount[] {\n return this.#require().accounts();\n }\n\n // ─── writes ───────────────────────────────────────────────────────────────\n\n /**\n * Resolve the calendar a write targets, and refuse a read-only one up front.\n *\n * Asking the store first means the refusal names the cause. Letting the Apple\n * Event fail instead produces a message from deep inside Calendar that does\n * not mention writability at all.\n */\n #writeTarget(named: string | undefined): { name: string | undefined; uuid: string | null } {\n const store = this.index();\n const wanted = named ?? this.config.defaultCalendar;\n if (!store) {\n // No store to check against. The JXA script asks Calendar itself, so the\n // guard is not lost — only the earlier, better-worded error is.\n return { name: wanted, uuid: null };\n }\n const all = store.calendars();\n if (!wanted) return { name: undefined, uuid: null };\n\n const lower = wanted.toLowerCase();\n const hits = all.filter(\n (c) => c.uuid?.toLowerCase() === lower || c.title?.toLowerCase() === lower,\n );\n if (!hits.length) {\n throw new CalendarNotFoundError(wanted, all.map((c) => c.title ?? \"\").filter(Boolean));\n }\n\n /**\n * Duplicate titles are real: the machine this was measured on has two\n * calendars both named `olouvignes@me.com`. A read can legitimately span\n * both, but a write has to land in exactly one — and only a NAME crosses to\n * Apple Events, so an ambiguous one is refused rather than resolved by coin\n * flip.\n */\n if (hits.length > 1) {\n throw new PreconditionError(\n `\"${wanted}\" matches ${hits.length} calendars, and a write has to name exactly one. ` +\n `Calendars are addressed by name when writing — Apple Events cannot resolve a ` +\n `calendar uid at all — so duplicates cannot be told apart. Rename one in Calendar.app, ` +\n `or write to a calendar whose name is unique.`,\n { requested: wanted, matches: hits.length },\n );\n }\n const hit = hits[0]!;\n if (hit.isSubscribed) throw new CalendarNotWritableError(hit.title ?? wanted);\n // The NAME is what crosses the boundary; the uuid is kept only for the ref.\n return { name: hit.title ?? wanted, uuid: hit.uuid };\n }\n\n /** Map a calendar name reported by Apple Events back to its store uuid. */\n #calendarUuidByName(name: string | null): string | null {\n if (!name) return null;\n const store = this.index();\n if (!store) return null;\n return store.calendars().find((c) => c.title === name)?.uuid ?? null;\n }\n\n /**\n * The calendar NAME a ref points at, for a write.\n *\n * A ref carries the store uuid, which Apple Events cannot resolve, so it has\n * to be turned back into a name before it crosses.\n */\n #nameForRefCalendar(calendarUid: string): string | undefined {\n if (!calendarUid) return undefined;\n const store = this.index();\n if (!store) return undefined;\n const lower = calendarUid.toLowerCase();\n return store.calendars().find((c) => c.uuid?.toLowerCase() === lower)?.title ?? undefined;\n }\n\n /**\n * Run a write script, re-inflating its application-level failures.\n *\n * Core turns a `{ok:false, error:{code, message}}` envelope into a generic\n * `ProtocolError` carrying the code, which surfaces as a bare `\"Birthdays\"`.\n * That matters more here than it looks: the store-side writability check is\n * derived from `subcal_url` and MISSES calendars that are read-only for other\n * reasons — Birthdays and Siri Suggestions both report `writable() === false`\n * while carrying no subscription URL. The JXA lane catches them correctly, so\n * the only thing lost was the explanation. This puts it back.\n */\n async #run<T>(scriptText: string, params: unknown): Promise<T> {\n try {\n return await withBusyRetry(() => this.runner.run<T>(scriptText, params));\n } catch (err) {\n const code = (err as { details?: { code?: string } })?.details?.code;\n const message = err instanceof Error ? err.message : String(err);\n if (code === \"CALENDAR_NOT_WRITABLE\") throw new CalendarNotWritableError(message);\n if (code === \"CALENDAR_NOT_FOUND\") throw new CalendarNotFoundError(message);\n if (code === \"EVENT_NOT_FOUND\") throw new EventNotFoundError(message);\n throw err;\n }\n }\n\n #shapeWrite(data: Record<string, unknown>, calendarUid: string | null): WriteResult {\n const uid = typeof data.uid === \"string\" ? data.uid : null;\n return {\n ref: encodeRef(calendarUid ?? \"\", uid ?? \"\"),\n uid,\n summary: typeof data.summary === \"string\" ? data.summary : null,\n start: typeof data.startDate === \"string\" ? data.startDate : null,\n end: typeof data.endDate === \"string\" ? data.endDate : null,\n allDay: data.alldayEvent === true,\n calendar: typeof data.calendarName === \"string\" ? data.calendarName : null,\n source: \"apple-events\",\n };\n }\n\n /**\n * Work out an event's end from whichever of the three forms the caller used.\n *\n * `end` wins over `durationMinutes`; with neither, the configured default\n * length applies. A zero-length event is refused rather than created, because\n * Calendar renders one as a point in time that is almost impossible to click.\n */\n #resolveWindow(\n start: string,\n end: string | undefined,\n durationMinutes: number | undefined,\n ): { startIso: string; endIso: string; allDayHint: boolean } {\n const now = this.#now();\n const from = parseDate(\"start\", start, now);\n if (end !== undefined) {\n /**\n * A bare day as `end` means THROUGH that day, not midnight at its start.\n *\n * `parseBound` already encodes this for range queries, and list_events\n * documents it — \"naming the same day for both gives that whole day\". Using\n * plain `parseDate` here made create disagree with list about the same\n * word: `{start: \"2026-09-21\", end: \"2026-09-21\"}`, the natural way to say\n * \"a one-day event\", was refused as ending before it began.\n */\n const to = parseBound(\"end\", end, \"end\", now);\n if (to.getTime() <= from.at.getTime()) {\n throw new PreconditionError(\n `end (${toLocalIso(to)}) is not after start (${from.iso}). An event cannot finish ` +\n `before it begins.`,\n );\n }\n return { startIso: from.iso, endIso: toLocalIso(to), allDayHint: from.kind === \"allDay\" };\n }\n const minutes =\n durationMinutes === undefined\n ? this.config.defaultEventDurationMinutes\n : parseDuration(\"durationMinutes\", durationMinutes);\n const to = new Date(from.at.getTime() + minutes * 60_000);\n return {\n startIso: from.iso,\n endIso: toLocalIso(to),\n allDayHint: from.kind === \"allDay\",\n };\n }\n\n async createEvent(fields: CreateEventFields): Promise<WriteResult> {\n const target = this.#writeTarget(fields.calendar);\n const win = this.#resolveWindow(fields.start, fields.end, fields.durationMinutes);\n const data = await this.#run<Record<string, unknown>>(CREATE_EVENT, {\n calendar: target.name ?? null,\n summary: fields.summary,\n startDate: win.startIso,\n endDate: win.endIso,\n // A bare day in `start` already means all-day; an explicit flag overrides.\n allDay: fields.allDay ?? win.allDayHint,\n ...(fields.location !== undefined ? { location: fields.location } : {}),\n ...(fields.description !== undefined ? { description: fields.description } : {}),\n ...(fields.url !== undefined ? { url: fields.url } : {}),\n });\n this.invalidate();\n // Prefer the uuid we resolved; fall back to looking up whichever calendar\n // Calendar actually used, which is the default-calendar case.\n const uuid =\n target.uuid ??\n this.#calendarUuidByName(typeof data.calendarName === \"string\" ? data.calendarName : null);\n return this.#shapeWrite(data, uuid);\n }\n\n async updateEvent(fields: UpdateEventFields): Promise<WriteResult> {\n const ref = decodeRef(fields.ref);\n /**\n * An occurrence ref is REFUSED rather than quietly applied to the series.\n *\n * Calendar's scripting dictionary has no way to detach a single occurrence —\n * the \"This Event\" edit in the UI has no scripting equivalent. Applying the\n * change to the series would move every future standup because someone\n * asked to move one lunch, which is a data-loss bug wearing a success\n * message. \"All future\" is absent for a related reason: it needs a rule\n * split, which is two writes with no transaction between them.\n */\n if (ref.isOccurrence) {\n throw new PreconditionError(\n `That ref names one occurrence of a repeating event, and Calendar's scripting interface ` +\n `cannot edit a single occurrence — only the whole series. Applying your change to the ` +\n `series would move every other occurrence too, so this is refused rather than done ` +\n `silently. To change just this one: delete it with apple_calendar_delete_events ` +\n `(scope \"occurrence\"), then create a replacement. To change them all, pass the ` +\n `seriesRef from apple_calendar_get_event instead.`,\n { ref: fields.ref, seriesRef: seriesRefOf(ref) },\n );\n }\n\n const window =\n fields.start !== undefined\n ? this.#resolveWindow(fields.start, fields.end, fields.durationMinutes)\n : null;\n\n const data = await this.#run<Record<string, unknown>>(UPDATE_EVENT, {\n calendar: this.#nameForRefCalendar(ref.calendarUid) ?? null,\n uid: ref.eventUid,\n ...(fields.summary !== undefined ? { summary: fields.summary } : {}),\n ...(window ? { startDate: window.startIso, endDate: window.endIso } : {}),\n ...(fields.allDay !== undefined ? { allDay: fields.allDay } : {}),\n ...(fields.location !== undefined ? { location: fields.location } : {}),\n ...(fields.description !== undefined ? { description: fields.description } : {}),\n ...(fields.url !== undefined ? { url: fields.url } : {}),\n });\n this.invalidate();\n return this.#shapeWrite(data, ref.calendarUid);\n }\n\n /**\n * Delete whole events.\n *\n * Only whole events: Calendar's scripting interface cannot remove a single\n * occurrence of a repeating one. `excludedDates` — the property Calendar.app\n * itself uses for \"Delete This Event\" — reads back a 1903 sentinel and throws\n * on assignment, measured on macOS 26.6. So an occurrence ref is refused\n * rather than silently deleting the whole series, which is the same shape of\n * refusal `updateEvent` makes and for the same underlying reason.\n */\n async deleteEvents(refs: readonly string[]): Promise<{ results: unknown[]; scope: string }> {\n const decoded = refs.map((r, i) => ({ ref: refs[i]!, parsed: decodeRef(r) }));\n\n const occurrence = decoded.find((d) => d.parsed.isOccurrence);\n if (occurrence) {\n throw new PreconditionError(\n `That ref names one occurrence of a repeating event, and Calendar's scripting interface ` +\n `cannot delete a single occurrence — the excluded-dates property it would need is not ` +\n `writable. Deleting the series instead would remove every other occurrence too, so ` +\n `this is refused rather than done silently. Delete this one in Calendar.app, or pass ` +\n `the seriesRef from apple_calendar_get_event to delete the whole series.`,\n { ref: occurrence.ref, seriesRef: seriesRefOf(occurrence.parsed) },\n );\n }\n\n // Grouped by calendar: findEvent costs one bulk uid fetch per calendar, so\n // deleting five events from one calendar should pay that once, not five times.\n const byCalendar = new Map<string, string[]>();\n for (const { parsed } of decoded) {\n const key = parsed.calendarUid || \"\";\n byCalendar.set(key, [...(byCalendar.get(key) ?? []), parsed.eventUid]);\n }\n const results: unknown[] = [];\n for (const [calendar, uids] of byCalendar) {\n const data = await this.#run<{ results: unknown[] }>(DELETE_EVENTS, {\n calendar: this.#nameForRefCalendar(calendar) ?? null,\n uids,\n });\n results.push(...(data.results ?? []));\n }\n this.invalidate();\n return { results, scope: \"series\" };\n }\n\n lanes(): LaneStatus {\n const located = this.locate();\n const store = this.index();\n return {\n applescript: \"not-used\",\n index: this.config.indexMode === \"off\" ? \"disabled\" : store ? \"live\" : \"unavailable\",\n indexMode: store?.mode ?? null,\n storeFingerprint: store?.caps.fingerprint ?? null,\n reason: store ? null : located.reason,\n };\n }\n}\n","import {\n BaseConfigSchema,\n parseBool,\n parseConfig,\n parseIntOpt,\n parseList,\n trimmed,\n} from \"@mgcrea/mcp-apple-core\";\nimport { z } from \"zod\";\n\nimport { DEFAULT_WEEKDAYS, WEEKDAY_KEYS } from \"./client/availability.js\";\n\nconst CLOCK = /^\\d{1,2}:\\d{2}$/;\n\n/**\n * `Mon`, `MONDAY` and `mon` all mean the same day.\n *\n * Normalised here rather than in the schema so the schema's error message stays\n * the useful one — it lists the seven keys — instead of rejecting a spelling\n * nobody would think was wrong.\n */\nconst parseWorkdays = (raw: string[] | undefined): string[] | undefined =>\n raw?.map((d) => d.trim().toLowerCase().slice(0, 3));\n\n/**\n * Configuration is environment-only — this server holds no secret at all, its\n * access is the macOS permission the user granted.\n *\n * `allowWrites`, `debug`, `osascriptPath`, `osascriptTimeoutMs` and `maxResults`\n * come from `BaseConfigSchema`.\n *\n * Note what is deliberately ABSENT relative to `packages/reminders`: there is no\n * `searchCacheTtlMs` and no degraded-listing cap, because both exist there to\n * manage an Apple Events READ lane. Calendar has none by design\n * (`docs/distribution.md`), and config for a lane that does not exist would\n * advertise a fallback this server cannot provide.\n */\nconst ConfigSchema = BaseConfigSchema.extend({\n /** Account allowlist (names or ids). Empty means every account. */\n accounts: z.array(z.string().min(1)).default([]),\n /**\n * Calendar allowlist (names or uids). Empty means every calendar.\n *\n * The important one for this surface: a work calendar and a personal one\n * routinely live in the same account, so the account is the wrong unit to\n * scope by whenever scoping is the point.\n */\n calendars: z.array(z.string().min(1)).default([]),\n /** Explicit store path. Bypasses discovery — for tests and forensic copies. */\n storePath: z.string().optional(),\n indexMode: z.enum([\"auto\", \"ro\", \"immutable\", \"off\"]).default(\"auto\"),\n /** Calendar a new event goes to when the caller names none. Empty = Calendar's default. */\n defaultCalendar: z.string().optional(),\n /**\n * Window for a range query that names only a start.\n *\n * A calendar has no natural \"everything\" answer the way a note list does, and\n * an unbounded default would scan a decade to report next Tuesday.\n */\n defaultRangeDays: z.number().int().min(1).max(366).default(7),\n /** Hard clamp, so one query cannot ask for a decade. */\n maxRangeDays: z.number().int().min(1).max(3_660).default(366),\n /**\n * The working day `find_availability` assumes when the caller names no hours.\n *\n * Local wall clock, both of them: working hours are a person's, so they hold\n * their numbers across a daylight-saving change rather than drifting an hour.\n * `24:00` is a legal end and means midnight at the close of that day.\n */\n workdayStart: z.string().regex(CLOCK, 'expected a time of day like \"09:00\"').default(\"09:00\"),\n workdayEnd: z.string().regex(CLOCK, 'expected a time of day like \"18:00\"').default(\"18:00\"),\n /** The days that working day applies to. */\n workdays: z\n .array(z.enum(WEEKDAY_KEYS))\n .min(1)\n .default([...DEFAULT_WEEKDAYS]),\n /** Length of a created event when the caller gives neither an end nor a duration. */\n defaultEventDurationMinutes: z.number().int().min(1).max(1_440).default(60),\n /** Whether events the user declined are included when the caller does not say. */\n includeDeclined: z.boolean().default(false),\n /** Whether events an organiser cancelled are included when the caller does not say. */\n includeCancelled: z.boolean().default(false),\n /**\n * Render override for timed events. Empty means the system zone.\n *\n * Validated as a real IANA name here rather than at render time: a bad zone\n * should fail at startup with the variable named, not once per event.\n */\n timeZone: z\n .string()\n .refine(\n (v) => {\n try {\n Intl.DateTimeFormat(\"en-US\", { timeZone: v });\n return true;\n } catch {\n return false;\n }\n },\n { message: 'not an IANA time zone name, e.g. \"Europe/Paris\"' },\n )\n .optional(),\n}).strict();\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\n/**\n * `env` is a parameter with a default so tests are hermetic — they pass their\n * own object rather than mutating (and having to restore) process.env.\n */\nexport const loadConfig = (env: NodeJS.ProcessEnv = process.env): Config =>\n parseConfig(ConfigSchema, {\n allowWrites: parseBool(env.APPLE_CALENDAR_ALLOW_WRITES),\n exposePrompts: parseBool(env.APPLE_CALENDAR_EXPOSE_PROMPTS),\n debug: parseBool(env.APPLE_CALENDAR_DEBUG),\n accounts: parseList(env.APPLE_CALENDAR_ACCOUNTS),\n calendars: parseList(env.APPLE_CALENDAR_CALENDARS),\n storePath: trimmed(env.APPLE_CALENDAR_STORE),\n indexMode: trimmed(env.APPLE_CALENDAR_INDEX_MODE),\n defaultCalendar: trimmed(env.APPLE_CALENDAR_DEFAULT_CALENDAR),\n defaultRangeDays: parseIntOpt(env.APPLE_CALENDAR_DEFAULT_RANGE_DAYS),\n maxRangeDays: parseIntOpt(env.APPLE_CALENDAR_MAX_RANGE_DAYS),\n defaultEventDurationMinutes: parseIntOpt(env.APPLE_CALENDAR_DEFAULT_EVENT_DURATION_MINUTES),\n workdayStart: trimmed(env.APPLE_CALENDAR_WORKDAY_START),\n workdayEnd: trimmed(env.APPLE_CALENDAR_WORKDAY_END),\n workdays: parseWorkdays(parseList(env.APPLE_CALENDAR_WORKDAYS)),\n includeDeclined: parseBool(env.APPLE_CALENDAR_INCLUDE_DECLINED),\n includeCancelled: parseBool(env.APPLE_CALENDAR_INCLUDE_CANCELLED),\n osascriptPath: trimmed(env.APPLE_CALENDAR_OSASCRIPT_PATH),\n osascriptTimeoutMs: parseIntOpt(env.APPLE_CALENDAR_OSASCRIPT_TIMEOUT_MS),\n maxResults: parseIntOpt(env.APPLE_CALENDAR_MAX_RESULTS),\n timeZone: trimmed(env.APPLE_CALENDAR_TIMEZONE),\n });\n","/**\n * The Calendar operating manual, served as `cupertino://calendar/guide` and\n * embedded ahead of every Calendar prompt. Static by design — see the note in\n * the Mail guide.\n */\nexport const CALENDAR_GUIDE = `# Apple Calendar — how to drive this server\n\n## Refs are opaque, and they name an occurrence\n\nEvents come back with a \\`ref\\` like \\`c1:<calendar>/<occurrence>/<uid>\\`. Pass it\nback verbatim. The part that matters: a ref names **one occurrence**, not the\nseries. Reading it reports that occurrence's times; acting on it acts on that\noccurrence. Do not assume changing one changes the repeat.\n\n## Which tool, under which constraint\n\n- **\"When am I free\", \"find me 45 minutes\", \"what does Thursday look like\"** is\n \\`apple_calendar_find_availability\\`. Reach for it instead of listing events and\n looking for gaps by hand — that is where an occurrence of a repeating meeting\n gets missed. It works in working hours (09:00–18:00 on weekdays by default),\n in this machine's timezone, and never offers time already past.\n- **What is on** is \\`apple_calendar_list_events\\` over a bounded range.\n- **Finding a specific event** is \\`apple_calendar_search_events\\`.\n- **Calendar names** must match what Calendar calls them — read the inventory\n resource first. A mistyped name matches nothing rather than erroring.\n\n## Empty is booked; degraded is unknown\n\nOn availability these are two different answers and must never be reported the\nsame way. An empty \\`slots\\` list means **the time is booked**. \\`degraded: true\\`\nmeans the question could not be answered — too many events to read, a store\nthat does not expand repeats, or a window past the expansion range — and the\nright reply is \"I could not check\", never \"you are free\".\n\nAll-day events do not block time by default, because an all-day event is as\noften a birthday as a holiday. They come back in \\`allDayEvents\\`: read that\nbefore booking over one rather than turning \\`allDayBusy\\` on blindly. Declined\nand cancelled events never block.\n\n## Writes, and the thing this server will not do\n\nMutating tools exist only when writes are enabled. If you cannot see\n\\`apple_calendar_create_event\\`, writes are off.\n\n**There is no \\`attendees\\` parameter, anywhere, on purpose.** Adding an attendee\nsends mail to a person. This server creates events on calendars; it does not\ninvite anyone. If the user wants someone invited, create the event and tell them\nto invite from Calendar — do not silently create an event they believe was sent.\n\nRead-only calendars are refused rather than silently skipped.\n`;\n","import {\n promptArg,\n registerWorkflowPrompt,\n requiredPromptArg,\n type PromptContext,\n} from \"@mgcrea/mcp-apple-core\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport { CALENDAR_GUIDE } from \"./guide.js\";\n\nconst CTX: PromptContext = { surface: \"calendar\", guide: CALENDAR_GUIDE };\n\n/**\n * Calendar's workflow prompts.\n *\n * The scheduling one carries the constraint that costs the most when it is\n * missed: this server can create an event but cannot invite anyone to it, so a\n * model that treats \"schedule a meeting with Ana\" as done has told the user\n * something false about a message that was never sent.\n */\nexport const registerPrompts = (server: McpServer, allowWrites: boolean): void => {\n registerWorkflowPrompt(server, CTX, {\n name: \"apple_calendar_whats_my_day\",\n title: \"What my day looks like\",\n description:\n \"Read back a day or a range as something you can act on — what is fixed, what is \" +\n \"movable, and where the real free time is. Read-only.\",\n argsSchema: {\n when: promptArg(\n 'Which day or range, e.g. \"today\", \"tomorrow\", \"next week\". Defaults to today.',\n ),\n calendar: promptArg(\"Restrict to one calendar, exactly as Calendar spells it.\"),\n },\n build: ({ when, calendar }) => `Tell me what ${when ?? \"today\"} looks like${\n calendar ? `, on the ${calendar} calendar` : \"\"\n }.\n\n1. \\`apple_calendar_list_events\\` over that range${\n calendar ? \" for that calendar\" : \"\"\n }. Bound the range explicitly — do not list open-endedly and trim afterwards.\n2. Then \\`apple_calendar_find_availability\\` over the same range, so you can say\n where the usable gaps are rather than leaving the user to subtract meetings\n from a day in their head. Report each gap's real length.\n3. Call out anything in \\`allDayEvents\\` — a holiday or someone's day off changes\n what the rest of the day means, and it does not block time by default.\n4. Flag the pressure points: back-to-back blocks with no gap, anything starting\n before or ending after normal hours, and any two events that overlap.\n\nIf a call comes back \\`degraded: true\\`, say which part of the range you could not\nread. An empty result means booked; degraded means unknown, and they must not be\nreported the same way.`,\n });\n\n if (!allowWrites) return;\n\n registerWorkflowPrompt(server, CTX, {\n name: \"apple_calendar_schedule\",\n title: \"Schedule something\",\n description:\n \"Find a time that genuinely works and put an event on the calendar. Requires writes. \" +\n \"Note that this cannot invite anyone — it creates the event only.\",\n argsSchema: {\n what: requiredPromptArg('What the event is, e.g. \"45 minutes with Ana about pricing\".'),\n when: promptArg('Constraint on when, e.g. \"next week\", \"Thursday afternoon\".'),\n calendar: promptArg(\"Which calendar to create it on, exactly as Calendar spells it.\"),\n },\n build: ({ what, when, calendar }) => `Schedule: ${what}${when ? `\\n\\nWhen: ${when}` : \"\"}\n\n1. Work out the duration from the description; ask if it is genuinely unclear\n rather than defaulting to an hour.\n2. \\`apple_calendar_find_availability\\` over ${\n when ? `the window implied by \"${when}\"` : \"the coming week\"\n }. Do not list events and pick a gap yourself — a repeating meeting's\n occurrence is exactly what that misses.\n3. Offer the user two or three real options before creating anything, with the\n full length of each gap so they can see whether it is tight. If availability\n comes back \\`degraded: true\\`, stop and say you could not check — do not offer\n a slot you could not verify.\n4. On their choice, \\`apple_calendar_create_event\\`${\n calendar\n ? ` on the ${calendar} calendar`\n : \" on a calendar from the inventory, not an invented name\"\n }.\n\n**Then be explicit about what did not happen: nobody was invited.** This server\nhas no \\`attendees\\` parameter on any tool — adding one would send mail, and it\ndeliberately cannot. If someone else needs to be at this, the event is on the\ncalendar and the invitation still has to be sent from Calendar by hand. Say so\nplainly; do not let \"scheduled with Ana\" stand when Ana was never told.`,\n });\n};\n","/**\n * Calendar's tool helpers. The generic half lives in `@mgcrea/mcp-apple-core`;\n * re-exported here so tools import from one place.\n *\n * Calendar's own zod args (event refs, the date grammar, the range block) land\n * with the tools that take them.\n */\nexport {\n compact,\n confirmArg,\n fail,\n limitArg,\n ok,\n okText,\n toFailure,\n wrap,\n wrapResult,\n type ToolResult,\n} from \"@mgcrea/mcp-apple-core\";\n\nimport { z } from \"zod\";\n\n// ─── Calendar's own args ─────────────────────────────────────────────────────\n\nexport const eventRefArg = z\n .string()\n .min(1)\n .describe(\n 'An opaque event ref from a list or search result (looks like \"c1:<calendar>/<occurrence>/<uid>\"). ' +\n \"Do not construct one by hand.\",\n );\n\nexport const calendarArg = z\n .string()\n .optional()\n .describe(\n 'A calendar name or uid, e.g. \"Work\". Use apple_calendar_list_calendars to see what exists.',\n );\n\n/**\n * The date grammar, written out in full.\n *\n * Load-bearing: the caller is usually a model that gets one retry, and the\n * difference between naming a day and naming an instant is the difference\n * between a whole-day range and a one-minute one.\n */\nexport const fromArg = z\n .string()\n .optional()\n .describe(\n 'Start of the window. ISO-8601 \"2026-08-21\" or \"2026-08-21T09:00\", or \"today\", \"tomorrow\", ' +\n '\"next monday\", \"+2d\". A bare day starts at that morning. Defaults to today.',\n );\n\nexport const toArg = z\n .string()\n .optional()\n .describe(\n \"End of the window. A bare day runs through that EVENING, so naming the same day for both \" +\n \"gives that whole day. Defaults to a week after the start.\",\n );\n\nexport const includeDeclinedArg = z\n .boolean()\n .optional()\n .describe(\"Include events you have declined. Off by default; they are still on the calendar.\");\n\nexport const includeCancelledArg = z\n .boolean()\n .optional()\n .describe(\n \"Include events an organiser cancelled but that are still in the store. Off by default.\",\n );\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport { ok, wrap } from \"./util.js\";\n\n/**\n * Build the report.\n *\n * Split out of the tool registration so the `cupertino://calendar/diagnostics`\n * resource can serve the same bytes. Two renderings of one probe: duplicated,\n * the resource and the tool would drift, and the disagreement would surface as\n * \"the diagnostics lied\" — the one thing this file must never do.\n */\nexport const buildDiagnostics = async (\n client: AppleCalendarClient,\n ctx: { allowWrites: boolean },\n): Promise<Record<string, unknown>> => {\n const lanes = client.lanes();\n const located = client.locate();\n const store = client.index();\n\n /**\n * Three-valued, and the middle value is the useful one.\n *\n * Calendar's store has a constant filename, so `stat` answers \"is it\n * there\" even when `access(2)` is denied. That lets this separate \"the\n * grant is missing\" from \"Calendar was never set up on this account\",\n * which Reminders cannot do — its filename carries a generated UUID, so\n * without the grant there is no path to test at all.\n */\n const fullDiskAccess = located.readable\n ? \"granted\"\n : located.exists\n ? \"denied (found the store, cannot read it)\"\n : \"unknown (no store file at the expected path)\";\n\n return {\n server: { lanes },\n permissions: {\n fullDiskAccess,\n automation: ctx.allowWrites\n ? \"needed for writes only. Reads never send an Apple Event, so a read-only setup \" +\n \"prompts for nothing.\"\n : \"not needed — writes are off, and reads never send an Apple Event. Turning \" +\n \"writes on will prompt for Automation the first time one runs.\",\n ...(located.readable\n ? {}\n : {\n howToGrant: [\n \"System Settings > Privacy & Security > Full Disk Access\",\n \"Add the app that launches this server (Terminal, iTerm, VS Code, Claude...), then restart it.\",\n \"Granting it to Calendar.app does nothing — the reader needs the permission, not Calendar.\",\n ],\n }),\n },\n store: {\n containerPath: located.containerPath,\n path: located.storePath,\n containerListable: located.containerListable,\n extrasPresent: located.extrasPresent,\n candidates: located.candidates.length,\n exists: located.exists,\n readable: located.readable,\n sizeBytes: located.size,\n walPresent: located.walPresent,\n walSizeBytes: located.walSizeBytes,\n fingerprint: lanes.storeFingerprint,\n reason: located.reason,\n },\n capabilities: store\n ? {\n hasOccurrenceCache: store.caps.hasOccurrenceCache,\n hasOccurrenceDays: store.caps.hasOccurrenceDays,\n hasRecurrence: store.caps.hasRecurrence,\n hasExceptionDates: store.caps.hasExceptionDates,\n hasLocation: store.caps.hasLocation,\n hasAttachments: store.caps.hasAttachments,\n hasParticipants: store.caps.hasParticipants,\n hasAlarms: store.caps.hasAlarms,\n itemColumns: store.caps.itemColumns.size,\n calendarColumns: store.caps.calendarColumns.size,\n }\n : null,\n settings: {\n allowWrites: ctx.allowWrites,\n // Off means the prompts and the cupertino:// resources are not registered\n // at all. Reported here because this tool still is, so it stays the one\n // place that explains a capability the client cannot see.\n exposePrompts: client.config.exposePrompts,\n accountAllowlist: client.config.accounts,\n calendarAllowlist: client.config.calendars,\n defaultCalendar: client.config.defaultCalendar ?? null,\n defaultRangeDays: client.config.defaultRangeDays,\n maxRangeDays: client.config.maxRangeDays,\n includeDeclined: client.config.includeDeclined,\n includeCancelled: client.config.includeCancelled,\n indexMode: client.config.indexMode,\n maxResults: client.config.maxResults,\n timeZone: client.config.timeZone ?? null,\n },\n caveats: [\n \"Writes go through Apple Events and are real side effects: on a shared, CalDAV or \" +\n \"Exchange calendar a created event syncs within seconds and other people see it. \" +\n \"There is no draft state and no undo.\",\n \"Single occurrences of a repeating event can be neither edited nor deleted through \" +\n \"this server, and that is a limit of Calendar's scripting interface rather than a \" +\n \"choice: there is no way to detach one occurrence, and the excluded-dates property \" +\n \"reads back a 1903 sentinel and throws on assignment (measured, macOS 26.6). Both \" +\n \"are refused rather than applied to the whole series. Calendar.app can still do them.\",\n 'Attendees cannot be set, because that emails a person. There is no \"all future ' +\n 'occurrences\" either, which would need two writes with no transaction between them.',\n \"Calendar reads through the file lane only. Apple Events was measured at 3.4s for \" +\n \"a single 90-day range query, with the cost falling per round trip rather than \" +\n \"per event, so there is no slower-but-working fallback to offer when Full Disk \" +\n \"Access is missing. Reads need the grant.\",\n \"Repeating events are expanded from OccurrenceCache, which was measured to reach \" +\n \"about two years either side of today on the probed store. That is an edge: a \" +\n \"range running past it returns fewer repeating events than exist, so every \" +\n \"list_events result carries `coverage`, and sets `truncated` rather than \" +\n \"returning a short list silently.\",\n \"The `status` and `invitationStatus` numbers are EventKit's documented constants, \" +\n \"and this store is assumed to mirror them — likely, but not measured. That is why \" +\n \"cancelled and declined events are only hidden when you ask, and why the raw \" +\n \"value is on every result.\",\n \"Writes will go through Apple Events, targeting com.apple.iCal. Note the bundle id \" +\n \"is iCal, not Calendar, which is the one place this surface differs from the \" +\n \"other three.\",\n ],\n };\n};\n\n/**\n * One tool that answers \"why is this not working\".\n *\n * Unlike the Reminders equivalent this probes no Apple Event before reading:\n * Calendar has no Apple Events read lane to probe (`docs/distribution.md`), so\n * firing one here would trigger the Automation prompt for a capability the\n * server does not yet have.\n */\nexport const registerDiagnosticsTools = (\n server: McpServer,\n client: AppleCalendarClient,\n ctx: { allowWrites: boolean },\n): void => {\n server.registerTool(\n \"apple_calendar_diagnostics\",\n {\n description:\n \"Report which lanes are live, which macOS permissions are granted, and what each \" +\n \"missing one is blocking. Start here when a tool fails or reports nothing — it names \" +\n \"the exact System Settings pane to open.\",\n inputSchema: {},\n annotations: { readOnlyHint: true },\n },\n async () => wrap(() => buildDiagnostics(client, ctx)).then((r) => r ?? ok({})),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport { calendarArg, confirmArg, eventRefArg, wrap } from \"./util.js\";\n\n/**\n * The mutating tools.\n *\n * Registered only when `allowWrites` is on — not merely refused, absent, so a\n * host is never told they exist.\n *\n * ## No `attendees` parameter, anywhere\n *\n * Adding an attendee sends an email to a person. That is not something to do\n * behind a tool call, and leaving it out of the schema is a stronger guarantee\n * than validating it away. `test/jxa.test.ts` asserts the scripts cannot set it\n * either, so there are two places to remove rather than one to forget.\n */\n\nconst startArg = z\n .string()\n .describe(\n 'When it starts. ISO-8601 \"2026-08-21\" for an all-day event or \"2026-08-21T09:00\" for a ' +\n 'timed one, or \"tomorrow 09:00\", \"next monday\", \"+2d\". Naming a bare day makes it all-day.',\n );\n\nconst endArg = z\n .string()\n .optional()\n .describe(\n \"When it ends. Give this or durationMinutes; with neither, the default length applies.\",\n );\n\nconst durationArg = z\n .number()\n .int()\n .min(1)\n .max(60 * 24 * 366)\n .optional()\n .describe(\"Length in minutes, as an alternative to `end`.\");\n\nexport const registerActionTools = (server: McpServer, client: AppleCalendarClient): void => {\n server.registerTool(\n \"apple_calendar_create_event\",\n {\n description:\n \"Create an event. THIS IS A REAL EVENT ON A REAL CALENDAR: on an iCloud, CalDAV or \" +\n \"Exchange calendar it syncs within seconds and there is no draft state and no undo. \" +\n \"Check apple_calendar_list_calendars first — writing to one where `isShared` is true is \" +\n \"visible to everyone else on that calendar, so prefer a personal one unless the user \" +\n \"meant to share it. Read-only calendars are refused. This tool cannot add attendees, \" +\n \"which would email a person. The result reports what Calendar actually stored, which is \" +\n \"not always what was asked for.\",\n inputSchema: {\n summary: z.string().min(1).describe(\"The event's title.\"),\n calendar: calendarArg,\n start: startArg,\n end: endArg,\n durationMinutes: durationArg,\n allDay: z\n .boolean()\n .optional()\n .describe(\n \"Force an all-day event. Usually unnecessary: a bare day in `start` means one.\",\n ),\n location: z.string().optional(),\n description: z.string().optional().describe(\"The event's notes.\"),\n url: z.string().optional(),\n },\n },\n async (args) => wrap(() => client.createEvent(args)),\n );\n\n server.registerTool(\n \"apple_calendar_update_event\",\n {\n description:\n \"Change an existing event. On a shared calendar the change is visible to everyone else \" +\n \"on it. Note that Apple Events has no transaction: if a change is refused part-way \" +\n \"through, earlier fields may already have been written, so the result is the truth \" +\n \"about what the event now looks like. Only whole events can be edited: a ref naming \" +\n \"ONE occurrence \" +\n \"of a repeating event is refused, because Calendar's scripting interface cannot detach a \" +\n \"single occurrence and applying the change to the series would move every other one too. \" +\n 'To change a single occurrence, delete it with scope \"occurrence\" and create a ' +\n \"replacement.\",\n inputSchema: {\n ref: eventRefArg,\n summary: z.string().min(1).optional(),\n start: startArg.optional(),\n end: endArg,\n durationMinutes: durationArg,\n allDay: z.boolean().optional(),\n location: z.string().optional(),\n description: z.string().optional(),\n url: z.string().optional(),\n },\n },\n async (args) => wrap(() => client.updateEvent(args)),\n );\n\n server.registerTool(\n \"apple_calendar_delete_events\",\n {\n description:\n \"Delete whole events. Each result says whether the event ACTUALLY went — Calendar \" +\n \"silently declines to delete a repeating event, reporting no error, so `deleted` is \" +\n \"decided by re-reading the calendar rather than by the call succeeding. \" +\n \"A ref naming ONE occurrence of a repeating event is refused: \" +\n \"Calendar's scripting interface cannot remove a single occurrence, and deleting the \" +\n \"series instead would take every other one with it. Delete a single occurrence in \" +\n \"Calendar.app, or pass the `seriesRef` from get_event to remove the whole series. \" +\n '\"All future occurrences\" is not offered either — it needs two writes with no ' +\n \"transaction between them.\",\n inputSchema: {\n refs: z\n .array(z.string().min(1))\n .min(1)\n .max(100)\n .describe(\n \"Event refs from a list or search result. Each must name a whole event, not one \" +\n \"occurrence of a repeating one.\",\n ),\n confirm: confirmArg,\n },\n },\n async ({ refs }) => wrap(() => client.deleteEvents(refs)),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nimport { WEEKDAY_KEYS } from \"../client/availability.js\";\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport {\n calendarArg,\n includeCancelledArg,\n includeDeclinedArg,\n limitArg,\n toArg,\n wrap,\n} from \"./util.js\";\n\n/**\n * The one read on this surface that answers a question instead of returning\n * rows, and the only one whose result is the COMPLEMENT of what it read.\n *\n * It lives apart from `events.ts` for that reason. The three tools there share\n * a failure mode — return fewer events than exist, flag it, and the caller is\n * merely under-informed. This one turns the same shortfall into a positive\n * claim that a time is free, so its refusal paths are load-bearing rather than\n * defensive, and keeping them next to a `list_events` that legitimately pages\n * would invite someone to make the two consistent.\n */\nexport const registerAvailabilityTools = (server: McpServer, client: AppleCalendarClient): void => {\n server.registerTool(\n \"apple_calendar_find_availability\",\n {\n description:\n \"Find the times nothing is on the calendar, long enough to hold a meeting of a given \" +\n 'length. This is the tool for \"when am I free\", \"find me 45 minutes next week\" or ' +\n '\"what does Thursday look like\" — reach for it instead of listing events and looking ' +\n \"for gaps yourself, which is where an occurrence of a repeating meeting gets missed. \" +\n \"Working hours default to 09:00-18:00 on weekdays, in THIS MACHINE'S timezone, and only \" +\n \"time inside them is offered. Slots start on a 15-minute boundary and each one reports \" +\n \"how long the whole gap is, which is usually longer than you asked for. \" +\n \"Time already past today is never offered. \" +\n \"It refuses rather than guesses: if the window holds too many events to read, if this \" +\n \"store does not expand repeating events, or if the window runs past the range the \" +\n \"expansion covers, you get `degraded: true` and a reason instead of free time that is \" +\n \"not free. An empty `slots` list means BOOKED; `degraded` means UNKNOWN. \" +\n \"Declined and cancelled events do not block time; all-day events do not either, but \" +\n \"they are listed in `allDayEvents` so you can see the holiday you are about to book over.\",\n inputSchema: {\n durationMinutes: z\n .number()\n .int()\n .min(1)\n .max(1_440)\n .describe(\"How long the meeting needs to be. A gap shorter than this is not returned.\"),\n from: z\n .string()\n .optional()\n .describe(\n 'Start of the window. ISO-8601 \"2026-08-21\" or \"2026-08-21T09:00\", or \"today\", ' +\n '\"tomorrow\", \"next monday\", \"+2d\". Defaults to today. A start already in the past ' +\n \"is pulled forward to now.\",\n ),\n to: toArg,\n calendar: calendarArg,\n dayStart: z\n .string()\n .optional()\n .describe(\n 'Earliest time of day to offer, local wall clock, e.g. \"09:00\". Defaults to ' +\n \"APPLE_CALENDAR_WORKDAY_START (09:00).\",\n ),\n dayEnd: z\n .string()\n .optional()\n .describe(\n 'Latest time of day to offer, e.g. \"18:00\" — or \"24:00\" for the end of the day. ' +\n \"Defaults to APPLE_CALENDAR_WORKDAY_END (18:00).\",\n ),\n weekdays: z\n .array(z.enum(WEEKDAY_KEYS))\n .min(1)\n .optional()\n .describe(\n 'Which days to offer, e.g. [\"mon\",\"tue\",\"wed\",\"thu\",\"fri\"]. Defaults to the ' +\n \"configured working week. Pass all seven to include the weekend.\",\n ),\n granularityMinutes: z\n .number()\n .int()\n .min(1)\n .max(60)\n .refine((n) => 60 % n === 0, {\n message: \"must divide 60 evenly — 5, 10, 15, 20, 30 or 60\",\n })\n .optional()\n .describe(\"Slot starts are rounded up to this boundary. Default 15.\"),\n allDayBusy: z\n .boolean()\n .optional()\n .describe(\n \"Let an all-day event block its whole day. Off by default, because an all-day event \" +\n 'is as often a birthday as a holiday, and blocking on \"Ana\\'s birthday\" would hide ' +\n \"a working day. They are reported in `allDayEvents` either way — read that before \" +\n \"booking rather than turning this on blindly.\",\n ),\n respectFreeMarking: z\n .boolean()\n .optional()\n .describe(\n 'Skip events the calendar marks as \"free\" rather than \"busy\". Off by default: the ' +\n \"stored constant is inferred from EventKit and has not been measured against a \" +\n \"real store, and reading it wrongly would offer a slot that is booked. Every busy \" +\n \"block reports its raw value so you can check before turning this on.\",\n ),\n includeDeclined: includeDeclinedArg,\n includeCancelled: includeCancelledArg,\n limit: limitArg,\n },\n annotations: { readOnlyHint: true },\n },\n async (args) =>\n wrap(async () =>\n client.findAvailability({\n ...args,\n dayStart: args.dayStart ?? client.config.workdayStart,\n dayEnd: args.dayEnd ?? client.config.workdayEnd,\n weekdays: args.weekdays ?? client.config.workdays,\n granularityMinutes: args.granularityMinutes ?? 15,\n allDayBusy: args.allDayBusy ?? false,\n respectFreeMarking: args.respectFreeMarking ?? false,\n limit: args.limit ?? Math.min(25, client.config.maxResults),\n }),\n ),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport { wrap } from \"./util.js\";\n\n/**\n * NOTE ON `async` BELOW: core's `wrap` is typed `() => Promise<T>` because every\n * other surface reaches Apple Events. Calendar reads synchronous SQLite, so the\n * thunks are marked async here rather than widening a shared signature for all\n * four surfaces to accommodate one.\n */\n\nexport const registerCalendarTools = (server: McpServer, client: AppleCalendarClient): void => {\n server.registerTool(\n \"apple_calendar_list_calendars\",\n {\n description:\n \"List every calendar with the account it belongs to, whether it is SHARED with other \" +\n \"people, and whether it looks writable. Two cautions. `isShared` means anything you \" +\n \"write there is visible to the others on it. And `isSubscribed` catches URL-subscribed \" +\n \"calendars but not every read-only one — Birthdays and Siri Suggestions are also \" +\n \"read-only — so a write can still be refused by Calendar itself.\",\n inputSchema: {},\n annotations: { readOnlyHint: true },\n },\n async () => wrap(async () => client.calendars()),\n );\n\n server.registerTool(\n \"apple_calendar_list_accounts\",\n {\n description:\n \"List the accounts Calendar syncs, with how many calendars each holds. Use this to scope \" +\n \"a search when the same calendar name exists in more than one account.\",\n inputSchema: {},\n annotations: { readOnlyHint: true },\n },\n async () => wrap(async () => client.accounts()),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport {\n calendarArg,\n eventRefArg,\n fromArg,\n includeCancelledArg,\n includeDeclinedArg,\n limitArg,\n toArg,\n wrap,\n} from \"./util.js\";\n\n/** Declared once and spread into both tools, so the two cannot drift apart. */\nconst filterSchema = {\n calendar: calendarArg,\n includeDeclined: includeDeclinedArg,\n includeCancelled: includeCancelledArg,\n limit: limitArg,\n};\n\nexport const registerEventTools = (server: McpServer, client: AppleCalendarClient): void => {\n server.registerTool(\n \"apple_calendar_list_events\",\n {\n description:\n \"List events in a date range, earliest first. Occurrences of repeating events are \" +\n \"expanded, so a weekly standup appears once per week rather than once. The result \" +\n \"carries a `coverage` block naming the window the expansion is known to cover; if your \" +\n \"range runs past it, `truncated` is set and says what is missing rather than returning a \" +\n \"short list silently. Returns a `ref` per event for get_event.\",\n inputSchema: { from: fromArg, to: toArg, ...filterSchema },\n annotations: { readOnlyHint: true },\n },\n async (args) =>\n wrap(async () =>\n client.listEvents({\n ...args,\n limit: args.limit ?? Math.min(50, client.config.maxResults),\n }),\n ),\n );\n\n server.registerTool(\n \"apple_calendar_search_events\",\n {\n description:\n \"Search events by text, most recent first. Unbounded in time unless you give `from` or \" +\n \"`to`. Matches each event ONCE at its series start — it does not expand occurrences, so \" +\n \"use list_events with a range to see individual instances of a repeating event.\",\n inputSchema: {\n query: z.string().min(1).describe(\"Text to look for. Matched case-insensitively.\"),\n scope: z\n .enum([\"summary\", \"full\"])\n .optional()\n .describe('\"summary\" (default) searches titles; \"full\" adds notes and location.'),\n from: fromArg,\n to: toArg,\n ...filterSchema,\n },\n annotations: { readOnlyHint: true },\n },\n async (args) =>\n wrap(async () =>\n client.searchEvents({\n ...args,\n limit: args.limit ?? Math.min(50, client.config.maxResults),\n }),\n ),\n );\n\n server.registerTool(\n \"apple_calendar_get_event\",\n {\n description:\n \"Full detail for one event: notes, location, URL, conference link, and whether it has \" +\n \"attendees or repeats. A ref naming one occurrence reports that occurrence's times and \" +\n \"carries a `seriesRef` pointing at the whole series.\",\n inputSchema: { ref: eventRefArg },\n annotations: { readOnlyHint: true },\n },\n async ({ ref }) => wrap(async () => client.getEvent(ref)),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport type { AppleCalendarClient } from \"../client/calendar.js\";\nimport { registerActionTools } from \"./actions.js\";\nimport { registerAvailabilityTools } from \"./availability.js\";\nimport { registerCalendarTools } from \"./calendars.js\";\nimport { registerDiagnosticsTools } from \"./diagnostics.js\";\nimport { registerEventTools } from \"./events.js\";\n\nexport type ToolContext = {\n /**\n * Register the mutating tools too. Off by default — with the flag off they are\n * not merely refused, they are invisible and cannot be called at all.\n */\n allowWrites: boolean;\n};\n\n/**\n * Register the Apple Calendar tools.\n *\n * The registered set is a pure function of `allowWrites` and nothing else. In\n * particular it does NOT vary with whether Full Disk Access is granted: that is\n * a runtime condition which can change while the process lives, and MCP clients\n * cache the tool list, so a tool that appears and disappears would leave clients\n * calling names the server no longer has. Tools that need the store instead\n * report their source, or explain what is missing.\n *\n * Writes go through Apple Events, always. Not a preference: `PRAGMA query_only`\n * is set on the store because Calendar owns it, holds it open and reconciles it\n * against a server, so writing to it would corrupt sync state.\n */\nexport const registerTools = (\n server: McpServer,\n client: AppleCalendarClient,\n ctx: ToolContext,\n): void => {\n registerDiagnosticsTools(server, client, ctx);\n registerCalendarTools(server, client);\n registerEventTools(server, client);\n registerAvailabilityTools(server, client);\n\n if (!ctx.allowWrites) return;\n registerActionTools(server, client);\n};\n","import {\n registerSurfaceResources,\n type Logger,\n type OsascriptRunner,\n} from \"@mgcrea/mcp-apple-core\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport { BUILD_INFO } from \"./build-info.js\";\nimport { AppleCalendarClient } from \"./client/calendar.js\";\nimport type { Config } from \"./config.js\";\nimport { CALENDAR_GUIDE } from \"./guide.js\";\nimport { registerPrompts } from \"./prompts.js\";\nimport { buildDiagnostics } from \"./tools/diagnostics.js\";\nimport { registerTools } from \"./tools/index.js\";\n\nexport const SERVER_NAME = BUILD_INFO.name;\nexport const SERVER_VERSION = BUILD_INFO.version;\n\nexport type CreateServerOptions = {\n config: Config;\n logger?: Logger;\n /** Injected by tests so nothing spawns a process or touches a real Calendar. */\n osascript?: OsascriptRunner;\n /** Injected by tests so a relative range resolves against a frozen clock. */\n now?: () => Date;\n};\n\nexport type CreatedServer = {\n server: McpServer;\n client: AppleCalendarClient;\n};\n\n/**\n * Build the server. Side-effect free: it opens no connection, spawns no\n * process and reads no file, so a test can construct it freely and every\n * external dependency arrives through an option.\n *\n */\nexport const createServer = (opts: CreateServerOptions): CreatedServer => {\n const { config } = opts;\n const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });\n\n const client = new AppleCalendarClient({\n config,\n ...(opts.logger ? { logger: opts.logger } : {}),\n ...(opts.osascript ? { osascript: opts.osascript } : {}),\n ...(opts.now ? { now: opts.now } : {}),\n });\n\n registerTools(server, client, { allowWrites: config.allowWrites });\n /*\n * One flag, both primitives — see `exposePrompts` in core's config. A prompt\n * embeds its surface guide, so registering prompts without the resources\n * would leave every expansion naming a `cupertino://…/guide` that this\n * server does not serve.\n */\n if (config.exposePrompts) {\n registerPrompts(server, config.allowWrites);\n registerSurfaceResources(server, {\n surface: \"calendar\",\n displayName: \"Calendar\",\n guide: CALENDAR_GUIDE,\n diagnostics: () => buildDiagnostics(client, { allowWrites: config.allowWrites }),\n inventory: {\n describes: \"accounts and calendars\",\n read: async () => ({\n accounts: await client.accounts(),\n calendars: await client.calendars(),\n }),\n },\n });\n }\n\n return { server, client };\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mgcrea/mcp-apple-calendar",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Model Context Protocol server for the macOS Apple Calendar app",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"apple",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
48
48
|
"zod": "^4.4.3",
|
|
49
|
-
"@mgcrea/mcp-apple-core": "^1.3.
|
|
49
|
+
"@mgcrea/mcp-apple-core": "^1.3.1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^26.2.0",
|