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,70 @@
|
||||
import type {
|
||||
EmailBisonActionResponse,
|
||||
EmailBisonAttachLeadsParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
actionOutput,
|
||||
actionOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const attachLeadsToCampaignTool: ToolConfig<
|
||||
EmailBisonAttachLeadsParams,
|
||||
EmailBisonActionResponse
|
||||
> = {
|
||||
id: 'emailbison_attach_leads_to_campaign',
|
||||
name: 'Email Bison Attach Leads to Campaign',
|
||||
description: 'Adds existing Email Bison leads to a campaign.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
campaignId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign ID',
|
||||
},
|
||||
leadIds: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead IDs to add to the campaign',
|
||||
items: { type: 'number', description: 'Lead ID' },
|
||||
},
|
||||
allowParallelSending: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Force add leads already in sequence in other campaigns',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
`/api/campaigns/${params.campaignId}/leads/attach-leads`,
|
||||
{},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
lead_ids: params.leadIds,
|
||||
allow_parallel_sending: params.allowParallelSending,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'campaign lead attachment result')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: actionOutput(data),
|
||||
}
|
||||
},
|
||||
outputs: actionOutputs,
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import type {
|
||||
EmailBisonActionResponse,
|
||||
EmailBisonAttachTagsToLeadsParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
actionOutput,
|
||||
actionOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const attachTagsToLeadsTool: ToolConfig<
|
||||
EmailBisonAttachTagsToLeadsParams,
|
||||
EmailBisonActionResponse
|
||||
> = {
|
||||
id: 'emailbison_attach_tags_to_leads',
|
||||
name: 'Email Bison Attach Tags to Leads',
|
||||
description: 'Attaches Email Bison tags to one or more leads.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
tagIds: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag IDs to attach',
|
||||
items: { type: 'number', description: 'Tag ID' },
|
||||
},
|
||||
leadIds: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead IDs to tag',
|
||||
items: { type: 'number', description: 'Lead ID' },
|
||||
},
|
||||
skipWebhooks: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Skip Email Bison webhooks for this action',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/tags/attach-to-leads', {}, params.apiBaseUrl),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
tag_ids: params.tagIds,
|
||||
lead_ids: params.leadIds,
|
||||
skip_webhooks: params.skipWebhooks,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'tag attachment result')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: actionOutput(data),
|
||||
}
|
||||
},
|
||||
outputs: actionOutputs,
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import type {
|
||||
EmailBisonCampaignResponse,
|
||||
EmailBisonCreateCampaignParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
campaignOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
mapCampaign,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createCampaignTool: ToolConfig<
|
||||
EmailBisonCreateCampaignParams,
|
||||
EmailBisonCampaignResponse
|
||||
> = {
|
||||
id: 'emailbison_create_campaign',
|
||||
name: 'Email Bison Create Campaign',
|
||||
description: 'Creates a new Email Bison campaign.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign name',
|
||||
},
|
||||
campaignType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign type: outbound or reply_followup',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/campaigns', {}, params.apiBaseUrl),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
name: params.name,
|
||||
type: params.campaignType,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'campaign')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapCampaign(data),
|
||||
}
|
||||
},
|
||||
outputs: campaignOutputs,
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import type { EmailBisonLeadMutationParams, EmailBisonLeadResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
leadOutputs,
|
||||
mapLead,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createLeadTool: ToolConfig<EmailBisonLeadMutationParams, EmailBisonLeadResponse> = {
|
||||
id: 'emailbison_create_lead',
|
||||
name: 'Email Bison Create Lead',
|
||||
description: 'Creates a single lead in Email Bison.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead first name',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead last name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead email address',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead job title',
|
||||
},
|
||||
company: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead company',
|
||||
},
|
||||
notes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Additional notes about the lead',
|
||||
},
|
||||
customVariables: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom variables to store on the lead',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Custom variable name' },
|
||||
value: { type: 'string', description: 'Custom variable value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/leads', {}, params.apiBaseUrl),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
first_name: params.firstName,
|
||||
last_name: params.lastName,
|
||||
email: params.email,
|
||||
title: params.title,
|
||||
company: params.company,
|
||||
notes: params.notes,
|
||||
custom_variables: params.customVariables,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'lead')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapLead(data),
|
||||
}
|
||||
},
|
||||
outputs: leadOutputs,
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { EmailBisonCreateTagParams, EmailBisonTagResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
mapTag,
|
||||
tagOutputs,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createTagTool: ToolConfig<EmailBisonCreateTagParams, EmailBisonTagResponse> = {
|
||||
id: 'emailbison_create_tag',
|
||||
name: 'Email Bison Create Tag',
|
||||
description: 'Creates a new Email Bison tag.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag name',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/tags', {}, params.apiBaseUrl),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) => jsonBody({ name: params.name }),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'tag')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapTag(data),
|
||||
}
|
||||
},
|
||||
outputs: tagOutputs,
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { EmailBisonGetLeadParams, EmailBisonLeadResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
leadOutputs,
|
||||
mapLead,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getLeadTool: ToolConfig<EmailBisonGetLeadParams, EmailBisonLeadResponse> = {
|
||||
id: 'emailbison_get_lead',
|
||||
name: 'Email Bison Get Lead',
|
||||
description: 'Retrieves a lead by Email Bison lead ID or email address.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
leadId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead ID or email address',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
`/api/leads/${encodeURIComponent(params.leadId.trim())}`,
|
||||
{},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'lead')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapLead(data),
|
||||
}
|
||||
},
|
||||
outputs: leadOutputs,
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export { attachLeadsToCampaignTool as emailBisonAttachLeadsToCampaignTool } from '@/tools/emailbison/attach_leads_to_campaign'
|
||||
export { attachTagsToLeadsTool as emailBisonAttachTagsToLeadsTool } from '@/tools/emailbison/attach_tags_to_leads'
|
||||
export { createCampaignTool as emailBisonCreateCampaignTool } from '@/tools/emailbison/create_campaign'
|
||||
export { createLeadTool as emailBisonCreateLeadTool } from '@/tools/emailbison/create_lead'
|
||||
export { createTagTool as emailBisonCreateTagTool } from '@/tools/emailbison/create_tag'
|
||||
export { getLeadTool as emailBisonGetLeadTool } from '@/tools/emailbison/get_lead'
|
||||
export { listCampaignsTool as emailBisonListCampaignsTool } from '@/tools/emailbison/list_campaigns'
|
||||
export { listLeadsTool as emailBisonListLeadsTool } from '@/tools/emailbison/list_leads'
|
||||
export { listRepliesTool as emailBisonListRepliesTool } from '@/tools/emailbison/list_replies'
|
||||
export { listTagsTool as emailBisonListTagsTool } from '@/tools/emailbison/list_tags'
|
||||
export * from '@/tools/emailbison/types'
|
||||
export { updateCampaignTool as emailBisonUpdateCampaignTool } from '@/tools/emailbison/update_campaign'
|
||||
export { updateCampaignStatusTool as emailBisonUpdateCampaignStatusTool } from '@/tools/emailbison/update_campaign_status'
|
||||
export { updateLeadTool as emailBisonUpdateLeadTool } from '@/tools/emailbison/update_lead'
|
||||
@@ -0,0 +1,42 @@
|
||||
import type {
|
||||
EmailBisonBaseParams,
|
||||
EmailBisonListCampaignsResponse,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonArrayData,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonUrl,
|
||||
listCampaignsOutputs,
|
||||
mapCampaign,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listCampaignsTool: ToolConfig<EmailBisonBaseParams, EmailBisonListCampaignsResponse> =
|
||||
{
|
||||
id: 'emailbison_list_campaigns',
|
||||
name: 'Email Bison List Campaigns',
|
||||
description: 'Retrieves Email Bison campaigns.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/campaigns', {}, params.apiBaseUrl),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonArrayData(response, 'campaigns')
|
||||
const campaigns = data.map(mapCampaign)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
campaigns,
|
||||
count: campaigns.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: listCampaignsOutputs,
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import type {
|
||||
EmailBisonListLeadsParams,
|
||||
EmailBisonListLeadsResponse,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonArrayData,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonUrl,
|
||||
listLeadsOutputs,
|
||||
mapLead,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listLeadsTool: ToolConfig<EmailBisonListLeadsParams, EmailBisonListLeadsResponse> = {
|
||||
id: 'emailbison_list_leads',
|
||||
name: 'Email Bison List Leads',
|
||||
description: 'Retrieves leads from Email Bison with optional search and tag filters.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term for filtering leads',
|
||||
},
|
||||
campaignStatus: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Lead campaign status filter: in_sequence, sequence_finished, sequence_stopped, never_contacted, or replied',
|
||||
},
|
||||
tagIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag IDs to include',
|
||||
items: { type: 'number', description: 'Tag ID' },
|
||||
},
|
||||
excludedTagIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag IDs to exclude',
|
||||
items: { type: 'number', description: 'Tag ID' },
|
||||
},
|
||||
withoutTags: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return leads without tags',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
'/api/leads',
|
||||
{
|
||||
search: params.search,
|
||||
'filters.lead_campaign_status': params.campaignStatus,
|
||||
'filters.tag_ids': params.tagIds,
|
||||
'filters.excluded_tag_ids': params.excludedTagIds,
|
||||
'filters.without_tags': params.withoutTags,
|
||||
},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonArrayData(response, 'leads')
|
||||
const leads = data.map(mapLead)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
leads,
|
||||
count: leads.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: listLeadsOutputs,
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import type {
|
||||
EmailBisonListRepliesParams,
|
||||
EmailBisonListRepliesResponse,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonArrayData,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonUrl,
|
||||
listRepliesOutputs,
|
||||
mapReply,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listRepliesTool: ToolConfig<
|
||||
EmailBisonListRepliesParams,
|
||||
EmailBisonListRepliesResponse
|
||||
> = {
|
||||
id: 'emailbison_list_replies',
|
||||
name: 'Email Bison List Replies',
|
||||
description:
|
||||
'Retrieves Email Bison replies with optional status, folder, campaign, sender, lead, and tag filters.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term for replies',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reply status: interested, automated_reply, or not_automated_reply',
|
||||
},
|
||||
folder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reply folder: inbox, sent, spam, bounced, or all',
|
||||
},
|
||||
read: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by read state',
|
||||
},
|
||||
campaignId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign ID',
|
||||
},
|
||||
senderEmailId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sender email ID',
|
||||
},
|
||||
leadId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead ID',
|
||||
},
|
||||
tagIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag IDs to filter replies by',
|
||||
items: { type: 'number', description: 'Tag ID' },
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
'/api/replies',
|
||||
{
|
||||
search: params.search,
|
||||
status: params.status,
|
||||
folder: params.folder,
|
||||
read: params.read,
|
||||
campaign_id: params.campaignId,
|
||||
sender_email_id: params.senderEmailId,
|
||||
lead_id: params.leadId,
|
||||
tag_ids: params.tagIds,
|
||||
},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonArrayData(response, 'replies')
|
||||
const replies = data.map(mapReply)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
replies,
|
||||
count: replies.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: listRepliesOutputs,
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { EmailBisonBaseParams, EmailBisonListTagsResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonArrayData,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonUrl,
|
||||
listTagsOutputs,
|
||||
mapTag,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listTagsTool: ToolConfig<EmailBisonBaseParams, EmailBisonListTagsResponse> = {
|
||||
id: 'emailbison_list_tags',
|
||||
name: 'Email Bison List Tags',
|
||||
description: 'Retrieves all Email Bison tags for the authenticated workspace.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/tags', {}, params.apiBaseUrl),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonArrayData(response, 'tags')
|
||||
const tags = data.map(mapTag)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
tags,
|
||||
count: tags.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: listTagsOutputs,
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface EmailBisonBaseParams {
|
||||
apiKey: string
|
||||
apiBaseUrl: string
|
||||
}
|
||||
|
||||
interface EmailBisonCustomVariable {
|
||||
name: string | null
|
||||
value: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonLeadStats {
|
||||
emails_sent: number | null
|
||||
opens: number | null
|
||||
replies: number | null
|
||||
unique_replies: number | null
|
||||
unique_opens: number | null
|
||||
}
|
||||
|
||||
export interface EmailBisonLead {
|
||||
id: number | null
|
||||
first_name: string | null
|
||||
last_name: string | null
|
||||
email: string | null
|
||||
title: string | null
|
||||
company: string | null
|
||||
notes: string | null
|
||||
status: string | null
|
||||
custom_variables: EmailBisonCustomVariable[]
|
||||
lead_campaign_data: unknown[]
|
||||
overall_stats: EmailBisonLeadStats
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonTag {
|
||||
id: number | null
|
||||
name: string | null
|
||||
default: boolean | null
|
||||
created_at?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonCampaignTag {
|
||||
id: number | null
|
||||
name: string | null
|
||||
default: boolean | null
|
||||
}
|
||||
|
||||
export interface EmailBisonCampaign {
|
||||
id: number | null
|
||||
uuid: string | null
|
||||
name: string | null
|
||||
type: string | null
|
||||
status: string | null
|
||||
emails_sent: number | null
|
||||
opened: number | null
|
||||
unique_opens: number | null
|
||||
replied: number | null
|
||||
unique_replies: number | null
|
||||
bounced: number | null
|
||||
unsubscribed: number | null
|
||||
interested: number | null
|
||||
total_leads_contacted: number | null
|
||||
total_leads: number | null
|
||||
max_emails_per_day: number | null
|
||||
max_new_leads_per_day: number | null
|
||||
plain_text: boolean | null
|
||||
open_tracking: boolean | null
|
||||
can_unsubscribe: boolean | null
|
||||
unsubscribe_text: string | null
|
||||
sequence_prioritization?: string | null
|
||||
tags: EmailBisonCampaignTag[]
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonReplyAddress {
|
||||
name: string | null
|
||||
address: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonReplyAttachment {
|
||||
id: number | null
|
||||
uuid: string | null
|
||||
reply_id: number | null
|
||||
file_name: string | null
|
||||
download_url: string | null
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonReply {
|
||||
id: number | null
|
||||
uuid: string | null
|
||||
folder: string | null
|
||||
subject: string | null
|
||||
read: boolean | null
|
||||
interested: boolean | null
|
||||
automated_reply: boolean | null
|
||||
html_body: string | null
|
||||
text_body: string | null
|
||||
raw_body: string | null
|
||||
headers: string | null
|
||||
date_received: string | null
|
||||
type: string | null
|
||||
tracked_reply: boolean | null
|
||||
scheduled_email_id: number | string | null
|
||||
campaign_id: number | string | null
|
||||
lead_id: number | null
|
||||
sender_email_id: number | null
|
||||
raw_message_id: string | null
|
||||
from_name: string | null
|
||||
from_email_address: string | null
|
||||
primary_to_email_address: string | null
|
||||
to: EmailBisonReplyAddress[]
|
||||
cc: string | null
|
||||
bcc: string | null
|
||||
parent_id: number | string | null
|
||||
attachments: EmailBisonReplyAttachment[]
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonListLeadsParams extends EmailBisonBaseParams {
|
||||
search?: string
|
||||
campaignStatus?: string
|
||||
tagIds?: number[]
|
||||
excludedTagIds?: number[]
|
||||
withoutTags?: boolean
|
||||
}
|
||||
|
||||
export interface EmailBisonGetLeadParams extends EmailBisonBaseParams {
|
||||
leadId: string
|
||||
}
|
||||
|
||||
export interface EmailBisonLeadMutationParams extends EmailBisonBaseParams {
|
||||
leadId?: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
title?: string
|
||||
company?: string
|
||||
notes?: string
|
||||
customVariables?: EmailBisonCustomVariable[]
|
||||
}
|
||||
|
||||
export interface EmailBisonCreateCampaignParams extends EmailBisonBaseParams {
|
||||
name: string
|
||||
campaignType?: 'outbound' | 'reply_followup'
|
||||
}
|
||||
|
||||
export interface EmailBisonUpdateCampaignParams extends EmailBisonBaseParams {
|
||||
campaignId: number
|
||||
name?: string
|
||||
maxEmailsPerDay?: number
|
||||
maxNewLeadsPerDay?: number
|
||||
plainText?: boolean
|
||||
openTracking?: boolean
|
||||
reputationBuilding?: boolean
|
||||
canUnsubscribe?: boolean
|
||||
includeAutoRepliesInStats?: boolean
|
||||
sequencePrioritization?: 'followups' | 'new_leads'
|
||||
}
|
||||
|
||||
export interface EmailBisonCampaignStatusParams extends EmailBisonBaseParams {
|
||||
campaignId: number
|
||||
action: 'pause' | 'resume' | 'archive'
|
||||
}
|
||||
|
||||
export interface EmailBisonAttachLeadsParams extends EmailBisonBaseParams {
|
||||
campaignId: number
|
||||
leadIds: number[]
|
||||
allowParallelSending?: boolean
|
||||
}
|
||||
|
||||
export interface EmailBisonListRepliesParams extends EmailBisonBaseParams {
|
||||
search?: string
|
||||
status?: string
|
||||
folder?: string
|
||||
read?: boolean
|
||||
campaignId?: number
|
||||
senderEmailId?: number
|
||||
leadId?: number
|
||||
tagIds?: number[]
|
||||
}
|
||||
|
||||
export interface EmailBisonCreateTagParams extends EmailBisonBaseParams {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface EmailBisonAttachTagsToLeadsParams extends EmailBisonBaseParams {
|
||||
tagIds: number[]
|
||||
leadIds: number[]
|
||||
skipWebhooks?: boolean
|
||||
}
|
||||
|
||||
export interface EmailBisonListLeadsResponse extends ToolResponse {
|
||||
output: {
|
||||
leads: EmailBisonLead[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonLeadResponse extends ToolResponse {
|
||||
output: EmailBisonLead
|
||||
}
|
||||
|
||||
export interface EmailBisonListCampaignsResponse extends ToolResponse {
|
||||
output: {
|
||||
campaigns: EmailBisonCampaign[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonCampaignResponse extends ToolResponse {
|
||||
output: EmailBisonCampaign
|
||||
}
|
||||
|
||||
export interface EmailBisonActionResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonListRepliesResponse extends ToolResponse {
|
||||
output: {
|
||||
replies: EmailBisonReply[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonListTagsResponse extends ToolResponse {
|
||||
output: {
|
||||
tags: EmailBisonTag[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonTagResponse extends ToolResponse {
|
||||
output: EmailBisonTag
|
||||
}
|
||||
|
||||
export type EmailBisonResponse =
|
||||
| EmailBisonListLeadsResponse
|
||||
| EmailBisonLeadResponse
|
||||
| EmailBisonListCampaignsResponse
|
||||
| EmailBisonCampaignResponse
|
||||
| EmailBisonActionResponse
|
||||
| EmailBisonListRepliesResponse
|
||||
| EmailBisonListTagsResponse
|
||||
| EmailBisonTagResponse
|
||||
@@ -0,0 +1,114 @@
|
||||
import type {
|
||||
EmailBisonCampaignResponse,
|
||||
EmailBisonUpdateCampaignParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
campaignOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
mapCampaign,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateCampaignTool: ToolConfig<
|
||||
EmailBisonUpdateCampaignParams,
|
||||
EmailBisonCampaignResponse
|
||||
> = {
|
||||
id: 'emailbison_update_campaign',
|
||||
name: 'Email Bison Update Campaign',
|
||||
description: 'Updates Email Bison campaign settings.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
campaignId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign name',
|
||||
},
|
||||
maxEmailsPerDay: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum emails per day',
|
||||
},
|
||||
maxNewLeadsPerDay: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum new leads per day',
|
||||
},
|
||||
plainText: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Send plain text emails',
|
||||
},
|
||||
openTracking: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Enable open tracking',
|
||||
},
|
||||
reputationBuilding: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Enable reputation building',
|
||||
},
|
||||
canUnsubscribe: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Enable unsubscribe link',
|
||||
},
|
||||
includeAutoRepliesInStats: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Include auto replies in campaign stats',
|
||||
},
|
||||
sequencePrioritization: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sequence prioritization: followups or new_leads',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(`/api/campaigns/${params.campaignId}/update`, {}, params.apiBaseUrl),
|
||||
method: 'PATCH',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
name: params.name,
|
||||
max_emails_per_day: params.maxEmailsPerDay,
|
||||
max_new_leads_per_day: params.maxNewLeadsPerDay,
|
||||
plain_text: params.plainText,
|
||||
open_tracking: params.openTracking,
|
||||
reputation_building: params.reputationBuilding,
|
||||
can_unsubscribe: params.canUnsubscribe,
|
||||
include_auto_replies_in_stats: params.includeAutoRepliesInStats,
|
||||
sequence_prioritization: params.sequencePrioritization,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'campaign')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapCampaign(data),
|
||||
}
|
||||
},
|
||||
outputs: campaignOutputs,
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import type {
|
||||
EmailBisonCampaignResponse,
|
||||
EmailBisonCampaignStatusParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
campaignOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
mapCampaign,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const CAMPAIGN_STATUS_ACTIONS = new Set(['pause', 'resume', 'archive'])
|
||||
|
||||
export const updateCampaignStatusTool: ToolConfig<
|
||||
EmailBisonCampaignStatusParams,
|
||||
EmailBisonCampaignResponse
|
||||
> = {
|
||||
id: 'emailbison_update_campaign_status',
|
||||
name: 'Email Bison Update Campaign Status',
|
||||
description: 'Pauses, resumes, or archives an Email Bison campaign.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
campaignId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign ID',
|
||||
},
|
||||
action: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Status action: pause, resume, or archive',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => {
|
||||
if (!CAMPAIGN_STATUS_ACTIONS.has(params.action)) {
|
||||
throw new Error('Email Bison campaign status action must be pause, resume, or archive')
|
||||
}
|
||||
|
||||
return emailBisonUrl(
|
||||
`/api/campaigns/${params.campaignId}/${params.action}`,
|
||||
{},
|
||||
params.apiBaseUrl
|
||||
)
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'campaign')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapCampaign(data),
|
||||
}
|
||||
},
|
||||
outputs: campaignOutputs,
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import type { EmailBisonLeadMutationParams, EmailBisonLeadResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
leadOutputs,
|
||||
mapLead,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateLeadTool: ToolConfig<EmailBisonLeadMutationParams, EmailBisonLeadResponse> = {
|
||||
id: 'emailbison_update_lead',
|
||||
name: 'Email Bison Update Lead',
|
||||
description:
|
||||
'Updates an existing Email Bison lead. Fields omitted from a PUT update may be cleared by Email Bison.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
leadId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead ID or email address',
|
||||
},
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead first name',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead last name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead email address',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead job title',
|
||||
},
|
||||
company: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead company',
|
||||
},
|
||||
notes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Additional notes about the lead',
|
||||
},
|
||||
customVariables: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom variables to store on the lead',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Custom variable name' },
|
||||
value: { type: 'string', description: 'Custom variable value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
`/api/leads/${encodeURIComponent(params.leadId?.trim() ?? '')}`,
|
||||
{},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'PUT',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
first_name: params.firstName,
|
||||
last_name: params.lastName,
|
||||
email: params.email,
|
||||
title: params.title,
|
||||
company: params.company,
|
||||
notes: params.notes,
|
||||
custom_variables: params.customVariables,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'lead')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapLead(data),
|
||||
}
|
||||
},
|
||||
outputs: leadOutputs,
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
import { filterUndefined, isRecordLike } from '@sim/utils/object'
|
||||
import type {
|
||||
EmailBisonBaseParams,
|
||||
EmailBisonCampaign,
|
||||
EmailBisonCampaignTag,
|
||||
EmailBisonLead,
|
||||
EmailBisonLeadStats,
|
||||
EmailBisonReply,
|
||||
EmailBisonReplyAddress,
|
||||
EmailBisonReplyAttachment,
|
||||
EmailBisonTag,
|
||||
} from '@/tools/emailbison/types'
|
||||
import type { OutputProperty, ToolConfig } from '@/tools/types'
|
||||
|
||||
type QueryValue = string | number | boolean | Array<string | number> | undefined | null
|
||||
|
||||
interface EmailBisonEnvelope<T> {
|
||||
data?: T
|
||||
}
|
||||
|
||||
export function emailBisonHeaders(params: EmailBisonBaseParams): Record<string, string> {
|
||||
return {
|
||||
Authorization: `Bearer ${params.apiKey.trim()}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
}
|
||||
|
||||
export const emailBisonBaseParamFields = {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Email Bison API token',
|
||||
},
|
||||
apiBaseUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Email Bison instance URL that issued the token',
|
||||
},
|
||||
} satisfies ToolConfig['params']
|
||||
|
||||
export function emailBisonUrl(
|
||||
path: string,
|
||||
query: Record<string, QueryValue>,
|
||||
baseUrl: string
|
||||
): string {
|
||||
const url = new URL(path, normalizeEmailBisonBaseUrl(baseUrl))
|
||||
|
||||
Object.entries(query).forEach(([key, value]) => {
|
||||
if (value === undefined || value === null || value === '') return
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item) => {
|
||||
url.searchParams.append(key, String(item))
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
url.searchParams.set(key, String(value))
|
||||
})
|
||||
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
function normalizeEmailBisonBaseUrl(baseUrl: string): string {
|
||||
const trimmedBaseUrl = baseUrl.trim()
|
||||
if (!trimmedBaseUrl) {
|
||||
throw new Error('Email Bison Instance URL is required')
|
||||
}
|
||||
|
||||
const rawBaseUrl = /^https?:\/\//i.test(trimmedBaseUrl)
|
||||
? trimmedBaseUrl
|
||||
: `https://${trimmedBaseUrl}`
|
||||
const parsed = new URL(rawBaseUrl)
|
||||
|
||||
if (parsed.protocol !== 'https:') {
|
||||
throw new Error('Email Bison Instance URL must use HTTPS')
|
||||
}
|
||||
|
||||
return parsed.origin
|
||||
}
|
||||
|
||||
export function jsonBody(fields: Record<string, unknown>): Record<string, unknown> {
|
||||
return filterUndefined(fields)
|
||||
}
|
||||
|
||||
async function emailBisonData<T>(response: Response): Promise<T | null> {
|
||||
const payload = (await response.json()) as EmailBisonEnvelope<T>
|
||||
return payload.data ?? null
|
||||
}
|
||||
|
||||
export async function emailBisonArrayData(response: Response, label: string): Promise<unknown[]> {
|
||||
const data = await emailBisonData<unknown[]>(response)
|
||||
if (!Array.isArray(data)) {
|
||||
throw new Error(`Email Bison response did not include a valid ${label} array`)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
export async function emailBisonRecordData(response: Response, label: string): Promise<unknown> {
|
||||
const data = await emailBisonData<unknown>(response)
|
||||
if (!isRecordLike(data)) {
|
||||
throw new Error(`Email Bison response did not include a valid ${label} object`)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
export function mapLead(value: unknown): EmailBisonLead {
|
||||
const record = toRecord(value)
|
||||
const stats = toRecord(record.overall_stats)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
first_name: toStringOrNull(record.first_name),
|
||||
last_name: toStringOrNull(record.last_name),
|
||||
email: toStringOrNull(record.email),
|
||||
title: toStringOrNull(record.title),
|
||||
company: toStringOrNull(record.company),
|
||||
notes: toStringOrNull(record.notes),
|
||||
status: toStringOrNull(record.status),
|
||||
custom_variables: toArray(record.custom_variables).map((item) => {
|
||||
const variable = toRecord(item)
|
||||
return {
|
||||
name: toStringOrNull(variable.name),
|
||||
value: toStringOrNull(variable.value),
|
||||
}
|
||||
}),
|
||||
lead_campaign_data: toArray(record.lead_campaign_data),
|
||||
overall_stats: mapLeadStats(stats),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapCampaign(value: unknown): EmailBisonCampaign {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
uuid: toStringOrNull(record.uuid),
|
||||
name: toStringOrNull(record.name),
|
||||
type: toStringOrNull(record.type),
|
||||
status: toStringOrNull(record.status),
|
||||
emails_sent: toNullableNumber(record.emails_sent),
|
||||
opened: toNullableNumber(record.opened),
|
||||
unique_opens: toNullableNumber(record.unique_opens),
|
||||
replied: toNullableNumber(record.replied),
|
||||
unique_replies: toNullableNumber(record.unique_replies),
|
||||
bounced: toNullableNumber(record.bounced),
|
||||
unsubscribed: toNullableNumber(record.unsubscribed),
|
||||
interested: toNullableNumber(record.interested),
|
||||
total_leads_contacted: toNullableNumber(record.total_leads_contacted),
|
||||
total_leads: toNullableNumber(record.total_leads),
|
||||
max_emails_per_day: toNullableNumber(record.max_emails_per_day),
|
||||
max_new_leads_per_day: toNullableNumber(record.max_new_leads_per_day),
|
||||
plain_text: toNullableBoolean(record.plain_text),
|
||||
open_tracking: toNullableBoolean(record.open_tracking),
|
||||
can_unsubscribe: toNullableBoolean(record.can_unsubscribe),
|
||||
unsubscribe_text: toStringOrNull(record.unsubscribe_text),
|
||||
...(record.sequence_prioritization !== undefined && {
|
||||
sequence_prioritization: toStringOrNull(record.sequence_prioritization),
|
||||
}),
|
||||
tags: toArray(record.tags).map(mapCampaignTag),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
function mapCampaignTag(value: unknown): EmailBisonCampaignTag {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
name: toStringOrNull(record.name),
|
||||
default: toNullableBoolean(record.default),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapTag(value: unknown): EmailBisonTag {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
name: toStringOrNull(record.name),
|
||||
default: toNullableBoolean(record.default),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapReply(value: unknown): EmailBisonReply {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
uuid: toStringOrNull(record.uuid),
|
||||
folder: toStringOrNull(record.folder),
|
||||
subject: toStringOrNull(record.subject),
|
||||
read: toNullableBoolean(record.read),
|
||||
interested: toNullableBoolean(record.interested),
|
||||
automated_reply: toNullableBoolean(record.automated_reply),
|
||||
html_body: toStringOrNull(record.html_body),
|
||||
text_body: toStringOrNull(record.text_body),
|
||||
raw_body: toStringOrNull(record.raw_body),
|
||||
headers: toStringOrNull(record.headers),
|
||||
date_received: toStringOrNull(record.date_received),
|
||||
type: toStringOrNull(record.type),
|
||||
tracked_reply: toNullableBoolean(record.tracked_reply),
|
||||
scheduled_email_id: toStringNumberOrNull(record.scheduled_email_id),
|
||||
campaign_id: toStringNumberOrNull(record.campaign_id),
|
||||
lead_id: toNullableNumber(record.lead_id),
|
||||
sender_email_id: toNullableNumber(record.sender_email_id),
|
||||
raw_message_id: toStringOrNull(record.raw_message_id),
|
||||
from_name: toStringOrNull(record.from_name),
|
||||
from_email_address: toStringOrNull(record.from_email_address),
|
||||
primary_to_email_address: toStringOrNull(record.primary_to_email_address),
|
||||
to: toArray(record.to).map(mapReplyAddress),
|
||||
cc: toStringOrNull(record.cc),
|
||||
bcc: toStringOrNull(record.bcc),
|
||||
parent_id: toStringNumberOrNull(record.parent_id),
|
||||
attachments: toArray(record.attachments).map(mapReplyAttachment),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
export function actionOutput(value: unknown): { success: boolean; message: string | null } {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
success: record.success === true,
|
||||
message: toStringOrNull(record.message),
|
||||
}
|
||||
}
|
||||
|
||||
export const leadOutputs = {
|
||||
id: { type: 'number', description: 'Lead ID' },
|
||||
first_name: { type: 'string', description: 'Lead first name' },
|
||||
last_name: { type: 'string', description: 'Lead last name' },
|
||||
email: { type: 'string', description: 'Lead email address' },
|
||||
title: { type: 'string', description: 'Lead title', optional: true },
|
||||
company: { type: 'string', description: 'Lead company', optional: true },
|
||||
notes: { type: 'string', description: 'Lead notes', optional: true },
|
||||
status: { type: 'string', description: 'Lead status', optional: true },
|
||||
custom_variables: {
|
||||
type: 'array',
|
||||
description: 'Lead custom variables',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Custom variable name' },
|
||||
value: { type: 'string', description: 'Custom variable value', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
lead_campaign_data: {
|
||||
type: 'array',
|
||||
description: 'Lead campaign data returned by Email Bison',
|
||||
},
|
||||
overall_stats: {
|
||||
type: 'object',
|
||||
description: 'Lead engagement stats',
|
||||
properties: {
|
||||
emails_sent: { type: 'number', description: 'Emails sent' },
|
||||
opens: { type: 'number', description: 'Email opens' },
|
||||
replies: { type: 'number', description: 'Replies' },
|
||||
unique_replies: { type: 'number', description: 'Unique replies' },
|
||||
unique_opens: { type: 'number', description: 'Unique opens' },
|
||||
},
|
||||
},
|
||||
created_at: { type: 'string', description: 'Lead creation timestamp', optional: true },
|
||||
updated_at: { type: 'string', description: 'Lead update timestamp', optional: true },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
const tagProperties = {
|
||||
id: { type: 'number', description: 'Tag ID' },
|
||||
name: { type: 'string', description: 'Tag name' },
|
||||
default: { type: 'boolean', description: 'Whether this is a default tag' },
|
||||
created_at: { type: 'string', description: 'Tag creation timestamp', optional: true },
|
||||
updated_at: { type: 'string', description: 'Tag update timestamp', optional: true },
|
||||
} satisfies Record<string, OutputProperty>
|
||||
|
||||
const campaignTagProperties = {
|
||||
id: { type: 'number', description: 'Tag ID' },
|
||||
name: { type: 'string', description: 'Tag name' },
|
||||
default: { type: 'boolean', description: 'Whether this is a default tag' },
|
||||
} satisfies Record<string, OutputProperty>
|
||||
|
||||
export const listLeadsOutputs = {
|
||||
leads: {
|
||||
type: 'array',
|
||||
description: 'List of leads',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: leadOutputs,
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of leads returned' },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const campaignOutputs = {
|
||||
id: { type: 'number', description: 'Campaign ID' },
|
||||
uuid: { type: 'string', description: 'Campaign UUID', optional: true },
|
||||
name: { type: 'string', description: 'Campaign name' },
|
||||
type: { type: 'string', description: 'Campaign type', optional: true },
|
||||
status: { type: 'string', description: 'Campaign status', optional: true },
|
||||
emails_sent: { type: 'number', description: 'Emails sent' },
|
||||
opened: { type: 'number', description: 'Total opens' },
|
||||
unique_opens: { type: 'number', description: 'Unique opens' },
|
||||
replied: { type: 'number', description: 'Total replies' },
|
||||
unique_replies: { type: 'number', description: 'Unique replies' },
|
||||
bounced: { type: 'number', description: 'Bounces' },
|
||||
unsubscribed: { type: 'number', description: 'Unsubscribes' },
|
||||
interested: { type: 'number', description: 'Interested replies' },
|
||||
total_leads_contacted: { type: 'number', description: 'Total leads contacted' },
|
||||
total_leads: { type: 'number', description: 'Total leads' },
|
||||
max_emails_per_day: { type: 'number', description: 'Maximum emails per day', optional: true },
|
||||
max_new_leads_per_day: {
|
||||
type: 'number',
|
||||
description: 'Maximum new leads per day',
|
||||
optional: true,
|
||||
},
|
||||
plain_text: {
|
||||
type: 'boolean',
|
||||
description: 'Whether campaign emails are plain text',
|
||||
optional: true,
|
||||
},
|
||||
open_tracking: {
|
||||
type: 'boolean',
|
||||
description: 'Whether open tracking is enabled',
|
||||
optional: true,
|
||||
},
|
||||
can_unsubscribe: {
|
||||
type: 'boolean',
|
||||
description: 'Whether recipients can unsubscribe',
|
||||
optional: true,
|
||||
},
|
||||
unsubscribe_text: { type: 'string', description: 'Unsubscribe text', optional: true },
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'Campaign tags',
|
||||
items: { type: 'object', properties: campaignTagProperties },
|
||||
},
|
||||
created_at: { type: 'string', description: 'Campaign creation timestamp', optional: true },
|
||||
updated_at: { type: 'string', description: 'Campaign update timestamp', optional: true },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const listCampaignsOutputs = {
|
||||
campaigns: {
|
||||
type: 'array',
|
||||
description: 'List of campaigns',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: campaignOutputs,
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of campaigns returned' },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const actionOutputs = {
|
||||
success: { type: 'boolean', description: 'Whether the action succeeded' },
|
||||
message: { type: 'string', description: 'Action message', optional: true },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const listRepliesOutputs = {
|
||||
replies: {
|
||||
type: 'array',
|
||||
description: 'List of replies',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Reply ID' },
|
||||
subject: { type: 'string', description: 'Reply subject', optional: true },
|
||||
text_body: { type: 'string', description: 'Reply text body', optional: true },
|
||||
from_email_address: { type: 'string', description: 'Sender email', optional: true },
|
||||
primary_to_email_address: {
|
||||
type: 'string',
|
||||
description: 'Primary recipient',
|
||||
optional: true,
|
||||
},
|
||||
date_received: { type: 'string', description: 'Date received', optional: true },
|
||||
interested: { type: 'boolean', description: 'Whether the reply is marked interested' },
|
||||
read: { type: 'boolean', description: 'Whether the reply is read' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of replies returned' },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const tagOutputs = {
|
||||
id: { type: 'number', description: 'Tag ID' },
|
||||
name: { type: 'string', description: 'Tag name' },
|
||||
default: { type: 'boolean', description: 'Whether this is a default tag' },
|
||||
created_at: { type: 'string', description: 'Tag creation timestamp', optional: true },
|
||||
updated_at: { type: 'string', description: 'Tag update timestamp', optional: true },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const listTagsOutputs = {
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'List of tags',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: tagProperties,
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of tags returned' },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
function mapLeadStats(record: Record<string, unknown>): EmailBisonLeadStats {
|
||||
return {
|
||||
emails_sent: toNullableNumber(record.emails_sent),
|
||||
opens: toNullableNumber(record.opens),
|
||||
replies: toNullableNumber(record.replies),
|
||||
unique_replies: toNullableNumber(record.unique_replies),
|
||||
unique_opens: toNullableNumber(record.unique_opens),
|
||||
}
|
||||
}
|
||||
|
||||
function mapReplyAddress(value: unknown): EmailBisonReplyAddress {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
name: toStringOrNull(record.name),
|
||||
address: toStringOrNull(record.address),
|
||||
}
|
||||
}
|
||||
|
||||
function mapReplyAttachment(value: unknown): EmailBisonReplyAttachment {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
uuid: toStringOrNull(record.uuid),
|
||||
reply_id: toNullableNumber(record.reply_id),
|
||||
file_name: toStringOrNull(record.file_name),
|
||||
download_url: toStringOrNull(record.download_url),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
function toRecord(value: unknown): Record<string, unknown> {
|
||||
return isRecordLike(value) ? value : {}
|
||||
}
|
||||
|
||||
function toArray(value: unknown): unknown[] {
|
||||
return Array.isArray(value) ? value : []
|
||||
}
|
||||
|
||||
function toStringOrNull(value: unknown): string | null {
|
||||
if (value === undefined || value === null) return null
|
||||
return String(value)
|
||||
}
|
||||
|
||||
function toStringNumberOrNull(value: unknown): number | string | null {
|
||||
if (typeof value === 'number' || typeof value === 'string') return value
|
||||
return null
|
||||
}
|
||||
|
||||
function toNumber(value: unknown): number | null {
|
||||
if (typeof value === 'number') return value
|
||||
if (typeof value === 'string') {
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function toNullableNumber(value: unknown): number | null {
|
||||
if (value === undefined || value === null) return null
|
||||
return toNumber(value)
|
||||
}
|
||||
|
||||
function toNullableBoolean(value: unknown): boolean | null {
|
||||
if (value === undefined || value === null) return null
|
||||
return typeof value === 'boolean' ? value : null
|
||||
}
|
||||
Reference in New Issue
Block a user