@laylzj777/my-apple-apple-eventkit 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +575 -0
- package/LICENSE +21 -0
- package/README.md +304 -0
- package/README.zh-CN.md +270 -0
- package/bin/event +0 -0
- package/bin/event-disclaim +0 -0
- package/dist/bin/apple-calendar.d.ts +6 -0
- package/dist/bin/apple-calendar.js +8 -0
- package/dist/bin/apple-calendar.js.map +1 -0
- package/dist/bin/apple-reminders.d.ts +7 -0
- package/dist/bin/apple-reminders.js +9 -0
- package/dist/bin/apple-reminders.js.map +1 -0
- package/dist/bootstrap.d.ts +11 -0
- package/dist/bootstrap.js +44 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/server/handlers.d.ts +12 -0
- package/dist/server/handlers.js +58 -0
- package/dist/server/handlers.js.map +1 -0
- package/dist/server/mode.d.ts +19 -0
- package/dist/server/mode.js +47 -0
- package/dist/server/mode.js.map +1 -0
- package/dist/server/promptAbstractions.d.ts +61 -0
- package/dist/server/promptAbstractions.js +140 -0
- package/dist/server/promptAbstractions.js.map +1 -0
- package/dist/server/prompts.d.ts +13 -0
- package/dist/server/prompts.js +461 -0
- package/dist/server/prompts.js.map +1 -0
- package/dist/server/server.d.ts +61 -0
- package/dist/server/server.js +112 -0
- package/dist/server/server.js.map +1 -0
- package/dist/tools/definitions.d.ts +9 -0
- package/dist/tools/definitions.js +272 -0
- package/dist/tools/definitions.js.map +1 -0
- package/dist/tools/handlers/calendarHandlers.d.ts +11 -0
- package/dist/tools/handlers/calendarHandlers.js +151 -0
- package/dist/tools/handlers/calendarHandlers.js.map +1 -0
- package/dist/tools/handlers/formatters.d.ts +24 -0
- package/dist/tools/handlers/formatters.js +105 -0
- package/dist/tools/handlers/formatters.js.map +1 -0
- package/dist/tools/handlers/index.d.ts +8 -0
- package/dist/tools/handlers/index.js +9 -0
- package/dist/tools/handlers/index.js.map +1 -0
- package/dist/tools/handlers/listHandlers.d.ts +10 -0
- package/dist/tools/handlers/listHandlers.js +47 -0
- package/dist/tools/handlers/listHandlers.js.map +1 -0
- package/dist/tools/handlers/reminderHandlers.d.ts +10 -0
- package/dist/tools/handlers/reminderHandlers.js +219 -0
- package/dist/tools/handlers/reminderHandlers.js.map +1 -0
- package/dist/tools/handlers/shared.d.ts +28 -0
- package/dist/tools/handlers/shared.js +57 -0
- package/dist/tools/handlers/shared.js.map +1 -0
- package/dist/tools/handlers/subtaskHandlers.d.ts +12 -0
- package/dist/tools/handlers/subtaskHandlers.js +113 -0
- package/dist/tools/handlers/subtaskHandlers.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +74 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +263 -0
- package/dist/types/index.js +42 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/prompts.d.ts +88 -0
- package/dist/types/prompts.js +6 -0
- package/dist/types/prompts.js.map +1 -0
- package/dist/types/repository.d.ts +197 -0
- package/dist/types/repository.js +14 -0
- package/dist/types/repository.js.map +1 -0
- package/dist/utils/binaryValidator.d.ts +52 -0
- package/dist/utils/binaryValidator.js +185 -0
- package/dist/utils/binaryValidator.js.map +1 -0
- package/dist/utils/calendarRepository.d.ts +49 -0
- package/dist/utils/calendarRepository.js +226 -0
- package/dist/utils/calendarRepository.js.map +1 -0
- package/dist/utils/constants.d.ts +79 -0
- package/dist/utils/constants.js +80 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/dateFiltering.d.ts +41 -0
- package/dist/utils/dateFiltering.js +129 -0
- package/dist/utils/dateFiltering.js.map +1 -0
- package/dist/utils/dateUtils.d.ts +27 -0
- package/dist/utils/dateUtils.js +71 -0
- package/dist/utils/dateUtils.js.map +1 -0
- package/dist/utils/errorHandling.d.ts +27 -0
- package/dist/utils/errorHandling.js +88 -0
- package/dist/utils/errorHandling.js.map +1 -0
- package/dist/utils/eventCli.d.ts +40 -0
- package/dist/utils/eventCli.js +294 -0
- package/dist/utils/eventCli.js.map +1 -0
- package/dist/utils/helpers.d.ts +35 -0
- package/dist/utils/helpers.js +61 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/looseDateParser.d.ts +19 -0
- package/dist/utils/looseDateParser.js +149 -0
- package/dist/utils/looseDateParser.js.map +1 -0
- package/dist/utils/projectUtils.d.ts +13 -0
- package/dist/utils/projectUtils.js +92 -0
- package/dist/utils/projectUtils.js.map +1 -0
- package/dist/utils/reminderDateParser.d.ts +8 -0
- package/dist/utils/reminderDateParser.js +89 -0
- package/dist/utils/reminderDateParser.js.map +1 -0
- package/dist/utils/reminderRepository.d.ts +33 -0
- package/dist/utils/reminderRepository.js +262 -0
- package/dist/utils/reminderRepository.js.map +1 -0
- package/dist/utils/subtaskUtils.d.ts +103 -0
- package/dist/utils/subtaskUtils.js +253 -0
- package/dist/utils/subtaskUtils.js.map +1 -0
- package/dist/utils/tagUtils.d.ts +58 -0
- package/dist/utils/tagUtils.js +177 -0
- package/dist/utils/tagUtils.js.map +1 -0
- package/dist/utils/timeHelpers.d.ts +40 -0
- package/dist/utils/timeHelpers.js +136 -0
- package/dist/utils/timeHelpers.js.map +1 -0
- package/dist/validation/schemas.d.ts +385 -0
- package/dist/validation/schemas.js +579 -0
- package/dist/validation/schemas.js.map +1 -0
- package/package.json +96 -0
- package/scripts/build-event.mjs +513 -0
- package/scripts/disclaim.c +69 -0
- package/scripts/event-Info.plist +29 -0
- package/scripts/event.entitlements +10 -0
- package/scripts/postinstall.mjs +91 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* utils/binaryValidator.ts
|
|
3
|
+
* Secure binary path validation and integrity checking
|
|
4
|
+
*/
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
/**
|
|
9
|
+
* Default security configuration.
|
|
10
|
+
*
|
|
11
|
+
* Each `allowedPaths` entry is a path suffix that the candidate binary must
|
|
12
|
+
* match against either its full normalized path or its parent directory.
|
|
13
|
+
* Suffix matching is segment-aligned so `foo-bin` doesn't partial-match
|
|
14
|
+
* `bin`. The defaults intentionally do NOT include a bare `bin` entry — that
|
|
15
|
+
* would pass for any `/<anywhere>/bin/<anything>` (e.g. `/usr/local/bin/curl`).
|
|
16
|
+
* Production callers pass an absolute path tied to the project root via
|
|
17
|
+
* configuration (see `src/utils/eventCli.ts`); the defaults below exist for
|
|
18
|
+
* tests that exercise the suffix matcher.
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_CONFIG = {
|
|
21
|
+
maxFileSize: 50 * 1024 * 1024, // 50MB max
|
|
22
|
+
allowedPaths: ['dist/swift/bin', 'src/swift/bin', 'swift/bin', 'bin/event'],
|
|
23
|
+
requireAbsolutePath: true,
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Binary validation error
|
|
27
|
+
*/
|
|
28
|
+
export class BinaryValidationError extends Error {
|
|
29
|
+
constructor(message, code) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.name = 'BinaryValidationError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validates binary path for security
|
|
37
|
+
*/
|
|
38
|
+
export function validateBinaryPath(binaryPath, config = {}) {
|
|
39
|
+
const fullConfig = { ...DEFAULT_CONFIG, ...config };
|
|
40
|
+
if (fullConfig.requireAbsolutePath && !path.isAbsolute(binaryPath)) {
|
|
41
|
+
throw new BinaryValidationError('Binary path must be absolute', 'INVALID_PATH');
|
|
42
|
+
}
|
|
43
|
+
const normalizedPath = path.normalize(binaryPath);
|
|
44
|
+
// `path.normalize` resolves any embedded `..`, so a surviving `..` segment
|
|
45
|
+
// would only come from a deliberately malformed input — check segments
|
|
46
|
+
// rather than substring (`..foo` is a legal filename).
|
|
47
|
+
const segments = normalizedPath.split(path.sep);
|
|
48
|
+
if (segments.includes('..')) {
|
|
49
|
+
throw new BinaryValidationError('Path traversal detected in binary path', 'PATH_TRAVERSAL');
|
|
50
|
+
}
|
|
51
|
+
// An entry matches when it is a suffix of either the full binary path
|
|
52
|
+
// (including its filename, e.g. `bin/event`) or the binary's parent
|
|
53
|
+
// directory. Suffix matching is segment-aligned — `endsWith('/bin')` after
|
|
54
|
+
// stripping trailing separators avoids the `foo-bin` partial-match trap
|
|
55
|
+
// while still working for absolute and relative allowed paths.
|
|
56
|
+
const parentDir = path.dirname(normalizedPath);
|
|
57
|
+
const isInAllowedPath = fullConfig.allowedPaths.some((allowedPath) => {
|
|
58
|
+
const allowedNormalized = path
|
|
59
|
+
.normalize(allowedPath)
|
|
60
|
+
.replace(/[\\/]+$/, '');
|
|
61
|
+
if (!allowedNormalized)
|
|
62
|
+
return false;
|
|
63
|
+
const sepSuffix = path.sep + allowedNormalized;
|
|
64
|
+
return (normalizedPath === allowedNormalized ||
|
|
65
|
+
normalizedPath.endsWith(sepSuffix) ||
|
|
66
|
+
parentDir === allowedNormalized ||
|
|
67
|
+
parentDir.endsWith(sepSuffix));
|
|
68
|
+
});
|
|
69
|
+
if (!isInAllowedPath) {
|
|
70
|
+
throw new BinaryValidationError('Binary path not in allowed directories', 'FORBIDDEN_PATH');
|
|
71
|
+
}
|
|
72
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
73
|
+
throw new BinaryValidationError(`Binary file not found: ${normalizedPath}`, 'FILE_NOT_FOUND');
|
|
74
|
+
}
|
|
75
|
+
const stats = fs.statSync(normalizedPath);
|
|
76
|
+
if (!stats.isFile()) {
|
|
77
|
+
throw new BinaryValidationError('Binary path does not point to a file', 'NOT_A_FILE');
|
|
78
|
+
}
|
|
79
|
+
if (stats.size > fullConfig.maxFileSize) {
|
|
80
|
+
throw new BinaryValidationError(`Binary file too large: ${stats.size} bytes`, 'FILE_TOO_LARGE');
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
fs.accessSync(normalizedPath, fs.constants.X_OK);
|
|
84
|
+
}
|
|
85
|
+
catch (_error) {
|
|
86
|
+
throw new BinaryValidationError('Binary file is not executable', 'NOT_EXECUTABLE');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Calculates SHA256 hash of binary file
|
|
91
|
+
*/
|
|
92
|
+
export function calculateBinaryHash(binaryPath) {
|
|
93
|
+
try {
|
|
94
|
+
const fileBuffer = fs.readFileSync(binaryPath);
|
|
95
|
+
return crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw new BinaryValidationError(`Failed to calculate binary hash: ${error.message}`, 'HASH_CALCULATION_FAILED');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validates binary integrity using hash
|
|
103
|
+
*/
|
|
104
|
+
export function validateBinaryIntegrity(binaryPath, expectedHash) {
|
|
105
|
+
try {
|
|
106
|
+
const actualHash = calculateBinaryHash(binaryPath);
|
|
107
|
+
const isValid = actualHash === expectedHash;
|
|
108
|
+
return isValid;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Comprehensive binary security validation
|
|
116
|
+
*/
|
|
117
|
+
export function validateBinarySecurity(binaryPath, config = {}) {
|
|
118
|
+
const errors = [];
|
|
119
|
+
let hash;
|
|
120
|
+
try {
|
|
121
|
+
// Path validation
|
|
122
|
+
validateBinaryPath(binaryPath, config);
|
|
123
|
+
// Hashing the multi-MB binary takes 10–30 ms and blocks the event loop;
|
|
124
|
+
// only do it when the caller will actually compare the result.
|
|
125
|
+
if (config.expectedHash) {
|
|
126
|
+
hash = calculateBinaryHash(binaryPath);
|
|
127
|
+
if (hash !== config.expectedHash) {
|
|
128
|
+
errors.push('Binary integrity check failed - hash mismatch');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error instanceof BinaryValidationError) {
|
|
134
|
+
errors.push(`${error.code}: ${error.message}`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
errors.push(`Unexpected validation error: ${error.message}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
isValid: errors.length === 0,
|
|
142
|
+
hash,
|
|
143
|
+
errors,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Secure binary path finder with validation
|
|
148
|
+
*/
|
|
149
|
+
export function findSecureBinaryPath(possiblePaths, config = {}) {
|
|
150
|
+
for (const binaryPath of possiblePaths) {
|
|
151
|
+
const validationResult = validateBinarySecurity(binaryPath, config);
|
|
152
|
+
if (validationResult.isValid) {
|
|
153
|
+
return { path: binaryPath, validationResult };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { path: null };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Environment-specific binary validation
|
|
160
|
+
*/
|
|
161
|
+
export function getEnvironmentBinaryConfig() {
|
|
162
|
+
if (process.env.NODE_ENV === 'test') {
|
|
163
|
+
// Relaxed validation for testing
|
|
164
|
+
return {
|
|
165
|
+
requireAbsolutePath: false,
|
|
166
|
+
maxFileSize: 100 * 1024 * 1024, // 100MB for test
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (process.env.NODE_ENV === 'development') {
|
|
170
|
+
// Development mode - log more details
|
|
171
|
+
return {
|
|
172
|
+
maxFileSize: 100 * 1024 * 1024, // 100MB for dev
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
// Production mode - strict validation
|
|
176
|
+
return {
|
|
177
|
+
expectedHash: process.env.SWIFT_BINARY_HASH,
|
|
178
|
+
// The universal bin/event sits at ~51.7MB today; 80MB matches the
|
|
179
|
+
// published-tarball size-sanity bound and leaves room for vendor bumps
|
|
180
|
+
// without surfacing as a misleading "binary not found" error.
|
|
181
|
+
maxFileSize: 80 * 1024 * 1024, // 80MB
|
|
182
|
+
requireAbsolutePath: true,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=binaryValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binaryValidator.js","sourceRoot":"","sources":["../../src/utils/binaryValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* calendarRepository.ts
|
|
3
|
+
* Repository for calendar event data access via the vendored `event` CLI.
|
|
4
|
+
*
|
|
5
|
+
* Read-only fields (URL, structured location, all-day toggle, availability,
|
|
6
|
+
* alarms, recurrence rules) are passed through from JSON when present;
|
|
7
|
+
* write paths only cover what `event` exposes today. See
|
|
8
|
+
* `docs/migration-to-event-cli.md` for the full table.
|
|
9
|
+
*/
|
|
10
|
+
import type { Calendar, CalendarEvent } from '../types/index.js';
|
|
11
|
+
import type { CreateEventData, EventJSON, ICalendarRepository, UpdateEventData } from '../types/repository.js';
|
|
12
|
+
type EventSpan = 'this-event' | 'future-events';
|
|
13
|
+
declare class CalendarRepository implements ICalendarRepository {
|
|
14
|
+
private listEvents;
|
|
15
|
+
private listEventsWideWindow;
|
|
16
|
+
findEventById(id: string): Promise<CalendarEvent>;
|
|
17
|
+
findEvents(filters?: {
|
|
18
|
+
startDate?: string;
|
|
19
|
+
endDate?: string;
|
|
20
|
+
calendarName?: string;
|
|
21
|
+
search?: string;
|
|
22
|
+
availability?: string;
|
|
23
|
+
}): Promise<CalendarEvent[]>;
|
|
24
|
+
/**
|
|
25
|
+
* `event` has no first-class "list calendars" command — calendar names are
|
|
26
|
+
* surfaced only as the `calendar` field on each event in `calendar list`.
|
|
27
|
+
* Derive a unique-by-name listing from a wide read window so callers of
|
|
28
|
+
* `calendar_calendars` see every calendar that contains an event. Calendars
|
|
29
|
+
* with zero events in the window won't appear; see
|
|
30
|
+
* `docs/migration-to-event-cli.md` for the workaround.
|
|
31
|
+
*/
|
|
32
|
+
findAllCalendars(): Promise<Calendar[]>;
|
|
33
|
+
/**
|
|
34
|
+
* When a date range is supplied, scope the listing to that window instead
|
|
35
|
+
* of the default wide window and annotate each calendar with how many
|
|
36
|
+
* events it contributed — this is the only per-calendar signal `event`
|
|
37
|
+
* exposes (it has no EventKit account/identifier info to filter or key
|
|
38
|
+
* on), so counts are grouped by calendar title rather than a stable id.
|
|
39
|
+
*/
|
|
40
|
+
findCalendars(filters?: {
|
|
41
|
+
startDate?: string;
|
|
42
|
+
endDate?: string;
|
|
43
|
+
}): Promise<Calendar[]>;
|
|
44
|
+
createEvent(data: CreateEventData): Promise<EventJSON>;
|
|
45
|
+
updateEvent(data: UpdateEventData): Promise<EventJSON>;
|
|
46
|
+
deleteEvent(id: string, span?: EventSpan): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
export declare const calendarRepository: CalendarRepository;
|
|
49
|
+
export { CalendarRepository };
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* calendarRepository.ts
|
|
3
|
+
* Repository for calendar event data access via the vendored `event` CLI.
|
|
4
|
+
*
|
|
5
|
+
* Read-only fields (URL, structured location, all-day toggle, availability,
|
|
6
|
+
* alarms, recurrence rules) are passed through from JSON when present;
|
|
7
|
+
* write paths only cover what `event` exposes today. See
|
|
8
|
+
* `docs/migration-to-event-cli.md` for the full table.
|
|
9
|
+
*/
|
|
10
|
+
import { CliUserError } from './errorHandling.js';
|
|
11
|
+
import { executeEventCliJson, executeEventCliPlain } from './eventCli.js';
|
|
12
|
+
import { addOptionalArg, nullToUndefined } from './helpers.js';
|
|
13
|
+
import { parseReminderDueDate } from './reminderDateParser.js';
|
|
14
|
+
const DEFAULT_READ_WINDOW_DAYS = 14;
|
|
15
|
+
/**
|
|
16
|
+
* When looking up a single event by ID, expand the read window aggressively
|
|
17
|
+
* so events scheduled years away (e.g. recurring annual reservations) can
|
|
18
|
+
* still be located without requiring the caller to know the date in advance.
|
|
19
|
+
* EventKit only supports a maximum 4-year predicate window — bound at
|
|
20
|
+
* roughly that to stay within Apple's limits.
|
|
21
|
+
*/
|
|
22
|
+
const FIND_BY_ID_WINDOW_DAYS = 365 * 4;
|
|
23
|
+
const formatDateOnly = (date) => {
|
|
24
|
+
const year = date.getFullYear();
|
|
25
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
26
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
27
|
+
return `${year}-${month}-${day}`;
|
|
28
|
+
};
|
|
29
|
+
const shiftDays = (date, days) => {
|
|
30
|
+
const shifted = new Date(date);
|
|
31
|
+
shifted.setDate(shifted.getDate() + days);
|
|
32
|
+
return shifted;
|
|
33
|
+
};
|
|
34
|
+
// `event calendar list --start/--end` expects bare `yyyy-MM-dd`; trim any
|
|
35
|
+
// time component from caller-supplied dates so the predicate succeeds.
|
|
36
|
+
const toDateOnly = (value) => {
|
|
37
|
+
const match = value.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
38
|
+
return match?.[1] ?? value;
|
|
39
|
+
};
|
|
40
|
+
const resolveReadDateRange = (filters) => {
|
|
41
|
+
const today = new Date();
|
|
42
|
+
if (filters.startDate && filters.endDate) {
|
|
43
|
+
return {
|
|
44
|
+
startDate: toDateOnly(filters.startDate),
|
|
45
|
+
endDate: toDateOnly(filters.endDate),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (!filters.startDate && !filters.endDate) {
|
|
49
|
+
return {
|
|
50
|
+
startDate: formatDateOnly(today),
|
|
51
|
+
endDate: formatDateOnly(shiftDays(today, DEFAULT_READ_WINDOW_DAYS)),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (filters.startDate && !filters.endDate) {
|
|
55
|
+
// `parseReminderDueDate` is the strict parser shared with the reminder
|
|
56
|
+
// pipeline; it rejects auto-corrected dates like `2025-02-30` that the
|
|
57
|
+
// native `Date` constructor silently coerces to March 2.
|
|
58
|
+
const start = parseReminderDueDate(filters.startDate);
|
|
59
|
+
return {
|
|
60
|
+
startDate: toDateOnly(filters.startDate),
|
|
61
|
+
endDate: formatDateOnly(shiftDays(start ?? today, DEFAULT_READ_WINDOW_DAYS)),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// !filters.startDate && filters.endDate
|
|
65
|
+
const endStr = filters.endDate;
|
|
66
|
+
const end = parseReminderDueDate(endStr);
|
|
67
|
+
return {
|
|
68
|
+
startDate: formatDateOnly(shiftDays(end ?? today, -DEFAULT_READ_WINDOW_DAYS)),
|
|
69
|
+
endDate: toDateOnly(endStr),
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
const EVENT_NULLABLE_FIELDS = [
|
|
73
|
+
'notes',
|
|
74
|
+
'location',
|
|
75
|
+
'structuredLocation',
|
|
76
|
+
'url',
|
|
77
|
+
'availability',
|
|
78
|
+
'alarms',
|
|
79
|
+
'recurrenceRules',
|
|
80
|
+
'organizer',
|
|
81
|
+
'attendees',
|
|
82
|
+
'status',
|
|
83
|
+
'isDetached',
|
|
84
|
+
'occurrenceDate',
|
|
85
|
+
'creationDate',
|
|
86
|
+
'lastModifiedDate',
|
|
87
|
+
'externalId',
|
|
88
|
+
];
|
|
89
|
+
const mapEvent = (event) => nullToUndefined(event, EVENT_NULLABLE_FIELDS);
|
|
90
|
+
/**
|
|
91
|
+
* Maps our schema span values to `event calendar delete --span` strings.
|
|
92
|
+
* `event` understands "this" (default → `.thisEvent`) and "all"
|
|
93
|
+
* (→ `.futureEvents`).
|
|
94
|
+
*/
|
|
95
|
+
const mapSpanForEvent = (span) => span === 'this-event' ? 'this' : span === 'future-events' ? 'all' : undefined;
|
|
96
|
+
class CalendarRepository {
|
|
97
|
+
async listEvents(startDate, endDate, calendarName) {
|
|
98
|
+
const args = ['calendar', 'list', '--start', startDate, '--end', endDate];
|
|
99
|
+
addOptionalArg(args, '--calendar', calendarName);
|
|
100
|
+
args.push('--json');
|
|
101
|
+
return executeEventCliJson(args);
|
|
102
|
+
}
|
|
103
|
+
// `findEventById` and `findAllCalendars` both need a wide read window
|
|
104
|
+
// because EventKit can't be queried for a single event by id directly via
|
|
105
|
+
// `event`, and empty calendars don't appear in narrow windows. Centralize
|
|
106
|
+
// the bounds here so both paths stay in sync.
|
|
107
|
+
listEventsWideWindow() {
|
|
108
|
+
const today = new Date();
|
|
109
|
+
return this.listEvents(formatDateOnly(shiftDays(today, -FIND_BY_ID_WINDOW_DAYS)), formatDateOnly(shiftDays(today, FIND_BY_ID_WINDOW_DAYS)));
|
|
110
|
+
}
|
|
111
|
+
async findEventById(id) {
|
|
112
|
+
const events = await this.listEventsWideWindow();
|
|
113
|
+
const event = events.find((e) => e.id === id);
|
|
114
|
+
if (!event) {
|
|
115
|
+
throw new CliUserError(`Event with ID '${id}' not found.`);
|
|
116
|
+
}
|
|
117
|
+
return mapEvent(event);
|
|
118
|
+
}
|
|
119
|
+
async findEvents(filters = {}) {
|
|
120
|
+
const dateRange = resolveReadDateRange({
|
|
121
|
+
startDate: filters.startDate,
|
|
122
|
+
endDate: filters.endDate,
|
|
123
|
+
});
|
|
124
|
+
const events = await this.listEvents(dateRange.startDate, dateRange.endDate, filters.calendarName);
|
|
125
|
+
let normalized = events.map(mapEvent);
|
|
126
|
+
// `event` does not surface `--search`; apply the substring match in TS
|
|
127
|
+
// against title / notes / location.
|
|
128
|
+
if (filters.search) {
|
|
129
|
+
const needle = filters.search.toLowerCase();
|
|
130
|
+
normalized = normalized.filter((event) => {
|
|
131
|
+
const haystack = [event.title, event.notes, event.location]
|
|
132
|
+
.filter((value) => Boolean(value))
|
|
133
|
+
.map((value) => value.toLowerCase());
|
|
134
|
+
return haystack.some((value) => value.includes(needle));
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (filters.availability) {
|
|
138
|
+
normalized = normalized.filter((event) => event.availability === filters.availability);
|
|
139
|
+
}
|
|
140
|
+
return normalized;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* `event` has no first-class "list calendars" command — calendar names are
|
|
144
|
+
* surfaced only as the `calendar` field on each event in `calendar list`.
|
|
145
|
+
* Derive a unique-by-name listing from a wide read window so callers of
|
|
146
|
+
* `calendar_calendars` see every calendar that contains an event. Calendars
|
|
147
|
+
* with zero events in the window won't appear; see
|
|
148
|
+
* `docs/migration-to-event-cli.md` for the workaround.
|
|
149
|
+
*/
|
|
150
|
+
async findAllCalendars() {
|
|
151
|
+
const events = await this.listEventsWideWindow();
|
|
152
|
+
const distinct = new Set();
|
|
153
|
+
for (const event of events) {
|
|
154
|
+
if (event.calendar)
|
|
155
|
+
distinct.add(event.calendar);
|
|
156
|
+
}
|
|
157
|
+
return Array.from(distinct)
|
|
158
|
+
.sort((a, b) => a.localeCompare(b))
|
|
159
|
+
.map((name) => ({ id: name, title: name }));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* When a date range is supplied, scope the listing to that window instead
|
|
163
|
+
* of the default wide window and annotate each calendar with how many
|
|
164
|
+
* events it contributed — this is the only per-calendar signal `event`
|
|
165
|
+
* exposes (it has no EventKit account/identifier info to filter or key
|
|
166
|
+
* on), so counts are grouped by calendar title rather than a stable id.
|
|
167
|
+
*/
|
|
168
|
+
async findCalendars(filters = {}) {
|
|
169
|
+
if (!filters.startDate && !filters.endDate) {
|
|
170
|
+
return this.findAllCalendars();
|
|
171
|
+
}
|
|
172
|
+
const dateRange = resolveReadDateRange({
|
|
173
|
+
startDate: filters.startDate,
|
|
174
|
+
endDate: filters.endDate,
|
|
175
|
+
});
|
|
176
|
+
const events = await this.listEvents(dateRange.startDate, dateRange.endDate);
|
|
177
|
+
const eventCountByTitle = new Map();
|
|
178
|
+
for (const event of events) {
|
|
179
|
+
if (!event.calendar)
|
|
180
|
+
continue;
|
|
181
|
+
eventCountByTitle.set(event.calendar, (eventCountByTitle.get(event.calendar) ?? 0) + 1);
|
|
182
|
+
}
|
|
183
|
+
return Array.from(eventCountByTitle.keys())
|
|
184
|
+
.sort((a, b) => a.localeCompare(b))
|
|
185
|
+
.map((title) => ({
|
|
186
|
+
id: title,
|
|
187
|
+
title,
|
|
188
|
+
eventCount: eventCountByTitle.get(title) ?? 0,
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
async createEvent(data) {
|
|
192
|
+
const args = [
|
|
193
|
+
'calendar',
|
|
194
|
+
'create',
|
|
195
|
+
'--title',
|
|
196
|
+
data.title,
|
|
197
|
+
'--start',
|
|
198
|
+
data.startDate,
|
|
199
|
+
'--end',
|
|
200
|
+
data.endDate,
|
|
201
|
+
];
|
|
202
|
+
addOptionalArg(args, '--calendar', data.calendar);
|
|
203
|
+
addOptionalArg(args, '--notes', data.notes);
|
|
204
|
+
addOptionalArg(args, '--location', data.location);
|
|
205
|
+
args.push('--json');
|
|
206
|
+
return executeEventCliJson(args);
|
|
207
|
+
}
|
|
208
|
+
async updateEvent(data) {
|
|
209
|
+
const args = ['calendar', 'update', '--id', data.id];
|
|
210
|
+
addOptionalArg(args, '--title', data.title);
|
|
211
|
+
addOptionalArg(args, '--start', data.startDate);
|
|
212
|
+
addOptionalArg(args, '--end', data.endDate);
|
|
213
|
+
addOptionalArg(args, '--location', data.location);
|
|
214
|
+
addOptionalArg(args, '--notes', data.notes);
|
|
215
|
+
args.push('--json');
|
|
216
|
+
return executeEventCliJson(args);
|
|
217
|
+
}
|
|
218
|
+
async deleteEvent(id, span) {
|
|
219
|
+
const args = ['calendar', 'delete', '--id', id];
|
|
220
|
+
addOptionalArg(args, '--span', mapSpanForEvent(span));
|
|
221
|
+
await executeEventCliPlain(args);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
export const calendarRepository = new CalendarRepository();
|
|
225
|
+
export { CalendarRepository };
|
|
226
|
+
//# sourceMappingURL=calendarRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendarRepository.js","sourceRoot":"","sources":["../../src/utils/calendarRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* constants.ts
|
|
3
|
+
* Centralized constants and configuration values to eliminate magic numbers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* File system and path constants
|
|
7
|
+
*/
|
|
8
|
+
export declare const FILE_SYSTEM: {
|
|
9
|
+
/** Maximum directory traversal depth when searching for project root */
|
|
10
|
+
readonly MAX_DIRECTORY_SEARCH_DEPTH: 10;
|
|
11
|
+
/** Package.json filename for project root detection */
|
|
12
|
+
readonly PACKAGE_JSON_FILENAME: "package.json";
|
|
13
|
+
/** Swift binary filename — points at the vendored `event` CLI (FradSer/event). */
|
|
14
|
+
readonly SWIFT_BINARY_NAME: "event";
|
|
15
|
+
/**
|
|
16
|
+
* TCC disclaim shim filename — spawns `event` as its own TCC-responsible
|
|
17
|
+
* process so EventKit permission prompts work from desktop MCP clients
|
|
18
|
+
* that lack usage-description strings (issue #93).
|
|
19
|
+
*/
|
|
20
|
+
readonly DISCLAIM_BINARY_NAME: "event-disclaim";
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Validation and security constants
|
|
24
|
+
*/
|
|
25
|
+
export declare const VALIDATION: {
|
|
26
|
+
/** Maximum lengths for different text fields */
|
|
27
|
+
readonly MAX_TITLE_LENGTH: 200;
|
|
28
|
+
readonly MAX_NOTE_LENGTH: 20000;
|
|
29
|
+
readonly MAX_LIST_NAME_LENGTH: 100;
|
|
30
|
+
readonly MAX_SEARCH_LENGTH: 100;
|
|
31
|
+
readonly MAX_URL_LENGTH: 500;
|
|
32
|
+
readonly MAX_LOCATION_LENGTH: 200;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Tool names for MCP server operations
|
|
36
|
+
*/
|
|
37
|
+
export declare const TOOLS: {
|
|
38
|
+
/** Reminder tasks management tool */
|
|
39
|
+
readonly REMINDERS_TASKS: "reminders_tasks";
|
|
40
|
+
/** Reminder lists management tool */
|
|
41
|
+
readonly REMINDERS_LISTS: "reminders_lists";
|
|
42
|
+
/** Reminder subtasks management tool */
|
|
43
|
+
readonly REMINDERS_SUBTASKS: "reminders_subtasks";
|
|
44
|
+
/** Calendar events management tool */
|
|
45
|
+
readonly CALENDAR_EVENTS: "calendar_events";
|
|
46
|
+
/** Calendar collections management tool */
|
|
47
|
+
readonly CALENDAR_CALENDARS: "calendar_calendars";
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Time and date constants for consistent time-based logic
|
|
51
|
+
*/
|
|
52
|
+
export declare const TIME: {
|
|
53
|
+
/** Working hours boundaries */
|
|
54
|
+
readonly WORKING_HOURS_START: 9;
|
|
55
|
+
readonly WORKING_HOURS_END: 18;
|
|
56
|
+
/** Time of day boundaries for categorization */
|
|
57
|
+
readonly MORNING_START: 5;
|
|
58
|
+
readonly NOON: 12;
|
|
59
|
+
readonly AFTERNOON_END: 17;
|
|
60
|
+
readonly EVENING_START: 17;
|
|
61
|
+
readonly NIGHT_START: 21;
|
|
62
|
+
/** Default time suggestions */
|
|
63
|
+
readonly LATER_TODAY_HOURS: 4;
|
|
64
|
+
readonly END_OF_WEEK_HOUR: 17;
|
|
65
|
+
readonly DEFAULT_MORNING_HOUR: 9;
|
|
66
|
+
/** Day of week constants (0 = Sunday, 6 = Saturday) */
|
|
67
|
+
readonly SUNDAY: 0;
|
|
68
|
+
readonly FRIDAY: 5;
|
|
69
|
+
readonly SATURDAY: 6;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Error message templates
|
|
73
|
+
*/
|
|
74
|
+
export declare const MESSAGES: {
|
|
75
|
+
readonly ERROR: {
|
|
76
|
+
readonly UNKNOWN_TOOL: (name: string) => string;
|
|
77
|
+
readonly UNKNOWN_ACTION: (tool: string, action: string) => string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* constants.ts
|
|
3
|
+
* Centralized constants and configuration values to eliminate magic numbers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* File system and path constants
|
|
7
|
+
*/
|
|
8
|
+
export const FILE_SYSTEM = {
|
|
9
|
+
/** Maximum directory traversal depth when searching for project root */
|
|
10
|
+
MAX_DIRECTORY_SEARCH_DEPTH: 10,
|
|
11
|
+
/** Package.json filename for project root detection */
|
|
12
|
+
PACKAGE_JSON_FILENAME: 'package.json',
|
|
13
|
+
/** Swift binary filename — points at the vendored `event` CLI (FradSer/event). */
|
|
14
|
+
SWIFT_BINARY_NAME: 'event',
|
|
15
|
+
/**
|
|
16
|
+
* TCC disclaim shim filename — spawns `event` as its own TCC-responsible
|
|
17
|
+
* process so EventKit permission prompts work from desktop MCP clients
|
|
18
|
+
* that lack usage-description strings (issue #93).
|
|
19
|
+
*/
|
|
20
|
+
DISCLAIM_BINARY_NAME: 'event-disclaim',
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Validation and security constants
|
|
24
|
+
*/
|
|
25
|
+
export const VALIDATION = {
|
|
26
|
+
/** Maximum lengths for different text fields */
|
|
27
|
+
MAX_TITLE_LENGTH: 200,
|
|
28
|
+
MAX_NOTE_LENGTH: 20000,
|
|
29
|
+
MAX_LIST_NAME_LENGTH: 100,
|
|
30
|
+
MAX_SEARCH_LENGTH: 100,
|
|
31
|
+
MAX_URL_LENGTH: 500,
|
|
32
|
+
MAX_LOCATION_LENGTH: 200,
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Tool names for MCP server operations
|
|
36
|
+
*/
|
|
37
|
+
export const TOOLS = {
|
|
38
|
+
/** Reminder tasks management tool */
|
|
39
|
+
REMINDERS_TASKS: 'reminders_tasks',
|
|
40
|
+
/** Reminder lists management tool */
|
|
41
|
+
REMINDERS_LISTS: 'reminders_lists',
|
|
42
|
+
/** Reminder subtasks management tool */
|
|
43
|
+
REMINDERS_SUBTASKS: 'reminders_subtasks',
|
|
44
|
+
/** Calendar events management tool */
|
|
45
|
+
CALENDAR_EVENTS: 'calendar_events',
|
|
46
|
+
/** Calendar collections management tool */
|
|
47
|
+
CALENDAR_CALENDARS: 'calendar_calendars',
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Time and date constants for consistent time-based logic
|
|
51
|
+
*/
|
|
52
|
+
export const TIME = {
|
|
53
|
+
/** Working hours boundaries */
|
|
54
|
+
WORKING_HOURS_START: 9,
|
|
55
|
+
WORKING_HOURS_END: 18,
|
|
56
|
+
/** Time of day boundaries for categorization */
|
|
57
|
+
MORNING_START: 5,
|
|
58
|
+
NOON: 12,
|
|
59
|
+
AFTERNOON_END: 17,
|
|
60
|
+
EVENING_START: 17,
|
|
61
|
+
NIGHT_START: 21,
|
|
62
|
+
/** Default time suggestions */
|
|
63
|
+
LATER_TODAY_HOURS: 4,
|
|
64
|
+
END_OF_WEEK_HOUR: 17,
|
|
65
|
+
DEFAULT_MORNING_HOUR: 9,
|
|
66
|
+
/** Day of week constants (0 = Sunday, 6 = Saturday) */
|
|
67
|
+
SUNDAY: 0,
|
|
68
|
+
FRIDAY: 5,
|
|
69
|
+
SATURDAY: 6,
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Error message templates
|
|
73
|
+
*/
|
|
74
|
+
export const MESSAGES = {
|
|
75
|
+
ERROR: {
|
|
76
|
+
UNKNOWN_TOOL: (name) => `Unknown tool: ${name}`,
|
|
77
|
+
UNKNOWN_ACTION: (tool, action) => `Unknown ${tool} action: ${action}`,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,wEAAwE;IACxE,0BAA0B,EAAE,EAAE;IAE9B,uDAAuD;IACvD,qBAAqB,EAAE,cAAc;IAErC,kFAAkF;IAClF,iBAAiB,EAAE,OAAO;IAE1B;;;;OAIG;IACH,oBAAoB,EAAE,gBAAgB;CAC9B,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,gDAAgD;IAChD,gBAAgB,EAAE,GAAG;IACrB,eAAe,EAAE,KAAK;IACtB,oBAAoB,EAAE,GAAG;IACzB,iBAAiB,EAAE,GAAG;IACtB,cAAc,EAAE,GAAG;IACnB,mBAAmB,EAAE,GAAG;CAChB,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,qCAAqC;IACrC,eAAe,EAAE,iBAAiB;IAClC,qCAAqC;IACrC,eAAe,EAAE,iBAAiB;IAClC,wCAAwC;IACxC,kBAAkB,EAAE,oBAAoB;IACxC,sCAAsC;IACtC,eAAe,EAAE,iBAAiB;IAClC,2CAA2C;IAC3C,kBAAkB,EAAE,oBAAoB;CAChC,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,+BAA+B;IAC/B,mBAAmB,EAAE,CAAC;IACtB,iBAAiB,EAAE,EAAE;IAErB,gDAAgD;IAChD,aAAa,EAAE,CAAC;IAChB,IAAI,EAAE,EAAE;IACR,aAAa,EAAE,EAAE;IACjB,aAAa,EAAE,EAAE;IACjB,WAAW,EAAE,EAAE;IAEf,+BAA+B;IAC/B,iBAAiB,EAAE,CAAC;IACpB,gBAAgB,EAAE,EAAE;IACpB,oBAAoB,EAAE,CAAC;IAEvB,uDAAuD;IACvD,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,QAAQ,EAAE,CAAC;CACH,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,KAAK,EAAE;QACL,YAAY,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,iBAAiB,IAAI,EAAE;QACvD,cAAc,EAAE,CAAC,IAAY,EAAE,MAAc,EAAE,EAAE,CAC/C,WAAW,IAAI,YAAY,MAAM,EAAE;KACtC;CACO,CAAC"}
|