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,122 @@
|
||||
import type {
|
||||
AddContactParams,
|
||||
ContactResult,
|
||||
SendGridContactObject,
|
||||
SendGridContactRequest,
|
||||
} from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridAddContactTool: ToolConfig<AddContactParams, ContactResult> = {
|
||||
id: 'sendgrid_add_contact',
|
||||
name: 'SendGrid Add Contact',
|
||||
description: 'Add a new contact to SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Contact email address',
|
||||
},
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Contact first name',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Contact last name',
|
||||
},
|
||||
customFields: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON object of custom field key-value pairs (use field IDs like e1_T, e2_N, e3_D, not field names)',
|
||||
},
|
||||
listIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list IDs to add the contact to',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/marketing/contacts',
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const contact: SendGridContactObject = {
|
||||
email: params.email,
|
||||
}
|
||||
|
||||
if (params.firstName) contact.first_name = params.firstName
|
||||
if (params.lastName) contact.last_name = params.lastName
|
||||
|
||||
if (params.customFields) {
|
||||
const customFields =
|
||||
typeof params.customFields === 'string'
|
||||
? JSON.parse(params.customFields)
|
||||
: params.customFields
|
||||
contact.custom_fields = customFields as Record<string, unknown>
|
||||
}
|
||||
|
||||
const body: SendGridContactRequest = {
|
||||
contacts: [contact],
|
||||
}
|
||||
|
||||
if (params.listIds) {
|
||||
body.list_ids = params.listIds.split(',').map((id) => id.trim())
|
||||
}
|
||||
|
||||
return { body: JSON.stringify(body) }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<ContactResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to add contact')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
jobId: data.job_id ?? null,
|
||||
email: params?.email || '',
|
||||
firstName: params?.firstName,
|
||||
lastName: params?.lastName,
|
||||
message:
|
||||
'Contact is being added. This is an asynchronous operation. Use the job ID to track status.',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
jobId: {
|
||||
type: 'string',
|
||||
description: 'Job ID for tracking the async contact creation',
|
||||
optional: true,
|
||||
},
|
||||
email: { type: 'string', description: 'Contact email address' },
|
||||
firstName: { type: 'string', description: 'Contact first name', optional: true },
|
||||
lastName: { type: 'string', description: 'Contact last name', optional: true },
|
||||
message: { type: 'string', description: 'Status message' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import type { AddContactsToListParams, SendGridContactObject } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridAddContactsToListTool: ToolConfig<AddContactsToListParams, ToolResponse> = {
|
||||
id: 'sendgrid_add_contacts_to_list',
|
||||
name: 'SendGrid Add Contacts to List',
|
||||
description:
|
||||
'Add or update contacts and assign them to a list in SendGrid (uses PUT /v3/marketing/contacts)',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List ID to add contacts to',
|
||||
},
|
||||
contacts: {
|
||||
type: 'json',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array of contact objects. Each contact must have at least: email (or phone_number_id/external_id/anonymous_id). Example: [{"email": "user@example.com", "first_name": "John"}]',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/marketing/contacts',
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const contactsArray: SendGridContactObject[] =
|
||||
typeof params.contacts === 'string' ? JSON.parse(params.contacts) : params.contacts
|
||||
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
list_ids: [params.listId],
|
||||
contacts: contactsArray,
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to add contacts to list')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as { job_id: string }
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
jobId: data.job_id,
|
||||
message: 'Contacts are being added to the list. This is an asynchronous operation.',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
jobId: { type: 'string', description: 'Job ID for tracking the async operation' },
|
||||
message: { type: 'string', description: 'Status message' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { CreateListParams, ListResult, SendGridList } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridCreateListTool: ToolConfig<CreateListParams, ListResult> = {
|
||||
id: 'sendgrid_create_list',
|
||||
name: 'SendGrid Create List',
|
||||
description: 'Create a new contact list in SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List name',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/marketing/lists',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
name: params.name,
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ListResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to create list')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridList
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
contactCount: data.contact_count,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'List ID' },
|
||||
name: { type: 'string', description: 'List name' },
|
||||
contactCount: { type: 'number', description: 'Number of contacts in the list' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import type { CreateTemplateParams, SendGridTemplate, TemplateResult } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridCreateTemplateTool: ToolConfig<CreateTemplateParams, TemplateResult> = {
|
||||
id: 'sendgrid_create_template',
|
||||
name: 'SendGrid Create Template',
|
||||
description: 'Create a new email template in SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template name',
|
||||
},
|
||||
generation: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template generation type (legacy or dynamic, default: dynamic)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/templates',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
name: params.name,
|
||||
generation: params.generation || 'dynamic',
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<TemplateResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to create template')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridTemplate
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
generation: data.generation,
|
||||
updatedAt: data.updated_at,
|
||||
versions: data.versions || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Template ID' },
|
||||
name: { type: 'string', description: 'Template name' },
|
||||
generation: { type: 'string', description: 'Template generation' },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp' },
|
||||
versions: { type: 'json', description: 'Array of template versions' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import type {
|
||||
CreateTemplateVersionParams,
|
||||
SendGridTemplateVersionRequest,
|
||||
TemplateVersionResult,
|
||||
} from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const INACTIVE_VALUES: unknown[] = [false, 'false', 0, '0']
|
||||
|
||||
/** Coerces any dynamic-reference form of SendGrid's active flag (boolean, string, or
|
||||
* number) to the 0/1 integer the API requires. Shared with the block's own
|
||||
* pre-coercion in blocks/blocks/sendgrid.ts so both layers stay in sync. */
|
||||
export function toActiveFlag(active: unknown): 0 | 1 {
|
||||
if (active === undefined) return 1
|
||||
return INACTIVE_VALUES.includes(active) ? 0 : 1
|
||||
}
|
||||
|
||||
export const sendGridCreateTemplateVersionTool: ToolConfig<
|
||||
CreateTemplateVersionParams,
|
||||
TemplateVersionResult
|
||||
> = {
|
||||
id: 'sendgrid_create_template_version',
|
||||
name: 'SendGrid Create Template Version',
|
||||
description: 'Create a new version of an email template in SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
templateId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Version name',
|
||||
},
|
||||
subject: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email subject line',
|
||||
},
|
||||
htmlContent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'HTML content of the template',
|
||||
},
|
||||
plainContent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Plain text content of the template',
|
||||
},
|
||||
active: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether this version is active (default: true)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/templates/${params.templateId}/versions`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: SendGridTemplateVersionRequest = {
|
||||
name: params.name,
|
||||
subject: params.subject,
|
||||
active: toActiveFlag(params.active),
|
||||
}
|
||||
|
||||
if (params.htmlContent) {
|
||||
body.html_content = params.htmlContent
|
||||
}
|
||||
|
||||
if (params.plainContent) {
|
||||
body.plain_content = params.plainContent
|
||||
}
|
||||
|
||||
return { body: JSON.stringify(body) }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<TemplateVersionResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to create template version')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
templateId: data.template_id,
|
||||
name: data.name,
|
||||
subject: data.subject,
|
||||
active: data.active === 1,
|
||||
htmlContent: data.html_content ?? null,
|
||||
plainContent: data.plain_content ?? null,
|
||||
updatedAt: data.updated_at ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Version ID' },
|
||||
templateId: { type: 'string', description: 'Template ID' },
|
||||
name: { type: 'string', description: 'Version name' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
active: { type: 'boolean', description: 'Whether this version is active' },
|
||||
htmlContent: { type: 'string', description: 'HTML content', optional: true },
|
||||
plainContent: { type: 'string', description: 'Plain text content', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { DeleteContactParams } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridDeleteContactsTool: ToolConfig<DeleteContactParams, ToolResponse> = {
|
||||
id: 'sendgrid_delete_contacts',
|
||||
name: 'SendGrid Delete Contacts',
|
||||
description: 'Delete one or more contacts from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
contactIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated contact IDs to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const ids = params.contactIds
|
||||
.split(',')
|
||||
.map((id) => id.trim())
|
||||
.join(',')
|
||||
return `https://api.sendgrid.com/v3/marketing/contacts?ids=${encodeURIComponent(ids)}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to delete contacts')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as { job_id: string }
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
jobId: data.job_id,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
jobId: { type: 'string', description: 'Job ID for the deletion request' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { DeleteListParams } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridDeleteListTool: ToolConfig<DeleteListParams, ToolResponse> = {
|
||||
id: 'sendgrid_delete_list',
|
||||
name: 'SendGrid Delete List',
|
||||
description: 'Delete a contact list from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List ID to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/marketing/lists/${params.listId}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to delete list')
|
||||
}
|
||||
|
||||
// API returns 204 No Content on success
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
message: 'List deleted successfully',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
message: { type: 'string', description: 'Success message' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { DeleteTemplateParams } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridDeleteTemplateTool: ToolConfig<DeleteTemplateParams, ToolResponse> = {
|
||||
id: 'sendgrid_delete_template',
|
||||
name: 'SendGrid Delete Template',
|
||||
description: 'Delete an email template from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
templateId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template ID to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/templates/${params.templateId}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to delete template')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {},
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import type { ContactResult, GetContactParams, SendGridContact } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridGetContactTool: ToolConfig<GetContactParams, ContactResult> = {
|
||||
id: 'sendgrid_get_contact',
|
||||
name: 'SendGrid Get Contact',
|
||||
description: 'Get a specific contact by ID from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
contactId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Contact ID',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/marketing/contacts/${params.contactId}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ContactResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to get contact')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridContact
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
email: data.email,
|
||||
firstName: data.first_name,
|
||||
lastName: data.last_name,
|
||||
createdAt: data.created_at,
|
||||
updatedAt: data.updated_at,
|
||||
listIds: data.list_ids ?? [],
|
||||
customFields: data.custom_fields ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Contact ID' },
|
||||
email: { type: 'string', description: 'Contact email address' },
|
||||
firstName: { type: 'string', description: 'Contact first name', optional: true },
|
||||
lastName: { type: 'string', description: 'Contact last name', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
|
||||
listIds: {
|
||||
type: 'json',
|
||||
description: 'Array of list IDs the contact belongs to',
|
||||
optional: true,
|
||||
},
|
||||
customFields: { type: 'json', description: 'Custom field values', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { GetListParams, ListResult, SendGridList } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridGetListTool: ToolConfig<GetListParams, ListResult> = {
|
||||
id: 'sendgrid_get_list',
|
||||
name: 'SendGrid Get List',
|
||||
description: 'Get a specific list by ID from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List ID',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/marketing/lists/${params.listId}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ListResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to get list')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridList
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
contactCount: data.contact_count,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'List ID' },
|
||||
name: { type: 'string', description: 'List name' },
|
||||
contactCount: { type: 'number', description: 'Number of contacts in the list' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { GetTemplateParams, SendGridTemplate, TemplateResult } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridGetTemplateTool: ToolConfig<GetTemplateParams, TemplateResult> = {
|
||||
id: 'sendgrid_get_template',
|
||||
name: 'SendGrid Get Template',
|
||||
description: 'Get a specific template by ID from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
templateId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template ID',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/templates/${params.templateId}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<TemplateResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to get template')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridTemplate
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
generation: data.generation,
|
||||
updatedAt: data.updated_at,
|
||||
versions: data.versions || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Template ID' },
|
||||
name: { type: 'string', description: 'Template name' },
|
||||
generation: { type: 'string', description: 'Template generation' },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp' },
|
||||
versions: { type: 'json', description: 'Array of template versions' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Mail Send
|
||||
|
||||
// Contact Management
|
||||
export { sendGridAddContactTool } from './add_contact'
|
||||
export { sendGridAddContactsToListTool } from './add_contacts_to_list'
|
||||
// List Management
|
||||
export { sendGridCreateListTool } from './create_list'
|
||||
// Template Management
|
||||
export { sendGridCreateTemplateTool } from './create_template'
|
||||
export { sendGridCreateTemplateVersionTool } from './create_template_version'
|
||||
export { sendGridDeleteContactsTool } from './delete_contacts'
|
||||
export { sendGridDeleteListTool } from './delete_list'
|
||||
export { sendGridDeleteTemplateTool } from './delete_template'
|
||||
export { sendGridGetContactTool } from './get_contact'
|
||||
export { sendGridGetListTool } from './get_list'
|
||||
export { sendGridGetTemplateTool } from './get_template'
|
||||
export { sendGridListAllListsTool } from './list_all_lists'
|
||||
export { sendGridListTemplatesTool } from './list_templates'
|
||||
export { sendGridRemoveContactsFromListTool } from './remove_contacts_from_list'
|
||||
export { sendGridSearchContactsTool } from './search_contacts'
|
||||
export { sendGridSendMailTool } from './send_mail'
|
||||
// Types
|
||||
export * from './types'
|
||||
@@ -0,0 +1,85 @@
|
||||
import type { ListAllListsParams, ListsResult, SendGridList } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridListAllListsTool: ToolConfig<ListAllListsParams, ListsResult> = {
|
||||
id: 'sendgrid_list_all_lists',
|
||||
name: 'SendGrid List All Lists',
|
||||
description: 'Get all contact lists from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of lists to return per page (default: 100, max: 1000)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response (nextPageToken) to fetch the next page',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.sendgrid.com/v3/marketing/lists')
|
||||
if (params.pageSize) {
|
||||
url.searchParams.append('page_size', params.pageSize.toString())
|
||||
}
|
||||
if (params.pageToken) {
|
||||
url.searchParams.append('page_token', params.pageToken)
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ListsResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to list all lists')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as {
|
||||
result?: SendGridList[]
|
||||
_metadata?: { next?: string }
|
||||
}
|
||||
|
||||
let nextPageToken: string | null = null
|
||||
if (data._metadata?.next) {
|
||||
try {
|
||||
nextPageToken = new URL(data._metadata.next).searchParams.get('page_token')
|
||||
} catch {
|
||||
nextPageToken = null
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
lists: data.result || [],
|
||||
nextPageToken,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
lists: { type: 'json', description: 'Array of lists' },
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token to pass as pageToken to fetch the next page, if more results exist',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import type { ListTemplatesParams, SendGridTemplate, TemplatesResult } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridListTemplatesTool: ToolConfig<ListTemplatesParams, TemplatesResult> = {
|
||||
id: 'sendgrid_list_templates',
|
||||
name: 'SendGrid List Templates',
|
||||
description: 'Get all email templates from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
generations: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by generation (legacy, dynamic, or both)',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Number of templates to return per page (default: 20, max: 200). ' +
|
||||
'When paginating with pageToken, pass the same pageSize used on the first request ' +
|
||||
'to keep page boundaries consistent.',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response (nextPageToken) to fetch the next page',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.sendgrid.com/v3/templates')
|
||||
if (params.generations) {
|
||||
url.searchParams.append('generations', params.generations)
|
||||
}
|
||||
url.searchParams.append('page_size', (params.pageSize || 20).toString())
|
||||
if (params.pageToken) {
|
||||
url.searchParams.append('page_token', params.pageToken)
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<TemplatesResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to list templates')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as {
|
||||
result?: SendGridTemplate[]
|
||||
_metadata?: { next?: string }
|
||||
}
|
||||
|
||||
let nextPageToken: string | null = null
|
||||
if (data._metadata?.next) {
|
||||
try {
|
||||
nextPageToken = new URL(data._metadata.next).searchParams.get('page_token')
|
||||
} catch {
|
||||
nextPageToken = null
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
templates: data.result || [],
|
||||
nextPageToken,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
templates: { type: 'json', description: 'Array of templates' },
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token to pass as pageToken to fetch the next page, if more results exist',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import type { RemoveContactsFromListParams } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridRemoveContactsFromListTool: ToolConfig<
|
||||
RemoveContactsFromListParams,
|
||||
ToolResponse
|
||||
> = {
|
||||
id: 'sendgrid_remove_contacts_from_list',
|
||||
name: 'SendGrid Remove Contacts from List',
|
||||
description: 'Remove contacts from a specific list in SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List ID',
|
||||
},
|
||||
contactIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated contact IDs to remove from the list',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const contactIds = params.contactIds
|
||||
.split(',')
|
||||
.map((id) => id.trim())
|
||||
.join(',')
|
||||
return `https://api.sendgrid.com/v3/marketing/lists/${params.listId}/contacts?contact_ids=${encodeURIComponent(contactIds)}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to remove contacts from list')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as { job_id?: string }
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
jobId: data.job_id ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
jobId: { type: 'string', description: 'Job ID for the request', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import type { ContactsResult, SearchContactsParams, SendGridContact } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridSearchContactsTool: ToolConfig<SearchContactsParams, ContactsResult> = {
|
||||
id: 'sendgrid_search_contacts',
|
||||
name: 'SendGrid Search Contacts',
|
||||
description: 'Search for contacts in SendGrid using a query',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
"Search query (e.g., \"email LIKE '%example.com%' AND CONTAINS(list_ids, 'list-id')\")",
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/marketing/contacts/search',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
query: params.query,
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ContactsResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to search contacts')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as {
|
||||
result?: SendGridContact[]
|
||||
contact_count?: number
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
contacts: data.result || [],
|
||||
contactCount: data.contact_count ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
contacts: { type: 'json', description: 'Array of matching contacts' },
|
||||
contactCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of contacts found',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import type { SendMailParams, SendMailResult } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridSendMailTool: ToolConfig<SendMailParams, SendMailResult> = {
|
||||
id: 'sendgrid_send_mail',
|
||||
name: 'SendGrid Send Mail',
|
||||
description: 'Send an email using SendGrid API',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
from: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sender email address (must be verified in SendGrid)',
|
||||
},
|
||||
fromName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sender name',
|
||||
},
|
||||
to: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Recipient email address',
|
||||
},
|
||||
toName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Recipient name',
|
||||
},
|
||||
subject: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email subject (required unless using a template with pre-defined subject)',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email body content (required unless using a template with pre-defined content)',
|
||||
},
|
||||
contentType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Content type (text/plain or text/html)',
|
||||
},
|
||||
cc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'CC email address',
|
||||
},
|
||||
bcc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'BCC email address',
|
||||
},
|
||||
replyTo: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reply-to email address',
|
||||
},
|
||||
replyToName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reply-to name',
|
||||
},
|
||||
attachments: {
|
||||
type: 'file[]',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Files to attach to the email (UserFile objects)',
|
||||
},
|
||||
templateId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'SendGrid template ID to use',
|
||||
},
|
||||
dynamicTemplateData: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'JSON object of dynamic template data',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/sendgrid/send-mail',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
apiKey: params.apiKey,
|
||||
from: params.from,
|
||||
fromName: params.fromName,
|
||||
to: params.to,
|
||||
toName: params.toName,
|
||||
subject: params.subject,
|
||||
content: params.content,
|
||||
contentType: params.contentType,
|
||||
cc: params.cc,
|
||||
bcc: params.bcc,
|
||||
replyTo: params.replyTo,
|
||||
replyToName: params.replyToName,
|
||||
templateId: params.templateId,
|
||||
dynamicTemplateData: params.dynamicTemplateData,
|
||||
attachments: params.attachments,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<SendMailResult> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
success: false,
|
||||
messageId: undefined,
|
||||
to: '',
|
||||
subject: '',
|
||||
},
|
||||
error: data.error || 'Failed to send email',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: data.output,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the email was sent successfully' },
|
||||
messageId: { type: 'string', description: 'SendGrid message ID', optional: true },
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
import type { UserFile } from '@/executor/types'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
// Shared type definitions
|
||||
export interface SendGridContact {
|
||||
id: string
|
||||
email: string
|
||||
first_name?: string
|
||||
last_name?: string
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
list_ids?: string[]
|
||||
custom_fields?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface SendGridList {
|
||||
id: string
|
||||
name: string
|
||||
contact_count: number
|
||||
_metadata?: {
|
||||
self?: string
|
||||
}
|
||||
}
|
||||
|
||||
interface SendGridTemplateVersion {
|
||||
id: string
|
||||
template_id: string
|
||||
name: string
|
||||
subject: string
|
||||
active: number | boolean
|
||||
html_content?: string
|
||||
plain_content?: string
|
||||
updated_at?: string
|
||||
}
|
||||
|
||||
export interface SendGridTemplate {
|
||||
id: string
|
||||
name: string
|
||||
generation: 'legacy' | 'dynamic'
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
versions?: SendGridTemplateVersion[]
|
||||
}
|
||||
|
||||
interface SendGridPersonalization {
|
||||
to: Array<{ email: string; name?: string }>
|
||||
cc?: Array<{ email: string }>
|
||||
bcc?: Array<{ email: string }>
|
||||
dynamic_template_data?: Record<string, unknown>
|
||||
}
|
||||
|
||||
interface SendGridAttachment {
|
||||
content: string
|
||||
filename: string
|
||||
type?: string
|
||||
disposition?: string
|
||||
content_id?: string
|
||||
}
|
||||
|
||||
interface SendGridMailBody {
|
||||
personalizations: SendGridPersonalization[]
|
||||
from: { email: string; name?: string }
|
||||
subject?: string
|
||||
template_id?: string
|
||||
content?: Array<{ type: 'text/plain' | 'text/html'; value?: string }>
|
||||
reply_to?: { email: string; name?: string }
|
||||
attachments?: SendGridAttachment[] | UserFile[]
|
||||
}
|
||||
|
||||
export interface SendGridContactObject {
|
||||
email: string
|
||||
first_name?: string
|
||||
last_name?: string
|
||||
custom_fields?: Record<string, unknown>
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface SendGridContactRequest {
|
||||
contacts: SendGridContactObject[]
|
||||
list_ids?: string[]
|
||||
}
|
||||
|
||||
export interface SendGridTemplateVersionRequest {
|
||||
name: string
|
||||
subject: string
|
||||
active: number
|
||||
html_content?: string
|
||||
plain_content?: string
|
||||
}
|
||||
|
||||
// Common types
|
||||
interface SendGridBaseParams {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface SendMailParams extends SendGridBaseParams {
|
||||
from: string
|
||||
fromName?: string
|
||||
to: string
|
||||
toName?: string
|
||||
subject?: string
|
||||
content?: string
|
||||
contentType?: 'text/plain' | 'text/html'
|
||||
cc?: string
|
||||
bcc?: string
|
||||
replyTo?: string
|
||||
replyToName?: string
|
||||
attachments?: UserFile[] | SendGridAttachment[] | string
|
||||
templateId?: string
|
||||
dynamicTemplateData?: string
|
||||
}
|
||||
|
||||
export interface SendMailResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
messageId?: string
|
||||
to: string
|
||||
subject: string
|
||||
}
|
||||
}
|
||||
|
||||
// Contact Management types
|
||||
export interface AddContactParams extends SendGridBaseParams {
|
||||
email: string
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
customFields?: string // JSON string
|
||||
listIds?: string // Comma-separated list IDs
|
||||
}
|
||||
|
||||
export interface SearchContactsParams extends SendGridBaseParams {
|
||||
query: string
|
||||
}
|
||||
|
||||
export interface GetContactParams extends SendGridBaseParams {
|
||||
contactId: string
|
||||
}
|
||||
|
||||
export interface DeleteContactParams extends SendGridBaseParams {
|
||||
contactIds: string // Comma-separated contact IDs
|
||||
}
|
||||
|
||||
export interface ContactResult extends ToolResponse {
|
||||
output: {
|
||||
id?: string
|
||||
jobId?: string | null
|
||||
email: string
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
createdAt?: string
|
||||
updatedAt?: string
|
||||
listIds?: string[]
|
||||
customFields?: Record<string, unknown> | null
|
||||
message?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface ContactsResult extends ToolResponse {
|
||||
output: {
|
||||
contacts: SendGridContact[]
|
||||
contactCount: number | null
|
||||
}
|
||||
}
|
||||
|
||||
// List Management types
|
||||
export interface CreateListParams extends SendGridBaseParams {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface GetListParams extends SendGridBaseParams {
|
||||
listId: string
|
||||
}
|
||||
|
||||
export interface DeleteListParams extends SendGridBaseParams {
|
||||
listId: string
|
||||
}
|
||||
|
||||
export interface ListAllListsParams extends SendGridBaseParams {
|
||||
pageSize?: number
|
||||
pageToken?: string
|
||||
}
|
||||
|
||||
export interface AddContactsToListParams extends SendGridBaseParams {
|
||||
listId: string
|
||||
contacts: string // JSON string array of contact objects with at least email
|
||||
}
|
||||
|
||||
export interface RemoveContactsFromListParams extends SendGridBaseParams {
|
||||
listId: string
|
||||
contactIds: string // Comma-separated contact IDs
|
||||
}
|
||||
|
||||
export interface ListResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
contactCount?: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface ListsResult extends ToolResponse {
|
||||
output: {
|
||||
lists: SendGridList[]
|
||||
nextPageToken: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// Template types
|
||||
export interface CreateTemplateParams extends SendGridBaseParams {
|
||||
name: string
|
||||
generation?: 'legacy' | 'dynamic'
|
||||
}
|
||||
|
||||
export interface GetTemplateParams extends SendGridBaseParams {
|
||||
templateId: string
|
||||
}
|
||||
|
||||
export interface DeleteTemplateParams extends SendGridBaseParams {
|
||||
templateId: string
|
||||
}
|
||||
|
||||
export interface ListTemplatesParams extends SendGridBaseParams {
|
||||
generations?: string // 'legacy' or 'dynamic' or both
|
||||
pageSize?: number
|
||||
pageToken?: string
|
||||
}
|
||||
|
||||
export interface CreateTemplateVersionParams extends SendGridBaseParams {
|
||||
templateId: string
|
||||
name: string
|
||||
subject: string
|
||||
htmlContent?: string
|
||||
plainContent?: string
|
||||
active?: boolean
|
||||
}
|
||||
|
||||
export interface TemplateResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
generation: string
|
||||
updatedAt?: string
|
||||
versions?: SendGridTemplateVersion[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface TemplatesResult extends ToolResponse {
|
||||
output: {
|
||||
templates: SendGridTemplate[]
|
||||
nextPageToken: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface TemplateVersionResult extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
templateId: string
|
||||
name: string
|
||||
subject: string
|
||||
active: boolean
|
||||
htmlContent: string | null
|
||||
plainContent: string | null
|
||||
updatedAt: string | null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user