chore: import upstream snapshot with attribution
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) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,82 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type {
FindymailFindEmailFromLinkedInParams,
FindymailFindEmailFromLinkedInResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_CONTACT_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findEmailFromLinkedInTool: ToolConfig<
FindymailFindEmailFromLinkedInParams,
FindymailFindEmailFromLinkedInResponse
> = {
id: 'findymail_find_email_from_linkedin',
name: 'Findymail Find Email From LinkedIn',
description:
"Find someone's email from a LinkedIn profile URL or username. Uses one finder credit when a verified email is found.",
version: '1.0.0',
hosting: findymailHosting<FindymailFindEmailFromLinkedInParams>((_params, output) => {
const contact = output.contact as { email?: string } | null
return contact?.email ? 1 : 0
}),
params: {
linkedin_url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
"Person's LinkedIn URL or username (e.g., 'https://linkedin.com/in/johndoe' or 'johndoe')",
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/business-profile',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ linkedin_url: params.linkedin_url }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { contact: null },
}
}
const data = await response.json()
const contact = data.contact ?? data.payload?.contact ?? null
return {
success: true,
output: {
contact: contact
? {
name: contact.name ?? '',
email: contact.email ?? '',
domain: contact.domain ?? '',
}
: null,
},
}
},
outputs: {
contact: { ...FINDYMAIL_CONTACT_OUTPUT, optional: true },
},
}
@@ -0,0 +1,87 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type {
FindymailFindEmailFromNameParams,
FindymailFindEmailFromNameResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_CONTACT_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findEmailFromNameTool: ToolConfig<
FindymailFindEmailFromNameParams,
FindymailFindEmailFromNameResponse
> = {
id: 'findymail_find_email_from_name',
name: 'Findymail Find Email From Name',
description:
"Find someone's email from their name and a company domain or company name. Uses one finder credit when a verified email is found.",
version: '1.0.0',
hosting: findymailHosting<FindymailFindEmailFromNameParams>((_params, output) => {
const contact = output.contact as { email?: string } | null
return contact?.email ? 1 : 0
}),
params: {
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Person's full name (e.g., 'John Doe')",
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Company domain (preferred) or company name (e.g., stripe.com)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/name',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ name: params.name, domain: params.domain }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { contact: null },
}
}
const data = await response.json()
const contact = data.contact ?? data.payload?.contact ?? null
return {
success: true,
output: {
contact: contact
? {
name: contact.name ?? '',
email: contact.email ?? '',
domain: contact.domain ?? '',
}
: null,
},
}
},
outputs: {
contact: { ...FINDYMAIL_CONTACT_OUTPUT, optional: true },
},
}
@@ -0,0 +1,88 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type {
FindymailFindEmailsByDomainParams,
FindymailFindEmailsByDomainResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_CONTACTS_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findEmailsByDomainTool: ToolConfig<
FindymailFindEmailsByDomainParams,
FindymailFindEmailsByDomainResponse
> = {
id: 'findymail_find_emails_by_domain',
name: 'Findymail Find Emails By Domain',
description:
'Find verified contacts at a given domain matching one or more target roles (max 3 roles). Limited to 5 concurrent synchronous requests.',
version: '1.0.0',
hosting: findymailHosting<FindymailFindEmailsByDomainParams>((_params, output) => {
// No contacts array means no verified contacts returned — no charge.
if (!Array.isArray(output.contacts)) {
return 0
}
// 1 finder credit per verified contact returned.
return output.contacts.length
}),
params: {
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Company domain (e.g., stripe.com)',
},
roles: {
type: 'array',
required: true,
visibility: 'user-or-llm',
description: 'Target roles at the company (max 3, e.g., ["CEO", "Founder"])',
items: { type: 'string' },
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/domain',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ domain: params.domain, roles: params.roles }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { contacts: [] },
}
}
const data = await response.json()
const raw = data.contacts ?? data.payload?.contacts ?? []
const contacts = Array.isArray(raw)
? raw.map((c: { name?: string; email?: string; domain?: string }) => ({
name: c.name ?? '',
email: c.email ?? '',
domain: c.domain ?? '',
}))
: []
return { success: true, output: { contacts } }
},
outputs: {
contacts: FINDYMAIL_CONTACTS_OUTPUT,
},
}
+111
View File
@@ -0,0 +1,111 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type {
FindymailFindEmployeesParams,
FindymailFindEmployeesResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_EMPLOYEES_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findEmployeesTool: ToolConfig<
FindymailFindEmployeesParams,
FindymailFindEmployeesResponse
> = {
id: 'findymail_find_employees',
name: 'Findymail Find Employees',
description:
'Find employees at a company by website and target job titles. Uses 1 credit per found contact. Does not return email addresses.',
version: '1.0.0',
hosting: findymailHosting<FindymailFindEmployeesParams>((_params, output) => {
// No employees array means no contacts found — no charge.
if (!Array.isArray(output.employees)) {
return 0
}
// 1 finder credit per contact found.
return output.employees.length
}),
params: {
website: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Company website or domain (e.g., google.com)',
},
job_titles: {
type: 'array',
required: true,
visibility: 'user-or-llm',
description: 'Target job titles to search for (max 10, e.g., ["Software Engineer", "CEO"])',
items: { type: 'string' },
},
count: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of contacts to return (max 5, default 1)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/employees',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
website: params.website,
job_titles: params.job_titles,
}
if (params.count !== undefined) body.count = params.count
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { employees: [] },
}
}
const data = await response.json()
const raw = Array.isArray(data) ? data : (data.data ?? [])
const employees = Array.isArray(raw)
? raw.map(
(e: {
name?: string
linkedinUrl?: string
companyWebsite?: string
companyName?: string
jobTitle?: string
}) => ({
name: e.name ?? '',
linkedinUrl: e.linkedinUrl ?? null,
companyWebsite: e.companyWebsite ?? null,
companyName: e.companyName ?? null,
jobTitle: e.jobTitle ?? null,
})
)
: []
return { success: true, output: { employees } }
},
outputs: {
employees: FINDYMAIL_EMPLOYEES_OUTPUT,
},
}
+78
View File
@@ -0,0 +1,78 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type { FindymailFindPhoneParams, FindymailFindPhoneResponse } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findPhoneTool: ToolConfig<FindymailFindPhoneParams, FindymailFindPhoneResponse> = {
id: 'findymail_find_phone',
name: 'Findymail Find Phone',
description:
"Find someone's phone number from a LinkedIn profile URL. Uses 10 finder credits if a phone is found. EU citizens are excluded for legal reasons.",
version: '1.0.0',
hosting: findymailHosting<FindymailFindPhoneParams>((_params, output) => {
// Phone lookups consume 10 finder credits, only when a number is found.
return output.phone ? 10 : 0
}),
params: {
linkedin_url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
"Person's LinkedIn URL or username (e.g., 'https://linkedin.com/in/johndoe' or 'johndoe')",
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/phone',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ linkedin_url: params.linkedin_url }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { phone: null, line_type: null },
}
}
const data = await response.json()
return {
success: true,
output: {
phone: data.phone ?? null,
line_type: data.line_type ?? null,
},
}
},
outputs: {
phone: {
type: 'string',
description: 'Phone number in E.164 format. Only available for US numbers.',
optional: true,
},
line_type: {
type: 'string',
description: 'Phone line type (e.g., "Mobile", "Landline")',
optional: true,
},
},
}
+109
View File
@@ -0,0 +1,109 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type {
FindymailGetCompanyParams,
FindymailGetCompanyResponse,
} from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const getCompanyTool: ToolConfig<FindymailGetCompanyParams, FindymailGetCompanyResponse> = {
id: 'findymail_get_company',
name: 'Findymail Get Company',
description:
'Retrieve company information from a LinkedIn URL, domain, or company name. Uses 1 finder credit per successful response.',
version: '1.0.0',
hosting: findymailHosting<FindymailGetCompanyParams>((_params, output) => {
// 1 finder credit per successful company match.
return output.name || output.domain ? 1 : 0
}),
params: {
linkedin_url: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Company LinkedIn URL (e.g., https://www.linkedin.com/company/stripe/)',
},
domain: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Company domain (e.g., stripe.com)',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Company name (e.g., Stripe)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/company',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, string> = {}
if (params.linkedin_url) body.linkedin_url = params.linkedin_url
if (params.domain) body.domain = params.domain
if (params.name) body.name = params.name
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: {
name: null,
domain: null,
company_size: null,
industry: null,
linkedin_url: null,
description: null,
},
}
}
const data = await response.json()
return {
success: true,
output: {
name: data.name ?? null,
domain: data.domain ?? null,
company_size: data.company_size ?? null,
industry: data.industry ?? null,
linkedin_url: data.linkedin_url ?? null,
description: data.description ?? null,
},
}
},
outputs: {
name: { type: 'string', description: 'Company name', optional: true },
domain: { type: 'string', description: 'Company domain', optional: true },
company_size: {
type: 'string',
description: 'Employee headcount range (e.g., 1001-5000)',
optional: true,
},
industry: { type: 'string', description: 'Industry classification', optional: true },
linkedin_url: { type: 'string', description: 'Company LinkedIn URL', optional: true },
description: { type: 'string', description: 'Company description', optional: true },
},
}
+57
View File
@@ -0,0 +1,57 @@
import type {
FindymailGetCreditsParams,
FindymailGetCreditsResponse,
} from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const getCreditsTool: ToolConfig<FindymailGetCreditsParams, FindymailGetCreditsResponse> = {
id: 'findymail_get_credits',
name: 'Findymail Get Credits',
description: 'Retrieve the remaining finder and verifier credits for the authenticated account.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/credits',
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { credits: 0, verifier_credits: 0 },
}
}
const data = await response.json()
return {
success: true,
output: {
credits: data.credits ?? 0,
verifier_credits: data.verifier_credits ?? 0,
},
}
},
outputs: {
credits: { type: 'number', description: 'Remaining finder credits' },
verifier_credits: { type: 'number', description: 'Remaining verifier credits' },
},
}
+42
View File
@@ -0,0 +1,42 @@
import type { ToolHostingConfig } from '@/tools/types'
/**
* Env var prefix for Findymail hosted keys. Provide keys as
* `FINDYMAIL_API_KEY_COUNT` plus `FINDYMAIL_API_KEY_1..N`.
*/
export const FINDYMAIL_API_KEY_PREFIX = 'FINDYMAIL_API_KEY'
/**
* Dollar cost of a single Findymail finder credit.
*
* Findymail charges per verified result: 1 credit per email, 10 credits per
* phone, and only when a result is found. Estimated from the $99/month Starter
* plan (5,000 credits ≈ $0.0198/credit) — https://www.findymail.com/pricing/.
*/
export const FINDYMAIL_CREDIT_USD = 0.02
/**
* Build a Findymail `hosting` config. `getCredits` returns the number of
* Findymail credits the call consumed, derived from the tool's output (per the
* documented per-endpoint credit model at https://www.findymail.com/api/).
*/
export function findymailHosting<P>(
getCredits: (params: P, output: Record<string, unknown>) => number
): ToolHostingConfig<P> {
return {
envKeyPrefix: FINDYMAIL_API_KEY_PREFIX,
apiKeyParam: 'apiKey',
byokProviderId: 'findymail',
pricing: {
type: 'custom',
getCost: (params, output) => {
const credits = getCredits(params, output)
return { cost: credits * FINDYMAIL_CREDIT_USD, metadata: { credits } }
},
},
rateLimit: {
mode: 'per_request',
requestsPerMinute: 60,
},
}
}
+25
View File
@@ -0,0 +1,25 @@
export * from './types'
import { findEmailFromLinkedInTool } from '@/tools/findymail/find_email_from_linkedin'
import { findEmailFromNameTool } from '@/tools/findymail/find_email_from_name'
import { findEmailsByDomainTool } from '@/tools/findymail/find_emails_by_domain'
import { findEmployeesTool } from '@/tools/findymail/find_employees'
import { findPhoneTool } from '@/tools/findymail/find_phone'
import { getCompanyTool } from '@/tools/findymail/get_company'
import { getCreditsTool } from '@/tools/findymail/get_credits'
import { lookupTechnologiesTool } from '@/tools/findymail/lookup_technologies'
import { reverseEmailLookupTool } from '@/tools/findymail/reverse_email_lookup'
import { searchTechnologiesTool } from '@/tools/findymail/search_technologies'
import { verifyEmailTool } from '@/tools/findymail/verify_email'
export const findymailVerifyEmailTool = verifyEmailTool
export const findymailFindEmailFromNameTool = findEmailFromNameTool
export const findymailFindEmailsByDomainTool = findEmailsByDomainTool
export const findymailFindEmailFromLinkedInTool = findEmailFromLinkedInTool
export const findymailReverseEmailLookupTool = reverseEmailLookupTool
export const findymailGetCompanyTool = getCompanyTool
export const findymailFindEmployeesTool = findEmployeesTool
export const findymailFindPhoneTool = findPhoneTool
export const findymailSearchTechnologiesTool = searchTechnologiesTool
export const findymailLookupTechnologiesTool = lookupTechnologiesTool
export const findymailGetCreditsTool = getCreditsTool
@@ -0,0 +1,105 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type {
FindymailLookupTechnologiesParams,
FindymailLookupTechnologiesResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_TECHNOLOGIES_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const lookupTechnologiesTool: ToolConfig<
FindymailLookupTechnologiesParams,
FindymailLookupTechnologiesResponse
> = {
id: 'findymail_lookup_technologies',
name: 'Findymail Lookup Technologies',
description:
'Get the technology stack for a company by domain. Optionally filter by technology names. 1 finder credit if technologies are found, free otherwise.',
version: '1.0.0',
hosting: findymailHosting<FindymailLookupTechnologiesParams>((_params, output) => {
// 1 finder credit when a technology stack is returned, free otherwise.
return Array.isArray(output.technologies) && output.technologies.length > 0 ? 1 : 0
}),
params: {
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Company domain to look up (e.g., stripe.com)',
},
technologies: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Filter by technology names, case-insensitive (e.g., ["React", "TypeScript"])',
items: { type: 'string' },
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/technologies',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = { domain: params.domain }
if (params.technologies && params.technologies.length > 0) {
body.technologies = params.technologies
}
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { domain: '', technologies: [] },
}
}
const data = await response.json()
const raw = data.technologies ?? []
const technologies = Array.isArray(raw)
? raw.map(
(t: {
name?: string
category?: string
subcategory?: string
last_detected_at?: string
}) => ({
name: t.name ?? '',
category: t.category ?? null,
subcategory: t.subcategory ?? null,
last_detected_at: t.last_detected_at ?? null,
})
)
: []
return {
success: true,
output: {
domain: data.domain ?? '',
technologies,
},
}
},
outputs: {
domain: { type: 'string', description: 'The resolved company domain' },
technologies: FINDYMAIL_TECHNOLOGIES_OUTPUT,
},
}
@@ -0,0 +1,158 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type {
FindymailReverseEmailLookupParams,
FindymailReverseEmailLookupResponse,
} from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const reverseEmailLookupTool: ToolConfig<
FindymailReverseEmailLookupParams,
FindymailReverseEmailLookupResponse
> = {
id: 'findymail_reverse_email_lookup',
name: 'Findymail Reverse Email Lookup',
description:
'Find a business profile from an email address. Uses 1 finder credit if a profile is found, 2 credits if returning full profile data.',
version: '1.0.0',
hosting: findymailHosting<FindymailReverseEmailLookupParams>((params, output) => {
const found = Boolean(output.email || output.linkedin_url || output.fullName)
if (!found) return 0
// 1 credit for a match, 2 when full profile enrichment is requested.
return params.with_profile ? 2 : 1
}),
params: {
email: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Work or personal email address to look up',
},
with_profile: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to return enriched profile metadata (default: false)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/reverse-email',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({
email: params.email,
...(params.with_profile ? { with_profile: true } : {}),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: {
email: null,
linkedin_url: null,
fullName: null,
username: null,
headline: null,
jobTitle: null,
summary: null,
city: null,
region: null,
country: null,
companyLinkedinUrl: null,
companyName: null,
companyWebsite: null,
isPremium: null,
isOpenProfile: null,
skills: [],
jobs: [],
educations: [],
certificates: [],
},
}
}
const data = await response.json()
return {
success: true,
output: {
email: data.email ?? null,
linkedin_url: data.linkedin_url ?? null,
fullName: data.fullName ?? null,
username: data.username ?? null,
headline: data.headline ?? null,
jobTitle: data.jobTitle ?? null,
summary: data.summary ?? null,
city: data.city ?? null,
region: data.region ?? null,
country: data.country ?? null,
companyLinkedinUrl: data.companyLinkedinUrl ?? null,
companyName: data.companyName ?? null,
companyWebsite: data.companyWebsite ?? null,
isPremium: data.isPremium ?? null,
isOpenProfile: data.isOpenProfile ?? null,
skills: data.skills ?? [],
jobs: data.jobs ?? [],
educations: data.educations ?? [],
certificates: data.certificates ?? [],
},
}
},
outputs: {
email: { type: 'string', description: 'The email address that was looked up', optional: true },
linkedin_url: { type: 'string', description: 'LinkedIn profile URL', optional: true },
fullName: { type: 'string', description: 'Full name from profile', optional: true },
username: { type: 'string', description: 'LinkedIn username', optional: true },
headline: { type: 'string', description: 'Profile headline', optional: true },
jobTitle: { type: 'string', description: 'Current job title', optional: true },
summary: { type: 'string', description: 'Profile summary', optional: true },
city: { type: 'string', description: 'City', optional: true },
region: { type: 'string', description: 'Region or state', optional: true },
country: { type: 'string', description: 'Country', optional: true },
companyLinkedinUrl: {
type: 'string',
description: 'Current company LinkedIn URL',
optional: true,
},
companyName: { type: 'string', description: 'Current company name', optional: true },
companyWebsite: { type: 'string', description: 'Current company website', optional: true },
isPremium: {
type: 'boolean',
description: 'Whether the profile has LinkedIn Premium',
optional: true,
},
isOpenProfile: {
type: 'boolean',
description: 'Whether the profile is an Open Profile',
optional: true,
},
skills: { type: 'array', description: 'List of profile skills' },
jobs: { type: 'array', description: 'Job history entries' },
educations: {
type: 'array',
description: 'Education history (school, degree, fieldOfStudy, startDate, endDate)',
},
certificates: {
type: 'array',
description: 'Certifications (name, issuingOrganization, issueDate, expirationDate)',
},
},
}
@@ -0,0 +1,87 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type {
FindymailSearchTechnologiesParams,
FindymailSearchTechnologiesResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_TECHNOLOGIES_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const searchTechnologiesTool: ToolConfig<
FindymailSearchTechnologiesParams,
FindymailSearchTechnologiesResponse
> = {
id: 'findymail_search_technologies',
name: 'Findymail Search Technologies',
description:
'Search the technology catalog by name. Returns up to 25 technologies. Free endpoint, rate limited to 10 requests per minute.',
version: '1.0.0',
hosting: findymailHosting<FindymailSearchTechnologiesParams>(() => {
// Free catalog search — consumes no Findymail credits.
return 0
}),
params: {
q: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Search term (min 2 characters, e.g., "React")',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: (params) => {
const url = new URL('https://app.findymail.com/api/technologies/search')
url.searchParams.append('q', params.q)
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { technologies: [] },
}
}
const data = await response.json()
const raw = data.data ?? []
const technologies = Array.isArray(raw)
? raw.map(
(t: {
name?: string
category?: string
subcategory?: string
last_detected_at?: string
}) => ({
name: t.name ?? '',
category: t.category ?? null,
subcategory: t.subcategory ?? null,
last_detected_at: t.last_detected_at ?? null,
})
)
: []
return { success: true, output: { technologies } }
},
outputs: {
technologies: FINDYMAIL_TECHNOLOGIES_OUTPUT,
},
}
+241
View File
@@ -0,0 +1,241 @@
import type { OutputProperty, ToolResponse } from '@/tools/types'
interface FindymailBaseParams {
apiKey: string
}
export const FINDYMAIL_CONTACT_OUTPUT_PROPERTIES = {
name: { type: 'string', description: 'Contact full name' },
email: { type: 'string', description: 'Contact email address' },
domain: { type: 'string', description: 'Email domain' },
} as const satisfies Record<string, OutputProperty>
export const FINDYMAIL_CONTACT_OUTPUT: OutputProperty = {
type: 'object',
description: 'Contact information',
properties: FINDYMAIL_CONTACT_OUTPUT_PROPERTIES,
}
export const FINDYMAIL_CONTACTS_OUTPUT: OutputProperty = {
type: 'array',
description: 'List of contacts found',
items: {
type: 'object',
properties: FINDYMAIL_CONTACT_OUTPUT_PROPERTIES,
},
}
export const FINDYMAIL_TECHNOLOGY_OUTPUT_PROPERTIES = {
name: { type: 'string', description: 'Technology name' },
category: { type: 'string', description: 'Technology category' },
subcategory: { type: 'string', description: 'Technology subcategory' },
last_detected_at: {
type: 'string',
description: 'Last detection timestamp (ISO 8601)',
optional: true,
},
} as const satisfies Record<string, OutputProperty>
export const FINDYMAIL_TECHNOLOGIES_OUTPUT: OutputProperty = {
type: 'array',
description: 'List of technologies',
items: {
type: 'object',
properties: FINDYMAIL_TECHNOLOGY_OUTPUT_PROPERTIES,
},
}
export const FINDYMAIL_EMPLOYEE_OUTPUT_PROPERTIES = {
name: { type: 'string', description: 'Employee full name' },
linkedinUrl: { type: 'string', description: 'LinkedIn profile URL', optional: true },
companyWebsite: { type: 'string', description: 'Company website', optional: true },
companyName: { type: 'string', description: 'Company name', optional: true },
jobTitle: { type: 'string', description: 'Job title', optional: true },
} as const satisfies Record<string, OutputProperty>
export const FINDYMAIL_EMPLOYEES_OUTPUT: OutputProperty = {
type: 'array',
description: 'List of employees matching the search criteria',
items: {
type: 'object',
properties: FINDYMAIL_EMPLOYEE_OUTPUT_PROPERTIES,
},
}
export interface FindymailContact {
name: string
email: string
domain: string
}
export interface FindymailVerifyEmailParams extends FindymailBaseParams {
email: string
}
export interface FindymailVerifyEmailResponse extends ToolResponse {
output: {
email: string
verified: boolean
provider: string | null
}
}
export interface FindymailFindEmailFromNameParams extends FindymailBaseParams {
name: string
domain: string
}
export interface FindymailFindEmailFromNameResponse extends ToolResponse {
output: {
contact: FindymailContact | null
}
}
export interface FindymailFindEmailsByDomainParams extends FindymailBaseParams {
domain: string
roles: string[]
}
export interface FindymailFindEmailsByDomainResponse extends ToolResponse {
output: {
contacts: FindymailContact[]
}
}
export interface FindymailFindEmailFromLinkedInParams extends FindymailBaseParams {
linkedin_url: string
}
export interface FindymailFindEmailFromLinkedInResponse extends ToolResponse {
output: {
contact: FindymailContact | null
}
}
export interface FindymailReverseEmailLookupParams extends FindymailBaseParams {
email: string
with_profile?: boolean
}
export interface FindymailReverseEmailLookupResponse extends ToolResponse {
output: {
email: string | null
linkedin_url: string | null
fullName: string | null
username: string | null
headline: string | null
jobTitle: string | null
summary: string | null
city: string | null
region: string | null
country: string | null
companyLinkedinUrl: string | null
companyName: string | null
companyWebsite: string | null
isPremium: boolean | null
isOpenProfile: boolean | null
skills: unknown[]
jobs: unknown[]
educations: unknown[]
certificates: unknown[]
}
}
export interface FindymailGetCompanyParams extends FindymailBaseParams {
linkedin_url?: string
domain?: string
name?: string
}
export interface FindymailGetCompanyResponse extends ToolResponse {
output: {
name: string | null
domain: string | null
company_size: string | null
industry: string | null
linkedin_url: string | null
description: string | null
}
}
export interface FindymailFindEmployeesParams extends FindymailBaseParams {
website: string
job_titles: string[]
count?: number
}
export interface FindymailEmployee {
name: string
linkedinUrl: string | null
companyWebsite: string | null
companyName: string | null
jobTitle: string | null
}
export interface FindymailFindEmployeesResponse extends ToolResponse {
output: {
employees: FindymailEmployee[]
}
}
export interface FindymailFindPhoneParams extends FindymailBaseParams {
linkedin_url: string
}
export interface FindymailFindPhoneResponse extends ToolResponse {
output: {
phone: string | null
line_type: string | null
}
}
export interface FindymailSearchTechnologiesParams extends FindymailBaseParams {
q: string
}
export interface FindymailTechnology {
name: string
category: string | null
subcategory: string | null
last_detected_at?: string | null
}
export interface FindymailSearchTechnologiesResponse extends ToolResponse {
output: {
technologies: FindymailTechnology[]
}
}
export interface FindymailLookupTechnologiesParams extends FindymailBaseParams {
domain: string
technologies?: string[]
}
export interface FindymailLookupTechnologiesResponse extends ToolResponse {
output: {
domain: string
technologies: FindymailTechnology[]
}
}
export interface FindymailGetCreditsParams extends FindymailBaseParams {}
export interface FindymailGetCreditsResponse extends ToolResponse {
output: {
credits: number
verifier_credits: number
}
}
export type FindymailResponse =
| FindymailVerifyEmailResponse
| FindymailFindEmailFromNameResponse
| FindymailFindEmailsByDomainResponse
| FindymailFindEmailFromLinkedInResponse
| FindymailReverseEmailLookupResponse
| FindymailGetCompanyResponse
| FindymailFindEmployeesResponse
| FindymailFindPhoneResponse
| FindymailSearchTechnologiesResponse
| FindymailLookupTechnologiesResponse
| FindymailGetCreditsResponse
+78
View File
@@ -0,0 +1,78 @@
import { findymailHosting } from '@/tools/findymail/hosting'
import type {
FindymailVerifyEmailParams,
FindymailVerifyEmailResponse,
} from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const verifyEmailTool: ToolConfig<FindymailVerifyEmailParams, FindymailVerifyEmailResponse> =
{
id: 'findymail_verify_email',
name: 'Findymail Verify Email',
description: 'Verifies the deliverability of an email address. Uses one verifier credit.',
version: '1.0.0',
hosting: findymailHosting<FindymailVerifyEmailParams>(() => {
// Each verification consumes one verifier credit, billed at the finder-credit rate.
return 1
}),
params: {
email: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Email address to verify (e.g., john@example.com)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/verify',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ email: params.email }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
(errorData as Record<string, string>).error ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { email: '', verified: false, provider: null },
}
}
const data = await response.json()
return {
success: true,
output: {
email: data.email ?? '',
verified: data.verified ?? false,
provider: data.provider ?? null,
},
}
},
outputs: {
email: { type: 'string', description: 'The verified email address' },
verified: { type: 'boolean', description: 'Whether the email is verified as deliverable' },
provider: {
type: 'string',
description: 'Email service provider (e.g., Google, Microsoft)',
optional: true,
},
},
}