@shin1ohno/sage 0.7.9 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -8
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +30 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/validation.d.ts +130 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +53 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/index.js +677 -214
- package/dist/index.js.map +1 -1
- package/dist/integrations/calendar-service.d.ts +6 -3
- package/dist/integrations/calendar-service.d.ts.map +1 -1
- package/dist/integrations/calendar-service.js +26 -4
- package/dist/integrations/calendar-service.js.map +1 -1
- package/dist/integrations/calendar-source-manager.d.ts +302 -0
- package/dist/integrations/calendar-source-manager.d.ts.map +1 -0
- package/dist/integrations/calendar-source-manager.js +862 -0
- package/dist/integrations/calendar-source-manager.js.map +1 -0
- package/dist/integrations/google-calendar-service.d.ts +176 -0
- package/dist/integrations/google-calendar-service.d.ts.map +1 -0
- package/dist/integrations/google-calendar-service.js +745 -0
- package/dist/integrations/google-calendar-service.js.map +1 -0
- package/dist/oauth/google-oauth-handler.d.ts +149 -0
- package/dist/oauth/google-oauth-handler.d.ts.map +1 -0
- package/dist/oauth/google-oauth-handler.js +365 -0
- package/dist/oauth/google-oauth-handler.js.map +1 -0
- package/dist/types/config.d.ts +15 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +21 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/google-calendar-types.d.ts +139 -0
- package/dist/types/google-calendar-types.d.ts.map +1 -0
- package/dist/types/google-calendar-types.js +46 -0
- package/dist/types/google-calendar-types.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +22 -4
- package/dist/version.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calendar Source Manager
|
|
3
|
+
* Requirements: 9, 11
|
|
4
|
+
* Design: .claude/specs/google-calendar-api/design.md (CalendarSourceManager section)
|
|
5
|
+
*
|
|
6
|
+
* Manages multiple calendar sources (EventKit, Google Calendar) with automatic
|
|
7
|
+
* source selection, fallback handling, and unified MCP tool interface.
|
|
8
|
+
*/
|
|
9
|
+
import { CalendarService } from './calendar-service.js';
|
|
10
|
+
import type { CalendarEvent, AvailableSlot } from './calendar-service.js';
|
|
11
|
+
import { GoogleCalendarService } from './google-calendar-service.js';
|
|
12
|
+
import type { CreateEventRequest } from './google-calendar-service.js';
|
|
13
|
+
import type { UserConfig } from '../types/config.js';
|
|
14
|
+
import type { SyncResult, SyncStatus } from '../types/google-calendar-types.js';
|
|
15
|
+
/**
|
|
16
|
+
* Request interface for finding available slots
|
|
17
|
+
* Requirement: 7
|
|
18
|
+
*/
|
|
19
|
+
export interface FindSlotsRequest {
|
|
20
|
+
startDate: string;
|
|
21
|
+
endDate: string;
|
|
22
|
+
minDurationMinutes?: number;
|
|
23
|
+
maxDurationMinutes?: number;
|
|
24
|
+
workingHours?: {
|
|
25
|
+
start: string;
|
|
26
|
+
end: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Options for CalendarSourceManager constructor
|
|
31
|
+
*/
|
|
32
|
+
export interface CalendarSourceManagerOptions {
|
|
33
|
+
calendarService?: CalendarService;
|
|
34
|
+
googleCalendarService?: GoogleCalendarService;
|
|
35
|
+
config?: UserConfig;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Calendar Source Manager
|
|
39
|
+
*
|
|
40
|
+
* Manages multiple calendar sources (EventKit, Google Calendar) with:
|
|
41
|
+
* - Automatic source detection and selection
|
|
42
|
+
* - Unified event operations across sources
|
|
43
|
+
* - Fallback handling for source failures
|
|
44
|
+
* - Event deduplication across sources
|
|
45
|
+
*/
|
|
46
|
+
export declare class CalendarSourceManager {
|
|
47
|
+
private calendarService?;
|
|
48
|
+
private googleCalendarService?;
|
|
49
|
+
private config?;
|
|
50
|
+
/**
|
|
51
|
+
* Constructor
|
|
52
|
+
*
|
|
53
|
+
* @param options - Optional services and config
|
|
54
|
+
*/
|
|
55
|
+
constructor(options?: CalendarSourceManagerOptions);
|
|
56
|
+
/**
|
|
57
|
+
* Detect available calendar sources
|
|
58
|
+
* Requirement: 9 (Platform detection and source availability)
|
|
59
|
+
*
|
|
60
|
+
* Uses detectPlatform() pattern from CalendarService to determine which
|
|
61
|
+
* calendar sources are available on the current platform.
|
|
62
|
+
*
|
|
63
|
+
* @returns Object indicating which sources are available
|
|
64
|
+
*/
|
|
65
|
+
detectAvailableSources(): Promise<{
|
|
66
|
+
eventkit: boolean;
|
|
67
|
+
google: boolean;
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Enable a calendar source
|
|
71
|
+
* Requirement: 11 (Calendar source management)
|
|
72
|
+
*
|
|
73
|
+
* Updates config to enable the specified source and validates that
|
|
74
|
+
* at least one source remains enabled.
|
|
75
|
+
*
|
|
76
|
+
* @param source - Calendar source to enable ('eventkit' | 'google')
|
|
77
|
+
* @throws Error if config is not available
|
|
78
|
+
*/
|
|
79
|
+
enableSource(source: 'eventkit' | 'google'): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Disable a calendar source
|
|
82
|
+
* Requirement: 11 (Calendar source management)
|
|
83
|
+
*
|
|
84
|
+
* Updates config to disable the specified source and validates that
|
|
85
|
+
* at least one source remains enabled.
|
|
86
|
+
*
|
|
87
|
+
* @param source - Calendar source to disable ('eventkit' | 'google')
|
|
88
|
+
* @throws Error if disabling would leave no sources enabled
|
|
89
|
+
*/
|
|
90
|
+
disableSource(source: 'eventkit' | 'google'): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Get enabled calendar sources
|
|
93
|
+
* Requirement: 9 (Source configuration reading)
|
|
94
|
+
*
|
|
95
|
+
* Reads from config.calendar.sources to determine which sources are enabled.
|
|
96
|
+
*
|
|
97
|
+
* @returns Array of enabled source names
|
|
98
|
+
*/
|
|
99
|
+
getEnabledSources(): ('eventkit' | 'google')[];
|
|
100
|
+
/**
|
|
101
|
+
* Get events from enabled calendar sources
|
|
102
|
+
* Requirement: 7, 10, 11 (Multi-source event retrieval with deduplication and fallback)
|
|
103
|
+
*
|
|
104
|
+
* Fetches events from all enabled sources with fallback handling and deduplication.
|
|
105
|
+
* - Task 17a: Basic parallel fetching
|
|
106
|
+
* - Task 17b: Deduplication and fallback logic
|
|
107
|
+
*
|
|
108
|
+
* Fallback behavior: If one source fails, continues with other sources.
|
|
109
|
+
* Only throws if ALL sources fail.
|
|
110
|
+
*
|
|
111
|
+
* @param startDate - Start date (ISO 8601)
|
|
112
|
+
* @param endDate - End date (ISO 8601)
|
|
113
|
+
* @param calendarId - Optional calendar ID filter
|
|
114
|
+
* @returns Array of deduplicated calendar events from all enabled sources
|
|
115
|
+
*/
|
|
116
|
+
getEvents(startDate: string, endDate: string, calendarId?: string): Promise<CalendarEvent[]>;
|
|
117
|
+
/**
|
|
118
|
+
* Check if two events are duplicates
|
|
119
|
+
* Requirement: 10 (Event deduplication)
|
|
120
|
+
*
|
|
121
|
+
* Uses two methods to detect duplicates:
|
|
122
|
+
* 1. iCalUID comparison (most reliable, RFC 5545 standard)
|
|
123
|
+
* 2. Title + time matching (fallback for events without iCalUID)
|
|
124
|
+
*
|
|
125
|
+
* Note: iCalUID support will be added in Task 24. Until then, this uses
|
|
126
|
+
* type assertions to access the optional iCalUID property.
|
|
127
|
+
*
|
|
128
|
+
* @param event1 - First event
|
|
129
|
+
* @param event2 - Second event
|
|
130
|
+
* @returns True if events are duplicates
|
|
131
|
+
*/
|
|
132
|
+
private areEventsDuplicate;
|
|
133
|
+
/**
|
|
134
|
+
* Deduplicate events array
|
|
135
|
+
* Requirement: 10 (Event deduplication)
|
|
136
|
+
*
|
|
137
|
+
* Removes duplicate events keeping the first occurrence.
|
|
138
|
+
* Uses areEventsDuplicate() for comparison.
|
|
139
|
+
*
|
|
140
|
+
* @param events - Array of events to deduplicate
|
|
141
|
+
* @returns Array of unique events
|
|
142
|
+
*/
|
|
143
|
+
private deduplicateEvents;
|
|
144
|
+
/**
|
|
145
|
+
* Create event in preferred calendar source
|
|
146
|
+
* Requirement: 3, 10, 11 (Multi-source event creation with routing and fallback)
|
|
147
|
+
*
|
|
148
|
+
* Creates an event in the specified source with fallback handling:
|
|
149
|
+
* 1. If preferredSource is specified, try that source first
|
|
150
|
+
* 2. If preferred source fails or not specified, try other enabled sources
|
|
151
|
+
* 3. Throws error if all sources fail
|
|
152
|
+
*
|
|
153
|
+
* Note: EventKit does not support event creation in current implementation,
|
|
154
|
+
* so only Google Calendar is supported for event creation.
|
|
155
|
+
*
|
|
156
|
+
* @param request - Event creation request
|
|
157
|
+
* @param preferredSource - Preferred source ('eventkit' | 'google')
|
|
158
|
+
* @returns Created calendar event
|
|
159
|
+
* @throws Error if all sources fail or no sources are enabled
|
|
160
|
+
*/
|
|
161
|
+
createEvent(request: CreateEventRequest, preferredSource?: 'eventkit' | 'google'): Promise<CalendarEvent>;
|
|
162
|
+
/**
|
|
163
|
+
* Delete event from calendar source
|
|
164
|
+
* Requirement: 5, 10, 11 (Multi-source event deletion with routing and error handling)
|
|
165
|
+
*
|
|
166
|
+
* Deletes an event from the specified source, or attempts deletion
|
|
167
|
+
* from all sources if source is not specified.
|
|
168
|
+
*
|
|
169
|
+
* If source specified:
|
|
170
|
+
* - Deletes from that source only
|
|
171
|
+
* - Throws error if source is not enabled or service not available
|
|
172
|
+
*
|
|
173
|
+
* If source not specified:
|
|
174
|
+
* - Attempts to delete from ALL enabled sources
|
|
175
|
+
* - Event may exist in both EventKit and Google Calendar (duplicate)
|
|
176
|
+
* - Uses Promise.allSettled() to try all sources
|
|
177
|
+
* - 404 errors (event not found) do not cause failure
|
|
178
|
+
* - Only throws if ALL attempts fail with non-404 errors
|
|
179
|
+
*
|
|
180
|
+
* @param eventId - Event ID to delete
|
|
181
|
+
* @param source - Optional source ('eventkit' | 'google')
|
|
182
|
+
* @throws Error if source specified and not available, or all sources fail with non-404 errors
|
|
183
|
+
*/
|
|
184
|
+
deleteEvent(eventId: string, source?: 'eventkit' | 'google'): Promise<void>;
|
|
185
|
+
/**
|
|
186
|
+
* Find available time slots considering all enabled sources
|
|
187
|
+
* Requirement: 7 (Multi-source slot detection)
|
|
188
|
+
* Task 20a: Basic filtering implementation
|
|
189
|
+
* Task 20b: Suitability calculation integration
|
|
190
|
+
*
|
|
191
|
+
* Fetches events from all enabled sources, merges and deduplicates,
|
|
192
|
+
* then calculates available slots based on working hours and preferences.
|
|
193
|
+
* Applies suitability scoring and sorts by suitability.
|
|
194
|
+
*
|
|
195
|
+
* @param request - Slot search request
|
|
196
|
+
* @returns Array of available time slots sorted by suitability
|
|
197
|
+
*/
|
|
198
|
+
findAvailableSlots(request: FindSlotsRequest): Promise<AvailableSlot[]>;
|
|
199
|
+
/**
|
|
200
|
+
* Get default working hours
|
|
201
|
+
* Requirement: 7
|
|
202
|
+
*
|
|
203
|
+
* Returns working hours from config or default (09:00 - 18:00)
|
|
204
|
+
*
|
|
205
|
+
* @returns Working hours configuration
|
|
206
|
+
*/
|
|
207
|
+
private getDefaultWorkingHours;
|
|
208
|
+
/**
|
|
209
|
+
* Calculate suitability for all slots
|
|
210
|
+
* Requirement: 7 (Task 20b)
|
|
211
|
+
*
|
|
212
|
+
* Applies suitability scoring to slots based on:
|
|
213
|
+
* - Day type (deep work vs meeting heavy) from config
|
|
214
|
+
* - Time of day (morning slots preferred)
|
|
215
|
+
* - Slot duration (longer slots better for deep work)
|
|
216
|
+
*
|
|
217
|
+
* @param slots - Array of slots to score
|
|
218
|
+
* @returns Array of slots with suitability applied
|
|
219
|
+
*/
|
|
220
|
+
private calculateSuitabilityForSlots;
|
|
221
|
+
/**
|
|
222
|
+
* Find available slots for a single day
|
|
223
|
+
* Requirement: 7
|
|
224
|
+
*
|
|
225
|
+
* Calculates gaps between events within working hours for a specific day.
|
|
226
|
+
*
|
|
227
|
+
* @param date - Date to find slots for
|
|
228
|
+
* @param events - All events (sorted by start time)
|
|
229
|
+
* @param workingHours - Working hours configuration
|
|
230
|
+
* @param minDuration - Minimum slot duration in minutes
|
|
231
|
+
* @param maxDuration - Maximum slot duration in minutes
|
|
232
|
+
* @returns Array of available slots for this day
|
|
233
|
+
*/
|
|
234
|
+
private findDaySlots;
|
|
235
|
+
/**
|
|
236
|
+
* Sync calendars between sources
|
|
237
|
+
* Requirement: 8 (Calendar synchronization)
|
|
238
|
+
*
|
|
239
|
+
* Synchronizes events between EventKit and Google Calendar when both
|
|
240
|
+
* sources are enabled. Handles conflicts and provides sync results.
|
|
241
|
+
*
|
|
242
|
+
* Note: This is a stub implementation. Full sync logic can be added later.
|
|
243
|
+
* Currently, it validates that both sources are enabled and returns empty results.
|
|
244
|
+
*
|
|
245
|
+
* @returns Sync result with statistics and errors
|
|
246
|
+
* @throws Error if both sources are not enabled
|
|
247
|
+
*/
|
|
248
|
+
syncCalendars(): Promise<SyncResult>;
|
|
249
|
+
/**
|
|
250
|
+
* Get sync status between calendar sources
|
|
251
|
+
* Requirement: 8 (Calendar synchronization)
|
|
252
|
+
*
|
|
253
|
+
* Returns the current sync status including last sync time,
|
|
254
|
+
* next scheduled sync, and source availability.
|
|
255
|
+
*
|
|
256
|
+
* Note: This is a stub implementation. lastSyncTime tracking will be added
|
|
257
|
+
* when full sync implementation is completed.
|
|
258
|
+
*
|
|
259
|
+
* @returns Sync status information
|
|
260
|
+
*/
|
|
261
|
+
getSyncStatus(): Promise<SyncStatus>;
|
|
262
|
+
/**
|
|
263
|
+
* Health check for calendar sources
|
|
264
|
+
* Requirement: 10, 11 (Health check for both sources)
|
|
265
|
+
* Task 22: Implementation
|
|
266
|
+
*
|
|
267
|
+
* Checks availability and health of all calendar sources by calling
|
|
268
|
+
* their respective isAvailable() methods. Returns a status object
|
|
269
|
+
* indicating which sources are currently healthy and available.
|
|
270
|
+
*
|
|
271
|
+
* This method never throws errors - it catches all failures and returns
|
|
272
|
+
* false for sources that are unavailable or unhealthy.
|
|
273
|
+
*
|
|
274
|
+
* @returns Object indicating health status of each source
|
|
275
|
+
*/
|
|
276
|
+
healthCheck(): Promise<{
|
|
277
|
+
eventkit: boolean;
|
|
278
|
+
google: boolean;
|
|
279
|
+
}>;
|
|
280
|
+
/**
|
|
281
|
+
* Respond to a calendar event
|
|
282
|
+
* Requirement: 6 (Event response/RSVP)
|
|
283
|
+
*
|
|
284
|
+
* Routes event response to the appropriate calendar source.
|
|
285
|
+
* Supports EventKit (via CalendarEventResponseService) and Google Calendar.
|
|
286
|
+
* If source is not specified, attempts to determine source by fetching
|
|
287
|
+
* the event from all enabled sources.
|
|
288
|
+
*
|
|
289
|
+
* @param eventId - Event ID
|
|
290
|
+
* @param response - Response type: 'accept', 'decline', or 'tentative'
|
|
291
|
+
* @param source - Optional source routing ('eventkit' or 'google')
|
|
292
|
+
* @param calendarId - Optional calendar ID (for Google Calendar)
|
|
293
|
+
* @returns Success status and message
|
|
294
|
+
* @throws Error if event not found or response fails
|
|
295
|
+
*/
|
|
296
|
+
respondToEvent(eventId: string, response: 'accept' | 'decline' | 'tentative', source?: 'eventkit' | 'google', calendarId?: string): Promise<{
|
|
297
|
+
success: boolean;
|
|
298
|
+
message: string;
|
|
299
|
+
source?: string;
|
|
300
|
+
}>;
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=calendar-source-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-source-manager.d.ts","sourceRoot":"","sources":["../../src/integrations/calendar-source-manager.ts"],"names":[],"mappings":"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"}
|