chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { CancelEmailParams, CancelEmailResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendCancelEmailTool')
|
||||
|
||||
export const resendCancelEmailTool: ToolConfig<CancelEmailParams, CancelEmailResult> = {
|
||||
id: 'resend_cancel_email',
|
||||
name: 'Cancel Email',
|
||||
description: 'Cancel a scheduled email before it is sent',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
cancelEmailId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the scheduled email to cancel',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: CancelEmailParams) =>
|
||||
`https://api.resend.com/emails/${encodeURIComponent(params.cancelEmailId.trim())}/cancel`,
|
||||
method: 'POST',
|
||||
headers: (params: CancelEmailParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<CancelEmailResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Cancel Email API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to cancel email',
|
||||
output: {
|
||||
id: '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Canceled email ID' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { CreateAudienceParams, CreateAudienceResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendCreateAudienceTool')
|
||||
|
||||
export const resendCreateAudienceTool: ToolConfig<CreateAudienceParams, CreateAudienceResult> = {
|
||||
id: 'resend_create_audience',
|
||||
name: 'Create Audience',
|
||||
description: 'Create a new audience in Resend',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
audienceName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the audience to create',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.resend.com/audiences',
|
||||
method: 'POST',
|
||||
headers: (params: CreateAudienceParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: CreateAudienceParams) => ({
|
||||
name: params.audienceName,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<CreateAudienceResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Create Audience API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to create audience',
|
||||
output: {
|
||||
id: '',
|
||||
name: '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Created audience ID' },
|
||||
name: { type: 'string', description: 'Audience name' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { CreateBroadcastParams, CreateBroadcastResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendCreateBroadcastTool')
|
||||
|
||||
export const resendCreateBroadcastTool: ToolConfig<CreateBroadcastParams, CreateBroadcastResult> = {
|
||||
id: 'resend_create_broadcast',
|
||||
name: 'Create Broadcast',
|
||||
description: 'Create a broadcast email for an audience in Resend',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
audienceId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the audience to send the broadcast to',
|
||||
},
|
||||
broadcastFrom: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Sender email address (e.g., "sender@example.com" or "Sender Name <sender@example.com>")',
|
||||
},
|
||||
broadcastSubject: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Broadcast email subject line',
|
||||
},
|
||||
broadcastHtml: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'HTML content of the broadcast',
|
||||
},
|
||||
broadcastText: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Plain text content of the broadcast',
|
||||
},
|
||||
broadcastReplyTo: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reply-to email address',
|
||||
},
|
||||
broadcastName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Friendly internal name for the broadcast',
|
||||
},
|
||||
broadcastPreviewText: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Preview text shown in the inbox before the email is opened',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.resend.com/broadcasts',
|
||||
method: 'POST',
|
||||
headers: (params: CreateBroadcastParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: CreateBroadcastParams) => ({
|
||||
audience_id: params.audienceId.trim(),
|
||||
from: params.broadcastFrom,
|
||||
subject: params.broadcastSubject,
|
||||
...(params.broadcastHtml && { html: params.broadcastHtml }),
|
||||
...(params.broadcastText && { text: params.broadcastText }),
|
||||
...(params.broadcastReplyTo && { reply_to: params.broadcastReplyTo }),
|
||||
...(params.broadcastName && { name: params.broadcastName }),
|
||||
...(params.broadcastPreviewText && { preview_text: params.broadcastPreviewText }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<CreateBroadcastResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Create Broadcast API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to create broadcast',
|
||||
output: {
|
||||
id: '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Created broadcast ID' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { CreateContactParams, CreateContactResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendCreateContactTool')
|
||||
|
||||
export const resendCreateContactTool: ToolConfig<CreateContactParams, CreateContactResult> = {
|
||||
id: 'resend_create_contact',
|
||||
name: 'Create Contact',
|
||||
description: 'Create a new contact in Resend',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email address of the contact',
|
||||
},
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'First name of the contact',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Last name of the contact',
|
||||
},
|
||||
unsubscribed: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the contact is unsubscribed from all broadcasts',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.resend.com/contacts',
|
||||
method: 'POST',
|
||||
headers: (params: CreateContactParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: CreateContactParams) => ({
|
||||
email: params.email,
|
||||
...(params.firstName && { first_name: params.firstName }),
|
||||
...(params.lastName && { last_name: params.lastName }),
|
||||
...(params.unsubscribed !== undefined && { unsubscribed: params.unsubscribed }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<CreateContactResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Create Contact API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to create contact',
|
||||
output: {
|
||||
id: '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Created contact ID' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { DeleteAudienceParams, DeleteAudienceResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendDeleteAudienceTool')
|
||||
|
||||
export const resendDeleteAudienceTool: ToolConfig<DeleteAudienceParams, DeleteAudienceResult> = {
|
||||
id: 'resend_delete_audience',
|
||||
name: 'Delete Audience',
|
||||
description: 'Delete an audience from Resend by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
audienceId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the audience to delete',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: DeleteAudienceParams) =>
|
||||
`https://api.resend.com/audiences/${encodeURIComponent(params.audienceId.trim())}`,
|
||||
method: 'DELETE',
|
||||
headers: (params: DeleteAudienceParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<DeleteAudienceResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.message && !data.deleted) {
|
||||
logger.error('Resend Delete Audience API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to delete audience',
|
||||
output: {
|
||||
id: '',
|
||||
deleted: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
deleted: data.deleted ?? true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Deleted audience ID' },
|
||||
deleted: { type: 'boolean', description: 'Whether the audience was successfully deleted' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { DeleteContactParams, DeleteContactResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendDeleteContactTool')
|
||||
|
||||
export const resendDeleteContactTool: ToolConfig<DeleteContactParams, DeleteContactResult> = {
|
||||
id: 'resend_delete_contact',
|
||||
name: 'Delete Contact',
|
||||
description: 'Delete a contact from Resend by ID or email',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
contactId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The contact ID or email address to delete',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: DeleteContactParams) =>
|
||||
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId.trim())}`,
|
||||
method: 'DELETE',
|
||||
headers: (params: DeleteContactParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<DeleteContactResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.message && !data.deleted) {
|
||||
logger.error('Resend Delete Contact API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to delete contact',
|
||||
output: {
|
||||
id: '',
|
||||
deleted: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.contact ?? data.id ?? '',
|
||||
deleted: data.deleted ?? true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Deleted contact ID' },
|
||||
deleted: { type: 'boolean', description: 'Whether the contact was successfully deleted' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { GetAudienceParams, GetAudienceResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendGetAudienceTool')
|
||||
|
||||
export const resendGetAudienceTool: ToolConfig<GetAudienceParams, GetAudienceResult> = {
|
||||
id: 'resend_get_audience',
|
||||
name: 'Get Audience',
|
||||
description: 'Retrieve details of an audience by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
audienceId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the audience to retrieve',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GetAudienceParams) =>
|
||||
`https://api.resend.com/audiences/${encodeURIComponent(params.audienceId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params: GetAudienceParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<GetAudienceResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Get Audience API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to retrieve audience',
|
||||
output: {
|
||||
id: '',
|
||||
name: '',
|
||||
createdAt: '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name ?? '',
|
||||
createdAt: data.created_at ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Audience ID' },
|
||||
name: { type: 'string', description: 'Audience name' },
|
||||
createdAt: { type: 'string', description: 'Audience creation timestamp' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { GetBroadcastParams, GetBroadcastResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendGetBroadcastTool')
|
||||
|
||||
export const resendGetBroadcastTool: ToolConfig<GetBroadcastParams, GetBroadcastResult> = {
|
||||
id: 'resend_get_broadcast',
|
||||
name: 'Get Broadcast',
|
||||
description: 'Retrieve details of a broadcast by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
broadcastId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the broadcast to retrieve',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GetBroadcastParams) =>
|
||||
`https://api.resend.com/broadcasts/${encodeURIComponent(params.broadcastId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params: GetBroadcastParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<GetBroadcastResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Get Broadcast API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to retrieve broadcast',
|
||||
output: {
|
||||
id: '',
|
||||
name: '',
|
||||
audienceId: null,
|
||||
segmentId: null,
|
||||
from: '',
|
||||
subject: '',
|
||||
replyTo: null,
|
||||
previewText: null,
|
||||
status: '',
|
||||
createdAt: '',
|
||||
scheduledAt: null,
|
||||
sentAt: null,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name ?? '',
|
||||
audienceId: data.audience_id ?? null,
|
||||
segmentId: data.segment_id ?? null,
|
||||
from: data.from ?? '',
|
||||
subject: data.subject ?? '',
|
||||
replyTo: data.reply_to ?? null,
|
||||
previewText: data.preview_text ?? null,
|
||||
status: data.status ?? '',
|
||||
createdAt: data.created_at ?? '',
|
||||
scheduledAt: data.scheduled_at ?? null,
|
||||
sentAt: data.sent_at ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Broadcast ID' },
|
||||
name: { type: 'string', description: 'Broadcast name' },
|
||||
audienceId: { type: 'string', description: 'Audience ID (legacy)', optional: true },
|
||||
segmentId: {
|
||||
type: 'string',
|
||||
description: 'Segment ID (the current recipient field)',
|
||||
optional: true,
|
||||
},
|
||||
from: { type: 'string', description: 'Sender email address' },
|
||||
subject: { type: 'string', description: 'Broadcast subject' },
|
||||
replyTo: { type: 'string', description: 'Reply-to email address', optional: true },
|
||||
previewText: { type: 'string', description: 'Inbox preview text', optional: true },
|
||||
status: { type: 'string', description: 'Broadcast status (e.g., draft, sent)' },
|
||||
createdAt: { type: 'string', description: 'Broadcast creation timestamp' },
|
||||
scheduledAt: { type: 'string', description: 'Scheduled send timestamp', optional: true },
|
||||
sentAt: { type: 'string', description: 'Timestamp the broadcast was sent', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { GetContactParams, GetContactResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendGetContactTool')
|
||||
|
||||
export const resendGetContactTool: ToolConfig<GetContactParams, GetContactResult> = {
|
||||
id: 'resend_get_contact',
|
||||
name: 'Get Contact',
|
||||
description: 'Retrieve details of a contact by ID or email',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
contactId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The contact ID or email address to retrieve',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GetContactParams) =>
|
||||
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params: GetContactParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<GetContactResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Get Contact API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to retrieve contact',
|
||||
output: {
|
||||
id: '',
|
||||
email: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
createdAt: '',
|
||||
unsubscribed: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
email: data.email ?? '',
|
||||
firstName: data.first_name ?? '',
|
||||
lastName: data.last_name ?? '',
|
||||
createdAt: data.created_at ?? '',
|
||||
unsubscribed: data.unsubscribed ?? false,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Contact ID' },
|
||||
email: { type: 'string', description: 'Contact email address' },
|
||||
firstName: { type: 'string', description: 'Contact first name' },
|
||||
lastName: { type: 'string', description: 'Contact last name' },
|
||||
createdAt: { type: 'string', description: 'Contact creation timestamp' },
|
||||
unsubscribed: { type: 'boolean', description: 'Whether the contact is unsubscribed' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { GetEmailParams, GetEmailResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendGetEmailTool')
|
||||
|
||||
export const resendGetEmailTool: ToolConfig<GetEmailParams, GetEmailResult> = {
|
||||
id: 'resend_get_email',
|
||||
name: 'Get Email',
|
||||
description: 'Retrieve details of a previously sent email by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
emailId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the email to retrieve',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GetEmailParams) => `https://api.resend.com/emails/${params.emailId.trim()}`,
|
||||
method: 'GET',
|
||||
headers: (params: GetEmailParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<GetEmailResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Get Email API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to retrieve email',
|
||||
output: {
|
||||
id: '',
|
||||
from: '',
|
||||
to: [],
|
||||
subject: '',
|
||||
html: '',
|
||||
text: null,
|
||||
cc: [],
|
||||
bcc: [],
|
||||
replyTo: [],
|
||||
lastEvent: '',
|
||||
createdAt: '',
|
||||
scheduledAt: null,
|
||||
tags: [],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
from: data.from ?? '',
|
||||
to: data.to ?? [],
|
||||
subject: data.subject ?? '',
|
||||
html: data.html ?? '',
|
||||
text: data.text ?? null,
|
||||
cc: data.cc ?? [],
|
||||
bcc: data.bcc ?? [],
|
||||
replyTo: data.reply_to ?? [],
|
||||
lastEvent: data.last_event ?? '',
|
||||
createdAt: data.created_at ?? '',
|
||||
scheduledAt: data.scheduled_at ?? null,
|
||||
tags: data.tags ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Email ID' },
|
||||
from: { type: 'string', description: 'Sender email address' },
|
||||
to: {
|
||||
type: 'array',
|
||||
description: 'Recipient email addresses',
|
||||
items: { type: 'string', description: 'Recipient email address' },
|
||||
},
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
html: { type: 'string', description: 'HTML email content' },
|
||||
text: { type: 'string', description: 'Plain text email content', optional: true },
|
||||
cc: {
|
||||
type: 'array',
|
||||
description: 'CC email addresses',
|
||||
items: { type: 'string', description: 'CC email address' },
|
||||
},
|
||||
bcc: {
|
||||
type: 'array',
|
||||
description: 'BCC email addresses',
|
||||
items: { type: 'string', description: 'BCC email address' },
|
||||
},
|
||||
replyTo: {
|
||||
type: 'array',
|
||||
description: 'Reply-to email addresses',
|
||||
items: { type: 'string', description: 'Reply-to email address' },
|
||||
},
|
||||
lastEvent: { type: 'string', description: 'Last event status (e.g., delivered, bounced)' },
|
||||
createdAt: { type: 'string', description: 'Email creation timestamp' },
|
||||
scheduledAt: { type: 'string', description: 'Scheduled send timestamp', optional: true },
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'Email tags as name-value pairs',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Tag name' },
|
||||
value: { type: 'string', description: 'Tag value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export { resendCancelEmailTool } from '@/tools/resend/cancel_email'
|
||||
export { resendCreateAudienceTool } from '@/tools/resend/create_audience'
|
||||
export { resendCreateBroadcastTool } from '@/tools/resend/create_broadcast'
|
||||
export { resendCreateContactTool } from '@/tools/resend/create_contact'
|
||||
export { resendDeleteAudienceTool } from '@/tools/resend/delete_audience'
|
||||
export { resendDeleteContactTool } from '@/tools/resend/delete_contact'
|
||||
export { resendGetAudienceTool } from '@/tools/resend/get_audience'
|
||||
export { resendGetBroadcastTool } from '@/tools/resend/get_broadcast'
|
||||
export { resendGetContactTool } from '@/tools/resend/get_contact'
|
||||
export { resendGetEmailTool } from '@/tools/resend/get_email'
|
||||
export { resendListAudiencesTool } from '@/tools/resend/list_audiences'
|
||||
export { resendListContactsTool } from '@/tools/resend/list_contacts'
|
||||
export { resendListDomainsTool } from '@/tools/resend/list_domains'
|
||||
export { resendSendTool } from '@/tools/resend/send'
|
||||
export { resendSendBroadcastTool } from '@/tools/resend/send_broadcast'
|
||||
export { resendUpdateContactTool } from '@/tools/resend/update_contact'
|
||||
export * from './types'
|
||||
@@ -0,0 +1,70 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { ListAudiencesParams, ListAudiencesResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendListAudiencesTool')
|
||||
|
||||
export const resendListAudiencesTool: ToolConfig<ListAudiencesParams, ListAudiencesResult> = {
|
||||
id: 'resend_list_audiences',
|
||||
name: 'List Audiences',
|
||||
description: 'List all audiences in Resend',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.resend.com/audiences',
|
||||
method: 'GET',
|
||||
headers: (params: ListAudiencesParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<ListAudiencesResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.message) {
|
||||
logger.error('Resend List Audiences API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to list audiences',
|
||||
output: {
|
||||
audiences: [],
|
||||
hasMore: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
audiences: data.data ?? [],
|
||||
hasMore: data.has_more ?? false,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
audiences: {
|
||||
type: 'array',
|
||||
description: 'Array of audiences',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Audience ID' },
|
||||
name: { type: 'string', description: 'Audience name' },
|
||||
created_at: { type: 'string', description: 'Audience creation timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
hasMore: { type: 'boolean', description: 'Whether there are more audiences to retrieve' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { ListContactsParams, ListContactsResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendListContactsTool')
|
||||
|
||||
export const resendListContactsTool: ToolConfig<ListContactsParams, ListContactsResult> = {
|
||||
id: 'resend_list_contacts',
|
||||
name: 'List Contacts',
|
||||
description: 'List all contacts in Resend',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.resend.com/contacts',
|
||||
method: 'GET',
|
||||
headers: (params: ListContactsParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<ListContactsResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.message) {
|
||||
logger.error('Resend List Contacts API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to list contacts',
|
||||
output: {
|
||||
contacts: [],
|
||||
hasMore: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
contacts: data.data ?? [],
|
||||
hasMore: data.has_more ?? false,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
contacts: {
|
||||
type: 'array',
|
||||
description: 'Array of contacts',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Contact ID' },
|
||||
email: { type: 'string', description: 'Contact email address' },
|
||||
first_name: { type: 'string', description: 'Contact first name' },
|
||||
last_name: { type: 'string', description: 'Contact last name' },
|
||||
created_at: { type: 'string', description: 'Contact creation timestamp' },
|
||||
unsubscribed: { type: 'boolean', description: 'Whether the contact is unsubscribed' },
|
||||
},
|
||||
},
|
||||
},
|
||||
hasMore: { type: 'boolean', description: 'Whether there are more contacts to retrieve' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { ListDomainsParams, ListDomainsResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendListDomainsTool')
|
||||
|
||||
export const resendListDomainsTool: ToolConfig<ListDomainsParams, ListDomainsResult> = {
|
||||
id: 'resend_list_domains',
|
||||
name: 'List Domains',
|
||||
description: 'List all verified domains in your Resend account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.resend.com/domains',
|
||||
method: 'GET',
|
||||
headers: (params: ListDomainsParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<ListDomainsResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.message) {
|
||||
logger.error('Resend List Domains API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to list domains',
|
||||
output: {
|
||||
domains: [],
|
||||
hasMore: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
domains: (data.data ?? []).map(
|
||||
(domain: {
|
||||
id: string
|
||||
name: string
|
||||
status: string
|
||||
region: string
|
||||
created_at: string
|
||||
}) => ({
|
||||
id: domain.id,
|
||||
name: domain.name,
|
||||
status: domain.status,
|
||||
region: domain.region,
|
||||
createdAt: domain.created_at,
|
||||
})
|
||||
),
|
||||
hasMore: data.has_more ?? false,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
domains: {
|
||||
type: 'array',
|
||||
description: 'Array of domains',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Domain ID' },
|
||||
name: { type: 'string', description: 'Domain name' },
|
||||
status: { type: 'string', description: 'Domain verification status' },
|
||||
region: { type: 'string', description: 'Region the domain is configured in' },
|
||||
createdAt: { type: 'string', description: 'Domain creation timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
hasMore: { type: 'boolean', description: 'Whether there are more domains to retrieve' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import type { MailSendParams, MailSendResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const resendSendTool: ToolConfig<MailSendParams, MailSendResult> = {
|
||||
id: 'resend_send',
|
||||
name: 'Send Email',
|
||||
description: 'Send an email using your own Resend API key and from address',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
fromAddress: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Email address to send from (e.g., "sender@example.com" or "Sender Name <sender@example.com>")',
|
||||
},
|
||||
to: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Recipient email address (e.g., "recipient@example.com" or "Recipient Name <recipient@example.com>")',
|
||||
},
|
||||
subject: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email subject line',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email body content (plain text or HTML based on contentType)',
|
||||
},
|
||||
contentType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Content type for the email body: "text" for plain text or "html" for HTML content',
|
||||
},
|
||||
cc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Carbon copy recipient email address',
|
||||
},
|
||||
bcc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Blind carbon copy recipient email address',
|
||||
},
|
||||
replyTo: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reply-to email address',
|
||||
},
|
||||
scheduledAt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Schedule email to be sent later in ISO 8601 format',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated key:value pairs for email tags (e.g., "category:welcome,type:onboarding")',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key for sending emails',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/mail/send',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: MailSendParams) => ({
|
||||
resendApiKey: params.resendApiKey,
|
||||
fromAddress: params.fromAddress,
|
||||
to: params.to,
|
||||
subject: params.subject,
|
||||
body: params.body,
|
||||
contentType: params.contentType || 'text',
|
||||
...(params.cc && { cc: params.cc }),
|
||||
...(params.bcc && { bcc: params.bcc }),
|
||||
...(params.replyTo && { replyTo: params.replyTo }),
|
||||
...(params.scheduledAt && { scheduledAt: params.scheduledAt }),
|
||||
...(params.tags && { tags: params.tags }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params): Promise<MailSendResult> => {
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
id: result.data?.id || '',
|
||||
to: params?.to || '',
|
||||
subject: params?.subject || '',
|
||||
body: params?.body || '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the email was sent successfully' },
|
||||
id: { type: 'string', description: 'Email ID returned by Resend' },
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
body: { type: 'string', description: 'Email body content' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { SendBroadcastParams, SendBroadcastResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendSendBroadcastTool')
|
||||
|
||||
export const resendSendBroadcastTool: ToolConfig<SendBroadcastParams, SendBroadcastResult> = {
|
||||
id: 'resend_send_broadcast',
|
||||
name: 'Send Broadcast',
|
||||
description: 'Send a broadcast immediately or schedule it for later',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
broadcastId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the broadcast to send',
|
||||
},
|
||||
broadcastScheduledAt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Schedule delivery in natural language (e.g., "in 1 min") or ISO 8601 format. Sends immediately if omitted',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: SendBroadcastParams) =>
|
||||
`https://api.resend.com/broadcasts/${encodeURIComponent(params.broadcastId.trim())}/send`,
|
||||
method: 'POST',
|
||||
headers: (params: SendBroadcastParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: SendBroadcastParams) => ({
|
||||
...(params.broadcastScheduledAt && { scheduled_at: params.broadcastScheduledAt }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<SendBroadcastResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Send Broadcast API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to send broadcast',
|
||||
output: {
|
||||
id: '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Broadcast ID' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
/** Send Email */
|
||||
export interface MailSendParams {
|
||||
resendApiKey: string
|
||||
fromAddress: string
|
||||
to: string
|
||||
subject: string
|
||||
body: string
|
||||
contentType?: 'text' | 'html'
|
||||
cc?: string
|
||||
bcc?: string
|
||||
replyTo?: string
|
||||
scheduledAt?: string
|
||||
tags?: string
|
||||
}
|
||||
|
||||
export interface MailSendResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
id: string
|
||||
to: string
|
||||
subject: string
|
||||
body: string
|
||||
}
|
||||
}
|
||||
|
||||
/** Get Email */
|
||||
export interface GetEmailParams {
|
||||
resendApiKey: string
|
||||
emailId: string
|
||||
}
|
||||
|
||||
export interface GetEmailResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
from: string
|
||||
to: string[]
|
||||
subject: string
|
||||
html: string
|
||||
text: string | null
|
||||
cc: string[]
|
||||
bcc: string[]
|
||||
replyTo: string[]
|
||||
lastEvent: string
|
||||
createdAt: string
|
||||
scheduledAt: string | null
|
||||
tags: Array<{ name: string; value: string }>
|
||||
}
|
||||
}
|
||||
|
||||
/** Create Contact */
|
||||
export interface CreateContactParams {
|
||||
resendApiKey: string
|
||||
email: string
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
unsubscribed?: boolean
|
||||
}
|
||||
|
||||
export interface CreateContactResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
/** List Contacts */
|
||||
export interface ListContactsParams {
|
||||
resendApiKey: string
|
||||
}
|
||||
|
||||
export interface ListContactsResult extends ToolResponse {
|
||||
output: {
|
||||
contacts: Array<{
|
||||
id: string
|
||||
email: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
created_at: string
|
||||
unsubscribed: boolean
|
||||
}>
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/** Get Contact */
|
||||
export interface GetContactParams {
|
||||
resendApiKey: string
|
||||
contactId: string
|
||||
}
|
||||
|
||||
export interface GetContactResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
email: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
createdAt: string
|
||||
unsubscribed: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/** Update Contact */
|
||||
export interface UpdateContactParams {
|
||||
resendApiKey: string
|
||||
contactId: string
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
unsubscribed?: boolean
|
||||
}
|
||||
|
||||
export interface UpdateContactResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete Contact */
|
||||
export interface DeleteContactParams {
|
||||
resendApiKey: string
|
||||
contactId: string
|
||||
}
|
||||
|
||||
export interface DeleteContactResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/** List Domains */
|
||||
export interface ListDomainsParams {
|
||||
resendApiKey: string
|
||||
}
|
||||
|
||||
export interface ListDomainsResult extends ToolResponse {
|
||||
output: {
|
||||
domains: Array<{
|
||||
id: string
|
||||
name: string
|
||||
status: string
|
||||
region: string
|
||||
createdAt: string
|
||||
}>
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/** Cancel Email */
|
||||
export interface CancelEmailParams {
|
||||
resendApiKey: string
|
||||
cancelEmailId: string
|
||||
}
|
||||
|
||||
export interface CancelEmailResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
/** Create Audience */
|
||||
export interface CreateAudienceParams {
|
||||
resendApiKey: string
|
||||
audienceName: string
|
||||
}
|
||||
|
||||
export interface CreateAudienceResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
/** Get Audience */
|
||||
export interface GetAudienceParams {
|
||||
resendApiKey: string
|
||||
audienceId: string
|
||||
}
|
||||
|
||||
export interface GetAudienceResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
createdAt: string
|
||||
}
|
||||
}
|
||||
|
||||
/** List Audiences */
|
||||
export interface ListAudiencesParams {
|
||||
resendApiKey: string
|
||||
}
|
||||
|
||||
export interface ListAudiencesResult extends ToolResponse {
|
||||
output: {
|
||||
audiences: Array<{
|
||||
id: string
|
||||
name: string
|
||||
created_at: string
|
||||
}>
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete Audience */
|
||||
export interface DeleteAudienceParams {
|
||||
resendApiKey: string
|
||||
audienceId: string
|
||||
}
|
||||
|
||||
export interface DeleteAudienceResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/** Create Broadcast */
|
||||
export interface CreateBroadcastParams {
|
||||
resendApiKey: string
|
||||
audienceId: string
|
||||
broadcastFrom: string
|
||||
broadcastSubject: string
|
||||
broadcastReplyTo?: string
|
||||
broadcastHtml?: string
|
||||
broadcastText?: string
|
||||
broadcastName?: string
|
||||
broadcastPreviewText?: string
|
||||
}
|
||||
|
||||
export interface CreateBroadcastResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
/** Send Broadcast */
|
||||
export interface SendBroadcastParams {
|
||||
resendApiKey: string
|
||||
broadcastId: string
|
||||
broadcastScheduledAt?: string
|
||||
}
|
||||
|
||||
export interface SendBroadcastResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
/** Get Broadcast */
|
||||
export interface GetBroadcastParams {
|
||||
resendApiKey: string
|
||||
broadcastId: string
|
||||
}
|
||||
|
||||
export interface GetBroadcastResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
audienceId: string | null
|
||||
segmentId: string | null
|
||||
from: string
|
||||
subject: string
|
||||
replyTo: string | string[] | null
|
||||
previewText: string | null
|
||||
status: string
|
||||
createdAt: string
|
||||
scheduledAt: string | null
|
||||
sentAt: string | null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { UpdateContactParams, UpdateContactResult } from '@/tools/resend/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('ResendUpdateContactTool')
|
||||
|
||||
export const resendUpdateContactTool: ToolConfig<UpdateContactParams, UpdateContactResult> = {
|
||||
id: 'resend_update_contact',
|
||||
name: 'Update Contact',
|
||||
description: 'Update an existing contact in Resend',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
contactId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The contact ID or email address to update',
|
||||
},
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated first name',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated last name',
|
||||
},
|
||||
unsubscribed: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the contact should be unsubscribed from all broadcasts',
|
||||
},
|
||||
resendApiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Resend API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: UpdateContactParams) =>
|
||||
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId.trim())}`,
|
||||
method: 'PATCH',
|
||||
headers: (params: UpdateContactParams) => ({
|
||||
Authorization: `Bearer ${params.resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: UpdateContactParams) => ({
|
||||
...(params.firstName !== undefined && { first_name: params.firstName }),
|
||||
...(params.lastName !== undefined && { last_name: params.lastName }),
|
||||
...(params.unsubscribed !== undefined && { unsubscribed: params.unsubscribed }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<UpdateContactResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
logger.error('Resend Update Contact API error:', JSON.stringify(data, null, 2))
|
||||
return {
|
||||
success: false,
|
||||
error: data.message || 'Failed to update contact',
|
||||
output: {
|
||||
id: '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Updated contact ID' },
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user