@plotday/tool-gmail 0.1.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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Plot Technologies Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,122 @@
1
+ import type { NewActivity } from "@plotday/twister";
2
+ export type GmailLabel = {
3
+ id: string;
4
+ name: string;
5
+ type: "system" | "user";
6
+ messageListVisibility?: string;
7
+ labelListVisibility?: string;
8
+ messagesTotal?: number;
9
+ messagesUnread?: number;
10
+ };
11
+ export type GmailThread = {
12
+ id: string;
13
+ historyId: string;
14
+ messages: GmailMessage[];
15
+ };
16
+ export type GmailMessage = {
17
+ id: string;
18
+ threadId: string;
19
+ labelIds: string[];
20
+ snippet: string;
21
+ historyId: string;
22
+ internalDate: string;
23
+ payload: GmailMessagePart;
24
+ sizeEstimate: number;
25
+ };
26
+ export type GmailMessagePart = {
27
+ partId?: string;
28
+ mimeType: string;
29
+ filename?: string;
30
+ headers: GmailHeader[];
31
+ body?: {
32
+ attachmentId?: string;
33
+ size: number;
34
+ data?: string;
35
+ };
36
+ parts?: GmailMessagePart[];
37
+ };
38
+ export type GmailHeader = {
39
+ name: string;
40
+ value: string;
41
+ };
42
+ export type EmailAddress = {
43
+ name: string | null;
44
+ email: string;
45
+ };
46
+ export type SyncState = {
47
+ channelId: string;
48
+ pageToken?: string;
49
+ historyId?: string;
50
+ lastSyncTime?: Date;
51
+ watchExpiration?: Date;
52
+ };
53
+ export declare class GmailApi {
54
+ accessToken: string;
55
+ private baseUrl;
56
+ constructor(accessToken: string);
57
+ call(endpoint: string, options?: {
58
+ method?: string;
59
+ params?: {
60
+ [key: string]: any;
61
+ };
62
+ body?: any;
63
+ }): Promise<any>;
64
+ getLabels(): Promise<GmailLabel[]>;
65
+ getThreads(labelId: string, pageToken?: string, maxResults?: number, query?: string): Promise<{
66
+ threads: Array<{
67
+ id: string;
68
+ historyId: string;
69
+ }>;
70
+ nextPageToken?: string;
71
+ resultSizeEstimate: number;
72
+ }>;
73
+ getThread(threadId: string): Promise<GmailThread>;
74
+ setupWatch(labelId: string, topicName: string): Promise<{
75
+ historyId: string;
76
+ expiration: string;
77
+ }>;
78
+ stopWatch(): Promise<void>;
79
+ getHistory(startHistoryId: string, labelId?: string, pageToken?: string): Promise<{
80
+ history: Array<{
81
+ id: string;
82
+ messages?: GmailMessage[];
83
+ messagesAdded?: Array<{
84
+ message: GmailMessage;
85
+ }>;
86
+ messagesDeleted?: Array<{
87
+ message: {
88
+ id: string;
89
+ };
90
+ }>;
91
+ labelsAdded?: Array<{
92
+ message: GmailMessage;
93
+ }>;
94
+ labelsRemoved?: Array<{
95
+ message: {
96
+ id: string;
97
+ };
98
+ }>;
99
+ }>;
100
+ historyId: string;
101
+ nextPageToken?: string;
102
+ }>;
103
+ }
104
+ /**
105
+ * Parses an email address header into name and email
106
+ * Handles formats like "John Doe <john@example.com>" or "john@example.com"
107
+ */
108
+ export declare function parseEmailAddress(headerValue: string): EmailAddress;
109
+ /**
110
+ * Transforms a Gmail thread into an array of Activities
111
+ * The first message is the parent, subsequent messages are replies
112
+ */
113
+ export declare function transformGmailThread(thread: GmailThread): NewActivity[];
114
+ /**
115
+ * Syncs threads from a Gmail label/query with pagination
116
+ * Returns threads and updated sync state
117
+ */
118
+ export declare function syncGmailChannel(api: GmailApi, state: SyncState, batchSize?: number): Promise<{
119
+ threads: GmailThread[];
120
+ state: SyncState;
121
+ hasMore: boolean;
122
+ }>;
@@ -0,0 +1,319 @@
1
+ import { ActivityLinkType, ActivityType } from "@plotday/twister";
2
+ export class GmailApi {
3
+ constructor(accessToken) {
4
+ this.accessToken = accessToken;
5
+ this.baseUrl = "https://gmail.googleapis.com/gmail/v1/users/me";
6
+ }
7
+ async call(endpoint, options) {
8
+ const method = options?.method || "GET";
9
+ const params = options?.params || {};
10
+ const body = options?.body;
11
+ // Build URL with query parameters
12
+ const url = new URL(`${this.baseUrl}${endpoint}`);
13
+ Object.entries(params).forEach(([key, value]) => {
14
+ if (value !== undefined && value !== null) {
15
+ url.searchParams.append(key, String(value));
16
+ }
17
+ });
18
+ const headers = {
19
+ Authorization: `Bearer ${this.accessToken}`,
20
+ "Content-Type": "application/json",
21
+ };
22
+ const response = await fetch(url.toString(), {
23
+ method,
24
+ headers,
25
+ body: body ? JSON.stringify(body) : undefined,
26
+ });
27
+ if (!response.ok) {
28
+ const errorText = await response.text();
29
+ throw new Error(`Gmail API error: ${response.status} ${response.statusText} - ${errorText}`);
30
+ }
31
+ return await response.json();
32
+ }
33
+ async getLabels() {
34
+ const data = await this.call("/labels");
35
+ return data.labels || [];
36
+ }
37
+ async getThreads(labelId, pageToken, maxResults = 20, query) {
38
+ const params = {
39
+ labelIds: labelId,
40
+ maxResults,
41
+ };
42
+ if (pageToken) {
43
+ params.pageToken = pageToken;
44
+ }
45
+ if (query) {
46
+ params.q = query;
47
+ }
48
+ const data = await this.call("/threads", { params });
49
+ return {
50
+ threads: data.threads || [],
51
+ nextPageToken: data.nextPageToken,
52
+ resultSizeEstimate: data.resultSizeEstimate || 0,
53
+ };
54
+ }
55
+ async getThread(threadId) {
56
+ const data = await this.call(`/threads/${threadId}`, {
57
+ params: { format: "full" },
58
+ });
59
+ return data;
60
+ }
61
+ async setupWatch(labelId, topicName) {
62
+ const data = await this.call("/watch", {
63
+ method: "POST",
64
+ body: {
65
+ labelIds: [labelId],
66
+ topicName,
67
+ },
68
+ });
69
+ return {
70
+ historyId: data.historyId,
71
+ expiration: data.expiration,
72
+ };
73
+ }
74
+ async stopWatch() {
75
+ await this.call("/stop", { method: "POST" });
76
+ }
77
+ async getHistory(startHistoryId, labelId, pageToken) {
78
+ const params = {
79
+ startHistoryId,
80
+ historyTypes: ["messageAdded", "messageDeleted"],
81
+ };
82
+ if (labelId) {
83
+ params.labelId = labelId;
84
+ }
85
+ if (pageToken) {
86
+ params.pageToken = pageToken;
87
+ }
88
+ const data = await this.call("/history", { params });
89
+ return {
90
+ history: data.history || [],
91
+ historyId: data.historyId,
92
+ nextPageToken: data.nextPageToken,
93
+ };
94
+ }
95
+ }
96
+ /**
97
+ * Parses an email address header into name and email
98
+ * Handles formats like "John Doe <john@example.com>" or "john@example.com"
99
+ */
100
+ export function parseEmailAddress(headerValue) {
101
+ const match = headerValue.match(/^(?:"?([^"]*)"?\s)?<?([^@]+@[^>]+)>?$/);
102
+ if (match) {
103
+ return {
104
+ name: match[1]?.trim() || null,
105
+ email: match[2].trim(),
106
+ };
107
+ }
108
+ // Fallback: treat entire string as email
109
+ return {
110
+ name: null,
111
+ email: headerValue.trim(),
112
+ };
113
+ }
114
+ /**
115
+ * Gets a specific header value from a message
116
+ */
117
+ function getHeader(message, name) {
118
+ const header = message.payload.headers.find((h) => h.name.toLowerCase() === name.toLowerCase());
119
+ return header?.value || null;
120
+ }
121
+ /**
122
+ * Extracts the body from a Gmail message (handles multipart messages)
123
+ */
124
+ function extractBody(part) {
125
+ // If this part has a body with data, return it
126
+ if (part.body?.data) {
127
+ // Gmail API returns base64url-encoded data
128
+ const decoded = atob(part.body.data.replace(/-/g, "+").replace(/_/g, "/"));
129
+ return decoded;
130
+ }
131
+ // If multipart, recursively search parts
132
+ if (part.parts) {
133
+ // Prefer plain text over HTML
134
+ const textPart = part.parts.find((p) => p.mimeType === "text/plain");
135
+ if (textPart) {
136
+ return extractBody(textPart);
137
+ }
138
+ const htmlPart = part.parts.find((p) => p.mimeType === "text/html");
139
+ if (htmlPart) {
140
+ // For HTML, strip tags for plain text representation
141
+ const html = extractBody(htmlPart);
142
+ return stripHtmlTags(html);
143
+ }
144
+ // Try first part as fallback
145
+ if (part.parts.length > 0) {
146
+ return extractBody(part.parts[0]);
147
+ }
148
+ }
149
+ return "";
150
+ }
151
+ /**
152
+ * Strips HTML tags for plain text representation
153
+ * This is a simple implementation - could be enhanced with a proper HTML parser
154
+ */
155
+ function stripHtmlTags(html) {
156
+ return html
157
+ .replace(/<style[^>]*>.*?<\/style>/gi, "")
158
+ .replace(/<script[^>]*>.*?<\/script>/gi, "")
159
+ .replace(/<[^>]+>/g, " ")
160
+ .replace(/\s+/g, " ")
161
+ .trim();
162
+ }
163
+ /**
164
+ * Extracts attachment information from a Gmail message
165
+ */
166
+ function extractAttachments(message) {
167
+ const attachments = [];
168
+ function processPart(part) {
169
+ // Check if this part is an attachment
170
+ if (part.filename && part.body?.attachmentId) {
171
+ attachments.push({
172
+ filename: part.filename,
173
+ url: `https://mail.google.com/mail/u/0/#inbox/${message.id}`,
174
+ });
175
+ }
176
+ // Recursively process sub-parts
177
+ if (part.parts) {
178
+ part.parts.forEach(processPart);
179
+ }
180
+ }
181
+ processPart(message.payload);
182
+ return attachments;
183
+ }
184
+ /**
185
+ * Transforms a Gmail thread into an array of Activities
186
+ * The first message is the parent, subsequent messages are replies
187
+ */
188
+ export function transformGmailThread(thread) {
189
+ if (!thread.messages || thread.messages.length === 0)
190
+ return [];
191
+ const activities = [];
192
+ const parentMessage = thread.messages[0];
193
+ // Extract key headers
194
+ const from = getHeader(parentMessage, "From");
195
+ const subject = getHeader(parentMessage, "Subject");
196
+ const to = getHeader(parentMessage, "To");
197
+ const cc = getHeader(parentMessage, "Cc");
198
+ // Parse sender
199
+ const sender = from ? parseEmailAddress(from) : null;
200
+ // Extract body
201
+ const body = extractBody(parentMessage.payload);
202
+ // Create parent activity
203
+ const parentActivity = {
204
+ type: ActivityType.Task,
205
+ title: subject || parentMessage.snippet || "Email",
206
+ note: body || parentMessage.snippet,
207
+ noteType: "text",
208
+ start: new Date(parseInt(parentMessage.internalDate)),
209
+ meta: {
210
+ source: `gmail:${thread.id}:${parentMessage.id}`,
211
+ threadId: thread.id,
212
+ messageId: parentMessage.id,
213
+ from: sender,
214
+ to,
215
+ cc,
216
+ labels: parentMessage.labelIds,
217
+ },
218
+ };
219
+ // Initialize links array
220
+ parentActivity.links = [];
221
+ // Add Gmail URL as action link
222
+ parentActivity.links.push({
223
+ type: ActivityLinkType.external,
224
+ title: "Open in Gmail",
225
+ url: `https://mail.google.com/mail/u/0/#inbox/${thread.id}`,
226
+ });
227
+ // Add attachments as links
228
+ const attachments = extractAttachments(parentMessage);
229
+ attachments.forEach((att) => {
230
+ parentActivity.links.push({
231
+ type: ActivityLinkType.external,
232
+ title: `Attachment: ${att.filename}`,
233
+ url: att.url,
234
+ });
235
+ });
236
+ activities.push(parentActivity);
237
+ // Create activities for replies (messages after the first)
238
+ for (let i = 1; i < thread.messages.length; i++) {
239
+ const message = thread.messages[i];
240
+ const replyFrom = getHeader(message, "From");
241
+ const replySender = replyFrom ? parseEmailAddress(replyFrom) : null;
242
+ const replyBody = extractBody(message.payload);
243
+ const replyActivity = {
244
+ type: ActivityType.Task,
245
+ title: `Re: ${subject || "Email"}`,
246
+ note: replyBody || message.snippet,
247
+ noteType: "text",
248
+ start: new Date(parseInt(message.internalDate)),
249
+ parent: { id: `gmail:${thread.id}:${parentMessage.id}` },
250
+ meta: {
251
+ source: `gmail:${thread.id}:${message.id}`,
252
+ threadId: thread.id,
253
+ messageId: message.id,
254
+ from: replySender,
255
+ labels: message.labelIds,
256
+ },
257
+ };
258
+ // Initialize links array
259
+ replyActivity.links = [];
260
+ // Add Gmail URL as action link
261
+ replyActivity.links.push({
262
+ type: ActivityLinkType.external,
263
+ title: "Open in Gmail",
264
+ url: `https://mail.google.com/mail/u/0/#inbox/${thread.id}`,
265
+ });
266
+ // Add attachments as links
267
+ const replyAttachments = extractAttachments(message);
268
+ replyAttachments.forEach((att) => {
269
+ replyActivity.links.push({
270
+ type: ActivityLinkType.external,
271
+ title: `Attachment: ${att.filename}`,
272
+ url: att.url,
273
+ });
274
+ });
275
+ activities.push(replyActivity);
276
+ }
277
+ return activities;
278
+ }
279
+ /**
280
+ * Syncs threads from a Gmail label/query with pagination
281
+ * Returns threads and updated sync state
282
+ */
283
+ export async function syncGmailChannel(api, state, batchSize = 20) {
284
+ // Extract query from channelId if it's a search filter
285
+ let labelId = state.channelId;
286
+ let query;
287
+ if (state.channelId.startsWith("search:")) {
288
+ query = state.channelId.substring(7);
289
+ labelId = "INBOX"; // Default to inbox for searches
290
+ }
291
+ const { threads: threadRefs, nextPageToken } = await api.getThreads(labelId, state.pageToken, batchSize, query);
292
+ // Fetch full thread details
293
+ const threads = [];
294
+ for (const threadRef of threadRefs) {
295
+ try {
296
+ const thread = await api.getThread(threadRef.id);
297
+ threads.push(thread);
298
+ }
299
+ catch (error) {
300
+ console.error(`Failed to fetch thread ${threadRef.id}:`, error);
301
+ // Continue with other threads even if one fails
302
+ }
303
+ }
304
+ const newState = {
305
+ channelId: state.channelId,
306
+ pageToken: nextPageToken,
307
+ lastSyncTime: new Date(),
308
+ };
309
+ // Update historyId from the last thread if available
310
+ if (threads.length > 0) {
311
+ newState.historyId = threads[threads.length - 1].historyId;
312
+ }
313
+ return {
314
+ threads,
315
+ state: newState,
316
+ hasMore: !!nextPageToken,
317
+ };
318
+ }
319
+ //# sourceMappingURL=gmail-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gmail-api.js","sourceRoot":"","sources":["../src/gmail-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import { type Activity, type ActivityLink, Tool, type ToolBuilder } from "@plotday/twister";
2
+ import { type MessageChannel, type MessageSyncOptions, type MessagingAuth, type MessagingTool } from "@plotday/twister/common/messaging";
3
+ import { type Callback } from "@plotday/twister/tools/callbacks";
4
+ import { type Authorization, Integrations } from "@plotday/twister/tools/integrations";
5
+ import { Network, type WebhookRequest } from "@plotday/twister/tools/network";
6
+ import { Plot } from "@plotday/twister/tools/plot";
7
+ /**
8
+ * Gmail integration tool implementing the MessagingTool interface.
9
+ *
10
+ * Supports inbox, labels, and search filters as channels.
11
+ *
12
+ * **Required OAuth Scopes:**
13
+ * - `https://www.googleapis.com/auth/gmail.readonly` - Read emails
14
+ * - `https://www.googleapis.com/auth/gmail.modify` - Modify labels
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * class MessagesTwist extends Twist {
19
+ * private gmail: Gmail;
20
+ *
21
+ * constructor(id: string, tools: Tools) {
22
+ * super();
23
+ * this.gmail = tools.get(Gmail);
24
+ * }
25
+ *
26
+ * async activate() {
27
+ * const authLink = await this.gmail.requestAuth(this.onGmailAuth);
28
+ *
29
+ * await this.plot.createActivity({
30
+ * type: ActivityType.Task,
31
+ * title: "Connect Gmail",
32
+ * links: [authLink]
33
+ * });
34
+ * }
35
+ *
36
+ * async onGmailAuth(auth: MessagingAuth) {
37
+ * const channels = await this.gmail.getChannels(auth.authToken);
38
+ *
39
+ * // Start syncing inbox
40
+ * const inbox = channels.find(c => c.primary);
41
+ * if (inbox) {
42
+ * await this.gmail.startSync(
43
+ * auth.authToken,
44
+ * inbox.id,
45
+ * this.onGmailThread,
46
+ * {
47
+ * timeMin: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Last 7 days
48
+ * }
49
+ * );
50
+ * }
51
+ * }
52
+ *
53
+ * async onGmailThread(thread: Activity[]) {
54
+ * // Process Gmail email threads
55
+ * for (const message of thread) {
56
+ * await this.plot.createActivity(message);
57
+ * }
58
+ * }
59
+ * }
60
+ * ```
61
+ */
62
+ export declare class Gmail extends Tool<Gmail> implements MessagingTool {
63
+ build(build: ToolBuilder): {
64
+ integrations: Promise<Integrations>;
65
+ network: Promise<Network>;
66
+ plot: Promise<Plot>;
67
+ };
68
+ requestAuth<TCallback extends (auth: MessagingAuth, ...args: any[]) => any>(callback: TCallback, ...extraArgs: any[]): Promise<ActivityLink>;
69
+ private getApi;
70
+ getChannels(authToken: string): Promise<MessageChannel[]>;
71
+ startSync<TCallback extends (thread: Activity[], ...args: any[]) => any>(authToken: string, channelId: string, callback: TCallback, options?: MessageSyncOptions, ...extraArgs: any[]): Promise<void>;
72
+ stopSync(authToken: string, channelId: string): Promise<void>;
73
+ private setupChannelWebhook;
74
+ syncBatch(_args: any, batchNumber: number, mode: "full" | "incremental", authToken: string, channelId: string): Promise<void>;
75
+ private processEmailThreads;
76
+ onGmailWebhook(request: WebhookRequest, channelId: string, authToken: string): Promise<void>;
77
+ private startIncrementalSync;
78
+ onAuthSuccess(authResult: Authorization, authToken: string, callback: Callback): Promise<void>;
79
+ }
80
+ export default Gmail;
package/dist/gmail.js ADDED
@@ -0,0 +1,350 @@
1
+ import { Tool, } from "@plotday/twister";
2
+ import { AuthLevel, AuthProvider, Integrations, } from "@plotday/twister/tools/integrations";
3
+ import { Network } from "@plotday/twister/tools/network";
4
+ import { ActivityAccess, ContactAccess, Plot } from "@plotday/twister/tools/plot";
5
+ import { GmailApi, syncGmailChannel, transformGmailThread, } from "./gmail-api";
6
+ /**
7
+ * Gmail integration tool implementing the MessagingTool interface.
8
+ *
9
+ * Supports inbox, labels, and search filters as channels.
10
+ *
11
+ * **Required OAuth Scopes:**
12
+ * - `https://www.googleapis.com/auth/gmail.readonly` - Read emails
13
+ * - `https://www.googleapis.com/auth/gmail.modify` - Modify labels
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * class MessagesTwist extends Twist {
18
+ * private gmail: Gmail;
19
+ *
20
+ * constructor(id: string, tools: Tools) {
21
+ * super();
22
+ * this.gmail = tools.get(Gmail);
23
+ * }
24
+ *
25
+ * async activate() {
26
+ * const authLink = await this.gmail.requestAuth(this.onGmailAuth);
27
+ *
28
+ * await this.plot.createActivity({
29
+ * type: ActivityType.Task,
30
+ * title: "Connect Gmail",
31
+ * links: [authLink]
32
+ * });
33
+ * }
34
+ *
35
+ * async onGmailAuth(auth: MessagingAuth) {
36
+ * const channels = await this.gmail.getChannels(auth.authToken);
37
+ *
38
+ * // Start syncing inbox
39
+ * const inbox = channels.find(c => c.primary);
40
+ * if (inbox) {
41
+ * await this.gmail.startSync(
42
+ * auth.authToken,
43
+ * inbox.id,
44
+ * this.onGmailThread,
45
+ * {
46
+ * timeMin: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Last 7 days
47
+ * }
48
+ * );
49
+ * }
50
+ * }
51
+ *
52
+ * async onGmailThread(thread: Activity[]) {
53
+ * // Process Gmail email threads
54
+ * for (const message of thread) {
55
+ * await this.plot.createActivity(message);
56
+ * }
57
+ * }
58
+ * }
59
+ * ```
60
+ */
61
+ export class Gmail extends Tool {
62
+ build(build) {
63
+ return {
64
+ integrations: build(Integrations),
65
+ network: build(Network, {
66
+ urls: ["https://gmail.googleapis.com/gmail/v1/*"],
67
+ }),
68
+ plot: build(Plot, {
69
+ contact: {
70
+ access: ContactAccess.Write,
71
+ },
72
+ activity: {
73
+ access: ActivityAccess.Create,
74
+ },
75
+ }),
76
+ };
77
+ }
78
+ async requestAuth(callback, ...extraArgs) {
79
+ console.log("Requesting Gmail auth");
80
+ // Gmail OAuth scopes for read-only access
81
+ const gmailScopes = [
82
+ "https://www.googleapis.com/auth/gmail.readonly",
83
+ "https://www.googleapis.com/auth/gmail.modify",
84
+ ];
85
+ // Generate opaque token for authorization
86
+ const authToken = crypto.randomUUID();
87
+ const callbackToken = await this.tools.callbacks.createFromParent(callback, ...extraArgs);
88
+ // Request auth and return the activity link
89
+ // Use User level for user-scoped Gmail authorization
90
+ return await this.tools.integrations.request({
91
+ provider: AuthProvider.Google,
92
+ level: AuthLevel.User,
93
+ scopes: gmailScopes,
94
+ }, this.onAuthSuccess, authToken, callbackToken);
95
+ }
96
+ async getApi(authToken) {
97
+ const authorization = await this.get(`authorization:${authToken}`);
98
+ if (!authorization) {
99
+ throw new Error("Authorization no longer available");
100
+ }
101
+ const token = await this.tools.integrations.get(authorization);
102
+ if (!token) {
103
+ throw new Error("Authorization no longer available");
104
+ }
105
+ return new GmailApi(token.token);
106
+ }
107
+ async getChannels(authToken) {
108
+ console.log("Fetching Gmail labels");
109
+ const api = await this.getApi(authToken);
110
+ const labels = await api.getLabels();
111
+ console.log("Got Gmail labels", labels);
112
+ const channels = [];
113
+ // Add standard labels as channels
114
+ for (const label of labels) {
115
+ // Filter out system labels that don't make sense as channels
116
+ if (label.type === "system" &&
117
+ !["INBOX", "SENT", "DRAFT", "IMPORTANT", "STARRED"].includes(label.id)) {
118
+ continue;
119
+ }
120
+ channels.push({
121
+ id: label.id,
122
+ name: label.name,
123
+ description: `${label.messagesTotal || 0} messages, ${label.messagesUnread || 0} unread`,
124
+ primary: label.id === "INBOX",
125
+ });
126
+ }
127
+ // Add a special "search" channel option
128
+ channels.push({
129
+ id: "search:from:important@example.com",
130
+ name: "Search (Custom Query)",
131
+ description: "Use custom Gmail search queries as channels",
132
+ primary: false,
133
+ });
134
+ return channels;
135
+ }
136
+ async startSync(authToken, channelId, callback, options, ...extraArgs) {
137
+ console.log("Starting Gmail sync for channel", channelId);
138
+ // Create callback token for parent
139
+ const callbackToken = await this.tools.callbacks.createFromParent(callback, ...extraArgs);
140
+ await this.set(`thread_callback_token_${channelId}`, callbackToken);
141
+ // Store auth token for channel
142
+ await this.set(`auth_token_${channelId}`, authToken);
143
+ // Setup webhook for this channel (Gmail Push Notifications)
144
+ await this.setupChannelWebhook(authToken, channelId);
145
+ const initialState = {
146
+ channelId,
147
+ lastSyncTime: options?.timeMin
148
+ ? typeof options.timeMin === "string"
149
+ ? new Date(options.timeMin)
150
+ : options.timeMin
151
+ : undefined,
152
+ };
153
+ await this.set(`sync_state_${channelId}`, initialState);
154
+ console.log("Starting initial Gmail sync");
155
+ // Start sync batch using run tool for long-running operation
156
+ const syncCallback = await this.callback(this.syncBatch, 1, "full", authToken, channelId);
157
+ await this.run(syncCallback);
158
+ }
159
+ async stopSync(authToken, channelId) {
160
+ console.log("Stopping Gmail sync for channel", channelId);
161
+ // Stop watching for push notifications
162
+ const api = await this.getApi(authToken);
163
+ try {
164
+ await api.stopWatch();
165
+ }
166
+ catch (error) {
167
+ console.error("Failed to stop Gmail watch:", error);
168
+ }
169
+ // Clear webhook
170
+ await this.clear(`channel_webhook_${channelId}`);
171
+ // Clear sync state
172
+ await this.clear(`sync_state_${channelId}`);
173
+ // Clear callback token
174
+ await this.clear(`thread_callback_token_${channelId}`);
175
+ // Clear auth token
176
+ await this.clear(`auth_token_${channelId}`);
177
+ }
178
+ async setupChannelWebhook(authToken, channelId) {
179
+ // Retrieve the authorization for this auth token
180
+ const authorization = await this.get(`authorization:${authToken}`);
181
+ if (!authorization) {
182
+ throw new Error("Authorization not found for Gmail webhook setup");
183
+ }
184
+ // Create Gmail webhook (returns Pub/Sub topic name, not a URL)
185
+ // When provider is Google with Gmail scopes, createWebhook returns a Pub/Sub topic name
186
+ const topicName = await this.tools.network.createWebhook({
187
+ callback: this.onGmailWebhook,
188
+ extraArgs: [channelId, authToken],
189
+ provider: AuthProvider.Google,
190
+ authorization,
191
+ });
192
+ const api = await this.getApi(authToken);
193
+ try {
194
+ // Setup Gmail watch with the Pub/Sub topic name
195
+ // topicName format: projects/{project_id}/topics/{topic_name}
196
+ const watchResult = await api.setupWatch(channelId, topicName);
197
+ // Store webhook data including expiration
198
+ await this.set(`channel_webhook_${channelId}`, {
199
+ topicName,
200
+ channelId,
201
+ historyId: watchResult.historyId,
202
+ expiration: new Date(parseInt(watchResult.expiration)),
203
+ createdAt: new Date().toISOString(),
204
+ });
205
+ console.log("Gmail webhook setup complete", {
206
+ channelId,
207
+ topicName,
208
+ expiration: watchResult.expiration,
209
+ });
210
+ }
211
+ catch (error) {
212
+ console.error("Failed to setup Gmail webhook:", error);
213
+ console.log("Continuing without webhooks - only manual/scheduled syncs will work");
214
+ }
215
+ }
216
+ async syncBatch(_args, batchNumber, mode, authToken, channelId) {
217
+ console.log(`Starting Gmail sync batch ${batchNumber} (${mode}) for channel ${channelId}`);
218
+ try {
219
+ const state = await this.get(`sync_state_${channelId}`);
220
+ if (!state) {
221
+ throw new Error("No sync state found");
222
+ }
223
+ const api = await this.getApi(authToken);
224
+ // Use smaller batch size for Gmail (20 threads) to avoid timeouts
225
+ const result = await syncGmailChannel(api, state, 20);
226
+ if (result.threads.length > 0) {
227
+ await this.processEmailThreads(result.threads, channelId, authToken);
228
+ console.log(`Synced ${result.threads.length} threads in batch ${batchNumber} for channel ${channelId}`);
229
+ }
230
+ await this.set(`sync_state_${channelId}`, result.state);
231
+ if (result.hasMore) {
232
+ const syncCallback = await this.callback(this.syncBatch, batchNumber + 1, mode, authToken, channelId);
233
+ await this.run(syncCallback);
234
+ }
235
+ else {
236
+ console.log(`Gmail ${mode} sync completed after ${batchNumber} batches for channel ${channelId}`);
237
+ if (mode === "full") {
238
+ await this.clear(`sync_state_${channelId}`);
239
+ }
240
+ }
241
+ }
242
+ catch (error) {
243
+ console.error(`Error in sync batch ${batchNumber} for channel ${channelId}:`, error);
244
+ throw error;
245
+ }
246
+ }
247
+ async processEmailThreads(threads, channelId, authToken) {
248
+ for (const thread of threads) {
249
+ try {
250
+ // Transform Gmail thread to Activity array
251
+ const activities = transformGmailThread(thread);
252
+ if (activities.length === 0)
253
+ continue;
254
+ // Extract email addresses from all messages and create contacts
255
+ const emailAddresses = new Set();
256
+ for (const activity of activities) {
257
+ const meta = activity.meta;
258
+ if (meta?.from?.email) {
259
+ emailAddresses.add(meta.from.email);
260
+ }
261
+ }
262
+ // Create contacts for all unique email addresses
263
+ if (emailAddresses.size > 0) {
264
+ const contacts = Array.from(emailAddresses).map((email) => {
265
+ // Try to find the name from the activity meta
266
+ const activity = activities.find((act) => act.meta?.from?.email === email);
267
+ const name = activity?.meta?.from?.name || null;
268
+ return {
269
+ email,
270
+ name: name || undefined,
271
+ };
272
+ });
273
+ await this.tools.plot.addContacts(contacts);
274
+ }
275
+ // Call parent callback with the thread
276
+ const callbackToken = await this.get(`thread_callback_token_${channelId}`);
277
+ if (callbackToken) {
278
+ // Pass activities as-is - the callback will handle conversion if needed
279
+ await this.run(callbackToken, activities);
280
+ }
281
+ }
282
+ catch (error) {
283
+ console.error(`Failed to process Gmail thread ${thread.id}:`, error);
284
+ // Continue processing other threads
285
+ }
286
+ }
287
+ }
288
+ async onGmailWebhook(request, channelId, authToken) {
289
+ console.log("Received Gmail webhook notification", {
290
+ body: request.body,
291
+ channelId,
292
+ });
293
+ // Gmail sends push notifications via Cloud Pub/Sub
294
+ // The message body is base64-encoded
295
+ const message = request.body?.message;
296
+ if (!message) {
297
+ console.warn("No message in webhook body");
298
+ return;
299
+ }
300
+ // Decode the Pub/Sub message
301
+ let data;
302
+ try {
303
+ const decoded = atob(message.data);
304
+ data = JSON.parse(decoded);
305
+ }
306
+ catch (error) {
307
+ console.error("Failed to decode Gmail webhook message:", error);
308
+ return;
309
+ }
310
+ console.log("Decoded Gmail notification:", data);
311
+ // Gmail notifications contain historyId for incremental sync
312
+ if (data.historyId) {
313
+ await this.startIncrementalSync(channelId, authToken, data.historyId);
314
+ }
315
+ }
316
+ async startIncrementalSync(channelId, authToken, historyId) {
317
+ const webhookData = await this.get(`channel_webhook_${channelId}`);
318
+ if (!webhookData) {
319
+ console.error("No channel webhook data found");
320
+ return;
321
+ }
322
+ // Check if watch has expired and renew if needed
323
+ const expiration = new Date(webhookData.expiration);
324
+ if (expiration < new Date()) {
325
+ console.log("Gmail watch expired, renewing...");
326
+ await this.setupChannelWebhook(authToken, channelId);
327
+ }
328
+ // For incremental sync, use the historyId from the notification
329
+ const incrementalState = {
330
+ channelId,
331
+ historyId,
332
+ lastSyncTime: new Date(),
333
+ };
334
+ await this.set(`sync_state_${channelId}`, incrementalState);
335
+ const syncCallback = await this.callback(this.syncBatch, 1, "incremental", authToken, channelId);
336
+ await this.run(syncCallback);
337
+ }
338
+ async onAuthSuccess(authResult, authToken, callback) {
339
+ // Store the actual auth token using opaque token as key
340
+ await this.set(`authorization:${authToken}`, authResult);
341
+ const authSuccessResult = {
342
+ authToken,
343
+ };
344
+ await this.run(callback, authSuccessResult);
345
+ // Clean up the callback token
346
+ await this.clear(`auth_callback_token:${authToken}`);
347
+ }
348
+ }
349
+ export default Gmail;
350
+ //# sourceMappingURL=gmail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gmail.js","sourceRoot":"","sources":["../src/gmail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export { default, Gmail } from "./gmail";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { default, Gmail } from "./gmail";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@plotday/tool-gmail",
3
+ "displayName": "Gmail",
4
+ "description": "Sync with Gmail inbox and messages",
5
+ "author": "Plot <team@plot.day> (https://plot.day)",
6
+ "license": "MIT",
7
+ "version": "0.1.1",
8
+ "type": "module",
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "dependencies": {
23
+ "@plotday/twister": "^0.20.0"
24
+ },
25
+ "devDependencies": {
26
+ "typescript": "^5.9.3"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/plotday/plot.git",
31
+ "directory": "tools/gmail"
32
+ },
33
+ "homepage": "https://plot.day",
34
+ "bugs": {
35
+ "url": "https://github.com/plotday/plot/issues"
36
+ },
37
+ "keywords": [
38
+ "plot",
39
+ "agent",
40
+ "tool",
41
+ "gmail",
42
+ "messaging",
43
+ "email"
44
+ ],
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "scripts": {
49
+ "build": "tsc",
50
+ "clean": "rm -rf dist"
51
+ }
52
+ }