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,134 @@
|
||||
import { leadmagicHosting } from '@/tools/leadmagic/hosting'
|
||||
import type {
|
||||
LeadMagicCompanySearchParams,
|
||||
LeadMagicCompanySearchResponse,
|
||||
} from '@/tools/leadmagic/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const companySearchTool: ToolConfig<
|
||||
LeadMagicCompanySearchParams,
|
||||
LeadMagicCompanySearchResponse
|
||||
> = {
|
||||
id: 'leadmagic_company_search',
|
||||
name: 'LeadMagic Company Search',
|
||||
description:
|
||||
'Enrich company data including firmographics, headcount, funding, and social profiles by domain, LinkedIn URL, or name. Charges 1 credit when a company is found; free when no result.',
|
||||
version: '1.0.0',
|
||||
|
||||
hosting: leadmagicHosting<LeadMagicCompanySearchParams>((_params, output) => {
|
||||
// 1 credit when company found, 0 otherwise.
|
||||
// Source: https://leadmagic.io/docs/v1/reference/company-search
|
||||
const consumed = output.credits_consumed
|
||||
return typeof consumed === 'number' ? consumed : output.companyName ? 1 : 0
|
||||
}),
|
||||
|
||||
params: {
|
||||
company_domain: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Company website domain (e.g., stripe.com). Provide at least one identifier.',
|
||||
},
|
||||
profile_url: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'LinkedIn company profile URL (e.g., https://linkedin.com/company/stripe). Provide at least one identifier.',
|
||||
},
|
||||
company_name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Company name (fallback if domain/URL unavailable). Provide at least one identifier.',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'LeadMagic API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.leadmagic.io/v1/companies/company-search',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'X-API-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, string> = {}
|
||||
if (params.company_domain) body.company_domain = params.company_domain
|
||||
if (params.profile_url) body.profile_url = params.profile_url
|
||||
if (params.company_name) body.company_name = params.company_name
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
(errorData as Record<string, string>).message ||
|
||||
`LeadMagic API error: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
companyName: data.companyName ?? null,
|
||||
companyId: data.companyId ?? null,
|
||||
industry: data.industry ?? null,
|
||||
employeeCount: data.employeeCount ?? null,
|
||||
employeeRange: data.employeeRange ?? null,
|
||||
founded: data.founded ?? null,
|
||||
headquarters: data.headquarters ?? null,
|
||||
revenue: data.revenue ?? null,
|
||||
funding: data.funding ?? null,
|
||||
description: data.description ?? null,
|
||||
specialties: data.specialties ?? [],
|
||||
competitors: data.competitors ?? [],
|
||||
followerCount: data.followerCount ?? null,
|
||||
twitter_url: data.twitter_url ?? null,
|
||||
facebook_url: data.facebook_url ?? null,
|
||||
b2b_profile_url: data.b2b_profile_url ?? null,
|
||||
logo_url: data.logo_url ?? null,
|
||||
credits_consumed: data.credits_consumed ?? 0,
|
||||
message: data.message ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
companyName: { type: 'string', description: 'Company name', optional: true },
|
||||
companyId: { type: 'number', description: 'Internal company identifier', optional: true },
|
||||
industry: { type: 'string', description: 'Industry classification', optional: true },
|
||||
employeeCount: { type: 'number', description: 'Number of employees', optional: true },
|
||||
employeeRange: {
|
||||
type: 'string',
|
||||
description: 'Headcount range (e.g., 1001-5000)',
|
||||
optional: true,
|
||||
},
|
||||
founded: { type: 'number', description: 'Year the company was founded', optional: true },
|
||||
headquarters: { type: 'json', description: 'Headquarters location object', optional: true },
|
||||
revenue: { type: 'string', description: 'Revenue range', optional: true },
|
||||
funding: { type: 'string', description: 'Total funding amount', optional: true },
|
||||
description: { type: 'string', description: 'Company description', optional: true },
|
||||
specialties: { type: 'array', description: 'Company specialties and focus areas' },
|
||||
competitors: { type: 'array', description: 'Competitor companies' },
|
||||
followerCount: { type: 'number', description: 'LinkedIn follower count', optional: true },
|
||||
twitter_url: { type: 'string', description: 'Twitter/X profile URL', optional: true },
|
||||
facebook_url: { type: 'string', description: 'Facebook page URL', optional: true },
|
||||
b2b_profile_url: {
|
||||
type: 'string',
|
||||
description: 'LinkedIn company profile URL',
|
||||
optional: true,
|
||||
},
|
||||
logo_url: { type: 'string', description: 'Company logo URL', optional: true },
|
||||
credits_consumed: { type: 'number', description: 'Credits charged (1 when company found)' },
|
||||
message: { type: 'string', description: 'Human-readable status message', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { leadmagicHosting } from '@/tools/leadmagic/hosting'
|
||||
import type {
|
||||
LeadMagicEmailToProfileParams,
|
||||
LeadMagicEmailToProfileResponse,
|
||||
} from '@/tools/leadmagic/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const emailToProfileTool: ToolConfig<
|
||||
LeadMagicEmailToProfileParams,
|
||||
LeadMagicEmailToProfileResponse
|
||||
> = {
|
||||
id: 'leadmagic_email_to_profile',
|
||||
name: 'LeadMagic Email to Profile',
|
||||
description:
|
||||
'Retrieve a LinkedIn profile URL from a work or personal email address. Charges 10 credits when a profile is found; free when no result.',
|
||||
version: '1.0.0',
|
||||
|
||||
hosting: leadmagicHosting<LeadMagicEmailToProfileParams>((_params, output) => {
|
||||
// 10 credits when profile found, 0 otherwise.
|
||||
// Source: https://leadmagic.io/docs/v1/reference/email-to-profile
|
||||
const consumed = output.credits_consumed
|
||||
return typeof consumed === 'number' ? consumed : output.profile_url ? 10 : 0
|
||||
}),
|
||||
|
||||
params: {
|
||||
work_email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Work email address (provide at least one of work_email or personal_email)',
|
||||
},
|
||||
personal_email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Personal email address (provide at least one of work_email or personal_email)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'LeadMagic API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.leadmagic.io/v1/people/b2b-profile',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'X-API-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, string> = {}
|
||||
if (params.work_email) body.work_email = params.work_email
|
||||
if (params.personal_email) body.personal_email = params.personal_email
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
(errorData as Record<string, string>).message ||
|
||||
`LeadMagic API error: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
profile_url: data.profile_url ?? null,
|
||||
credits_consumed: data.credits_consumed ?? 0,
|
||||
message: data.message ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
profile_url: {
|
||||
type: 'string',
|
||||
description: 'LinkedIn profile URL for the provided email',
|
||||
optional: true,
|
||||
},
|
||||
credits_consumed: { type: 'number', description: 'Credits charged (10 when profile found)' },
|
||||
message: { type: 'string', description: 'Human-readable status message', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import { leadmagicHosting } from '@/tools/leadmagic/hosting'
|
||||
import type { LeadMagicFindEmailParams, LeadMagicFindEmailResponse } from '@/tools/leadmagic/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const findEmailTool: ToolConfig<LeadMagicFindEmailParams, LeadMagicFindEmailResponse> = {
|
||||
id: 'leadmagic_find_email',
|
||||
name: 'LeadMagic Find Email',
|
||||
description:
|
||||
"Find someone's verified work email from their name and company domain. Charges 1 credit when a valid email is found; free when no result.",
|
||||
version: '1.0.0',
|
||||
|
||||
hosting: leadmagicHosting<LeadMagicFindEmailParams>((_params, output) => {
|
||||
// 1 credit per valid email found, 0 credits when not found.
|
||||
// Source: https://leadmagic.io/docs/v1/reference/email-finder
|
||||
const consumed = output.credits_consumed
|
||||
return typeof consumed === 'number' ? consumed : output.email ? 1 : 0
|
||||
}),
|
||||
|
||||
params: {
|
||||
first_name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: "Person's first name (use with last_name, or use full_name instead)",
|
||||
},
|
||||
last_name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: "Person's last name (use with first_name, or use full_name instead)",
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: "Person's full name (alternative to first_name + last_name)",
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Company domain (preferred, e.g. stripe.com)',
|
||||
},
|
||||
company_name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Company name (fallback if domain is unavailable)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'LeadMagic API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.leadmagic.io/v1/people/email-finder',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'X-API-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, string> = {}
|
||||
if (params.first_name) body.first_name = params.first_name
|
||||
if (params.last_name) body.last_name = params.last_name
|
||||
if (params.full_name) body.full_name = params.full_name
|
||||
if (params.domain) body.domain = params.domain
|
||||
if (params.company_name) body.company_name = params.company_name
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
(errorData as Record<string, string>).message ||
|
||||
`LeadMagic API error: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
email: data.email ?? null,
|
||||
status: data.status ?? null,
|
||||
credits_consumed: data.credits_consumed ?? 0,
|
||||
message: data.message ?? null,
|
||||
employment_verified: data.employment_verified ?? null,
|
||||
has_mx: data.has_mx ?? null,
|
||||
mx_record: data.mx_record ?? null,
|
||||
mx_provider: data.mx_provider ?? null,
|
||||
company_name: data.company_name ?? null,
|
||||
company_industry: data.company_industry ?? null,
|
||||
company_size: data.company_size ?? null,
|
||||
company_profile_url: data.company_profile_url ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
email: { type: 'string', description: 'Found work email address', optional: true },
|
||||
status: { type: 'string', description: 'Result status (valid, invalid, etc.)', optional: true },
|
||||
credits_consumed: { type: 'number', description: 'Credits charged (1 when email found)' },
|
||||
message: { type: 'string', description: 'Human-readable status message', optional: true },
|
||||
employment_verified: {
|
||||
type: 'boolean',
|
||||
description: 'Whether employment at the company was verified',
|
||||
optional: true,
|
||||
},
|
||||
has_mx: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the domain has a valid MX record',
|
||||
optional: true,
|
||||
},
|
||||
mx_record: { type: 'string', description: 'MX record for the email domain', optional: true },
|
||||
mx_provider: { type: 'string', description: 'Email provider', optional: true },
|
||||
company_name: { type: 'string', description: 'Company name', optional: true },
|
||||
company_industry: { type: 'string', description: 'Company industry', optional: true },
|
||||
company_size: { type: 'string', description: 'Company size range', optional: true },
|
||||
company_profile_url: {
|
||||
type: 'string',
|
||||
description: 'Company LinkedIn/B2B profile URL',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { leadmagicHosting } from '@/tools/leadmagic/hosting'
|
||||
import type {
|
||||
LeadMagicFindMobileParams,
|
||||
LeadMagicFindMobileResponse,
|
||||
} from '@/tools/leadmagic/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const findMobileTool: ToolConfig<LeadMagicFindMobileParams, LeadMagicFindMobileResponse> = {
|
||||
id: 'leadmagic_find_mobile',
|
||||
name: 'LeadMagic Find Mobile',
|
||||
description:
|
||||
"Find a person's direct mobile number from their LinkedIn profile URL or email. Charges 5 credits when a number is found; free when no result.",
|
||||
version: '1.0.0',
|
||||
|
||||
hosting: leadmagicHosting<LeadMagicFindMobileParams>((_params, output) => {
|
||||
// 5 credits per mobile number found, 0 when not found.
|
||||
// Source: https://leadmagic.io/docs/v1/reference/mobile-finder
|
||||
const consumed = output.credits_consumed
|
||||
return typeof consumed === 'number' ? consumed : output.mobile_number ? 5 : 0
|
||||
}),
|
||||
|
||||
params: {
|
||||
profile_url: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'LinkedIn profile URL (provide at least one identifier)',
|
||||
},
|
||||
work_email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Work email address (provide at least one identifier)',
|
||||
},
|
||||
personal_email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Personal email address (provide at least one identifier)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'LeadMagic API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.leadmagic.io/v1/people/mobile-finder',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'X-API-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, string> = {}
|
||||
if (params.profile_url) body.profile_url = params.profile_url
|
||||
if (params.work_email) body.work_email = params.work_email
|
||||
if (params.personal_email) body.personal_email = params.personal_email
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
(errorData as Record<string, string>).message ||
|
||||
`LeadMagic API error: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
profile_url: data.profile_url ?? null,
|
||||
email: data.email ?? null,
|
||||
mobile_number: data.mobile_number ?? null,
|
||||
credits_consumed: data.credits_consumed ?? 0,
|
||||
message: data.message ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
profile_url: {
|
||||
type: 'string',
|
||||
description: 'LinkedIn profile URL used for lookup',
|
||||
optional: true,
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Email address associated with the profile',
|
||||
optional: true,
|
||||
},
|
||||
mobile_number: { type: 'string', description: 'Direct mobile phone number', optional: true },
|
||||
credits_consumed: { type: 'number', description: 'Credits charged (5 when mobile found)' },
|
||||
message: { type: 'string', description: 'Status message from the API', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import type {
|
||||
LeadMagicGetCreditsParams,
|
||||
LeadMagicGetCreditsResponse,
|
||||
} from '@/tools/leadmagic/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getCreditsTool: ToolConfig<LeadMagicGetCreditsParams, LeadMagicGetCreditsResponse> = {
|
||||
id: 'leadmagic_get_credits',
|
||||
name: 'LeadMagic Get Credits',
|
||||
description:
|
||||
'Retrieve the current credit balance for the authenticated LeadMagic account. This endpoint is free and consumes no credits.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'LeadMagic API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.leadmagic.io/v1/credits',
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'X-API-Key': params.apiKey,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
(errorData as Record<string, string>).message ||
|
||||
`LeadMagic API error: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
credits: data.credits ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
credits: { type: 'number', description: 'Current credit balance' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { ToolHostingConfig } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Env var prefix for LeadMagic hosted keys. Provide keys as
|
||||
* `LEADMAGIC_API_KEY_COUNT` plus `LEADMAGIC_API_KEY_1..N`.
|
||||
*/
|
||||
export const LEADMAGIC_API_KEY_PREFIX = 'LEADMAGIC_API_KEY'
|
||||
|
||||
/**
|
||||
* Dollar cost of a single LeadMagic credit.
|
||||
*
|
||||
* LeadMagic charges only when data is found (not_found results are free).
|
||||
* Based on the entry Basic plan ($49/month, 2,000 credits ≈ $0.0245/credit);
|
||||
* per-credit drops at higher tiers (Essential/Growth). Email finder: 1 credit.
|
||||
* Mobile finder: 5 credits. Company search: 1 credit.
|
||||
*
|
||||
* Source: https://leadmagic.io/pricing
|
||||
*/
|
||||
export const LEADMAGIC_CREDIT_USD = 0.0245
|
||||
|
||||
/**
|
||||
* Build a LeadMagic `hosting` config. `getCredits` returns the number of
|
||||
* LeadMagic credits the call consumed, derived from the tool's output (per the
|
||||
* documented per-endpoint credit model at https://leadmagic.io/docs).
|
||||
*
|
||||
* LeadMagic responses include a `credits_consumed` field on every endpoint.
|
||||
* When no result is found, `credits_consumed` is 0.
|
||||
*/
|
||||
export function leadmagicHosting<P>(
|
||||
getCredits: (params: P, output: Record<string, unknown>) => number
|
||||
): ToolHostingConfig<P> {
|
||||
return {
|
||||
envKeyPrefix: LEADMAGIC_API_KEY_PREFIX,
|
||||
apiKeyParam: 'apiKey',
|
||||
byokProviderId: 'leadmagic',
|
||||
pricing: {
|
||||
type: 'custom',
|
||||
getCost: (params, output) => {
|
||||
const credits = getCredits(params, output)
|
||||
return { cost: credits * LEADMAGIC_CREDIT_USD, metadata: { credits } }
|
||||
},
|
||||
},
|
||||
rateLimit: {
|
||||
mode: 'per_request',
|
||||
requestsPerMinute: 60,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export * from './types'
|
||||
|
||||
import { companySearchTool } from '@/tools/leadmagic/company_search'
|
||||
import { emailToProfileTool } from '@/tools/leadmagic/email_to_profile'
|
||||
import { findEmailTool } from '@/tools/leadmagic/find_email'
|
||||
import { findMobileTool } from '@/tools/leadmagic/find_mobile'
|
||||
import { getCreditsTool } from '@/tools/leadmagic/get_credits'
|
||||
import { profileSearchTool } from '@/tools/leadmagic/profile_search'
|
||||
import { profileToEmailTool } from '@/tools/leadmagic/profile_to_email'
|
||||
import { roleFinderTool } from '@/tools/leadmagic/role_finder'
|
||||
import { validateEmailTool } from '@/tools/leadmagic/validate_email'
|
||||
|
||||
export const leadmagicValidateEmailTool = validateEmailTool
|
||||
export const leadmagicFindEmailTool = findEmailTool
|
||||
export const leadmagicFindMobileTool = findMobileTool
|
||||
export const leadmagicProfileSearchTool = profileSearchTool
|
||||
export const leadmagicProfileToEmailTool = profileToEmailTool
|
||||
export const leadmagicEmailToProfileTool = emailToProfileTool
|
||||
export const leadmagicCompanySearchTool = companySearchTool
|
||||
export const leadmagicRoleFinderTool = roleFinderTool
|
||||
export const leadmagicGetCreditsTool = getCreditsTool
|
||||
@@ -0,0 +1,128 @@
|
||||
import { leadmagicHosting } from '@/tools/leadmagic/hosting'
|
||||
import type {
|
||||
LeadMagicProfileSearchParams,
|
||||
LeadMagicProfileSearchResponse,
|
||||
} from '@/tools/leadmagic/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const profileSearchTool: ToolConfig<
|
||||
LeadMagicProfileSearchParams,
|
||||
LeadMagicProfileSearchResponse
|
||||
> = {
|
||||
id: 'leadmagic_profile_search',
|
||||
name: 'LeadMagic Profile Search',
|
||||
description:
|
||||
'Enrich a LinkedIn profile with work history, education, skills, and contact data. Charges 1 credit per successful enrichment; free when profile not found.',
|
||||
version: '1.0.0',
|
||||
|
||||
hosting: leadmagicHosting<LeadMagicProfileSearchParams>((_params, output) => {
|
||||
// 1 credit per successful enrichment, 0 when not found.
|
||||
// Source: https://leadmagic.io/docs/v1/reference/profile-search
|
||||
const consumed = output.credits_consumed
|
||||
return typeof consumed === 'number' ? consumed : output.full_name ? 1 : 0
|
||||
}),
|
||||
|
||||
params: {
|
||||
profile_url: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'LinkedIn profile URL or username (e.g., https://linkedin.com/in/johndoe)',
|
||||
},
|
||||
extended_response: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Include additional profile image URL in the response (default: false)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'LeadMagic API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.leadmagic.io/v1/people/profile-search',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'X-API-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = { profile_url: params.profile_url }
|
||||
if (params.extended_response !== undefined) body.extended_response = params.extended_response
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
(errorData as Record<string, string>).message ||
|
||||
`LeadMagic API error: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
profile_url: data.profile_url ?? null,
|
||||
first_name: data.first_name ?? null,
|
||||
last_name: data.last_name ?? null,
|
||||
full_name: data.full_name ?? null,
|
||||
professional_title: data.professional_title ?? null,
|
||||
bio: data.bio ?? null,
|
||||
location: data.location ?? null,
|
||||
country: data.country ?? null,
|
||||
followers_range: data.followers_range ?? null,
|
||||
company_name: data.company_name ?? null,
|
||||
company_industry: data.company_industry ?? null,
|
||||
company_website: data.company_website ?? null,
|
||||
total_tenure_years: data.total_tenure_years ?? null,
|
||||
total_tenure_months: data.total_tenure_months ?? null,
|
||||
work_experience: data.work_experience ?? [],
|
||||
education: data.education ?? [],
|
||||
certifications: data.certifications ?? [],
|
||||
credits_consumed: data.credits_consumed ?? 0,
|
||||
message: data.message ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
profile_url: { type: 'string', description: 'LinkedIn profile URL', optional: true },
|
||||
first_name: { type: 'string', description: 'First name', optional: true },
|
||||
last_name: { type: 'string', description: 'Last name', optional: true },
|
||||
full_name: { type: 'string', description: 'Full name', optional: true },
|
||||
professional_title: { type: 'string', description: 'Current job title', optional: true },
|
||||
bio: { type: 'string', description: 'Profile bio / summary', optional: true },
|
||||
location: { type: 'string', description: 'Location string', optional: true },
|
||||
country: { type: 'string', description: 'Country', optional: true },
|
||||
followers_range: { type: 'string', description: 'LinkedIn follower range', optional: true },
|
||||
company_name: { type: 'string', description: 'Current employer', optional: true },
|
||||
company_industry: {
|
||||
type: 'string',
|
||||
description: 'Industry of current employer',
|
||||
optional: true,
|
||||
},
|
||||
company_website: { type: 'string', description: 'Company website', optional: true },
|
||||
total_tenure_years: {
|
||||
type: 'string',
|
||||
description: 'Total professional tenure in years',
|
||||
optional: true,
|
||||
},
|
||||
total_tenure_months: {
|
||||
type: 'string',
|
||||
description: 'Total professional tenure in months',
|
||||
optional: true,
|
||||
},
|
||||
work_experience: { type: 'array', description: 'Work history entries' },
|
||||
education: { type: 'array', description: 'Education history entries' },
|
||||
certifications: { type: 'array', description: 'Professional certifications' },
|
||||
credits_consumed: { type: 'number', description: 'Credits charged (1 when profile found)' },
|
||||
message: { type: 'string', description: 'Human-readable status message', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import { leadmagicHosting } from '@/tools/leadmagic/hosting'
|
||||
import type {
|
||||
LeadMagicProfileToEmailParams,
|
||||
LeadMagicProfileToEmailResponse,
|
||||
} from '@/tools/leadmagic/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const profileToEmailTool: ToolConfig<
|
||||
LeadMagicProfileToEmailParams,
|
||||
LeadMagicProfileToEmailResponse
|
||||
> = {
|
||||
id: 'leadmagic_profile_to_email',
|
||||
name: 'LeadMagic Profile to Email',
|
||||
description:
|
||||
'Extract a verified work email from a LinkedIn profile URL. Charges 5 credits when an email is found; free when no result.',
|
||||
version: '1.0.0',
|
||||
|
||||
hosting: leadmagicHosting<LeadMagicProfileToEmailParams>((_params, output) => {
|
||||
// 5 credits when email found, 0 otherwise.
|
||||
// Source: https://leadmagic.io/docs/v1/reference/profile-to-email
|
||||
const consumed = output.credits_consumed
|
||||
return typeof consumed === 'number' ? consumed : output.email ? 5 : 0
|
||||
}),
|
||||
|
||||
params: {
|
||||
profile_url: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'LinkedIn profile URL or username (e.g., https://linkedin.com/in/johndoe)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'LeadMagic API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.leadmagic.io/v1/people/b2b-profile-email',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'X-API-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({ profile_url: params.profile_url }),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
(errorData as Record<string, string>).message ||
|
||||
`LeadMagic API error: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
email: data.email ?? null,
|
||||
profile_url: data.profile_url ?? null,
|
||||
credits_consumed: data.credits_consumed ?? 0,
|
||||
message: data.message ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Work email address found for this profile',
|
||||
optional: true,
|
||||
},
|
||||
profile_url: {
|
||||
type: 'string',
|
||||
description: 'LinkedIn profile URL used for lookup',
|
||||
optional: true,
|
||||
},
|
||||
credits_consumed: { type: 'number', description: 'Credits charged (5 when email found)' },
|
||||
message: { type: 'string', description: 'Human-readable status message', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { leadmagicHosting } from '@/tools/leadmagic/hosting'
|
||||
import type {
|
||||
LeadMagicRoleFinderParams,
|
||||
LeadMagicRoleFinderResponse,
|
||||
} from '@/tools/leadmagic/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const roleFinderTool: ToolConfig<LeadMagicRoleFinderParams, LeadMagicRoleFinderResponse> = {
|
||||
id: 'leadmagic_role_finder',
|
||||
name: 'LeadMagic Role Finder',
|
||||
description:
|
||||
'Find the person holding a specific job role at a company. Charges 2 credits when a matching person is found; free when no result.',
|
||||
version: '1.0.0',
|
||||
|
||||
hosting: leadmagicHosting<LeadMagicRoleFinderParams>((_params, output) => {
|
||||
// 2 credits when a person is found, 0 otherwise.
|
||||
// Source: https://leadmagic.io/docs/v1/reference/role-finder
|
||||
const consumed = output.credits_consumed
|
||||
return typeof consumed === 'number' ? consumed : output.full_name ? 2 : 0
|
||||
}),
|
||||
|
||||
params: {
|
||||
job_title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Job role to search for (e.g., Head of Sales, CTO). Supports partial matching.',
|
||||
},
|
||||
company_domain: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Company website domain (e.g., stripe.com). Provide domain or company_name.',
|
||||
},
|
||||
company_name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Company name (fallback if domain unavailable). Provide domain or company_name.',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'LeadMagic API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.leadmagic.io/v1/people/role-finder',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'X-API-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, string> = { job_title: params.job_title }
|
||||
if (params.company_domain) body.company_domain = params.company_domain
|
||||
if (params.company_name) body.company_name = params.company_name
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
(errorData as Record<string, string>).message ||
|
||||
`LeadMagic API error: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
first_name: data.first_name ?? null,
|
||||
last_name: data.last_name ?? null,
|
||||
full_name: data.full_name ?? null,
|
||||
profile_url: data.profile_url ?? null,
|
||||
job_title: data.job_title ?? null,
|
||||
company_name: data.company_name ?? null,
|
||||
company_website: data.company_website ?? null,
|
||||
credits_consumed: data.credits_consumed ?? 0,
|
||||
message: data.message ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
first_name: { type: 'string', description: 'First name of the person found', optional: true },
|
||||
last_name: { type: 'string', description: 'Last name of the person found', optional: true },
|
||||
full_name: { type: 'string', description: 'Full name of the person found', optional: true },
|
||||
profile_url: { type: 'string', description: 'LinkedIn profile URL', optional: true },
|
||||
job_title: { type: 'string', description: 'Verified job title at the company', optional: true },
|
||||
company_name: { type: 'string', description: 'Company name', optional: true },
|
||||
company_website: { type: 'string', description: 'Company website', optional: true },
|
||||
credits_consumed: { type: 'number', description: 'Credits charged (2 when person found)' },
|
||||
message: { type: 'string', description: 'Human-readable status message', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
import type { OutputProperty, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface LeadMagicBaseParams {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared output property constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const LEADMAGIC_PROFILE_OUTPUT_PROPERTIES = {
|
||||
profile_url: { type: 'string', description: 'LinkedIn profile URL' },
|
||||
first_name: { type: 'string', description: 'First name' },
|
||||
last_name: { type: 'string', description: 'Last name' },
|
||||
full_name: { type: 'string', description: 'Full name' },
|
||||
professional_title: { type: 'string', description: 'Current job title', optional: true },
|
||||
bio: { type: 'string', description: 'Profile bio / summary', optional: true },
|
||||
location: { type: 'string', description: 'Location string', optional: true },
|
||||
country: { type: 'string', description: 'Country', optional: true },
|
||||
company_name: { type: 'string', description: 'Current employer', optional: true },
|
||||
company_industry: { type: 'string', description: 'Industry of current employer', optional: true },
|
||||
company_website: { type: 'string', description: 'Company website', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Email Validation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LeadMagicValidateEmailParams extends LeadMagicBaseParams {
|
||||
email: string
|
||||
}
|
||||
|
||||
export interface LeadMagicValidateEmailResponse extends ToolResponse {
|
||||
output: {
|
||||
email: string
|
||||
email_status: string
|
||||
is_domain_catch_all: boolean | null
|
||||
credits_consumed: number
|
||||
message: string | null
|
||||
mx_record: string | null
|
||||
mx_provider: string | null
|
||||
mx_gateway: string | null
|
||||
mx_security_gateway: boolean | null
|
||||
company_name: string | null
|
||||
company_industry: string | null
|
||||
company_size: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Email Finder
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LeadMagicFindEmailParams extends LeadMagicBaseParams {
|
||||
first_name?: string
|
||||
last_name?: string
|
||||
full_name?: string
|
||||
domain?: string
|
||||
company_name?: string
|
||||
}
|
||||
|
||||
export interface LeadMagicFindEmailResponse extends ToolResponse {
|
||||
output: {
|
||||
email: string | null
|
||||
status: string | null
|
||||
credits_consumed: number
|
||||
message: string | null
|
||||
employment_verified: boolean | null
|
||||
has_mx: boolean | null
|
||||
mx_record: string | null
|
||||
mx_provider: string | null
|
||||
company_name: string | null
|
||||
company_industry: string | null
|
||||
company_size: string | null
|
||||
company_profile_url: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mobile Finder
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LeadMagicFindMobileParams extends LeadMagicBaseParams {
|
||||
profile_url?: string
|
||||
work_email?: string
|
||||
personal_email?: string
|
||||
}
|
||||
|
||||
export interface LeadMagicFindMobileResponse extends ToolResponse {
|
||||
output: {
|
||||
profile_url: string | null
|
||||
email: string | null
|
||||
mobile_number: string | null
|
||||
credits_consumed: number
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Profile Search (LinkedIn enrichment by profile URL)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LeadMagicProfileSearchParams extends LeadMagicBaseParams {
|
||||
profile_url: string
|
||||
extended_response?: boolean
|
||||
}
|
||||
|
||||
export interface LeadMagicProfileSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
profile_url: string | null
|
||||
first_name: string | null
|
||||
last_name: string | null
|
||||
full_name: string | null
|
||||
professional_title: string | null
|
||||
bio: string | null
|
||||
location: string | null
|
||||
country: string | null
|
||||
followers_range: string | null
|
||||
company_name: string | null
|
||||
company_industry: string | null
|
||||
company_website: string | null
|
||||
total_tenure_years: string | null
|
||||
total_tenure_months: string | null
|
||||
work_experience: unknown[]
|
||||
education: unknown[]
|
||||
certifications: unknown[]
|
||||
credits_consumed: number
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Profile to Email (LinkedIn URL → work email)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LeadMagicProfileToEmailParams extends LeadMagicBaseParams {
|
||||
profile_url: string
|
||||
}
|
||||
|
||||
export interface LeadMagicProfileToEmailResponse extends ToolResponse {
|
||||
output: {
|
||||
email: string | null
|
||||
profile_url: string | null
|
||||
credits_consumed: number
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Email to Profile (work/personal email → LinkedIn profile URL)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LeadMagicEmailToProfileParams extends LeadMagicBaseParams {
|
||||
work_email?: string
|
||||
personal_email?: string
|
||||
}
|
||||
|
||||
export interface LeadMagicEmailToProfileResponse extends ToolResponse {
|
||||
output: {
|
||||
profile_url: string | null
|
||||
credits_consumed: number
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Company Search
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LeadMagicCompanySearchParams extends LeadMagicBaseParams {
|
||||
company_domain?: string
|
||||
profile_url?: string
|
||||
company_name?: string
|
||||
}
|
||||
|
||||
export interface LeadMagicCompanySearchResponse extends ToolResponse {
|
||||
output: {
|
||||
companyName: string | null
|
||||
companyId: number | null
|
||||
industry: string | null
|
||||
employeeCount: number | null
|
||||
employeeRange: string | null
|
||||
founded: number | null
|
||||
headquarters: Record<string, string> | null
|
||||
revenue: string | null
|
||||
funding: string | null
|
||||
description: string | null
|
||||
specialties: unknown[]
|
||||
competitors: unknown[]
|
||||
followerCount: number | null
|
||||
twitter_url: string | null
|
||||
facebook_url: string | null
|
||||
b2b_profile_url: string | null
|
||||
logo_url: string | null
|
||||
credits_consumed: number
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Role Finder
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LeadMagicRoleFinderParams extends LeadMagicBaseParams {
|
||||
job_title: string
|
||||
company_domain?: string
|
||||
company_name?: string
|
||||
}
|
||||
|
||||
export interface LeadMagicRoleFinderResponse extends ToolResponse {
|
||||
output: {
|
||||
first_name: string | null
|
||||
last_name: string | null
|
||||
full_name: string | null
|
||||
profile_url: string | null
|
||||
job_title: string | null
|
||||
company_name: string | null
|
||||
company_website: string | null
|
||||
credits_consumed: number
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Get Credits (balance check — free, no hosting)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LeadMagicGetCreditsParams extends LeadMagicBaseParams {}
|
||||
|
||||
export interface LeadMagicGetCreditsResponse extends ToolResponse {
|
||||
output: {
|
||||
credits: number
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Union response type
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type LeadMagicResponse =
|
||||
| LeadMagicValidateEmailResponse
|
||||
| LeadMagicFindEmailResponse
|
||||
| LeadMagicFindMobileResponse
|
||||
| LeadMagicProfileSearchResponse
|
||||
| LeadMagicProfileToEmailResponse
|
||||
| LeadMagicEmailToProfileResponse
|
||||
| LeadMagicCompanySearchResponse
|
||||
| LeadMagicRoleFinderResponse
|
||||
| LeadMagicGetCreditsResponse
|
||||
@@ -0,0 +1,119 @@
|
||||
import { leadmagicHosting } from '@/tools/leadmagic/hosting'
|
||||
import type {
|
||||
LeadMagicValidateEmailParams,
|
||||
LeadMagicValidateEmailResponse,
|
||||
} from '@/tools/leadmagic/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const validateEmailTool: ToolConfig<
|
||||
LeadMagicValidateEmailParams,
|
||||
LeadMagicValidateEmailResponse
|
||||
> = {
|
||||
id: 'leadmagic_validate_email',
|
||||
name: 'LeadMagic Validate Email',
|
||||
description:
|
||||
'Verify an email address for deliverability. Charges 0.25 credits for definitive SMTP results (valid/invalid); unknown and RFC-invalid results are free.',
|
||||
version: '1.0.0',
|
||||
|
||||
hosting: leadmagicHosting<LeadMagicValidateEmailParams>((_params, output) => {
|
||||
// 0.25 credits for valid or SMTP-verified-invalid; free for unknown/syntax failures.
|
||||
// We use the API-reported credits_consumed field.
|
||||
// Source: https://leadmagic.io/docs/v1/reference/email-validation
|
||||
const consumed = output.credits_consumed
|
||||
return typeof consumed === 'number' ? consumed : 0
|
||||
}),
|
||||
|
||||
params: {
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email address to validate (e.g., john@example.com)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'LeadMagic API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.leadmagic.io/v1/people/email-validation',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'X-API-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({ email: params.email }),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
(errorData as Record<string, string>).message ||
|
||||
`LeadMagic API error: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
email: data.email ?? '',
|
||||
email_status: data.email_status ?? '',
|
||||
is_domain_catch_all: data.is_domain_catch_all ?? null,
|
||||
credits_consumed: data.credits_consumed ?? 0,
|
||||
message: data.message ?? null,
|
||||
mx_record: data.mx_record ?? null,
|
||||
mx_provider: data.mx_provider ?? null,
|
||||
mx_gateway: data.mx_gateway ?? null,
|
||||
mx_security_gateway: data.mx_security_gateway ?? null,
|
||||
company_name: data.company_name ?? null,
|
||||
company_industry: data.company_industry ?? null,
|
||||
company_size: data.company_size ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
email: { type: 'string', description: 'The validated email address' },
|
||||
email_status: {
|
||||
type: 'string',
|
||||
description: 'Validation result: valid, invalid, or unknown',
|
||||
},
|
||||
is_domain_catch_all: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the domain accepts all emails (catch-all)',
|
||||
optional: true,
|
||||
},
|
||||
credits_consumed: {
|
||||
type: 'number',
|
||||
description: 'Credits charged for this request (0.25 for definitive results)',
|
||||
},
|
||||
message: { type: 'string', description: 'Human-readable status message', optional: true },
|
||||
mx_record: { type: 'string', description: 'MX record for the domain', optional: true },
|
||||
mx_provider: {
|
||||
type: 'string',
|
||||
description: 'Email provider (e.g., Google, Microsoft)',
|
||||
optional: true,
|
||||
},
|
||||
mx_gateway: {
|
||||
type: 'string',
|
||||
description: 'MX gateway for the domain',
|
||||
optional: true,
|
||||
},
|
||||
mx_security_gateway: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the domain uses a security gateway',
|
||||
optional: true,
|
||||
},
|
||||
company_name: {
|
||||
type: 'string',
|
||||
description: 'Company name associated with the email domain',
|
||||
optional: true,
|
||||
},
|
||||
company_industry: { type: 'string', description: 'Industry of the company', optional: true },
|
||||
company_size: { type: 'string', description: 'Company size range', optional: true },
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user