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

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+122
View File
@@ -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' },
},
}
+64
View File
@@ -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' },
},
}
+51
View File
@@ -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: {},
}
+70
View File
@@ -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 },
},
}
+56
View File
@@ -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' },
},
}
+60
View File
@@ -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' },
},
}
+23
View File
@@ -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'
+85
View File
@@ -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,
},
},
}
+95
View File
@@ -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,
},
},
}
+156
View File
@@ -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' },
},
}
+265
View File
@@ -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
}
}