@poncho-ai/messaging 0.2.6 → 0.2.8
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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +13 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +23 -3
- package/package.json +2 -2
- package/src/adapters/resend/index.ts +35 -3
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/messaging@0.2.
|
|
2
|
+
> @poncho-ai/messaging@0.2.8 build /home/runner/work/poncho-ai/poncho-ai/packages/messaging
|
|
3
3
|
> tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
[34mCLI[39m tsup v8.5.1
|
|
8
8
|
[34mCLI[39m Target: es2022
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
11
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mESM[39m [1mdist/index.js [22m[32m30.13 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 77ms
|
|
12
12
|
[34mDTS[39m Build start
|
|
13
|
-
[32mDTS[39m ⚡️ Build success in
|
|
14
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m8.
|
|
13
|
+
[32mDTS[39m ⚡️ Build success in 5331ms
|
|
14
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m8.98 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @poncho-ai/messaging
|
|
2
2
|
|
|
3
|
+
## 0.2.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`deb134e`](https://github.com/cesr/poncho-ai/commit/deb134e8a6ecf38d85dc200f57998e33406eff61) Thanks [@cesr](https://github.com/cesr)! - Retry Resend API requests on transient socket errors (common on Vercel cold starts).
|
|
8
|
+
|
|
9
|
+
## 0.2.7
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`075b9ac`](https://github.com/cesr/poncho-ai/commit/075b9ac3556847af913bf2b58f030575c3b99852)]:
|
|
14
|
+
- @poncho-ai/sdk@1.4.0
|
|
15
|
+
|
|
3
16
|
## 0.2.6
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -144,6 +144,7 @@ interface ResendAdapterOptions {
|
|
|
144
144
|
apiKeyEnv?: string;
|
|
145
145
|
webhookSecretEnv?: string;
|
|
146
146
|
fromEnv?: string;
|
|
147
|
+
replyToEnv?: string;
|
|
147
148
|
allowedSenders?: string[];
|
|
148
149
|
mode?: "auto-reply" | "tool";
|
|
149
150
|
allowedRecipients?: string[];
|
|
@@ -157,9 +158,11 @@ declare class ResendAdapter implements MessagingAdapter {
|
|
|
157
158
|
private apiKey;
|
|
158
159
|
private webhookSecret;
|
|
159
160
|
private fromAddress;
|
|
161
|
+
private replyToAddress;
|
|
160
162
|
private readonly apiKeyEnv;
|
|
161
163
|
private readonly webhookSecretEnv;
|
|
162
164
|
private readonly fromEnv;
|
|
165
|
+
private readonly replyToEnv;
|
|
163
166
|
private readonly allowedSenders;
|
|
164
167
|
private readonly allowedRecipients;
|
|
165
168
|
private readonly maxSendsPerRun;
|
package/dist/index.js
CHANGED
|
@@ -450,6 +450,20 @@ function matchesSenderPattern(sender, patterns) {
|
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
// src/adapters/resend/index.ts
|
|
453
|
+
var isSocketError = (err) => err instanceof TypeError && err.message === "fetch failed" && err.cause?.code === "UND_ERR_SOCKET";
|
|
454
|
+
async function fetchWithRetry(input, init, retries = 2) {
|
|
455
|
+
for (let attempt = 0; ; attempt++) {
|
|
456
|
+
try {
|
|
457
|
+
return await fetch(input, init);
|
|
458
|
+
} catch (err) {
|
|
459
|
+
if (attempt < retries && isSocketError(err)) {
|
|
460
|
+
await new Promise((r) => setTimeout(r, 200 * (attempt + 1)));
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
throw err;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
453
467
|
function verifySvixSignature(rawBody, svixId, svixTimestamp, svixSignature, secret) {
|
|
454
468
|
const now = Math.floor(Date.now() / 1e3);
|
|
455
469
|
const ts = parseInt(svixTimestamp, 10);
|
|
@@ -518,9 +532,11 @@ var ResendAdapter = class {
|
|
|
518
532
|
apiKey = "";
|
|
519
533
|
webhookSecret = "";
|
|
520
534
|
fromAddress = "";
|
|
535
|
+
replyToAddress = "";
|
|
521
536
|
apiKeyEnv;
|
|
522
537
|
webhookSecretEnv;
|
|
523
538
|
fromEnv;
|
|
539
|
+
replyToEnv;
|
|
524
540
|
allowedSenders;
|
|
525
541
|
allowedRecipients;
|
|
526
542
|
maxSendsPerRun;
|
|
@@ -535,6 +551,7 @@ var ResendAdapter = class {
|
|
|
535
551
|
this.apiKeyEnv = options.apiKeyEnv ?? "RESEND_API_KEY";
|
|
536
552
|
this.webhookSecretEnv = options.webhookSecretEnv ?? "RESEND_WEBHOOK_SECRET";
|
|
537
553
|
this.fromEnv = options.fromEnv ?? "RESEND_FROM";
|
|
554
|
+
this.replyToEnv = options.replyToEnv ?? "RESEND_REPLY_TO";
|
|
538
555
|
this.allowedSenders = options.allowedSenders;
|
|
539
556
|
this.mode = options.mode ?? "auto-reply";
|
|
540
557
|
this.autoReply = this.mode !== "tool";
|
|
@@ -552,6 +569,7 @@ var ResendAdapter = class {
|
|
|
552
569
|
this.apiKey = process.env[this.apiKeyEnv] ?? "";
|
|
553
570
|
this.webhookSecret = process.env[this.webhookSecretEnv] ?? "";
|
|
554
571
|
this.fromAddress = process.env[this.fromEnv] ?? "";
|
|
572
|
+
this.replyToAddress = process.env[this.replyToEnv] ?? "";
|
|
555
573
|
if (!this.apiKey) {
|
|
556
574
|
throw new Error(
|
|
557
575
|
`Resend messaging: ${this.apiKeyEnv} environment variable is not set`
|
|
@@ -605,6 +623,7 @@ var ResendAdapter = class {
|
|
|
605
623
|
subject,
|
|
606
624
|
text: content,
|
|
607
625
|
html: markdownToEmailHtml(content),
|
|
626
|
+
reply_to: this.replyToAddress ? [this.replyToAddress] : void 0,
|
|
608
627
|
headers,
|
|
609
628
|
attachments: attachments && attachments.length > 0 ? attachments : void 0
|
|
610
629
|
});
|
|
@@ -712,6 +731,7 @@ var ResendAdapter = class {
|
|
|
712
731
|
html: markdownToEmailHtml(body),
|
|
713
732
|
cc: cc && cc.length > 0 ? cc : void 0,
|
|
714
733
|
bcc: bcc && bcc.length > 0 ? bcc : void 0,
|
|
734
|
+
reply_to: this.replyToAddress ? [this.replyToAddress] : void 0,
|
|
715
735
|
headers: Object.keys(headers).length > 0 ? headers : void 0
|
|
716
736
|
});
|
|
717
737
|
if (result.error) {
|
|
@@ -798,7 +818,7 @@ var ResendAdapter = class {
|
|
|
798
818
|
let emailHeaders;
|
|
799
819
|
if (emailId) {
|
|
800
820
|
try {
|
|
801
|
-
const resp = await
|
|
821
|
+
const resp = await fetchWithRetry(
|
|
802
822
|
`https://api.resend.com/emails/receiving/${emailId}`,
|
|
803
823
|
{ headers: { Authorization: `Bearer ${this.apiKey}` } }
|
|
804
824
|
);
|
|
@@ -854,7 +874,7 @@ var ResendAdapter = class {
|
|
|
854
874
|
if (!emailId || !webhookAttachments || webhookAttachments.length === 0) return [];
|
|
855
875
|
let attachments = [];
|
|
856
876
|
try {
|
|
857
|
-
const resp = await
|
|
877
|
+
const resp = await fetchWithRetry(
|
|
858
878
|
`https://api.resend.com/emails/receiving/${emailId}/attachments`,
|
|
859
879
|
{ headers: { Authorization: `Bearer ${this.apiKey}` } }
|
|
860
880
|
);
|
|
@@ -873,7 +893,7 @@ var ResendAdapter = class {
|
|
|
873
893
|
for (const att of attachments) {
|
|
874
894
|
if (!att.download_url) continue;
|
|
875
895
|
try {
|
|
876
|
-
const resp = await
|
|
896
|
+
const resp = await fetchWithRetry(att.download_url);
|
|
877
897
|
if (!resp.ok) continue;
|
|
878
898
|
const buf = Buffer.from(await resp.arrayBuffer());
|
|
879
899
|
results.push({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/messaging",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Messaging platform adapters for Poncho agents (Slack, Telegram, etc.)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@poncho-ai/sdk": "1.
|
|
23
|
+
"@poncho-ai/sdk": "1.4.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"resend": ">=4.0.0"
|
|
@@ -9,6 +9,30 @@ import type {
|
|
|
9
9
|
RouteRegistrar,
|
|
10
10
|
ThreadRef,
|
|
11
11
|
} from "../../types.js";
|
|
12
|
+
|
|
13
|
+
const isSocketError = (err: unknown): boolean =>
|
|
14
|
+
err instanceof TypeError &&
|
|
15
|
+
err.message === "fetch failed" &&
|
|
16
|
+
(err as { cause?: { code?: string } }).cause?.code === "UND_ERR_SOCKET";
|
|
17
|
+
|
|
18
|
+
async function fetchWithRetry(
|
|
19
|
+
input: string | URL | Request,
|
|
20
|
+
init?: RequestInit,
|
|
21
|
+
retries = 2,
|
|
22
|
+
): Promise<Response> {
|
|
23
|
+
for (let attempt = 0; ; attempt++) {
|
|
24
|
+
try {
|
|
25
|
+
return await fetch(input, init);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
if (attempt < retries && isSocketError(err)) {
|
|
28
|
+
await new Promise((r) => setTimeout(r, 200 * (attempt + 1)));
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
12
36
|
import {
|
|
13
37
|
buildReplyHeaders,
|
|
14
38
|
buildReplySubject,
|
|
@@ -34,6 +58,7 @@ interface ResendClient {
|
|
|
34
58
|
html?: string;
|
|
35
59
|
cc?: string[];
|
|
36
60
|
bcc?: string[];
|
|
61
|
+
reply_to?: string[];
|
|
37
62
|
headers?: Record<string, string>;
|
|
38
63
|
attachments?: Array<{ filename: string; content?: string; path?: string; contentType?: string }>;
|
|
39
64
|
}): Promise<{ data?: { id: string }; error?: unknown }>;
|
|
@@ -158,6 +183,7 @@ export interface ResendAdapterOptions {
|
|
|
158
183
|
apiKeyEnv?: string;
|
|
159
184
|
webhookSecretEnv?: string;
|
|
160
185
|
fromEnv?: string;
|
|
186
|
+
replyToEnv?: string;
|
|
161
187
|
allowedSenders?: string[];
|
|
162
188
|
mode?: "auto-reply" | "tool";
|
|
163
189
|
allowedRecipients?: string[];
|
|
@@ -188,9 +214,11 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
188
214
|
private apiKey = "";
|
|
189
215
|
private webhookSecret = "";
|
|
190
216
|
private fromAddress = "";
|
|
217
|
+
private replyToAddress = "";
|
|
191
218
|
private readonly apiKeyEnv: string;
|
|
192
219
|
private readonly webhookSecretEnv: string;
|
|
193
220
|
private readonly fromEnv: string;
|
|
221
|
+
private readonly replyToEnv: string;
|
|
194
222
|
private readonly allowedSenders: string[] | undefined;
|
|
195
223
|
private readonly allowedRecipients: string[] | undefined;
|
|
196
224
|
private readonly maxSendsPerRun: number;
|
|
@@ -208,6 +236,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
208
236
|
this.apiKeyEnv = options.apiKeyEnv ?? "RESEND_API_KEY";
|
|
209
237
|
this.webhookSecretEnv = options.webhookSecretEnv ?? "RESEND_WEBHOOK_SECRET";
|
|
210
238
|
this.fromEnv = options.fromEnv ?? "RESEND_FROM";
|
|
239
|
+
this.replyToEnv = options.replyToEnv ?? "RESEND_REPLY_TO";
|
|
211
240
|
this.allowedSenders = options.allowedSenders;
|
|
212
241
|
this.mode = options.mode ?? "auto-reply";
|
|
213
242
|
this.autoReply = this.mode !== "tool";
|
|
@@ -228,6 +257,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
228
257
|
this.apiKey = process.env[this.apiKeyEnv] ?? "";
|
|
229
258
|
this.webhookSecret = process.env[this.webhookSecretEnv] ?? "";
|
|
230
259
|
this.fromAddress = process.env[this.fromEnv] ?? "";
|
|
260
|
+
this.replyToAddress = process.env[this.replyToEnv] ?? "";
|
|
231
261
|
|
|
232
262
|
if (!this.apiKey) {
|
|
233
263
|
throw new Error(
|
|
@@ -298,6 +328,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
298
328
|
subject,
|
|
299
329
|
text: content,
|
|
300
330
|
html: markdownToEmailHtml(content),
|
|
331
|
+
reply_to: this.replyToAddress ? [this.replyToAddress] : undefined,
|
|
301
332
|
headers,
|
|
302
333
|
attachments: attachments && attachments.length > 0 ? attachments : undefined,
|
|
303
334
|
});
|
|
@@ -423,6 +454,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
423
454
|
html: markdownToEmailHtml(body),
|
|
424
455
|
cc: cc && cc.length > 0 ? cc : undefined,
|
|
425
456
|
bcc: bcc && bcc.length > 0 ? bcc : undefined,
|
|
457
|
+
reply_to: this.replyToAddress ? [this.replyToAddress] : undefined,
|
|
426
458
|
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
427
459
|
});
|
|
428
460
|
|
|
@@ -542,7 +574,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
542
574
|
|
|
543
575
|
if (emailId) {
|
|
544
576
|
try {
|
|
545
|
-
const resp = await
|
|
577
|
+
const resp = await fetchWithRetry(
|
|
546
578
|
`https://api.resend.com/emails/receiving/${emailId}`,
|
|
547
579
|
{ headers: { Authorization: `Bearer ${this.apiKey}` } },
|
|
548
580
|
);
|
|
@@ -616,7 +648,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
616
648
|
// Fetch attachment metadata (with download_url) from the Resend API
|
|
617
649
|
let attachments: Array<{ filename?: string; content_type?: string; download_url?: string }> = [];
|
|
618
650
|
try {
|
|
619
|
-
const resp = await
|
|
651
|
+
const resp = await fetchWithRetry(
|
|
620
652
|
`https://api.resend.com/emails/receiving/${emailId}/attachments`,
|
|
621
653
|
{ headers: { Authorization: `Bearer ${this.apiKey}` } },
|
|
622
654
|
);
|
|
@@ -636,7 +668,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
636
668
|
for (const att of attachments) {
|
|
637
669
|
if (!att.download_url) continue;
|
|
638
670
|
try {
|
|
639
|
-
const resp = await
|
|
671
|
+
const resp = await fetchWithRetry(att.download_url);
|
|
640
672
|
if (!resp.ok) continue;
|
|
641
673
|
const buf = Buffer.from(await resp.arrayBuffer());
|
|
642
674
|
results.push({
|