@automate.ax/integration-contracts 0.68.1 → 0.68.2
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/gmail/gmail.d.ts +12 -1
- package/dist/gmail/gmail.js +39 -3
- package/package.json +2 -2
- package/src/gmail/gmail.ts +51 -2
package/dist/gmail/gmail.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { ATTACHMENT_INPUT_SCHEMA, DRAFT_IDENTIFIER_SCHEMA, LABEL_SCHEMA, MESSAGE
|
|
|
5
5
|
export declare const AUTOMATE_AX_EMAIL_HEADER = "X-Sent-From-Automate-Ax";
|
|
6
6
|
/** Internal options for constructing one RFC 5322 message. */
|
|
7
7
|
export interface BuildMessageOptions {
|
|
8
|
+
/** Signed identity of the automation composing the message. */
|
|
9
|
+
automationOriginToken?: string;
|
|
8
10
|
/** Files and inline MIME parts attached to the message. */
|
|
9
11
|
attachments?: z.input<typeof ATTACHMENT_INPUT_SCHEMA>[];
|
|
10
12
|
/** Blind-copy recipients. */
|
|
@@ -46,6 +48,14 @@ export declare function getGmailApi(secret: Record<string, unknown>): gmail_v1.G
|
|
|
46
48
|
* @param options - Message recipients, content, headers, and attachments.
|
|
47
49
|
*/
|
|
48
50
|
export declare function buildRawMessage(options: BuildMessageOptions): Promise<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Replaces Automate.ax's origin header without changing the MIME body.
|
|
53
|
+
*
|
|
54
|
+
* @param raw - Base64url-encoded RFC 5322 message.
|
|
55
|
+
* @param token - Signed automation-origin token.
|
|
56
|
+
* @throws When the message has no RFC 5322 header boundary.
|
|
57
|
+
*/
|
|
58
|
+
export declare function setRawMessageAutomationOrigin(raw: string, token: string): string;
|
|
49
59
|
/**
|
|
50
60
|
* Resolves a Gmail label name or ID to its canonical ID.
|
|
51
61
|
*
|
|
@@ -117,8 +127,9 @@ export declare function toMessageMetadata(message: gmail_v1.Schema$Message, oper
|
|
|
117
127
|
* @param gmailApi - Authenticated Gmail client.
|
|
118
128
|
* @param original - Message receiving the reply.
|
|
119
129
|
* @param input - Reply body, recipients, headers, and attachments.
|
|
130
|
+
* @param automationOriginToken - Signed identity of the sending automation.
|
|
120
131
|
*/
|
|
121
|
-
export declare function sendReply(gmailApi: gmail_v1.Gmail, original: gmail_v1.Schema$Message, input: z.input<typeof REPLY_OPTIONS_SCHEMA
|
|
132
|
+
export declare function sendReply(gmailApi: gmail_v1.Gmail, original: gmail_v1.Schema$Message, input: z.input<typeof REPLY_OPTIONS_SCHEMA>, automationOriginToken?: string): Promise<{
|
|
122
133
|
messageId: string;
|
|
123
134
|
threadId: string;
|
|
124
135
|
labelIds: string[];
|
package/dist/gmail/gmail.js
CHANGED
|
@@ -4,9 +4,9 @@ import MailComposer from "nodemailer/lib/mail-composer";
|
|
|
4
4
|
import PostalMime, { addressParser, } from "postal-mime";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
export const AUTOMATE_AX_EMAIL_HEADER = "X-Sent-From-Automate-Ax";
|
|
7
|
+
const AUTOMATE_AX_EMAIL_HEADER_LOWERCASE = AUTOMATE_AX_EMAIL_HEADER.toLowerCase();
|
|
7
8
|
const AUTOMATE_EMAIL_HEADERS = {
|
|
8
9
|
"X-Mailer": "Automate.ax",
|
|
9
|
-
[AUTOMATE_AX_EMAIL_HEADER]: "true",
|
|
10
10
|
};
|
|
11
11
|
const HTML_TO_TEXT = compile({ wordwrap: false });
|
|
12
12
|
const GMAIL_SECRET_SCHEMA = z.object({
|
|
@@ -56,8 +56,11 @@ export async function buildRawMessage(options) {
|
|
|
56
56
|
cc: options.cc,
|
|
57
57
|
from: options.from,
|
|
58
58
|
headers: {
|
|
59
|
-
...options.headers,
|
|
59
|
+
...Object.fromEntries(Object.entries(options.headers ?? {}).filter(([name]) => name.toLowerCase() !== AUTOMATE_AX_EMAIL_HEADER_LOWERCASE)),
|
|
60
60
|
...AUTOMATE_EMAIL_HEADERS,
|
|
61
|
+
...(options.automationOriginToken && {
|
|
62
|
+
[AUTOMATE_AX_EMAIL_HEADER]: options.automationOriginToken,
|
|
63
|
+
}),
|
|
61
64
|
},
|
|
62
65
|
html: options.html,
|
|
63
66
|
inReplyTo: options.inReplyTo,
|
|
@@ -73,6 +76,37 @@ export async function buildRawMessage(options) {
|
|
|
73
76
|
message.keepBcc = true;
|
|
74
77
|
return (await message.build()).toString("base64url");
|
|
75
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Replaces Automate.ax's origin header without changing the MIME body.
|
|
81
|
+
*
|
|
82
|
+
* @param raw - Base64url-encoded RFC 5322 message.
|
|
83
|
+
* @param token - Signed automation-origin token.
|
|
84
|
+
* @throws When the message has no RFC 5322 header boundary.
|
|
85
|
+
*/
|
|
86
|
+
export function setRawMessageAutomationOrigin(raw, token) {
|
|
87
|
+
const message = Buffer.from(raw, "base64url").toString("latin1");
|
|
88
|
+
const headerSeparator = message.includes("\r\n\r\n") ? "\r\n" : "\n";
|
|
89
|
+
const headerEnd = message.indexOf(`${headerSeparator}${headerSeparator}`);
|
|
90
|
+
if (headerEnd === -1)
|
|
91
|
+
throw new Error("Email message has no header boundary.");
|
|
92
|
+
const headers = [];
|
|
93
|
+
let removesCurrentHeader = false;
|
|
94
|
+
for (const line of message.slice(0, headerEnd).split(headerSeparator)) {
|
|
95
|
+
if (/^[\t ]/.test(line)) {
|
|
96
|
+
if (!removesCurrentHeader)
|
|
97
|
+
headers.push(line);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const colon = line.indexOf(":");
|
|
101
|
+
removesCurrentHeader =
|
|
102
|
+
colon !== -1 &&
|
|
103
|
+
line.slice(0, colon).toLowerCase() === AUTOMATE_AX_EMAIL_HEADER_LOWERCASE;
|
|
104
|
+
if (!removesCurrentHeader)
|
|
105
|
+
headers.push(line);
|
|
106
|
+
}
|
|
107
|
+
headers.push(`${AUTOMATE_AX_EMAIL_HEADER}: ${token}`);
|
|
108
|
+
return Buffer.from(`${headers.join(headerSeparator)}${headerSeparator}${headerSeparator}${message.slice(headerEnd + headerSeparator.length * 2)}`, "latin1").toString("base64url");
|
|
109
|
+
}
|
|
76
110
|
/**
|
|
77
111
|
* Resolves a Gmail label name or ID to its canonical ID.
|
|
78
112
|
*
|
|
@@ -235,13 +269,15 @@ export function toMessageMetadata(message, operation) {
|
|
|
235
269
|
* @param gmailApi - Authenticated Gmail client.
|
|
236
270
|
* @param original - Message receiving the reply.
|
|
237
271
|
* @param input - Reply body, recipients, headers, and attachments.
|
|
272
|
+
* @param automationOriginToken - Signed identity of the sending automation.
|
|
238
273
|
*/
|
|
239
|
-
export async function sendReply(gmailApi, original, input) {
|
|
274
|
+
export async function sendReply(gmailApi, original, input, automationOriginToken) {
|
|
240
275
|
const replyOptions = await getReplyOptions(gmailApi, original, input.replyAll ?? false);
|
|
241
276
|
return toMessageMetadata((await gmailApi.users.messages.send({
|
|
242
277
|
requestBody: {
|
|
243
278
|
raw: await buildRawMessage({
|
|
244
279
|
...getReplyMessageOptions(replyOptions, input),
|
|
280
|
+
automationOriginToken,
|
|
245
281
|
bcc: input.bcc,
|
|
246
282
|
from: input.from,
|
|
247
283
|
headers: input.headers,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automate.ax/integration-contracts",
|
|
3
|
-
"version": "0.68.
|
|
3
|
+
"version": "0.68.2",
|
|
4
4
|
"description": "Shared integration payload contracts and provider primitives for Automate.ax.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@automate.ax/codec": "0.68.
|
|
44
|
+
"@automate.ax/codec": "0.68.2",
|
|
45
45
|
"@googleapis/calendar": "^15.0.0",
|
|
46
46
|
"@googleapis/forms": "^6.0.1",
|
|
47
47
|
"@googleapis/gmail": "^12.0.0",
|
package/src/gmail/gmail.ts
CHANGED
|
@@ -19,9 +19,10 @@ import {
|
|
|
19
19
|
} from "./schemas"
|
|
20
20
|
|
|
21
21
|
export const AUTOMATE_AX_EMAIL_HEADER = "X-Sent-From-Automate-Ax"
|
|
22
|
+
const AUTOMATE_AX_EMAIL_HEADER_LOWERCASE =
|
|
23
|
+
AUTOMATE_AX_EMAIL_HEADER.toLowerCase()
|
|
22
24
|
const AUTOMATE_EMAIL_HEADERS = {
|
|
23
25
|
"X-Mailer": "Automate.ax",
|
|
24
|
-
[AUTOMATE_AX_EMAIL_HEADER]: "true",
|
|
25
26
|
}
|
|
26
27
|
const HTML_TO_TEXT = compile({ wordwrap: false })
|
|
27
28
|
const GMAIL_SECRET_SCHEMA = z.object({
|
|
@@ -37,6 +38,9 @@ const LABEL_TYPE_SCHEMA = z.enum(["system", "user"])
|
|
|
37
38
|
|
|
38
39
|
/** Internal options for constructing one RFC 5322 message. */
|
|
39
40
|
export interface BuildMessageOptions {
|
|
41
|
+
/** Signed identity of the automation composing the message. */
|
|
42
|
+
automationOriginToken?: string
|
|
43
|
+
|
|
40
44
|
/** Files and inline MIME parts attached to the message. */
|
|
41
45
|
attachments?: z.input<typeof ATTACHMENT_INPUT_SCHEMA>[]
|
|
42
46
|
|
|
@@ -120,8 +124,15 @@ export async function buildRawMessage(options: BuildMessageOptions) {
|
|
|
120
124
|
cc: options.cc,
|
|
121
125
|
from: options.from,
|
|
122
126
|
headers: {
|
|
123
|
-
...
|
|
127
|
+
...Object.fromEntries(
|
|
128
|
+
Object.entries(options.headers ?? {}).filter(
|
|
129
|
+
([name]) => name.toLowerCase() !== AUTOMATE_AX_EMAIL_HEADER_LOWERCASE,
|
|
130
|
+
),
|
|
131
|
+
),
|
|
124
132
|
...AUTOMATE_EMAIL_HEADERS,
|
|
133
|
+
...(options.automationOriginToken && {
|
|
134
|
+
[AUTOMATE_AX_EMAIL_HEADER]: options.automationOriginToken,
|
|
135
|
+
}),
|
|
125
136
|
},
|
|
126
137
|
html: options.html,
|
|
127
138
|
inReplyTo: options.inReplyTo,
|
|
@@ -140,6 +151,41 @@ export async function buildRawMessage(options: BuildMessageOptions) {
|
|
|
140
151
|
return (await message.build()).toString("base64url")
|
|
141
152
|
}
|
|
142
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Replaces Automate.ax's origin header without changing the MIME body.
|
|
156
|
+
*
|
|
157
|
+
* @param raw - Base64url-encoded RFC 5322 message.
|
|
158
|
+
* @param token - Signed automation-origin token.
|
|
159
|
+
* @throws When the message has no RFC 5322 header boundary.
|
|
160
|
+
*/
|
|
161
|
+
export function setRawMessageAutomationOrigin(raw: string, token: string) {
|
|
162
|
+
const message = Buffer.from(raw, "base64url").toString("latin1")
|
|
163
|
+
const headerSeparator = message.includes("\r\n\r\n") ? "\r\n" : "\n"
|
|
164
|
+
const headerEnd = message.indexOf(`${headerSeparator}${headerSeparator}`)
|
|
165
|
+
if (headerEnd === -1) throw new Error("Email message has no header boundary.")
|
|
166
|
+
|
|
167
|
+
const headers: string[] = []
|
|
168
|
+
let removesCurrentHeader = false
|
|
169
|
+
for (const line of message.slice(0, headerEnd).split(headerSeparator)) {
|
|
170
|
+
if (/^[\t ]/.test(line)) {
|
|
171
|
+
if (!removesCurrentHeader) headers.push(line)
|
|
172
|
+
continue
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const colon = line.indexOf(":")
|
|
176
|
+
removesCurrentHeader =
|
|
177
|
+
colon !== -1 &&
|
|
178
|
+
line.slice(0, colon).toLowerCase() === AUTOMATE_AX_EMAIL_HEADER_LOWERCASE
|
|
179
|
+
if (!removesCurrentHeader) headers.push(line)
|
|
180
|
+
}
|
|
181
|
+
headers.push(`${AUTOMATE_AX_EMAIL_HEADER}: ${token}`)
|
|
182
|
+
|
|
183
|
+
return Buffer.from(
|
|
184
|
+
`${headers.join(headerSeparator)}${headerSeparator}${headerSeparator}${message.slice(headerEnd + headerSeparator.length * 2)}`,
|
|
185
|
+
"latin1",
|
|
186
|
+
).toString("base64url")
|
|
187
|
+
}
|
|
188
|
+
|
|
143
189
|
/**
|
|
144
190
|
* Resolves a Gmail label name or ID to its canonical ID.
|
|
145
191
|
*
|
|
@@ -345,11 +391,13 @@ export function toMessageMetadata(
|
|
|
345
391
|
* @param gmailApi - Authenticated Gmail client.
|
|
346
392
|
* @param original - Message receiving the reply.
|
|
347
393
|
* @param input - Reply body, recipients, headers, and attachments.
|
|
394
|
+
* @param automationOriginToken - Signed identity of the sending automation.
|
|
348
395
|
*/
|
|
349
396
|
export async function sendReply(
|
|
350
397
|
gmailApi: gmail_v1.Gmail,
|
|
351
398
|
original: gmail_v1.Schema$Message,
|
|
352
399
|
input: z.input<typeof REPLY_OPTIONS_SCHEMA>,
|
|
400
|
+
automationOriginToken?: string,
|
|
353
401
|
) {
|
|
354
402
|
const replyOptions = await getReplyOptions(
|
|
355
403
|
gmailApi,
|
|
@@ -362,6 +410,7 @@ export async function sendReply(
|
|
|
362
410
|
requestBody: {
|
|
363
411
|
raw: await buildRawMessage({
|
|
364
412
|
...getReplyMessageOptions(replyOptions, input),
|
|
413
|
+
automationOriginToken,
|
|
365
414
|
bcc: input.bcc,
|
|
366
415
|
from: input.from,
|
|
367
416
|
headers: input.headers,
|