@cli4ai/gmail 1.0.11 → 1.0.14
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/cli4ai.json +4 -4
- package/dist/lib/api.d.ts +91 -0
- package/dist/lib/api.js +240 -0
- package/dist/lib/attachments.d.ts +35 -0
- package/dist/lib/attachments.js +148 -0
- package/dist/lib/drafts.d.ts +89 -0
- package/dist/lib/drafts.js +334 -0
- package/dist/lib/labels.d.ts +47 -0
- package/dist/lib/labels.js +143 -0
- package/dist/lib/messages.d.ts +77 -0
- package/dist/lib/messages.js +223 -0
- package/dist/lib/send.d.ts +37 -0
- package/dist/lib/send.js +259 -0
- package/dist/lib/threads.d.ts +55 -0
- package/dist/lib/threads.js +101 -0
- package/dist/run.d.ts +2 -0
- package/dist/run.js +260 -0
- package/package.json +14 -6
- package/run.ts +0 -316
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { getGmail, output, encodeBase64Url, extractHeaders, extractBody } from './api.js';
|
|
4
|
+
/**
|
|
5
|
+
* Create multipart message with attachment
|
|
6
|
+
*/
|
|
7
|
+
export function createMultipartMessage({ to, cc, bcc, subject, body, inReplyTo, references, attachments = [] }) {
|
|
8
|
+
const boundary = `boundary_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
9
|
+
const headers = [];
|
|
10
|
+
headers.push(`To: ${to}`);
|
|
11
|
+
if (cc)
|
|
12
|
+
headers.push(`Cc: ${cc}`);
|
|
13
|
+
if (bcc)
|
|
14
|
+
headers.push(`Bcc: ${bcc}`);
|
|
15
|
+
headers.push(`Subject: ${subject}`);
|
|
16
|
+
if (inReplyTo) {
|
|
17
|
+
headers.push(`In-Reply-To: ${inReplyTo}`);
|
|
18
|
+
headers.push(`References: ${references || inReplyTo}`);
|
|
19
|
+
}
|
|
20
|
+
headers.push('MIME-Version: 1.0');
|
|
21
|
+
headers.push(`Content-Type: multipart/mixed; boundary="${boundary}"`);
|
|
22
|
+
const parts = [];
|
|
23
|
+
// Text body part
|
|
24
|
+
parts.push(`--${boundary}`);
|
|
25
|
+
parts.push('Content-Type: text/plain; charset=utf-8');
|
|
26
|
+
parts.push('Content-Transfer-Encoding: 7bit');
|
|
27
|
+
parts.push('');
|
|
28
|
+
parts.push(body);
|
|
29
|
+
// Attachment parts
|
|
30
|
+
for (const att of attachments) {
|
|
31
|
+
const filename = path.basename(att.path);
|
|
32
|
+
const content = fs.readFileSync(att.path);
|
|
33
|
+
const base64Content = content.toString('base64');
|
|
34
|
+
const mimeType = att.mimeType || getMimeType(filename);
|
|
35
|
+
parts.push(`--${boundary}`);
|
|
36
|
+
parts.push(`Content-Type: ${mimeType}; name="${filename}"`);
|
|
37
|
+
parts.push('Content-Transfer-Encoding: base64');
|
|
38
|
+
parts.push(`Content-Disposition: attachment; filename="${filename}"`);
|
|
39
|
+
parts.push('');
|
|
40
|
+
// Split base64 into lines of 76 chars
|
|
41
|
+
for (let i = 0; i < base64Content.length; i += 76) {
|
|
42
|
+
parts.push(base64Content.slice(i, i + 76));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
parts.push(`--${boundary}--`);
|
|
46
|
+
const message = headers.join('\r\n') + '\r\n\r\n' + parts.join('\r\n');
|
|
47
|
+
return encodeBase64Url(message);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Simple message without attachments
|
|
51
|
+
*/
|
|
52
|
+
export function createSimpleMessage({ to, cc, bcc, subject, body, inReplyTo, references }) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
lines.push(`To: ${to}`);
|
|
55
|
+
if (cc)
|
|
56
|
+
lines.push(`Cc: ${cc}`);
|
|
57
|
+
if (bcc)
|
|
58
|
+
lines.push(`Bcc: ${bcc}`);
|
|
59
|
+
lines.push(`Subject: ${subject}`);
|
|
60
|
+
lines.push('Content-Type: text/plain; charset=utf-8');
|
|
61
|
+
if (inReplyTo) {
|
|
62
|
+
lines.push(`In-Reply-To: ${inReplyTo}`);
|
|
63
|
+
lines.push(`References: ${references || inReplyTo}`);
|
|
64
|
+
}
|
|
65
|
+
lines.push('');
|
|
66
|
+
lines.push(body);
|
|
67
|
+
return encodeBase64Url(lines.join('\r\n'));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get MIME type from filename
|
|
71
|
+
*/
|
|
72
|
+
function getMimeType(filename) {
|
|
73
|
+
const ext = path.extname(filename).toLowerCase();
|
|
74
|
+
const mimeTypes = {
|
|
75
|
+
'.pdf': 'application/pdf',
|
|
76
|
+
'.doc': 'application/msword',
|
|
77
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
78
|
+
'.xls': 'application/vnd.ms-excel',
|
|
79
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
80
|
+
'.png': 'image/png',
|
|
81
|
+
'.jpg': 'image/jpeg',
|
|
82
|
+
'.jpeg': 'image/jpeg',
|
|
83
|
+
'.gif': 'image/gif',
|
|
84
|
+
'.txt': 'text/plain',
|
|
85
|
+
'.csv': 'text/csv',
|
|
86
|
+
'.zip': 'application/zip',
|
|
87
|
+
'.json': 'application/json',
|
|
88
|
+
'.xml': 'application/xml',
|
|
89
|
+
'.html': 'text/html'
|
|
90
|
+
};
|
|
91
|
+
return mimeTypes[ext] || 'application/octet-stream';
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* List all drafts
|
|
95
|
+
*/
|
|
96
|
+
export async function list(options = {}) {
|
|
97
|
+
const gmail = await getGmail();
|
|
98
|
+
const limit = options.limit || 20;
|
|
99
|
+
const res = await gmail.users.drafts.list({
|
|
100
|
+
userId: 'me',
|
|
101
|
+
maxResults: limit
|
|
102
|
+
});
|
|
103
|
+
if (!res.data.drafts || res.data.drafts.length === 0) {
|
|
104
|
+
output([], options);
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
// Get details for each draft
|
|
108
|
+
const drafts = await Promise.all(res.data.drafts.map(async (draft) => {
|
|
109
|
+
const draftRes = await gmail.users.drafts.get({
|
|
110
|
+
userId: 'me',
|
|
111
|
+
id: draft.id,
|
|
112
|
+
format: 'metadata',
|
|
113
|
+
metadataHeaders: ['To', 'Subject', 'Date']
|
|
114
|
+
});
|
|
115
|
+
const data = draftRes.data;
|
|
116
|
+
const headers = extractHeaders(data.message?.payload?.headers);
|
|
117
|
+
return {
|
|
118
|
+
id: draft.id,
|
|
119
|
+
messageId: data.message?.id,
|
|
120
|
+
to: headers.to || '(no recipient)',
|
|
121
|
+
subject: headers.subject || '(no subject)',
|
|
122
|
+
snippet: data.message?.snippet || ''
|
|
123
|
+
};
|
|
124
|
+
}));
|
|
125
|
+
output(drafts, options);
|
|
126
|
+
return drafts;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get a draft
|
|
130
|
+
*/
|
|
131
|
+
export async function get(draftId, options = {}) {
|
|
132
|
+
const gmail = await getGmail();
|
|
133
|
+
const res = await gmail.users.drafts.get({
|
|
134
|
+
userId: 'me',
|
|
135
|
+
id: draftId,
|
|
136
|
+
format: 'full'
|
|
137
|
+
});
|
|
138
|
+
const headers = extractHeaders(res.data.message?.payload?.headers);
|
|
139
|
+
const body = extractBody(res.data.message?.payload);
|
|
140
|
+
const draft = {
|
|
141
|
+
id: res.data.id,
|
|
142
|
+
messageId: res.data.message?.id,
|
|
143
|
+
threadId: res.data.message?.threadId || undefined,
|
|
144
|
+
to: headers.to || '',
|
|
145
|
+
cc: headers.cc || null,
|
|
146
|
+
subject: headers.subject || '(no subject)',
|
|
147
|
+
body: body
|
|
148
|
+
};
|
|
149
|
+
output(draft, options);
|
|
150
|
+
return draft;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create a new draft
|
|
154
|
+
*/
|
|
155
|
+
export async function create(to, subject, body, options = {}) {
|
|
156
|
+
const gmail = await getGmail();
|
|
157
|
+
body = body.replace(/\\n/g, '\n');
|
|
158
|
+
let raw;
|
|
159
|
+
if (options.attach) {
|
|
160
|
+
const attachments = Array.isArray(options.attach) ? options.attach : [options.attach];
|
|
161
|
+
const attachmentPaths = attachments.map(a => ({ path: a }));
|
|
162
|
+
raw = createMultipartMessage({
|
|
163
|
+
to,
|
|
164
|
+
cc: options.cc,
|
|
165
|
+
bcc: options.bcc,
|
|
166
|
+
subject,
|
|
167
|
+
body,
|
|
168
|
+
attachments: attachmentPaths
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
raw = createSimpleMessage({
|
|
173
|
+
to,
|
|
174
|
+
cc: options.cc,
|
|
175
|
+
bcc: options.bcc,
|
|
176
|
+
subject,
|
|
177
|
+
body
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
const res = await gmail.users.drafts.create({
|
|
181
|
+
userId: 'me',
|
|
182
|
+
requestBody: {
|
|
183
|
+
message: { raw }
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
const result = {
|
|
187
|
+
ok: true,
|
|
188
|
+
id: res.data.id || undefined,
|
|
189
|
+
messageId: res.data.message?.id,
|
|
190
|
+
to,
|
|
191
|
+
subject
|
|
192
|
+
};
|
|
193
|
+
output(result, options);
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Create a draft reply to a message
|
|
198
|
+
*/
|
|
199
|
+
export async function createReply(messageId, body, options = {}) {
|
|
200
|
+
const gmail = await getGmail();
|
|
201
|
+
// Get original message
|
|
202
|
+
const original = await gmail.users.messages.get({
|
|
203
|
+
userId: 'me',
|
|
204
|
+
id: messageId,
|
|
205
|
+
format: 'metadata',
|
|
206
|
+
metadataHeaders: ['From', 'To', 'Subject', 'Message-ID', 'References']
|
|
207
|
+
});
|
|
208
|
+
const headers = extractHeaders(original.data.payload?.headers);
|
|
209
|
+
// Determine reply-to address
|
|
210
|
+
const replyTo = headers.from;
|
|
211
|
+
let subject = headers.subject || '';
|
|
212
|
+
if (!subject.toLowerCase().startsWith('re:')) {
|
|
213
|
+
subject = `Re: ${subject}`;
|
|
214
|
+
}
|
|
215
|
+
body = body.replace(/\\n/g, '\n');
|
|
216
|
+
let raw;
|
|
217
|
+
if (options.attach) {
|
|
218
|
+
const attachments = Array.isArray(options.attach) ? options.attach : [options.attach];
|
|
219
|
+
const attachmentPaths = attachments.map(a => ({ path: a }));
|
|
220
|
+
raw = createMultipartMessage({
|
|
221
|
+
to: replyTo,
|
|
222
|
+
subject,
|
|
223
|
+
body,
|
|
224
|
+
inReplyTo: headers.message_id,
|
|
225
|
+
references: headers.references ? `${headers.references} ${headers.message_id}` : headers.message_id,
|
|
226
|
+
attachments: attachmentPaths
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
raw = createSimpleMessage({
|
|
231
|
+
to: replyTo,
|
|
232
|
+
subject,
|
|
233
|
+
body,
|
|
234
|
+
inReplyTo: headers.message_id,
|
|
235
|
+
references: headers.references ? `${headers.references} ${headers.message_id}` : headers.message_id
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
const res = await gmail.users.drafts.create({
|
|
239
|
+
userId: 'me',
|
|
240
|
+
requestBody: {
|
|
241
|
+
message: {
|
|
242
|
+
raw,
|
|
243
|
+
threadId: original.data.threadId || undefined
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
const result = {
|
|
248
|
+
ok: true,
|
|
249
|
+
id: res.data.id || undefined,
|
|
250
|
+
messageId: res.data.message?.id,
|
|
251
|
+
threadId: original.data.threadId || undefined,
|
|
252
|
+
to: replyTo,
|
|
253
|
+
subject,
|
|
254
|
+
inReplyTo: messageId
|
|
255
|
+
};
|
|
256
|
+
output(result, options);
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Update a draft
|
|
261
|
+
*/
|
|
262
|
+
export async function update(draftId, to, subject, body, options = {}) {
|
|
263
|
+
const gmail = await getGmail();
|
|
264
|
+
body = body.replace(/\\n/g, '\n');
|
|
265
|
+
let raw;
|
|
266
|
+
if (options.attach) {
|
|
267
|
+
const attachments = Array.isArray(options.attach) ? options.attach : [options.attach];
|
|
268
|
+
const attachmentPaths = attachments.map(a => ({ path: a }));
|
|
269
|
+
raw = createMultipartMessage({
|
|
270
|
+
to,
|
|
271
|
+
cc: options.cc,
|
|
272
|
+
subject,
|
|
273
|
+
body,
|
|
274
|
+
attachments: attachmentPaths
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
raw = createSimpleMessage({
|
|
279
|
+
to,
|
|
280
|
+
cc: options.cc,
|
|
281
|
+
subject,
|
|
282
|
+
body
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
const res = await gmail.users.drafts.update({
|
|
286
|
+
userId: 'me',
|
|
287
|
+
id: draftId,
|
|
288
|
+
requestBody: {
|
|
289
|
+
message: { raw }
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
const result = {
|
|
293
|
+
ok: true,
|
|
294
|
+
id: res.data.id || undefined,
|
|
295
|
+
messageId: res.data.message?.id,
|
|
296
|
+
to,
|
|
297
|
+
subject
|
|
298
|
+
};
|
|
299
|
+
output(result, options);
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Delete a draft
|
|
304
|
+
*/
|
|
305
|
+
export async function remove(draftId, options = {}) {
|
|
306
|
+
const gmail = await getGmail();
|
|
307
|
+
await gmail.users.drafts.delete({
|
|
308
|
+
userId: 'me',
|
|
309
|
+
id: draftId
|
|
310
|
+
});
|
|
311
|
+
const result = { ok: true, deleted: draftId };
|
|
312
|
+
output(result, options);
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Send a draft
|
|
317
|
+
*/
|
|
318
|
+
export async function send(draftId, options = {}) {
|
|
319
|
+
const gmail = await getGmail();
|
|
320
|
+
const res = await gmail.users.drafts.send({
|
|
321
|
+
userId: 'me',
|
|
322
|
+
requestBody: {
|
|
323
|
+
id: draftId
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
const result = {
|
|
327
|
+
ok: true,
|
|
328
|
+
id: res.data.id || undefined,
|
|
329
|
+
threadId: res.data.threadId || undefined,
|
|
330
|
+
sentDraft: draftId
|
|
331
|
+
};
|
|
332
|
+
output(result, options);
|
|
333
|
+
return result;
|
|
334
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type OutputOptions } from './api.js';
|
|
2
|
+
/** Label data */
|
|
3
|
+
export interface LabelData {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
type?: string;
|
|
7
|
+
messageListVisibility?: string;
|
|
8
|
+
labelListVisibility?: string;
|
|
9
|
+
messagesTotal?: number;
|
|
10
|
+
messagesUnread?: number;
|
|
11
|
+
threadsTotal?: number;
|
|
12
|
+
threadsUnread?: number;
|
|
13
|
+
}
|
|
14
|
+
/** Label action result */
|
|
15
|
+
export interface LabelActionResult {
|
|
16
|
+
ok: boolean;
|
|
17
|
+
id?: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
messageId?: string;
|
|
20
|
+
labelAdded?: string;
|
|
21
|
+
labelRemoved?: string;
|
|
22
|
+
deleted?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* List all labels
|
|
26
|
+
*/
|
|
27
|
+
export declare function list(options?: OutputOptions): Promise<LabelData[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Get label details with counts
|
|
30
|
+
*/
|
|
31
|
+
export declare function get(labelId: string, options?: OutputOptions): Promise<LabelData>;
|
|
32
|
+
/**
|
|
33
|
+
* Add label to message
|
|
34
|
+
*/
|
|
35
|
+
export declare function addToMessage(messageId: string, labelName: string, options?: OutputOptions): Promise<LabelActionResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Remove label from message
|
|
38
|
+
*/
|
|
39
|
+
export declare function removeFromMessage(messageId: string, labelName: string, options?: OutputOptions): Promise<LabelActionResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Create a new label
|
|
42
|
+
*/
|
|
43
|
+
export declare function create(labelName: string, options?: OutputOptions): Promise<LabelActionResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Delete a label
|
|
46
|
+
*/
|
|
47
|
+
export declare function remove(labelName: string, options?: OutputOptions): Promise<LabelActionResult>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { getGmail, output } from './api.js';
|
|
2
|
+
/**
|
|
3
|
+
* List all labels
|
|
4
|
+
*/
|
|
5
|
+
export async function list(options = {}) {
|
|
6
|
+
const gmail = await getGmail();
|
|
7
|
+
const res = await gmail.users.labels.list({
|
|
8
|
+
userId: 'me'
|
|
9
|
+
});
|
|
10
|
+
const labels = (res.data.labels || []).map(label => ({
|
|
11
|
+
id: label.id,
|
|
12
|
+
name: label.name,
|
|
13
|
+
type: label.type || undefined,
|
|
14
|
+
messageListVisibility: label.messageListVisibility || undefined,
|
|
15
|
+
labelListVisibility: label.labelListVisibility || undefined
|
|
16
|
+
}));
|
|
17
|
+
// Sort: system labels first, then user labels
|
|
18
|
+
labels.sort((a, b) => {
|
|
19
|
+
if (a.type === 'system' && b.type !== 'system')
|
|
20
|
+
return -1;
|
|
21
|
+
if (a.type !== 'system' && b.type === 'system')
|
|
22
|
+
return 1;
|
|
23
|
+
return a.name.localeCompare(b.name);
|
|
24
|
+
});
|
|
25
|
+
output(labels, options);
|
|
26
|
+
return labels;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get label details with counts
|
|
30
|
+
*/
|
|
31
|
+
export async function get(labelId, options = {}) {
|
|
32
|
+
const gmail = await getGmail();
|
|
33
|
+
// Resolve label name to ID if needed
|
|
34
|
+
const resolvedId = await resolveLabelId(gmail, labelId);
|
|
35
|
+
const res = await gmail.users.labels.get({
|
|
36
|
+
userId: 'me',
|
|
37
|
+
id: resolvedId
|
|
38
|
+
});
|
|
39
|
+
const label = {
|
|
40
|
+
id: res.data.id,
|
|
41
|
+
name: res.data.name,
|
|
42
|
+
type: res.data.type || undefined,
|
|
43
|
+
messagesTotal: res.data.messagesTotal || undefined,
|
|
44
|
+
messagesUnread: res.data.messagesUnread || undefined,
|
|
45
|
+
threadsTotal: res.data.threadsTotal || undefined,
|
|
46
|
+
threadsUnread: res.data.threadsUnread || undefined
|
|
47
|
+
};
|
|
48
|
+
output(label, options);
|
|
49
|
+
return label;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Add label to message
|
|
53
|
+
*/
|
|
54
|
+
export async function addToMessage(messageId, labelName, options = {}) {
|
|
55
|
+
const gmail = await getGmail();
|
|
56
|
+
const labelId = await resolveLabelId(gmail, labelName);
|
|
57
|
+
await gmail.users.messages.modify({
|
|
58
|
+
userId: 'me',
|
|
59
|
+
id: messageId,
|
|
60
|
+
requestBody: {
|
|
61
|
+
addLabelIds: [labelId]
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const result = { ok: true, messageId, labelAdded: labelName };
|
|
65
|
+
output(result, options);
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Remove label from message
|
|
70
|
+
*/
|
|
71
|
+
export async function removeFromMessage(messageId, labelName, options = {}) {
|
|
72
|
+
const gmail = await getGmail();
|
|
73
|
+
const labelId = await resolveLabelId(gmail, labelName);
|
|
74
|
+
await gmail.users.messages.modify({
|
|
75
|
+
userId: 'me',
|
|
76
|
+
id: messageId,
|
|
77
|
+
requestBody: {
|
|
78
|
+
removeLabelIds: [labelId]
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const result = { ok: true, messageId, labelRemoved: labelName };
|
|
82
|
+
output(result, options);
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create a new label
|
|
87
|
+
*/
|
|
88
|
+
export async function create(labelName, options = {}) {
|
|
89
|
+
const gmail = await getGmail();
|
|
90
|
+
const res = await gmail.users.labels.create({
|
|
91
|
+
userId: 'me',
|
|
92
|
+
requestBody: {
|
|
93
|
+
name: labelName,
|
|
94
|
+
labelListVisibility: 'labelShow',
|
|
95
|
+
messageListVisibility: 'show'
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
const result = {
|
|
99
|
+
ok: true,
|
|
100
|
+
id: res.data.id,
|
|
101
|
+
name: res.data.name
|
|
102
|
+
};
|
|
103
|
+
output(result, options);
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Delete a label
|
|
108
|
+
*/
|
|
109
|
+
export async function remove(labelName, options = {}) {
|
|
110
|
+
const gmail = await getGmail();
|
|
111
|
+
const labelId = await resolveLabelId(gmail, labelName);
|
|
112
|
+
// Don't allow deleting system labels
|
|
113
|
+
const systemLabels = ['INBOX', 'SENT', 'TRASH', 'SPAM', 'STARRED', 'UNREAD', 'IMPORTANT', 'DRAFT'];
|
|
114
|
+
if (labelId.startsWith('CATEGORY_') || systemLabels.includes(labelId)) {
|
|
115
|
+
throw new Error(`Cannot delete system label: ${labelName}`);
|
|
116
|
+
}
|
|
117
|
+
await gmail.users.labels.delete({
|
|
118
|
+
userId: 'me',
|
|
119
|
+
id: labelId
|
|
120
|
+
});
|
|
121
|
+
const result = { ok: true, deleted: labelName };
|
|
122
|
+
output(result, options);
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Resolve label name to ID
|
|
127
|
+
*/
|
|
128
|
+
async function resolveLabelId(gmail, labelNameOrId) {
|
|
129
|
+
// Check if already an ID (system labels or Label_xxx format)
|
|
130
|
+
const systemLabels = ['INBOX', 'SENT', 'TRASH', 'SPAM', 'STARRED', 'UNREAD', 'IMPORTANT', 'DRAFT', 'CHAT'];
|
|
131
|
+
if (labelNameOrId.startsWith('Label_') ||
|
|
132
|
+
labelNameOrId.startsWith('CATEGORY_') ||
|
|
133
|
+
systemLabels.includes(labelNameOrId.toUpperCase())) {
|
|
134
|
+
return labelNameOrId.toUpperCase();
|
|
135
|
+
}
|
|
136
|
+
// Look up by name
|
|
137
|
+
const res = await gmail.users.labels.list({ userId: 'me' });
|
|
138
|
+
const label = res.data.labels?.find(l => l.name?.toLowerCase() === labelNameOrId.toLowerCase());
|
|
139
|
+
if (!label) {
|
|
140
|
+
throw new Error(`Label not found: ${labelNameOrId}`);
|
|
141
|
+
}
|
|
142
|
+
return label.id;
|
|
143
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type OutputOptions } from './api.js';
|
|
2
|
+
/** Message summary */
|
|
3
|
+
export interface MessageSummary {
|
|
4
|
+
id: string;
|
|
5
|
+
threadId?: string;
|
|
6
|
+
date: string | null;
|
|
7
|
+
from: string;
|
|
8
|
+
to: string;
|
|
9
|
+
subject: string;
|
|
10
|
+
snippet?: string;
|
|
11
|
+
labels?: string[];
|
|
12
|
+
unread?: boolean;
|
|
13
|
+
starred?: boolean;
|
|
14
|
+
important?: boolean;
|
|
15
|
+
body?: string;
|
|
16
|
+
cc?: string | null;
|
|
17
|
+
}
|
|
18
|
+
/** Action result */
|
|
19
|
+
export interface ActionResult {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
archived?: string;
|
|
22
|
+
trashed?: string;
|
|
23
|
+
untrashed?: string;
|
|
24
|
+
starred?: string;
|
|
25
|
+
unstarred?: string;
|
|
26
|
+
markedRead?: string;
|
|
27
|
+
markedUnread?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Message list options */
|
|
30
|
+
export interface MessageListOptions extends OutputOptions {
|
|
31
|
+
limit?: number;
|
|
32
|
+
body?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* List inbox messages
|
|
36
|
+
*/
|
|
37
|
+
export declare function inbox(options?: MessageListOptions): Promise<MessageSummary[]>;
|
|
38
|
+
/**
|
|
39
|
+
* List unread messages
|
|
40
|
+
*/
|
|
41
|
+
export declare function unread(options?: MessageListOptions): Promise<MessageSummary[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Search messages with Gmail query syntax
|
|
44
|
+
*/
|
|
45
|
+
export declare function search(query: string, options?: MessageListOptions): Promise<MessageSummary[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Read a full message
|
|
48
|
+
*/
|
|
49
|
+
export declare function read(messageId: string, options?: OutputOptions): Promise<MessageSummary>;
|
|
50
|
+
/**
|
|
51
|
+
* Archive a message (remove from INBOX)
|
|
52
|
+
*/
|
|
53
|
+
export declare function archive(messageId: string, options?: OutputOptions): Promise<ActionResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Trash a message
|
|
56
|
+
*/
|
|
57
|
+
export declare function trash(messageId: string, options?: OutputOptions): Promise<ActionResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Untrash a message
|
|
60
|
+
*/
|
|
61
|
+
export declare function untrash(messageId: string, options?: OutputOptions): Promise<ActionResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Star a message
|
|
64
|
+
*/
|
|
65
|
+
export declare function star(messageId: string, options?: OutputOptions): Promise<ActionResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Unstar a message
|
|
68
|
+
*/
|
|
69
|
+
export declare function unstar(messageId: string, options?: OutputOptions): Promise<ActionResult>;
|
|
70
|
+
/**
|
|
71
|
+
* Mark as read
|
|
72
|
+
*/
|
|
73
|
+
export declare function markRead(messageId: string, options?: OutputOptions): Promise<ActionResult>;
|
|
74
|
+
/**
|
|
75
|
+
* Mark as unread
|
|
76
|
+
*/
|
|
77
|
+
export declare function markUnread(messageId: string, options?: OutputOptions): Promise<ActionResult>;
|