@clonecommand/cloud 0.0.7 → 0.0.10
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/email/index.d.ts +50 -2
- package/dist/email/index.js +101 -2
- package/dist/email/types.d.ts +25 -4
- package/docs/email.md +62 -0
- package/package.json +2 -2
package/dist/email/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CloneCommandCloud } from "..";
|
|
2
|
-
import { InboundEmail, SendEmailOptions } from "./types";
|
|
2
|
+
import { InboundEmail, SendEmailOptions, EmailAttachment } from "./types";
|
|
3
3
|
/**
|
|
4
4
|
* Client for interacting with CloneCommand Email services.
|
|
5
5
|
*/
|
|
@@ -21,7 +21,8 @@ export declare class EmailClient {
|
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* await ccc.email.send({
|
|
24
|
-
*
|
|
24
|
+
* from: { name: 'Support', address: 'support@mail.example.com' },
|
|
25
|
+
* to: [{ name: 'User', address: 'user@example.com' }],
|
|
25
26
|
* subject: 'Hello',
|
|
26
27
|
* text: 'This is a test'
|
|
27
28
|
* });
|
|
@@ -58,4 +59,51 @@ export declare class EmailClient {
|
|
|
58
59
|
* @returns A promise resolving to true if successful.
|
|
59
60
|
*/
|
|
60
61
|
delete(emailId: string): Promise<boolean>;
|
|
62
|
+
/**
|
|
63
|
+
* Sends a raw MIME-formatted email.
|
|
64
|
+
* This is useful for sending emails with attachments, like calendar invites.
|
|
65
|
+
*
|
|
66
|
+
* @param rawMessage - The full MIME message as a string, including headers and body.
|
|
67
|
+
* @returns A promise resolving to the Message-ID of the sent email.
|
|
68
|
+
* @throws Error if sending fails.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* const boundary = `----=_Part_${Date.now()}`;
|
|
72
|
+
* const rawMessage = `From: events@mail.yourdomain.com
|
|
73
|
+
* To: user@example.com
|
|
74
|
+
* Subject: Meeting Invite
|
|
75
|
+
* MIME-Version: 1.0
|
|
76
|
+
* Content-Type: multipart/mixed; boundary="${boundary}"
|
|
77
|
+
*
|
|
78
|
+
* --${boundary}
|
|
79
|
+
* Content-Type: text/plain
|
|
80
|
+
*
|
|
81
|
+
* You're invited to a meeting.
|
|
82
|
+
*
|
|
83
|
+
* --${boundary}
|
|
84
|
+
* Content-Type: text/calendar; method=REQUEST; name="invite.ics"
|
|
85
|
+
* Content-Disposition: attachment; filename="invite.ics"
|
|
86
|
+
*
|
|
87
|
+
* BEGIN:VCALENDAR
|
|
88
|
+
* ...
|
|
89
|
+
* END:VCALENDAR
|
|
90
|
+
* --${boundary}--`;
|
|
91
|
+
*
|
|
92
|
+
* await ccc.email.sendRaw(rawMessage);
|
|
93
|
+
*/
|
|
94
|
+
sendRaw(rawMessage: string): Promise<string>;
|
|
95
|
+
/**
|
|
96
|
+
* Retrieves the attachments from a specific inbound email.
|
|
97
|
+
*
|
|
98
|
+
* @param emailId - The unique ID of the email.
|
|
99
|
+
* @returns A promise resolving to an array of attachments.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* const attachments = await ccc.email.getAttachments(emailId);
|
|
103
|
+
* for (const att of attachments) {
|
|
104
|
+
* const content = Buffer.from(att.content, 'base64');
|
|
105
|
+
* console.log(`${att.filename}: ${att.contentType}, ${att.size} bytes`);
|
|
106
|
+
* }
|
|
107
|
+
*/
|
|
108
|
+
getAttachments(emailId: string): Promise<EmailAttachment[]>;
|
|
61
109
|
}
|
package/dist/email/index.js
CHANGED
|
@@ -43,7 +43,8 @@ export class EmailClient {
|
|
|
43
43
|
*
|
|
44
44
|
* @example
|
|
45
45
|
* await ccc.email.send({
|
|
46
|
-
*
|
|
46
|
+
* from: { name: 'Support', address: 'support@mail.example.com' },
|
|
47
|
+
* to: [{ name: 'User', address: 'user@example.com' }],
|
|
47
48
|
* subject: 'Hello',
|
|
48
49
|
* text: 'This is a test'
|
|
49
50
|
* });
|
|
@@ -57,6 +58,13 @@ export class EmailClient {
|
|
|
57
58
|
sendEmail(projectId: $projectId, email: $email)
|
|
58
59
|
}
|
|
59
60
|
`;
|
|
61
|
+
const normalize = (to) => {
|
|
62
|
+
if (typeof to === 'string')
|
|
63
|
+
return { address: to };
|
|
64
|
+
return to;
|
|
65
|
+
};
|
|
66
|
+
const toList = Array.isArray(options.to) ? options.to : [options.to];
|
|
67
|
+
const normalizedTo = toList.map(normalize);
|
|
60
68
|
const result = await this.ccc.request('/graphql', {
|
|
61
69
|
method: 'POST',
|
|
62
70
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -66,7 +74,7 @@ export class EmailClient {
|
|
|
66
74
|
projectId: pid,
|
|
67
75
|
email: {
|
|
68
76
|
from: options.from,
|
|
69
|
-
to:
|
|
77
|
+
to: normalizedTo,
|
|
70
78
|
subject: options.subject,
|
|
71
79
|
html: options.html,
|
|
72
80
|
text: options.text
|
|
@@ -202,4 +210,95 @@ export class EmailClient {
|
|
|
202
210
|
throw new Error(result.errors[0].message);
|
|
203
211
|
return result.data.deleteEmail;
|
|
204
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Sends a raw MIME-formatted email.
|
|
215
|
+
* This is useful for sending emails with attachments, like calendar invites.
|
|
216
|
+
*
|
|
217
|
+
* @param rawMessage - The full MIME message as a string, including headers and body.
|
|
218
|
+
* @returns A promise resolving to the Message-ID of the sent email.
|
|
219
|
+
* @throws Error if sending fails.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* const boundary = `----=_Part_${Date.now()}`;
|
|
223
|
+
* const rawMessage = `From: events@mail.yourdomain.com
|
|
224
|
+
* To: user@example.com
|
|
225
|
+
* Subject: Meeting Invite
|
|
226
|
+
* MIME-Version: 1.0
|
|
227
|
+
* Content-Type: multipart/mixed; boundary="${boundary}"
|
|
228
|
+
*
|
|
229
|
+
* --${boundary}
|
|
230
|
+
* Content-Type: text/plain
|
|
231
|
+
*
|
|
232
|
+
* You're invited to a meeting.
|
|
233
|
+
*
|
|
234
|
+
* --${boundary}
|
|
235
|
+
* Content-Type: text/calendar; method=REQUEST; name="invite.ics"
|
|
236
|
+
* Content-Disposition: attachment; filename="invite.ics"
|
|
237
|
+
*
|
|
238
|
+
* BEGIN:VCALENDAR
|
|
239
|
+
* ...
|
|
240
|
+
* END:VCALENDAR
|
|
241
|
+
* --${boundary}--`;
|
|
242
|
+
*
|
|
243
|
+
* await ccc.email.sendRaw(rawMessage);
|
|
244
|
+
*/
|
|
245
|
+
async sendRaw(rawMessage) {
|
|
246
|
+
const pid = this.ccc.projectId;
|
|
247
|
+
if (!pid)
|
|
248
|
+
throw new Error("projectId could not be resolved. Ensure CC_PROJECT_TOKEN is set.");
|
|
249
|
+
const query = `
|
|
250
|
+
mutation SendRawEmail($projectId: ID!, $rawMessage: String!) {
|
|
251
|
+
sendRawEmail(projectId: $projectId, rawMessage: $rawMessage)
|
|
252
|
+
}
|
|
253
|
+
`;
|
|
254
|
+
const result = await this.ccc.request('/graphql', {
|
|
255
|
+
method: 'POST',
|
|
256
|
+
headers: { 'Content-Type': 'application/json' },
|
|
257
|
+
body: JSON.stringify({
|
|
258
|
+
query,
|
|
259
|
+
variables: { projectId: pid, rawMessage }
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
if (result.errors)
|
|
263
|
+
throw new Error(result.errors[0].message);
|
|
264
|
+
return result.data.sendRawEmail;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Retrieves the attachments from a specific inbound email.
|
|
268
|
+
*
|
|
269
|
+
* @param emailId - The unique ID of the email.
|
|
270
|
+
* @returns A promise resolving to an array of attachments.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* const attachments = await ccc.email.getAttachments(emailId);
|
|
274
|
+
* for (const att of attachments) {
|
|
275
|
+
* const content = Buffer.from(att.content, 'base64');
|
|
276
|
+
* console.log(`${att.filename}: ${att.contentType}, ${att.size} bytes`);
|
|
277
|
+
* }
|
|
278
|
+
*/
|
|
279
|
+
async getAttachments(emailId) {
|
|
280
|
+
const query = `
|
|
281
|
+
query Email($id: ID!) {
|
|
282
|
+
email(id: $id) {
|
|
283
|
+
attachments {
|
|
284
|
+
filename
|
|
285
|
+
contentType
|
|
286
|
+
size
|
|
287
|
+
content
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
`;
|
|
292
|
+
const result = await this.ccc.request('/graphql', {
|
|
293
|
+
method: 'POST',
|
|
294
|
+
headers: { 'Content-Type': 'application/json' },
|
|
295
|
+
body: JSON.stringify({
|
|
296
|
+
query,
|
|
297
|
+
variables: { id: emailId }
|
|
298
|
+
})
|
|
299
|
+
});
|
|
300
|
+
if (result.errors)
|
|
301
|
+
throw new Error(result.errors[0].message);
|
|
302
|
+
return result.data.email?.attachments || [];
|
|
303
|
+
}
|
|
205
304
|
}
|
package/dist/email/types.d.ts
CHANGED
|
@@ -35,17 +35,25 @@ export interface InboundEmail {
|
|
|
35
35
|
/** References header. */
|
|
36
36
|
references?: string;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Represents an email address with an optional display name.
|
|
40
|
+
*/
|
|
41
|
+
export interface EmailAddress {
|
|
42
|
+
/** The display name of the account (e.g., "John Doe"). */
|
|
43
|
+
name?: string;
|
|
44
|
+
/** The raw email address (e.g., "john@example.com"). */
|
|
45
|
+
address: string;
|
|
46
|
+
}
|
|
38
47
|
/**
|
|
39
48
|
* Options for sending an email.
|
|
40
49
|
*/
|
|
41
50
|
export interface SendEmailOptions {
|
|
42
51
|
/**
|
|
43
52
|
* The sender address. Must be a verified domain.
|
|
44
|
-
* Use "Name <email@domain.com>" or just "email@domain.com".
|
|
45
53
|
*/
|
|
46
|
-
from?:
|
|
47
|
-
/** The recipient(s). Can be a string or an array of
|
|
48
|
-
to: string | string[];
|
|
54
|
+
from?: EmailAddress;
|
|
55
|
+
/** The recipient(s). Can be a string, an EmailAddress object, or an array of either. */
|
|
56
|
+
to: string | EmailAddress | (string | EmailAddress)[];
|
|
49
57
|
/** The subject line. */
|
|
50
58
|
subject: string;
|
|
51
59
|
/** The HTML body of the email. */
|
|
@@ -53,3 +61,16 @@ export interface SendEmailOptions {
|
|
|
53
61
|
/** The plain text body of the email. */
|
|
54
62
|
text?: string;
|
|
55
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Represents an email attachment.
|
|
66
|
+
*/
|
|
67
|
+
export interface EmailAttachment {
|
|
68
|
+
/** The filename of the attachment. */
|
|
69
|
+
filename: string;
|
|
70
|
+
/** The MIME type of the attachment. */
|
|
71
|
+
contentType: string;
|
|
72
|
+
/** The size of the attachment in bytes. */
|
|
73
|
+
size: number;
|
|
74
|
+
/** The content of the attachment, Base64 encoded. */
|
|
75
|
+
content: string;
|
|
76
|
+
}
|
package/docs/email.md
CHANGED
|
@@ -5,7 +5,9 @@ Managed email sending and receiving with inbound processing and domain verificat
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Send Emails**: Send transactional emails via Amazon SES (managed backend).
|
|
8
|
+
- **Send Raw Emails**: Send MIME-formatted emails with attachments (e.g., calendar invites).
|
|
8
9
|
- **Inbound Processing**: Receive emails, store them in GCS, and access them via API.
|
|
10
|
+
- **Attachment Support**: Extract and download attachments from inbound emails.
|
|
9
11
|
- **Domain Verification**: Automates DNS verification for custom domains.
|
|
10
12
|
|
|
11
13
|
## Usage
|
|
@@ -37,6 +39,43 @@ const messageId = await ccc.email.send({
|
|
|
37
39
|
});
|
|
38
40
|
```
|
|
39
41
|
|
|
42
|
+
### Sending Raw Emails (with Attachments)
|
|
43
|
+
|
|
44
|
+
Send MIME-formatted emails for advanced use cases like calendar invites.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// Construct a MIME message with an attachment
|
|
48
|
+
const boundary = `----=_Part_${Date.now()}`;
|
|
49
|
+
const rawMessage = `From: events@mail.yourdomain.com
|
|
50
|
+
To: user@example.com
|
|
51
|
+
Subject: Meeting Invite
|
|
52
|
+
MIME-Version: 1.0
|
|
53
|
+
Content-Type: multipart/mixed; boundary="${boundary}"
|
|
54
|
+
|
|
55
|
+
--${boundary}
|
|
56
|
+
Content-Type: text/plain; charset="utf-8"
|
|
57
|
+
|
|
58
|
+
You have been invited to a meeting.
|
|
59
|
+
|
|
60
|
+
--${boundary}
|
|
61
|
+
Content-Type: text/calendar; method=REQUEST; name="invite.ics"
|
|
62
|
+
Content-Disposition: attachment; filename="invite.ics"
|
|
63
|
+
|
|
64
|
+
BEGIN:VCALENDAR
|
|
65
|
+
VERSION:2.0
|
|
66
|
+
METHOD:REQUEST
|
|
67
|
+
BEGIN:VEVENT
|
|
68
|
+
UID:event-123@yourapp.com
|
|
69
|
+
DTSTART:20260210T100000Z
|
|
70
|
+
DTEND:20260210T110000Z
|
|
71
|
+
SUMMARY:Team Meeting
|
|
72
|
+
END:VEVENT
|
|
73
|
+
END:VCALENDAR
|
|
74
|
+
--${boundary}--`;
|
|
75
|
+
|
|
76
|
+
const messageId = await ccc.email.sendRaw(rawMessage);
|
|
77
|
+
```
|
|
78
|
+
|
|
40
79
|
### Retrieving Inbound Emails
|
|
41
80
|
|
|
42
81
|
Fetch the latest emails received by the project's inbound address.
|
|
@@ -54,12 +93,35 @@ for (const email of emails) {
|
|
|
54
93
|
}
|
|
55
94
|
```
|
|
56
95
|
|
|
96
|
+
### Retrieving Attachments
|
|
97
|
+
|
|
98
|
+
Get attachments from an inbound email.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const attachments = await ccc.email.getAttachments(emailId);
|
|
102
|
+
|
|
103
|
+
for (const att of attachments) {
|
|
104
|
+
console.log(`${att.filename}: ${att.contentType}, ${att.size} bytes`);
|
|
105
|
+
|
|
106
|
+
// Decode the Base64 content
|
|
107
|
+
const content = Buffer.from(att.content, 'base64');
|
|
108
|
+
|
|
109
|
+
// For calendar responses, parse the ICS
|
|
110
|
+
if (att.contentType.includes('calendar')) {
|
|
111
|
+
const icsText = content.toString('utf-8');
|
|
112
|
+
console.log('Calendar data:', icsText);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
57
117
|
## Agent Capabilities
|
|
58
118
|
|
|
59
119
|
The email service is designed to be easily used by AI agents to build automated workflows:
|
|
60
120
|
- **Auto-Reply Bots**: Poll `getInbound()` and `send()` replies.
|
|
61
121
|
- **Verification flows**: Trigger emails and read the verification codes from the inbox.
|
|
122
|
+
- **Calendar Workflows**: Send calendar invites with `sendRaw()` and process responses via `getAttachments()`.
|
|
62
123
|
|
|
63
124
|
## Authentication
|
|
64
125
|
|
|
65
126
|
Requires the `CC_PROJECT_TOKEN` environment variable, automatically injected in CloneCommand deployments.
|
|
127
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clonecommand/cloud",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The official SDK for CloneCommand managed services",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -33,4 +33,4 @@
|
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"typescript": "^5.0.0"
|
|
35
35
|
}
|
|
36
|
-
}
|
|
36
|
+
}
|