@hmawla/co-assistant 1.0.6 → 1.0.7
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/index.js +28 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +22 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/plugins/gmail/index.compiled.mjs +14071 -0
- package/plugins/google-calendar/index.compiled.mjs +361 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// plugins/google-calendar/auth.ts
|
|
2
|
+
var TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
|
|
3
|
+
var EXPIRY_MARGIN_MS = 6e4;
|
|
4
|
+
var CalendarAuth = class {
|
|
5
|
+
clientId;
|
|
6
|
+
clientSecret;
|
|
7
|
+
refreshToken;
|
|
8
|
+
accessToken = null;
|
|
9
|
+
expiresAt = 0;
|
|
10
|
+
constructor(clientId, clientSecret, refreshToken) {
|
|
11
|
+
this.clientId = clientId;
|
|
12
|
+
this.clientSecret = clientSecret;
|
|
13
|
+
this.refreshToken = refreshToken;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check whether all required credentials have been provided.
|
|
17
|
+
*/
|
|
18
|
+
isConfigured() {
|
|
19
|
+
return Boolean(this.clientId && this.clientSecret && this.refreshToken);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Return a valid access token, refreshing it first if necessary.
|
|
23
|
+
*
|
|
24
|
+
* @throws {Error} If credentials are missing or the token exchange fails.
|
|
25
|
+
*/
|
|
26
|
+
async getAccessToken() {
|
|
27
|
+
if (!this.isConfigured()) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"CalendarAuth is not configured \u2014 client ID, client secret, and refresh token are all required."
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (this.accessToken && Date.now() < this.expiresAt) {
|
|
33
|
+
return this.accessToken;
|
|
34
|
+
}
|
|
35
|
+
const body = new URLSearchParams({
|
|
36
|
+
client_id: this.clientId,
|
|
37
|
+
client_secret: this.clientSecret,
|
|
38
|
+
refresh_token: this.refreshToken,
|
|
39
|
+
grant_type: "refresh_token"
|
|
40
|
+
});
|
|
41
|
+
const response = await fetch(TOKEN_ENDPOINT, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
44
|
+
body: body.toString()
|
|
45
|
+
});
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
const text = await response.text();
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Failed to refresh Google access token (${response.status}): ${text}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const data = await response.json();
|
|
53
|
+
this.accessToken = data.access_token;
|
|
54
|
+
this.expiresAt = Date.now() + data.expires_in * 1e3 - EXPIRY_MARGIN_MS;
|
|
55
|
+
return this.accessToken;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// plugins/google-calendar/tools.ts
|
|
60
|
+
var CALENDAR_API = "https://www.googleapis.com/calendar/v3";
|
|
61
|
+
async function calendarFetch(auth, path, options = {}) {
|
|
62
|
+
const token = await auth.getAccessToken();
|
|
63
|
+
const res = await fetch(`${CALENDAR_API}${path}`, {
|
|
64
|
+
...options,
|
|
65
|
+
headers: {
|
|
66
|
+
Authorization: `Bearer ${token}`,
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
...options.headers
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
const text = await res.text();
|
|
73
|
+
throw new Error(`Google Calendar API error (${res.status}): ${text}`);
|
|
74
|
+
}
|
|
75
|
+
if (res.status === 204) return { success: true };
|
|
76
|
+
return res.json();
|
|
77
|
+
}
|
|
78
|
+
function formatEventTime(dt) {
|
|
79
|
+
if (dt.dateTime) {
|
|
80
|
+
return new Date(dt.dateTime).toLocaleString("en-US", {
|
|
81
|
+
weekday: "short",
|
|
82
|
+
month: "short",
|
|
83
|
+
day: "numeric",
|
|
84
|
+
year: "numeric",
|
|
85
|
+
hour: "numeric",
|
|
86
|
+
minute: "2-digit",
|
|
87
|
+
timeZoneName: "short"
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (dt.date) {
|
|
91
|
+
return (/* @__PURE__ */ new Date(dt.date + "T00:00:00")).toLocaleDateString("en-US", {
|
|
92
|
+
weekday: "short",
|
|
93
|
+
month: "short",
|
|
94
|
+
day: "numeric",
|
|
95
|
+
year: "numeric"
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return "N/A";
|
|
99
|
+
}
|
|
100
|
+
function formatEvent(event) {
|
|
101
|
+
const lines = [];
|
|
102
|
+
lines.push(`\u{1F4C5} ${event.summary ?? "(no title)"}`);
|
|
103
|
+
if (event.start) lines.push(` Start: ${formatEventTime(event.start)}`);
|
|
104
|
+
if (event.end) lines.push(` End: ${formatEventTime(event.end)}`);
|
|
105
|
+
if (event.location) lines.push(` \u{1F4CD} ${event.location}`);
|
|
106
|
+
if (event.description) lines.push(` ${event.description}`);
|
|
107
|
+
if (event.attendees?.length) {
|
|
108
|
+
const emails = event.attendees.map((a) => a.email).join(", ");
|
|
109
|
+
lines.push(` \u{1F465} ${emails}`);
|
|
110
|
+
}
|
|
111
|
+
lines.push(` ID: ${event.id}`);
|
|
112
|
+
return lines.join("\n");
|
|
113
|
+
}
|
|
114
|
+
function createCalendarTools(auth) {
|
|
115
|
+
const listEvents = {
|
|
116
|
+
name: "list_events",
|
|
117
|
+
description: "List upcoming calendar events. Optionally filter by date range.",
|
|
118
|
+
parameters: {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
maxResults: {
|
|
122
|
+
type: "number",
|
|
123
|
+
description: "Maximum number of events to return (default 10)."
|
|
124
|
+
},
|
|
125
|
+
timeMin: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "Start of the time range as an ISO 8601 date-time string. Defaults to now."
|
|
128
|
+
},
|
|
129
|
+
timeMax: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: "End of the time range as an ISO 8601 date-time string."
|
|
132
|
+
},
|
|
133
|
+
calendarId: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: 'Calendar ID to query (default "primary").'
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
handler: async (args) => {
|
|
140
|
+
try {
|
|
141
|
+
const calendarId = encodeURIComponent(
|
|
142
|
+
args.calendarId ?? "primary"
|
|
143
|
+
);
|
|
144
|
+
const params = new URLSearchParams({
|
|
145
|
+
maxResults: String(args.maxResults ?? 10),
|
|
146
|
+
timeMin: args.timeMin ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
147
|
+
orderBy: "startTime",
|
|
148
|
+
singleEvents: "true"
|
|
149
|
+
});
|
|
150
|
+
if (args.timeMax) params.set("timeMax", args.timeMax);
|
|
151
|
+
const data = await calendarFetch(
|
|
152
|
+
auth,
|
|
153
|
+
`/calendars/${calendarId}/events?${params.toString()}`
|
|
154
|
+
);
|
|
155
|
+
const events = data.items ?? [];
|
|
156
|
+
if (events.length === 0) {
|
|
157
|
+
return "No upcoming events found.";
|
|
158
|
+
}
|
|
159
|
+
return events.map(formatEvent).join("\n\n");
|
|
160
|
+
} catch (error) {
|
|
161
|
+
return `Error listing events: ${error.message}`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const createEvent = {
|
|
166
|
+
name: "create_event",
|
|
167
|
+
description: "Create a new calendar event.",
|
|
168
|
+
parameters: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
summary: { type: "string", description: "Event title." },
|
|
172
|
+
description: { type: "string", description: "Event description." },
|
|
173
|
+
startTime: {
|
|
174
|
+
type: "string",
|
|
175
|
+
description: "Start date-time in ISO 8601 format."
|
|
176
|
+
},
|
|
177
|
+
endTime: {
|
|
178
|
+
type: "string",
|
|
179
|
+
description: "End date-time in ISO 8601 format."
|
|
180
|
+
},
|
|
181
|
+
location: { type: "string", description: "Event location." },
|
|
182
|
+
attendees: {
|
|
183
|
+
type: "array",
|
|
184
|
+
items: { type: "string" },
|
|
185
|
+
description: "List of attendee email addresses."
|
|
186
|
+
},
|
|
187
|
+
calendarId: {
|
|
188
|
+
type: "string",
|
|
189
|
+
description: 'Calendar ID (default "primary").'
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
required: ["summary", "startTime", "endTime"]
|
|
193
|
+
},
|
|
194
|
+
handler: async (args) => {
|
|
195
|
+
try {
|
|
196
|
+
const calendarId = encodeURIComponent(
|
|
197
|
+
args.calendarId ?? "primary"
|
|
198
|
+
);
|
|
199
|
+
const body = {
|
|
200
|
+
summary: args.summary,
|
|
201
|
+
start: { dateTime: args.startTime },
|
|
202
|
+
end: { dateTime: args.endTime }
|
|
203
|
+
};
|
|
204
|
+
if (args.description) body.description = args.description;
|
|
205
|
+
if (args.location) body.location = args.location;
|
|
206
|
+
if (args.attendees) {
|
|
207
|
+
body.attendees = args.attendees.map((email) => ({
|
|
208
|
+
email
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
const event = await calendarFetch(
|
|
212
|
+
auth,
|
|
213
|
+
`/calendars/${calendarId}/events`,
|
|
214
|
+
{ method: "POST", body: JSON.stringify(body) }
|
|
215
|
+
);
|
|
216
|
+
return `\u2705 Event created successfully!
|
|
217
|
+
|
|
218
|
+
${formatEvent(event)}`;
|
|
219
|
+
} catch (error) {
|
|
220
|
+
return `Error creating event: ${error.message}`;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
const updateEvent = {
|
|
225
|
+
name: "update_event",
|
|
226
|
+
description: "Update an existing calendar event by event ID.",
|
|
227
|
+
parameters: {
|
|
228
|
+
type: "object",
|
|
229
|
+
properties: {
|
|
230
|
+
eventId: { type: "string", description: "The event ID to update." },
|
|
231
|
+
summary: { type: "string", description: "New event title." },
|
|
232
|
+
description: { type: "string", description: "New event description." },
|
|
233
|
+
startTime: {
|
|
234
|
+
type: "string",
|
|
235
|
+
description: "New start date-time in ISO 8601 format."
|
|
236
|
+
},
|
|
237
|
+
endTime: {
|
|
238
|
+
type: "string",
|
|
239
|
+
description: "New end date-time in ISO 8601 format."
|
|
240
|
+
},
|
|
241
|
+
location: { type: "string", description: "New event location." },
|
|
242
|
+
calendarId: {
|
|
243
|
+
type: "string",
|
|
244
|
+
description: 'Calendar ID (default "primary").'
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
required: ["eventId"]
|
|
248
|
+
},
|
|
249
|
+
handler: async (args) => {
|
|
250
|
+
try {
|
|
251
|
+
const calendarId = encodeURIComponent(
|
|
252
|
+
args.calendarId ?? "primary"
|
|
253
|
+
);
|
|
254
|
+
const eventId = encodeURIComponent(args.eventId);
|
|
255
|
+
const body = {};
|
|
256
|
+
if (args.summary !== void 0) body.summary = args.summary;
|
|
257
|
+
if (args.description !== void 0)
|
|
258
|
+
body.description = args.description;
|
|
259
|
+
if (args.startTime !== void 0)
|
|
260
|
+
body.start = { dateTime: args.startTime };
|
|
261
|
+
if (args.endTime !== void 0) body.end = { dateTime: args.endTime };
|
|
262
|
+
if (args.location !== void 0) body.location = args.location;
|
|
263
|
+
const event = await calendarFetch(
|
|
264
|
+
auth,
|
|
265
|
+
`/calendars/${calendarId}/events/${eventId}`,
|
|
266
|
+
{ method: "PATCH", body: JSON.stringify(body) }
|
|
267
|
+
);
|
|
268
|
+
return `\u2705 Event updated successfully!
|
|
269
|
+
|
|
270
|
+
${formatEvent(event)}`;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
return `Error updating event: ${error.message}`;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const deleteEvent = {
|
|
277
|
+
name: "delete_event",
|
|
278
|
+
description: "Delete a calendar event by event ID.",
|
|
279
|
+
parameters: {
|
|
280
|
+
type: "object",
|
|
281
|
+
properties: {
|
|
282
|
+
eventId: { type: "string", description: "The event ID to delete." },
|
|
283
|
+
calendarId: {
|
|
284
|
+
type: "string",
|
|
285
|
+
description: 'Calendar ID (default "primary").'
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
required: ["eventId"]
|
|
289
|
+
},
|
|
290
|
+
handler: async (args) => {
|
|
291
|
+
try {
|
|
292
|
+
const calendarId = encodeURIComponent(
|
|
293
|
+
args.calendarId ?? "primary"
|
|
294
|
+
);
|
|
295
|
+
const eventId = encodeURIComponent(args.eventId);
|
|
296
|
+
await calendarFetch(
|
|
297
|
+
auth,
|
|
298
|
+
`/calendars/${calendarId}/events/${eventId}`,
|
|
299
|
+
{ method: "DELETE" }
|
|
300
|
+
);
|
|
301
|
+
return `\u2705 Event ${args.eventId} deleted successfully.`;
|
|
302
|
+
} catch (error) {
|
|
303
|
+
return `Error deleting event: ${error.message}`;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
return [listEvents, createEvent, updateEvent, deleteEvent];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// plugins/google-calendar/index.ts
|
|
311
|
+
var createPlugin = () => {
|
|
312
|
+
let auth;
|
|
313
|
+
let tools = [];
|
|
314
|
+
let logger;
|
|
315
|
+
return {
|
|
316
|
+
id: "google-calendar",
|
|
317
|
+
name: "Google Calendar Plugin",
|
|
318
|
+
version: "1.0.0",
|
|
319
|
+
description: "View, create, and manage Google Calendar events",
|
|
320
|
+
requiredCredentials: [
|
|
321
|
+
"GCAL_CLIENT_ID",
|
|
322
|
+
"GCAL_CLIENT_SECRET",
|
|
323
|
+
"GCAL_REFRESH_TOKEN"
|
|
324
|
+
],
|
|
325
|
+
async initialize(context) {
|
|
326
|
+
logger = context.logger;
|
|
327
|
+
logger.info("Initialising Google Calendar plugin\u2026");
|
|
328
|
+
auth = new CalendarAuth(
|
|
329
|
+
context.credentials.GCAL_CLIENT_ID,
|
|
330
|
+
context.credentials.GCAL_CLIENT_SECRET,
|
|
331
|
+
context.credentials.GCAL_REFRESH_TOKEN
|
|
332
|
+
);
|
|
333
|
+
if (!auth.isConfigured()) {
|
|
334
|
+
throw new Error(
|
|
335
|
+
"Google Calendar plugin is missing one or more required credentials."
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
tools = createCalendarTools(auth);
|
|
339
|
+
logger.info(`Registered ${tools.length} calendar tools`);
|
|
340
|
+
},
|
|
341
|
+
getTools() {
|
|
342
|
+
return tools;
|
|
343
|
+
},
|
|
344
|
+
async destroy() {
|
|
345
|
+
tools = [];
|
|
346
|
+
logger?.info("Google Calendar plugin destroyed");
|
|
347
|
+
},
|
|
348
|
+
async healthCheck() {
|
|
349
|
+
try {
|
|
350
|
+
await auth.getAccessToken();
|
|
351
|
+
return true;
|
|
352
|
+
} catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
};
|
|
358
|
+
var index_default = createPlugin;
|
|
359
|
+
export {
|
|
360
|
+
index_default as default
|
|
361
|
+
};
|