chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,99 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevClassifyNaicsParams,
ContextDevClassifyNaicsResponse,
} from '@/tools/context_dev/types'
import { CLASSIFICATION_CODE_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevClassifyNaicsTool: ToolConfig<
ContextDevClassifyNaicsParams,
ContextDevClassifyNaicsResponse
> = {
id: 'context_dev_classify_naics',
name: 'Context.dev Classify NAICS',
description: 'Classify a brand into NAICS industry codes from its domain or company name.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevClassifyNaicsParams>(),
params: {
input: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Brand domain or company name to classify (e.g., "stripe.com" or "Stripe")',
},
minResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum number of codes to return (1-10, default: 1)',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of codes to return (1-10, default: 5)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/web/naics`)
appendParam(url.searchParams, 'input', params.input)
appendParam(url.searchParams, 'minResults', params.minResults)
appendParam(url.searchParams, 'maxResults', params.maxResults)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
status: data.status ?? '',
domain: data.domain ?? null,
type: data.type ?? null,
codes: data.codes ?? [],
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
status: { type: 'string', description: 'Classification status' },
domain: { type: 'string', description: 'Resolved domain', optional: true },
type: { type: 'string', description: 'Input type that was resolved', optional: true },
codes: {
type: 'array',
description: 'Matched NAICS codes with name and confidence',
items: { type: 'object', properties: CLASSIFICATION_CODE_OUTPUT_PROPERTIES },
},
...CREDIT_OUTPUTS,
},
}
+120
View File
@@ -0,0 +1,120 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevClassifySicParams,
ContextDevClassifySicResponse,
} from '@/tools/context_dev/types'
import { CLASSIFICATION_CODE_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevClassifySicTool: ToolConfig<
ContextDevClassifySicParams,
ContextDevClassifySicResponse
> = {
id: 'context_dev_classify_sic',
name: 'Context.dev Classify SIC',
description: 'Classify a brand into SIC industry codes from its domain or company name.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevClassifySicParams>(),
params: {
input: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Brand domain or company name to classify (e.g., "stripe.com" or "Stripe")',
},
type: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'SIC taxonomy version: "original_sic" (default) or "latest_sec"',
},
minResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum number of codes to return (1-10, default: 1)',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of codes to return (1-10, default: 5)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/web/sic`)
appendParam(url.searchParams, 'input', params.input)
appendParam(url.searchParams, 'type', params.type)
appendParam(url.searchParams, 'minResults', params.minResults)
appendParam(url.searchParams, 'maxResults', params.maxResults)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
status: data.status ?? '',
domain: data.domain ?? null,
type: data.type ?? null,
classification: data.classification ?? null,
codes: data.codes ?? [],
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
status: { type: 'string', description: 'Classification status' },
domain: { type: 'string', description: 'Resolved domain', optional: true },
type: { type: 'string', description: 'Input type that was resolved', optional: true },
classification: {
type: 'string',
description: 'SIC taxonomy version used (original_sic or latest_sec)',
optional: true,
},
codes: {
type: 'array',
description: 'Matched SIC codes with name, confidence, and group metadata',
items: {
type: 'object',
properties: {
...CLASSIFICATION_CODE_OUTPUT_PROPERTIES,
majorGroup: { type: 'string', description: 'Major group code (original_sic only)' },
majorGroupName: { type: 'string', description: 'Major group name (original_sic only)' },
office: { type: 'string', description: 'SEC office (latest_sec only)' },
},
},
},
...CREDIT_OUTPUTS,
},
}
+147
View File
@@ -0,0 +1,147 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type { ContextDevCrawlParams, ContextDevCrawlResponse } from '@/tools/context_dev/types'
import { CRAWL_RESULT_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevJsonHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevCrawlTool: ToolConfig<ContextDevCrawlParams, ContextDevCrawlResponse> = {
id: 'context_dev_crawl',
name: 'Context.dev Crawl',
description: 'Crawl an entire website and return each discovered page as clean markdown.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevCrawlParams>(),
params: {
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The starting URL to crawl (must include http:// or https://)',
},
maxPages: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of pages to crawl (1-500, default: 100)',
},
maxDepth: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum link depth from the starting URL (0 = start page only)',
},
urlRegex: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Regex pattern to filter which URLs are crawled',
},
includeLinks: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Preserve hyperlinks in the markdown output (default: true)',
},
includeImages: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Include image references in the markdown output (default: false)',
},
useMainContentOnly: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Strip headers, footers, and sidebars from each page (default: false)',
},
followSubdomains: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Follow links to subdomains of the starting domain (default: false)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache duration in milliseconds (0-2592000000, default: 86400000)',
},
waitForMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Browser wait time after page load in milliseconds (0-30000)',
},
stopAfterMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Soft crawl time budget in milliseconds (10000-110000, default: 80000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'POST',
url: () => `${CONTEXT_DEV_BASE_URL}/web/crawl`,
headers: (params) => contextDevJsonHeaders(params.apiKey),
body: (params) => {
const body: Record<string, any> = { url: params.url }
if (params.maxPages != null) body.maxPages = params.maxPages
if (params.maxDepth != null) body.maxDepth = params.maxDepth
if (params.urlRegex) body.urlRegex = params.urlRegex
if (params.includeLinks != null) body.includeLinks = params.includeLinks
if (params.includeImages != null) body.includeImages = params.includeImages
if (params.useMainContentOnly != null) body.useMainContentOnly = params.useMainContentOnly
if (params.followSubdomains != null) body.followSubdomains = params.followSubdomains
if (params.maxAgeMs != null) body.maxAgeMs = params.maxAgeMs
if (params.waitForMs != null) body.waitForMs = params.waitForMs
if (params.stopAfterMs != null) body.stopAfterMs = params.stopAfterMs
if (params.timeoutMS != null) body.timeoutMS = params.timeoutMS
return body
},
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
results: data.results ?? [],
metadata: data.metadata ?? {},
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
results: {
type: 'array',
description: 'Crawled pages with markdown content and per-page metadata',
items: { type: 'object', properties: CRAWL_RESULT_OUTPUT_PROPERTIES },
},
metadata: {
type: 'object',
description: 'Crawl summary (numUrls, maxCrawlDepth, numSucceeded, numFailed, numSkipped)',
},
...CREDIT_OUTPUTS,
},
}
+138
View File
@@ -0,0 +1,138 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type { ContextDevExtractParams, ContextDevExtractResponse } from '@/tools/context_dev/types'
import {
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevJsonHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevExtractTool: ToolConfig<ContextDevExtractParams, ContextDevExtractResponse> =
{
id: 'context_dev_extract',
name: 'Context.dev Extract',
description: 'Crawl a website and extract structured data matching a provided JSON schema.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevExtractParams>(),
params: {
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The starting website URL (must include http:// or https://)',
},
schema: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description: 'JSON Schema describing the structure of the data to extract',
},
instructions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional extraction guidance for link prioritization (max 2000 chars)',
},
factCheck: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Require extracted values to be grounded in page facts (default: false)',
},
followSubdomains: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Follow links on subdomains of the starting domain (default: false)',
},
maxPages: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of pages to analyze (1-50, default: 5)',
},
maxDepth: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum link depth from the starting URL',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache duration in milliseconds (0-2592000000, default: 604800000)',
},
stopAfterMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Soft crawl time budget in milliseconds (10000-110000, default: 80000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'POST',
url: () => `${CONTEXT_DEV_BASE_URL}/web/extract`,
headers: (params) => contextDevJsonHeaders(params.apiKey),
body: (params) => {
const body: Record<string, any> = { url: params.url, schema: params.schema }
if (params.instructions) body.instructions = params.instructions
if (params.factCheck != null) body.factCheck = params.factCheck
if (params.followSubdomains != null) body.followSubdomains = params.followSubdomains
if (params.maxPages != null) body.maxPages = params.maxPages
if (params.maxDepth != null) body.maxDepth = params.maxDepth
if (params.maxAgeMs != null) body.maxAgeMs = params.maxAgeMs
if (params.stopAfterMs != null) body.stopAfterMs = params.stopAfterMs
if (params.timeoutMS != null) body.timeoutMS = params.timeoutMS
return body
},
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
status: data.status ?? '',
url: data.url ?? '',
urlsAnalyzed: data.urls_analyzed ?? [],
data: data.data ?? {},
metadata: data.metadata ?? {},
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
status: { type: 'string', description: 'Extraction status' },
url: { type: 'string', description: 'The starting URL that was crawled' },
urlsAnalyzed: {
type: 'array',
description: 'URLs that were analyzed during extraction',
items: { type: 'string', description: 'Analyzed page URL' },
},
data: { type: 'json', description: 'Structured data matching the requested schema' },
metadata: {
type: 'object',
description: 'Crawl summary (numUrls, maxCrawlDepth, numSucceeded, numFailed, numSkipped)',
},
...CREDIT_OUTPUTS,
},
}
@@ -0,0 +1,93 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevExtractProductParams,
ContextDevExtractProductResponse,
} from '@/tools/context_dev/types'
import { PRODUCT_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevJsonHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevExtractProductTool: ToolConfig<
ContextDevExtractProductParams,
ContextDevExtractProductResponse
> = {
id: 'context_dev_extract_product',
name: 'Context.dev Extract Product',
description: 'Detect and extract structured product details from a single product page URL.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevExtractProductParams>(),
params: {
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The product page URL (must include http:// or https://)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache duration in milliseconds (0-2592000000, default: 604800000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'POST',
url: () => `${CONTEXT_DEV_BASE_URL}/brand/ai/product`,
headers: (params) => contextDevJsonHeaders(params.apiKey),
body: (params) => {
const body: Record<string, any> = { url: params.url }
if (params.maxAgeMs != null) body.maxAgeMs = params.maxAgeMs
if (params.timeoutMS != null) body.timeoutMS = params.timeoutMS
return body
},
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
isProductPage: data.is_product_page ?? false,
platform: data.platform ?? null,
product: data.product ?? null,
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
isProductPage: { type: 'boolean', description: 'Whether the URL is a product page' },
platform: {
type: 'string',
description: 'Detected platform (amazon, tiktok_shop, etsy, generic)',
optional: true,
},
product: {
type: 'object',
description: 'Extracted product details',
properties: PRODUCT_OUTPUT_PROPERTIES,
},
...CREDIT_OUTPUTS,
},
}
@@ -0,0 +1,92 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevExtractProductsParams,
ContextDevExtractProductsResponse,
} from '@/tools/context_dev/types'
import { PRODUCT_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevJsonHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevExtractProductsTool: ToolConfig<
ContextDevExtractProductsParams,
ContextDevExtractProductsResponse
> = {
id: 'context_dev_extract_products',
name: 'Context.dev Extract Products',
description: "Extract the product catalog from a brand's website by domain (beta).",
version: '1.0.0',
hosting: contextDevHosting<ContextDevExtractProductsParams>(),
params: {
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain to extract products from (e.g., "example.com")',
},
maxProducts: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of products to extract (1-12)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache duration in milliseconds (0-2592000000, default: 604800000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'POST',
url: () => `${CONTEXT_DEV_BASE_URL}/brand/ai/products`,
headers: (params) => contextDevJsonHeaders(params.apiKey),
body: (params) => {
const body: Record<string, any> = { domain: params.domain }
if (params.maxProducts != null) body.maxProducts = params.maxProducts
if (params.maxAgeMs != null) body.maxAgeMs = params.maxAgeMs
if (params.timeoutMS != null) body.timeoutMS = params.timeoutMS
return body
},
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
products: data.products ?? [],
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
products: {
type: 'array',
description: 'Extracted products with pricing, features, and metadata',
items: { type: 'object', properties: PRODUCT_OUTPUT_PROPERTIES },
},
...CREDIT_OUTPUTS,
},
}
+91
View File
@@ -0,0 +1,91 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type { ContextDevBrandResponse, ContextDevGetBrandParams } from '@/tools/context_dev/types'
import { BRAND_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
parseContextDevResponse,
transformBrandResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevGetBrandTool: ToolConfig<ContextDevGetBrandParams, ContextDevBrandResponse> =
{
id: 'context_dev_get_brand',
name: 'Context.dev Get Brand',
description:
'Retrieve brand data for a domain: logos, colors, backdrops, socials, address, and industry.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevGetBrandParams>(),
params: {
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain to retrieve brand data for (e.g., "airbnb.com")',
},
forceLanguage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Override the detected language with a supported language code',
},
maxSpeed: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Skip time-consuming operations for a faster response (default: false)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache max age in milliseconds (86400000-31536000000, default: 7776000000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/brand/retrieve`)
appendParam(url.searchParams, 'domain', params.domain)
appendParam(url.searchParams, 'force_language', params.forceLanguage)
appendParam(url.searchParams, 'maxSpeed', params.maxSpeed)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return { success: true, output: transformBrandResponse(data) }
},
outputs: {
status: { type: 'string', description: 'Retrieval status' },
brand: {
type: 'object',
description: 'Brand data object',
properties: BRAND_OUTPUT_PROPERTIES,
},
...CREDIT_OUTPUTS,
},
}
@@ -0,0 +1,96 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevBrandResponse,
ContextDevGetBrandByEmailParams,
} from '@/tools/context_dev/types'
import { BRAND_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
parseContextDevResponse,
transformBrandResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevGetBrandByEmailTool: ToolConfig<
ContextDevGetBrandByEmailParams,
ContextDevBrandResponse
> = {
id: 'context_dev_get_brand_by_email',
name: 'Context.dev Get Brand by Email',
description:
'Retrieve brand data from a work email address. Free/disposable emails are rejected (422).',
version: '1.0.0',
hosting: contextDevHosting<ContextDevGetBrandByEmailParams>(),
params: {
email: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Work email address; the domain is extracted (free providers are rejected)',
},
forceLanguage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Override the detected language with a supported language code',
},
maxSpeed: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Skip time-consuming operations for a faster response (default: false)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache max age in milliseconds (86400000-31536000000, default: 7776000000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/brand/retrieve-by-email`)
appendParam(url.searchParams, 'email', params.email)
appendParam(url.searchParams, 'force_language', params.forceLanguage)
appendParam(url.searchParams, 'maxSpeed', params.maxSpeed)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return { success: true, output: transformBrandResponse(data) }
},
outputs: {
status: { type: 'string', description: 'Retrieval status' },
brand: {
type: 'object',
description: 'Brand data object',
properties: BRAND_OUTPUT_PROPERTIES,
},
...CREDIT_OUTPUTS,
},
}
@@ -0,0 +1,103 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevBrandResponse,
ContextDevGetBrandByNameParams,
} from '@/tools/context_dev/types'
import { BRAND_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
parseContextDevResponse,
transformBrandResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevGetBrandByNameTool: ToolConfig<
ContextDevGetBrandByNameParams,
ContextDevBrandResponse
> = {
id: 'context_dev_get_brand_by_name',
name: 'Context.dev Get Brand by Name',
description:
'Retrieve brand data by company name: logos, colors, socials, address, and industry.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevGetBrandByNameParams>(),
params: {
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Company name to retrieve brand data for (3-30 chars, e.g., "Apple Inc")',
},
countryGl: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ISO 2-letter country code to prioritize (e.g., "us")',
},
forceLanguage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Override the detected language with a supported language code',
},
maxSpeed: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Skip time-consuming operations for a faster response (default: false)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache max age in milliseconds (86400000-31536000000, default: 7776000000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/brand/retrieve-by-name`)
appendParam(url.searchParams, 'name', params.name)
appendParam(url.searchParams, 'country_gl', params.countryGl)
appendParam(url.searchParams, 'force_language', params.forceLanguage)
appendParam(url.searchParams, 'maxSpeed', params.maxSpeed)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return { success: true, output: transformBrandResponse(data) }
},
outputs: {
status: { type: 'string', description: 'Retrieval status' },
brand: {
type: 'object',
description: 'Brand data object',
properties: BRAND_OUTPUT_PROPERTIES,
},
...CREDIT_OUTPUTS,
},
}
@@ -0,0 +1,102 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevBrandResponse,
ContextDevGetBrandByTickerParams,
} from '@/tools/context_dev/types'
import { BRAND_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
parseContextDevResponse,
transformBrandResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevGetBrandByTickerTool: ToolConfig<
ContextDevGetBrandByTickerParams,
ContextDevBrandResponse
> = {
id: 'context_dev_get_brand_by_ticker',
name: 'Context.dev Get Brand by Ticker',
description: 'Retrieve brand data for a public company by its stock ticker symbol.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevGetBrandByTickerParams>(),
params: {
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Stock ticker symbol (e.g., "AAPL", "GOOGL", "BRK.A")',
},
tickerExchange: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Exchange code for the ticker (e.g., "NASDAQ", "NYSE", "LSE"). Default: NASDAQ',
},
forceLanguage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Override the detected language with a supported language code',
},
maxSpeed: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Skip time-consuming operations for a faster response (default: false)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache max age in milliseconds (86400000-31536000000, default: 7776000000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/brand/retrieve-by-ticker`)
appendParam(url.searchParams, 'ticker', params.ticker)
appendParam(url.searchParams, 'ticker_exchange', params.tickerExchange)
appendParam(url.searchParams, 'force_language', params.forceLanguage)
appendParam(url.searchParams, 'maxSpeed', params.maxSpeed)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return { success: true, output: transformBrandResponse(data) }
},
outputs: {
status: { type: 'string', description: 'Retrieval status' },
brand: {
type: 'object',
description: 'Brand data object',
properties: BRAND_OUTPUT_PROPERTIES,
},
...CREDIT_OUTPUTS,
},
}
@@ -0,0 +1,54 @@
/**
* @vitest-environment node
*/
import { describe, expect, it } from 'vitest'
import { contextDevClassifyNaicsTool } from '@/tools/context_dev/classify_naics'
import { contextDevGetBrandTool } from '@/tools/context_dev/get_brand'
import { CONTEXT_DEV_CREDIT_USD } from '@/tools/context_dev/hosting'
import { contextDevScrapeMarkdownTool } from '@/tools/context_dev/scrape_markdown'
import { contextDevScreenshotTool } from '@/tools/context_dev/screenshot'
import type { ToolConfig } from '@/tools/types'
function cost(tool: ToolConfig<any, any>, params: any, output: Record<string, unknown>) {
const pricing = tool.hosting?.pricing
if (!pricing || pricing.type !== 'custom') throw new Error('Expected custom pricing')
const result = pricing.getCost(params, output)
return typeof result === 'number' ? { cost: result } : result
}
describe('Context.dev hosted key pricing', () => {
it('declares hosting with the shared env prefix and BYOK provider on every tool', () => {
for (const tool of [
contextDevScrapeMarkdownTool,
contextDevScreenshotTool,
contextDevGetBrandTool,
contextDevClassifyNaicsTool,
]) {
expect(tool.hosting?.envKeyPrefix).toBe('CONTEXT_DEV_API_KEY')
expect(tool.hosting?.byokProviderId).toBe('context_dev')
}
})
it('charges the reported credits at the per-credit rate for a 1-credit scrape', () => {
expect(cost(contextDevScrapeMarkdownTool, {}, { creditsConsumed: 1 }).cost).toBeCloseTo(
CONTEXT_DEV_CREDIT_USD
)
})
it('charges the reported credits at the per-credit rate for a 5-credit screenshot', () => {
expect(cost(contextDevScreenshotTool, {}, { creditsConsumed: 5 }).cost).toBeCloseTo(
5 * CONTEXT_DEV_CREDIT_USD
)
})
it('charges the reported credits at the per-credit rate for a 10-credit brand lookup', () => {
expect(cost(contextDevGetBrandTool, {}, { creditsConsumed: 10 }).cost).toBeCloseTo(
10 * CONTEXT_DEV_CREDIT_USD
)
})
it('charges zero when the response has no credit accounting', () => {
expect(cost(contextDevClassifyNaicsTool, {}, { creditsConsumed: null }).cost).toBe(0)
expect(cost(contextDevClassifyNaicsTool, {}, {}).cost).toBe(0)
})
})
+42
View File
@@ -0,0 +1,42 @@
import type { ToolHostingConfig } from '@/tools/types'
/**
* Env var prefix for Context.dev hosted keys. Provide keys as
* `CONTEXT_DEV_API_KEY_COUNT` plus `CONTEXT_DEV_API_KEY_1..N`.
*/
export const CONTEXT_DEV_API_KEY_PREFIX = 'CONTEXT_DEV_API_KEY'
/**
* Dollar cost of a single Context.dev credit.
*
* Estimated from the $25/month Developer plan (10,000 credits = $0.0025/credit)
* — https://www.context.dev/pricing. Endpoints cost 1, 5, or 10 credits
* depending on operation; actual usage is read from each response's
* `key_metadata.credits_consumed` rather than hardcoded per endpoint.
*/
export const CONTEXT_DEV_CREDIT_USD = 0.0025
/**
* Build a Context.dev `hosting` config. Every Context.dev response reports the
* exact credits consumed via `key_metadata.credits_consumed`, already surfaced
* on tool output as `creditsConsumed` by `extractCreditMetadata` — so cost is
* read directly from the response rather than estimated per endpoint.
*/
export function contextDevHosting<P>(): ToolHostingConfig<P> {
return {
envKeyPrefix: CONTEXT_DEV_API_KEY_PREFIX,
apiKeyParam: 'apiKey',
byokProviderId: 'context_dev',
pricing: {
type: 'custom',
getCost: (_params, output) => {
const credits = (output.creditsConsumed as number | null) ?? 0
return { cost: credits * CONTEXT_DEV_CREDIT_USD, metadata: { credits } }
},
},
rateLimit: {
mode: 'per_request',
requestsPerMinute: 60,
},
}
}
@@ -0,0 +1,124 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevBrandResponse,
ContextDevIdentifyTransactionParams,
} from '@/tools/context_dev/types'
import { BRAND_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
parseContextDevResponse,
transformBrandResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevIdentifyTransactionTool: ToolConfig<
ContextDevIdentifyTransactionParams,
ContextDevBrandResponse
> = {
id: 'context_dev_identify_transaction',
name: 'Context.dev Identify Transaction',
description:
'Identify the brand behind a raw bank/card transaction descriptor and return its brand data.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevIdentifyTransactionParams>(),
params: {
transactionInfo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The raw transaction descriptor or identifier to resolve to a brand',
},
countryGl: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ISO 2-letter country code from the transaction (e.g., "us", "gb")',
},
city: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'City name to prioritize in the search',
},
mcc: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Merchant Category Code for the business category',
},
phone: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Phone number from the transaction for verification',
},
highConfidenceOnly: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Enforce additional verification steps for higher confidence (default: false)',
},
forceLanguage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Override the detected language with a supported language code',
},
maxSpeed: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Skip time-consuming operations for a faster response (default: false)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/brand/transaction_identifier`)
appendParam(url.searchParams, 'transaction_info', params.transactionInfo)
appendParam(url.searchParams, 'country_gl', params.countryGl)
appendParam(url.searchParams, 'city', params.city)
appendParam(url.searchParams, 'mcc', params.mcc)
appendParam(url.searchParams, 'phone', params.phone)
appendParam(url.searchParams, 'high_confidence_only', params.highConfidenceOnly)
appendParam(url.searchParams, 'force_language', params.forceLanguage)
appendParam(url.searchParams, 'maxSpeed', params.maxSpeed)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return { success: true, output: transformBrandResponse(data) }
},
outputs: {
status: { type: 'string', description: 'Identification status' },
brand: {
type: 'object',
description: 'Brand data for the identified merchant',
properties: BRAND_OUTPUT_PROPERTIES,
},
...CREDIT_OUTPUTS,
},
}
+19
View File
@@ -0,0 +1,19 @@
export { contextDevClassifyNaicsTool } from '@/tools/context_dev/classify_naics'
export { contextDevClassifySicTool } from '@/tools/context_dev/classify_sic'
export { contextDevCrawlTool } from '@/tools/context_dev/crawl'
export { contextDevExtractTool } from '@/tools/context_dev/extract'
export { contextDevExtractProductTool } from '@/tools/context_dev/extract_product'
export { contextDevExtractProductsTool } from '@/tools/context_dev/extract_products'
export { contextDevGetBrandTool } from '@/tools/context_dev/get_brand'
export { contextDevGetBrandByEmailTool } from '@/tools/context_dev/get_brand_by_email'
export { contextDevGetBrandByNameTool } from '@/tools/context_dev/get_brand_by_name'
export { contextDevGetBrandByTickerTool } from '@/tools/context_dev/get_brand_by_ticker'
export { contextDevIdentifyTransactionTool } from '@/tools/context_dev/identify_transaction'
export { contextDevMapTool } from '@/tools/context_dev/map'
export { contextDevScrapeFontsTool } from '@/tools/context_dev/scrape_fonts'
export { contextDevScrapeHtmlTool } from '@/tools/context_dev/scrape_html'
export { contextDevScrapeImagesTool } from '@/tools/context_dev/scrape_images'
export { contextDevScrapeMarkdownTool } from '@/tools/context_dev/scrape_markdown'
export { contextDevScrapeStyleguideTool } from '@/tools/context_dev/scrape_styleguide'
export { contextDevScreenshotTool } from '@/tools/context_dev/screenshot'
export { contextDevSearchTool } from '@/tools/context_dev/search'
+94
View File
@@ -0,0 +1,94 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type { ContextDevMapParams, ContextDevMapResponse } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevMapTool: ToolConfig<ContextDevMapParams, ContextDevMapResponse> = {
id: 'context_dev_map',
name: 'Context.dev Map',
description: 'Build a sitemap of a domain and return every discovered page URL.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevMapParams>(),
params: {
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain to build a sitemap for (e.g., "example.com")',
},
maxLinks: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of URLs to return (1-100000, default: 10000)',
},
urlRegex: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'RE2-compatible regex to filter URLs (max 256 chars)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/web/scrape/sitemap`)
appendParam(url.searchParams, 'domain', params.domain)
appendParam(url.searchParams, 'maxLinks', params.maxLinks)
appendParam(url.searchParams, 'urlRegex', params.urlRegex)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
domain: data.domain ?? '',
urls: data.urls ?? [],
meta: data.meta ?? {},
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
domain: { type: 'string', description: 'The domain that was mapped' },
urls: {
type: 'array',
description: 'All page URLs discovered from the sitemap',
items: { type: 'string', description: 'Page URL' },
},
meta: {
type: 'object',
description:
'Sitemap discovery stats (sitemapsDiscovered, sitemapsFetched, sitemapsSkipped, errors)',
},
...CREDIT_OUTPUTS,
},
}
@@ -0,0 +1,95 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevScrapeFontsParams,
ContextDevScrapeFontsResponse,
} from '@/tools/context_dev/types'
import { FONT_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevScrapeFontsTool: ToolConfig<
ContextDevScrapeFontsParams,
ContextDevScrapeFontsResponse
> = {
id: 'context_dev_scrape_fonts',
name: 'Context.dev Scrape Fonts',
description: 'Extract the font families, usage stats, and font files used by a domain.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevScrapeFontsParams>(),
params: {
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain to extract fonts from (e.g., "example.com")',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache max age in milliseconds (86400000-31536000000, default: 7776000000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/web/fonts`)
appendParam(url.searchParams, 'domain', params.domain)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
status: data.status ?? '',
domain: data.domain ?? '',
fonts: data.fonts ?? [],
fontLinks: data.fontLinks ?? {},
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
status: { type: 'string', description: 'Extraction status' },
domain: { type: 'string', description: 'The domain that was analyzed' },
fonts: {
type: 'array',
description: 'Fonts with usage statistics and fallbacks',
items: { type: 'object', properties: FONT_OUTPUT_PROPERTIES },
},
fontLinks: {
type: 'json',
description: 'Font family download links keyed by font name (type, files, category)',
},
...CREDIT_OUTPUTS,
},
}
+110
View File
@@ -0,0 +1,110 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevScrapeHtmlParams,
ContextDevScrapeHtmlResponse,
} from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevScrapeHtmlTool: ToolConfig<
ContextDevScrapeHtmlParams,
ContextDevScrapeHtmlResponse
> = {
id: 'context_dev_scrape_html',
name: 'Context.dev Scrape HTML',
description: 'Scrape any URL and return the raw HTML content of the page.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevScrapeHtmlParams>(),
params: {
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The full URL to scrape (must include http:// or https://)',
},
useMainContentOnly: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Return only main content, excluding headers, footers, and navigation',
},
includeFrames: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Render iframe contents inline into the returned HTML (default: false)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache duration in milliseconds (0-2592000000, default: 86400000)',
},
waitForMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Browser wait time after page load in milliseconds (0-30000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/web/scrape/html`)
appendParam(url.searchParams, 'url', params.url)
appendParam(url.searchParams, 'useMainContentOnly', params.useMainContentOnly)
appendParam(url.searchParams, 'includeFrames', params.includeFrames)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'waitForMs', params.waitForMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
html: data.html ?? '',
url: data.url ?? '',
type: data.type ?? 'html',
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
html: { type: 'string', description: 'Raw HTML content of the page' },
url: { type: 'string', description: 'The scraped URL' },
type: {
type: 'string',
description:
'Detected content type (html, xml, json, text, csv, markdown, svg, pdf, doc, docx)',
},
...CREDIT_OUTPUTS,
},
}
+118
View File
@@ -0,0 +1,118 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevScrapeImagesParams,
ContextDevScrapeImagesResponse,
} from '@/tools/context_dev/types'
import { IMAGE_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevScrapeImagesTool: ToolConfig<
ContextDevScrapeImagesParams,
ContextDevScrapeImagesResponse
> = {
id: 'context_dev_scrape_images',
name: 'Context.dev Scrape Images',
description: 'Discover every image asset on a page, with optional dimension and type enrichment.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevScrapeImagesParams>(),
params: {
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The full URL to scrape images from (must include http:// or https://)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache duration in milliseconds (0-2592000000, default: 86400000)',
},
waitForMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Browser wait time after page load in milliseconds (0-30000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
enrichResolution: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Measure image dimensions (enables 5-credit enrichment)',
},
enrichHostedUrl: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Host images on a CDN and return their URL and MIME type (enables enrichment)',
},
enrichClassification: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Classify each image by visual asset type (enables enrichment)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/web/scrape/images`)
appendParam(url.searchParams, 'url', params.url)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'waitForMs', params.waitForMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
appendParam(url.searchParams, 'enrichment[resolution]', params.enrichResolution)
appendParam(url.searchParams, 'enrichment[hostedUrl]', params.enrichHostedUrl)
appendParam(url.searchParams, 'enrichment[classification]', params.enrichClassification)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
success: data.success ?? true,
images: data.images ?? [],
url: data.url ?? '',
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether the scrape succeeded' },
images: {
type: 'array',
description: 'Discovered image assets with source, element, type, and optional enrichment',
items: { type: 'object', properties: IMAGE_OUTPUT_PROPERTIES },
},
url: { type: 'string', description: 'The scraped URL' },
...CREDIT_OUTPUTS,
},
}
@@ -0,0 +1,118 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevScrapeMarkdownParams,
ContextDevScrapeMarkdownResponse,
} from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevScrapeMarkdownTool: ToolConfig<
ContextDevScrapeMarkdownParams,
ContextDevScrapeMarkdownResponse
> = {
id: 'context_dev_scrape_markdown',
name: 'Context.dev Scrape Markdown',
description: 'Scrape any URL and return clean, LLM-ready markdown content.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevScrapeMarkdownParams>(),
params: {
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The full URL to scrape (must include http:// or https://)',
},
useMainContentOnly: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Return only main content, excluding headers, footers, and navigation',
},
includeLinks: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Preserve hyperlinks in the markdown output (default: true)',
},
includeImages: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Include image references in the markdown output (default: false)',
},
includeFrames: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Render iframe contents inline (default: false)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache duration in milliseconds (0-2592000000, default: 86400000)',
},
waitForMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Browser wait time after page load in milliseconds (0-30000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/web/scrape/markdown`)
appendParam(url.searchParams, 'url', params.url)
appendParam(url.searchParams, 'useMainContentOnly', params.useMainContentOnly)
appendParam(url.searchParams, 'includeLinks', params.includeLinks)
appendParam(url.searchParams, 'includeImages', params.includeImages)
appendParam(url.searchParams, 'includeFrames', params.includeFrames)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'waitForMs', params.waitForMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
markdown: data.markdown ?? '',
url: data.url ?? '',
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
markdown: { type: 'string', description: 'Page content as clean markdown' },
url: { type: 'string', description: 'The scraped URL' },
...CREDIT_OUTPUTS,
},
}
@@ -0,0 +1,90 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevScrapeStyleguideParams,
ContextDevScrapeStyleguideResponse,
} from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevScrapeStyleguideTool: ToolConfig<
ContextDevScrapeStyleguideParams,
ContextDevScrapeStyleguideResponse
> = {
id: 'context_dev_scrape_styleguide',
name: 'Context.dev Scrape Styleguide',
description:
"Extract a domain's design system: colors, typography, spacing, shadows, and UI components.",
version: '1.0.0',
hosting: contextDevHosting<ContextDevScrapeStyleguideParams>(),
params: {
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain to extract the styleguide from (e.g., "example.com")',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache max age in milliseconds (86400000-31536000000, default: 7776000000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/web/styleguide`)
appendParam(url.searchParams, 'domain', params.domain)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
status: data.status ?? '',
domain: data.domain ?? '',
styleguide: data.styleguide ?? null,
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
status: { type: 'string', description: 'Extraction status' },
domain: { type: 'string', description: 'The domain that was analyzed' },
styleguide: {
type: 'json',
description:
'Design system: mode, colors, typography, elementSpacing, shadows, fontLinks, components',
},
...CREDIT_OUTPUTS,
},
}
+168
View File
@@ -0,0 +1,168 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type {
ContextDevScreenshotParams,
ContextDevScreenshotResponse,
} from '@/tools/context_dev/types'
import {
appendParam,
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig, ToolFileData } from '@/tools/types'
/** Maps a lowercase image file extension to its MIME type. */
const IMAGE_MIME_BY_EXTENSION: Record<string, string> = {
png: 'image/png',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
webp: 'image/webp',
gif: 'image/gif',
avif: 'image/avif',
}
/**
* Derives the file extension and MIME type for a stored screenshot from its URL,
* falling back to PNG when the URL has no recognizable image extension.
*/
function screenshotFileMeta(url: string): { extension: string; mimeType: string } {
try {
const ext = new URL(url).pathname.split('.').pop()?.toLowerCase() ?? ''
if (IMAGE_MIME_BY_EXTENSION[ext]) {
return { extension: ext, mimeType: IMAGE_MIME_BY_EXTENSION[ext] }
}
} catch {
// Unparseable URL — fall back to the default below.
}
return { extension: 'png', mimeType: 'image/png' }
}
export const contextDevScreenshotTool: ToolConfig<
ContextDevScreenshotParams,
ContextDevScreenshotResponse
> = {
id: 'context_dev_screenshot',
name: 'Context.dev Screenshot',
description: 'Capture a screenshot of any web page and store it as a downloadable image file.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevScreenshotParams>(),
params: {
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The full URL to capture (must include http:// or https://)',
},
fullScreenshot: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Capture the full scrollable page instead of just the viewport (default: false)',
},
handleCookiePopup: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Attempt to dismiss cookie banners before capturing (default: false)',
},
viewportWidth: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Viewport width in pixels (240-7680, default: 1920)',
},
viewportHeight: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Viewport height in pixels (240-4320, default: 1080)',
},
maxAgeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Cache duration in milliseconds (0-2592000000, default: 86400000)',
},
waitForMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Post-load delay before capturing in milliseconds (0-30000, default: 3000)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'GET',
url: (params) => {
const url = new URL(`${CONTEXT_DEV_BASE_URL}/web/screenshot`)
appendParam(url.searchParams, 'directUrl', params.url)
appendParam(url.searchParams, 'fullScreenshot', params.fullScreenshot)
appendParam(url.searchParams, 'handleCookiePopup', params.handleCookiePopup)
appendParam(url.searchParams, 'viewport[width]', params.viewportWidth)
appendParam(url.searchParams, 'viewport[height]', params.viewportHeight)
appendParam(url.searchParams, 'maxAgeMs', params.maxAgeMs)
appendParam(url.searchParams, 'waitForMs', params.waitForMs)
appendParam(url.searchParams, 'timeoutMS', params.timeoutMS)
return url.toString()
},
headers: (params) => contextDevHeaders(params.apiKey),
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
const screenshotUrl: string = data.screenshot ?? ''
const domain: string | null = data.domain ?? null
const { extension, mimeType } = screenshotFileMeta(screenshotUrl)
const file: ToolFileData | undefined = screenshotUrl
? {
name: `${domain ?? 'screenshot'}.${extension}`,
mimeType,
url: screenshotUrl,
}
: undefined
return {
success: true,
output: {
...(file ? { file } : {}),
screenshotUrl,
screenshotType: data.screenshotType ?? null,
domain,
width: data.width ?? null,
height: data.height ?? null,
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
file: { type: 'file', description: 'Stored screenshot image file', optional: true },
screenshotUrl: { type: 'string', description: 'Public URL of the captured screenshot' },
screenshotType: {
type: 'string',
description: 'Screenshot type (viewport or fullPage)',
optional: true,
},
domain: { type: 'string', description: 'Domain that was captured', optional: true },
width: { type: 'number', description: 'Screenshot width in pixels', optional: true },
height: { type: 'number', description: 'Screenshot height in pixels', optional: true },
...CREDIT_OUTPUTS,
},
}
+137
View File
@@ -0,0 +1,137 @@
import { contextDevHosting } from '@/tools/context_dev/hosting'
import type { ContextDevSearchParams, ContextDevSearchResponse } from '@/tools/context_dev/types'
import { SEARCH_RESULT_OUTPUT_PROPERTIES } from '@/tools/context_dev/types'
import {
CONTEXT_DEV_BASE_URL,
CREDIT_OUTPUTS,
contextDevJsonHeaders,
extractCreditMetadata,
parseContextDevResponse,
} from '@/tools/context_dev/utils'
import type { ToolConfig } from '@/tools/types'
export const contextDevSearchTool: ToolConfig<ContextDevSearchParams, ContextDevSearchResponse> = {
id: 'context_dev_search',
name: 'Context.dev Search',
description: 'Search the web with natural language and optionally scrape results to markdown.',
version: '1.0.0',
hosting: contextDevHosting<ContextDevSearchParams>(),
params: {
query: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The natural language search query (1-500 characters)',
},
includeDomains: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Only return results from these domains',
},
excludeDomains: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Exclude results from these domains',
},
freshness: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Recency filter (last_24_hours, last_week, last_month, last_year)',
},
numResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (10-100, default 10)',
},
country: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Restrict results to a country (ISO 3166-1 alpha-2 code, e.g. US)',
},
queryFanout: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Expand the query into parallel variants for broader coverage',
},
markdownEnabled: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Scrape each result page to markdown (default: false)',
},
timeoutMS: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Request timeout in milliseconds (1000-300000)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Context.dev API key',
},
},
request: {
method: 'POST',
url: () => `${CONTEXT_DEV_BASE_URL}/web/search`,
headers: (params) => contextDevJsonHeaders(params.apiKey),
body: (params) => {
const body: Record<string, any> = { query: params.query }
if (params.includeDomains?.length) body.includeDomains = params.includeDomains
if (params.excludeDomains?.length) body.excludeDomains = params.excludeDomains
if (params.freshness) body.freshness = params.freshness
if (params.numResults != null) {
// Context.dev accepts 10-100 results — clamp to the documented bounds.
const requested = Math.trunc(Number(params.numResults))
if (Number.isFinite(requested)) {
body.numResults = Math.min(100, Math.max(10, requested))
}
}
if (params.country) {
const country = String(params.country).trim().toUpperCase()
if (!/^[A-Z]{2}$/.test(country)) {
throw new Error('country must be a 2-letter ISO 3166-1 alpha-2 code (e.g., US)')
}
body.country = country
}
if (params.queryFanout != null) body.queryFanout = params.queryFanout
if (params.markdownEnabled != null) {
body.markdownOptions = { enabled: params.markdownEnabled }
}
if (params.timeoutMS != null) body.timeoutMS = params.timeoutMS
return body
},
},
transformResponse: async (response: Response) => {
const data = await parseContextDevResponse(response)
return {
success: true,
output: {
results: data.results ?? [],
query: data.query ?? '',
...extractCreditMetadata(data.key_metadata),
},
}
},
outputs: {
results: {
type: 'array',
description: 'Search results with url, title, description, relevance, and optional markdown',
items: { type: 'object', properties: SEARCH_RESULT_OUTPUT_PROPERTIES },
},
query: { type: 'string', description: 'The query that was searched' },
...CREDIT_OUTPUTS,
},
}
+434
View File
@@ -0,0 +1,434 @@
import type { ToolFileData, ToolResponse } from '@/tools/types'
/** Credit accounting fields surfaced on every Context.dev tool output. */
interface CreditFields {
creditsConsumed: number | null
creditsRemaining: number | null
}
export interface ContextDevScrapeMarkdownParams {
apiKey: string
url: string
useMainContentOnly?: boolean
includeLinks?: boolean
includeImages?: boolean
includeFrames?: boolean
maxAgeMs?: number
waitForMs?: number
timeoutMS?: number
}
export interface ContextDevScrapeMarkdownResponse extends ToolResponse {
output: CreditFields & {
markdown: string
url: string
}
}
export interface ContextDevScrapeHtmlParams {
apiKey: string
url: string
useMainContentOnly?: boolean
includeFrames?: boolean
maxAgeMs?: number
waitForMs?: number
timeoutMS?: number
}
export interface ContextDevScrapeHtmlResponse extends ToolResponse {
output: CreditFields & {
html: string
url: string
type: string
}
}
export interface ContextDevScreenshotParams {
apiKey: string
url: string
fullScreenshot?: boolean
handleCookiePopup?: boolean
viewportWidth?: number
viewportHeight?: number
maxAgeMs?: number
waitForMs?: number
timeoutMS?: number
}
export interface ContextDevScreenshotResponse extends ToolResponse {
output: CreditFields & {
file?: ToolFileData
screenshotUrl: string
screenshotType: string | null
domain: string | null
width: number | null
height: number | null
}
}
export interface ContextDevScrapeImagesParams {
apiKey: string
url: string
maxAgeMs?: number
waitForMs?: number
timeoutMS?: number
enrichResolution?: boolean
enrichHostedUrl?: boolean
enrichClassification?: boolean
}
export interface ContextDevScrapeImagesResponse extends ToolResponse {
output: CreditFields & {
success: boolean
images: Array<Record<string, unknown>>
url: string
}
}
export interface ContextDevCrawlParams {
apiKey: string
url: string
maxPages?: number
maxDepth?: number
urlRegex?: string
includeLinks?: boolean
includeImages?: boolean
useMainContentOnly?: boolean
followSubdomains?: boolean
maxAgeMs?: number
waitForMs?: number
stopAfterMs?: number
timeoutMS?: number
}
export interface ContextDevCrawlResponse extends ToolResponse {
output: CreditFields & {
results: Array<{
markdown: string
metadata: Record<string, unknown>
}>
metadata: Record<string, unknown>
}
}
export interface ContextDevMapParams {
apiKey: string
domain: string
maxLinks?: number
urlRegex?: string
timeoutMS?: number
}
export interface ContextDevMapResponse extends ToolResponse {
output: CreditFields & {
domain: string
urls: string[]
meta: Record<string, unknown>
}
}
export interface ContextDevSearchParams {
apiKey: string
query: string
includeDomains?: string[]
excludeDomains?: string[]
freshness?: string
numResults?: number
country?: string
queryFanout?: boolean
markdownEnabled?: boolean
timeoutMS?: number
}
export interface ContextDevSearchResponse extends ToolResponse {
output: CreditFields & {
results: Array<Record<string, unknown>>
query: string
}
}
export interface ContextDevExtractParams {
apiKey: string
url: string
schema: Record<string, unknown>
instructions?: string
factCheck?: boolean
followSubdomains?: boolean
maxPages?: number
maxDepth?: number
maxAgeMs?: number
stopAfterMs?: number
timeoutMS?: number
}
export interface ContextDevExtractResponse extends ToolResponse {
output: CreditFields & {
status: string
url: string
urlsAnalyzed: string[]
data: Record<string, unknown>
metadata: Record<string, unknown>
}
}
export interface ContextDevExtractProductParams {
apiKey: string
url: string
maxAgeMs?: number
timeoutMS?: number
}
export interface ContextDevExtractProductResponse extends ToolResponse {
output: CreditFields & {
isProductPage: boolean
platform: string | null
product: Record<string, unknown> | null
}
}
export interface ContextDevExtractProductsParams {
apiKey: string
domain: string
maxProducts?: number
maxAgeMs?: number
timeoutMS?: number
}
export interface ContextDevExtractProductsResponse extends ToolResponse {
output: CreditFields & {
products: Array<Record<string, unknown>>
}
}
export interface ContextDevScrapeFontsParams {
apiKey: string
domain: string
maxAgeMs?: number
timeoutMS?: number
}
export interface ContextDevScrapeFontsResponse extends ToolResponse {
output: CreditFields & {
status: string
domain: string
fonts: Array<Record<string, unknown>>
fontLinks: Record<string, unknown>
}
}
export interface ContextDevScrapeStyleguideParams {
apiKey: string
domain: string
maxAgeMs?: number
timeoutMS?: number
}
export interface ContextDevScrapeStyleguideResponse extends ToolResponse {
output: CreditFields & {
status: string
domain: string
styleguide: Record<string, unknown> | null
}
}
export interface ContextDevClassifyNaicsParams {
apiKey: string
input: string
minResults?: number
maxResults?: number
timeoutMS?: number
}
export interface ContextDevClassifyNaicsResponse extends ToolResponse {
output: CreditFields & {
status: string
domain: string | null
type: string | null
codes: Array<Record<string, unknown>>
}
}
export interface ContextDevClassifySicParams {
apiKey: string
input: string
type?: string
minResults?: number
maxResults?: number
timeoutMS?: number
}
export interface ContextDevClassifySicResponse extends ToolResponse {
output: CreditFields & {
status: string
domain: string | null
type: string | null
classification: string | null
codes: Array<Record<string, unknown>>
}
}
/** Shared response shape for every brand-returning endpoint (full brand object). */
export interface ContextDevBrandResponse extends ToolResponse {
output: CreditFields & {
status: string
brand: Record<string, unknown> | null
}
}
export interface ContextDevGetBrandParams {
apiKey: string
domain: string
forceLanguage?: string
maxSpeed?: boolean
maxAgeMs?: number
timeoutMS?: number
}
export interface ContextDevGetBrandByNameParams {
apiKey: string
name: string
countryGl?: string
forceLanguage?: string
maxSpeed?: boolean
maxAgeMs?: number
timeoutMS?: number
}
export interface ContextDevGetBrandByEmailParams {
apiKey: string
email: string
forceLanguage?: string
maxSpeed?: boolean
maxAgeMs?: number
timeoutMS?: number
}
export interface ContextDevGetBrandByTickerParams {
apiKey: string
ticker: string
tickerExchange?: string
forceLanguage?: string
maxSpeed?: boolean
maxAgeMs?: number
timeoutMS?: number
}
export interface ContextDevIdentifyTransactionParams {
apiKey: string
transactionInfo: string
countryGl?: string
city?: string
mcc?: string
phone?: number
highConfidenceOnly?: boolean
forceLanguage?: string
maxSpeed?: boolean
timeoutMS?: number
}
/** Output schema for a single web search result. */
export const SEARCH_RESULT_OUTPUT_PROPERTIES = {
url: { type: 'string', description: 'Result page URL' },
title: { type: 'string', description: 'Result page title' },
description: { type: 'string', description: 'Result snippet/description' },
relevance: { type: 'string', description: 'Relevance rating (high, medium, low)' },
markdown: {
type: 'json',
description: 'Scraped markdown for the result (when markdown scraping is enabled)',
},
} as const
/** Output schema for a single crawled page. */
export const CRAWL_RESULT_OUTPUT_PROPERTIES = {
markdown: { type: 'string', description: 'Page content as markdown' },
metadata: { type: 'json', description: 'Page metadata (url, title, crawlDepth, statusCode)' },
} as const
/** Output schema for a single industry classification code. */
export const CLASSIFICATION_CODE_OUTPUT_PROPERTIES = {
code: { type: 'string', description: 'Industry code' },
name: { type: 'string', description: 'Industry name' },
confidence: { type: 'string', description: 'Match confidence (high, medium, low)' },
} as const
/** Output schema for the full brand object returned by brand-intelligence endpoints. */
export const BRAND_OUTPUT_PROPERTIES = {
domain: { type: 'string', description: 'Brand domain' },
title: { type: 'string', description: 'Brand title' },
description: { type: 'string', description: 'Brand description' },
slogan: { type: 'string', description: 'Brand slogan' },
colors: { type: 'json', description: 'Brand colors (hex and name)' },
logos: { type: 'json', description: 'Brand logos with mode, colors, resolution, and type' },
backdrops: { type: 'json', description: 'Brand backdrop images' },
socials: { type: 'json', description: 'Social media profiles (type and url)' },
address: { type: 'json', description: 'Brand address' },
stock: { type: 'json', description: 'Stock info (ticker and exchange)' },
is_nsfw: { type: 'boolean', description: 'Whether the brand contains adult content' },
email: { type: 'string', description: 'Brand contact email' },
phone: { type: 'string', description: 'Brand contact phone' },
industries: { type: 'json', description: 'Industry taxonomy (eic industry/subindustry pairs)' },
links: {
type: 'json',
description: 'Key brand links (careers, privacy, terms, blog, pricing, contact)',
},
primary_language: { type: 'string', description: 'Primary language of the brand site' },
} as const
/** Output schema for the reduced brand object returned by the simplified endpoint. */
export const SIMPLIFIED_BRAND_OUTPUT_PROPERTIES = {
domain: { type: 'string', description: 'Brand domain' },
title: { type: 'string', description: 'Brand title' },
colors: { type: 'json', description: 'Brand colors (hex and name)' },
logos: { type: 'json', description: 'Brand logos with mode, colors, resolution, and type' },
backdrops: { type: 'json', description: 'Brand backdrop images' },
} as const
/** Output schema for a single extracted product. */
export const PRODUCT_OUTPUT_PROPERTIES = {
name: { type: 'string', description: 'Product name' },
description: { type: 'string', description: 'Product description' },
price: { type: 'number', description: 'Product price' },
currency: { type: 'string', description: 'Price currency' },
billing_frequency: {
type: 'string',
description: 'Billing frequency (monthly, yearly, one_time, usage_based)',
},
pricing_model: {
type: 'string',
description: 'Pricing model (per_seat, flat, tiered, freemium, custom)',
},
url: { type: 'string', description: 'Product URL' },
category: { type: 'string', description: 'Product category' },
features: { type: 'json', description: 'Product features' },
target_audience: { type: 'json', description: 'Target audience' },
tags: { type: 'json', description: 'Product tags' },
image_url: { type: 'string', description: 'Primary product image URL' },
images: { type: 'json', description: 'Product image URLs' },
sku: { type: 'string', description: 'Product SKU' },
} as const
/** Output schema for a single font usage entry. */
export const FONT_OUTPUT_PROPERTIES = {
font: { type: 'string', description: 'Font family name' },
uses: { type: 'json', description: 'Where the font is used' },
fallbacks: { type: 'json', description: 'Fallback font families' },
num_elements: { type: 'number', description: 'Number of elements using the font' },
num_words: { type: 'number', description: 'Number of words rendered in the font' },
percent_words: { type: 'number', description: 'Percent of words using the font' },
percent_elements: { type: 'number', description: 'Percent of elements using the font' },
} as const
/** Output schema for a single scraped image. */
export const IMAGE_OUTPUT_PROPERTIES = {
src: { type: 'string', description: 'Image source URL or data' },
element: {
type: 'string',
description: 'Source element (img, svg, link, source, video, css, object, meta, background)',
},
type: { type: 'string', description: 'Image representation (url, html, base64)' },
alt: { type: 'string', description: 'Alt text', optional: true },
enrichment: {
type: 'json',
description: 'Optional enrichment (width, height, mimetype, url, type) when requested',
},
} as const
+99
View File
@@ -0,0 +1,99 @@
/** Base URL for all Context.dev API endpoints. */
export const CONTEXT_DEV_BASE_URL = 'https://api.context.dev/v1'
/**
* Builds the standard Context.dev request headers with Bearer authentication.
*/
export function contextDevHeaders(apiKey: string): Record<string, string> {
return {
Authorization: `Bearer ${apiKey}`,
Accept: 'application/json',
}
}
/**
* Builds JSON request headers with Bearer authentication for POST endpoints.
*/
export function contextDevJsonHeaders(apiKey: string): Record<string, string> {
return {
...contextDevHeaders(apiKey),
'Content-Type': 'application/json',
}
}
/**
* Throws a descriptive error when a Context.dev response is not successful.
* Returns the parsed JSON body on success.
*/
export async function parseContextDevResponse(response: Response): Promise<any> {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Context.dev API error (${response.status}): ${errorText}`)
}
return response.json()
}
/** Shape of the credit accounting object present on every Context.dev response. */
interface ContextDevKeyMetadata {
credits_consumed?: number
credits_remaining?: number
}
/**
* Extracts the credit accounting fields shared by every Context.dev response.
*/
export function extractCreditMetadata(keyMetadata: ContextDevKeyMetadata | undefined): {
creditsConsumed: number | null
creditsRemaining: number | null
} {
return {
creditsConsumed: keyMetadata?.credits_consumed ?? null,
creditsRemaining: keyMetadata?.credits_remaining ?? null,
}
}
/**
* Normalizes a brand-returning Context.dev response into the shared tool output shape.
* Used by every endpoint that returns a `brand` object.
*/
export function transformBrandResponse(data: any): {
status: string
brand: Record<string, unknown> | null
creditsConsumed: number | null
creditsRemaining: number | null
} {
return {
status: data.status ?? '',
brand: data.brand ?? null,
...extractCreditMetadata(data.key_metadata),
}
}
/**
* Appends a parameter to a URLSearchParams instance only when it is defined and non-empty.
* Booleans are serialized as the literal strings 'true' / 'false'.
*/
export function appendParam(
search: URLSearchParams,
key: string,
value: string | number | boolean | undefined | null
): void {
if (value === undefined || value === null || value === '') return
const serialized = typeof value === 'string' ? value.trim() : String(value)
if (serialized === '') return
search.append(key, serialized)
}
/** Output definitions for the credit accounting fields, reused across every tool. */
export const CREDIT_OUTPUTS = {
creditsConsumed: {
type: 'number',
description: 'Credits consumed by this request',
optional: true,
},
creditsRemaining: {
type: 'number',
description: 'Credits remaining on the API key',
optional: true,
},
} as const