d25d482dc2
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
423 lines
14 KiB
TypeScript
423 lines
14 KiB
TypeScript
import { ashbyAuthHeaders, ashbyErrorMessage } from '@/tools/ashby/utils'
|
|
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
|
|
|
interface AshbyGetJobPostingParams {
|
|
apiKey: string
|
|
jobPostingId: string
|
|
jobBoardId?: string
|
|
expandJob?: boolean
|
|
}
|
|
|
|
interface AshbyDescriptionPart {
|
|
html: string | null
|
|
plain: string | null
|
|
}
|
|
|
|
interface AshbyJobPosting {
|
|
id: string
|
|
title: string
|
|
descriptionPlain: string | null
|
|
descriptionHtml: string | null
|
|
descriptionSocial: string | null
|
|
descriptionParts: {
|
|
descriptionOpening: AshbyDescriptionPart | null
|
|
descriptionBody: AshbyDescriptionPart | null
|
|
descriptionClosing: AshbyDescriptionPart | null
|
|
} | null
|
|
departmentName: string | null
|
|
teamName: string | null
|
|
teamNameHierarchy: string[]
|
|
jobId: string | null
|
|
locationName: string | null
|
|
locationIds: {
|
|
primaryLocationId: string | null
|
|
secondaryLocationIds: string[]
|
|
} | null
|
|
address: {
|
|
postalAddress: {
|
|
addressCountry: string | null
|
|
addressRegion: string | null
|
|
addressLocality: string | null
|
|
postalCode: string | null
|
|
streetAddress: string | null
|
|
} | null
|
|
} | null
|
|
isRemote: boolean
|
|
workplaceType: string | null
|
|
employmentType: string | null
|
|
isListed: boolean
|
|
suppressDescriptionOpening: boolean
|
|
suppressDescriptionClosing: boolean
|
|
publishedDate: string | null
|
|
applicationDeadline: string | null
|
|
externalLink: string | null
|
|
applyLink: string | null
|
|
compensation: {
|
|
compensationTierSummary: string | null
|
|
summaryComponents: Array<{
|
|
summary: string | null
|
|
compensationTypeLabel: string | null
|
|
interval: string | null
|
|
currencyCode: string | null
|
|
minValue: number | null
|
|
maxValue: number | null
|
|
}>
|
|
shouldDisplayCompensationOnJobBoard: boolean
|
|
} | null
|
|
applicationLimitCalloutHtml: string | null
|
|
updatedAt: string | null
|
|
job: Record<string, unknown> | null
|
|
}
|
|
|
|
interface AshbyGetJobPostingResponse extends ToolResponse {
|
|
output: AshbyJobPosting
|
|
}
|
|
|
|
function mapDescriptionPart(raw: unknown): AshbyDescriptionPart | null {
|
|
if (!raw || typeof raw !== 'object') return null
|
|
const p = raw as Record<string, unknown>
|
|
return {
|
|
html: (p.html as string) ?? null,
|
|
plain: (p.plain as string) ?? null,
|
|
}
|
|
}
|
|
|
|
export const getJobPostingTool: ToolConfig<AshbyGetJobPostingParams, AshbyGetJobPostingResponse> = {
|
|
id: 'ashby_get_job_posting',
|
|
name: 'Ashby Get Job Posting',
|
|
description: 'Retrieves full details about a single job posting by its ID.',
|
|
version: '1.0.0',
|
|
|
|
params: {
|
|
apiKey: {
|
|
type: 'string',
|
|
required: true,
|
|
visibility: 'user-only',
|
|
description: 'Ashby API Key',
|
|
},
|
|
jobPostingId: {
|
|
type: 'string',
|
|
required: true,
|
|
visibility: 'user-or-llm',
|
|
description: 'The UUID of the job posting to fetch',
|
|
},
|
|
jobBoardId: {
|
|
type: 'string',
|
|
required: false,
|
|
visibility: 'user-or-llm',
|
|
description:
|
|
'Optional job board UUID. If omitted, returns posting for the external job board.',
|
|
},
|
|
expandJob: {
|
|
type: 'boolean',
|
|
required: false,
|
|
visibility: 'user-or-llm',
|
|
description: 'Whether to expand and include the related job object in the response',
|
|
},
|
|
},
|
|
|
|
request: {
|
|
url: 'https://api.ashbyhq.com/jobPosting.info',
|
|
method: 'POST',
|
|
headers: (params) => ashbyAuthHeaders(params.apiKey),
|
|
body: (params) => {
|
|
const body: Record<string, unknown> = {
|
|
jobPostingId: params.jobPostingId.trim(),
|
|
}
|
|
if (params.jobBoardId) body.jobBoardId = params.jobBoardId.trim()
|
|
if (params.expandJob) body.expand = ['job']
|
|
return body
|
|
},
|
|
},
|
|
|
|
transformResponse: async (response: Response) => {
|
|
const data = await response.json()
|
|
|
|
if (!data.success) {
|
|
throw new Error(ashbyErrorMessage(data, 'Failed to get job posting'))
|
|
}
|
|
|
|
const r = (data.results ?? {}) as Record<string, unknown> & {
|
|
descriptionParts?: Record<string, unknown>
|
|
locationIds?: { primaryLocationId?: string; secondaryLocationIds?: string[] }
|
|
address?: { postalAddress?: Record<string, unknown> }
|
|
compensation?: Record<string, unknown> & {
|
|
summaryComponents?: Array<Record<string, unknown>>
|
|
}
|
|
}
|
|
|
|
const pa = r.address?.postalAddress
|
|
const comp = r.compensation
|
|
const summaryComponents = Array.isArray(comp?.summaryComponents) ? comp.summaryComponents : []
|
|
const descParts = r.descriptionParts
|
|
|
|
return {
|
|
success: true,
|
|
output: {
|
|
id: (r.id as string) ?? '',
|
|
title: (r.title as string) ?? '',
|
|
descriptionPlain: (r.descriptionPlain as string) ?? null,
|
|
descriptionHtml: (r.descriptionHtml as string) ?? null,
|
|
descriptionSocial: (r.descriptionSocial as string) ?? null,
|
|
descriptionParts: descParts
|
|
? {
|
|
descriptionOpening: mapDescriptionPart(descParts.descriptionOpening),
|
|
descriptionBody: mapDescriptionPart(descParts.descriptionBody),
|
|
descriptionClosing: mapDescriptionPart(descParts.descriptionClosing),
|
|
}
|
|
: null,
|
|
departmentName: (r.departmentName as string) ?? null,
|
|
teamName: (r.teamName as string) ?? null,
|
|
teamNameHierarchy: Array.isArray(r.teamNameHierarchy)
|
|
? (r.teamNameHierarchy as string[])
|
|
: [],
|
|
jobId: (r.jobId as string) ?? null,
|
|
locationName: (r.locationName as string) ?? null,
|
|
locationIds: r.locationIds
|
|
? {
|
|
primaryLocationId: r.locationIds.primaryLocationId ?? null,
|
|
secondaryLocationIds: Array.isArray(r.locationIds.secondaryLocationIds)
|
|
? r.locationIds.secondaryLocationIds
|
|
: [],
|
|
}
|
|
: null,
|
|
address: r.address
|
|
? {
|
|
postalAddress: pa
|
|
? {
|
|
addressCountry: (pa.addressCountry as string) ?? null,
|
|
addressRegion: (pa.addressRegion as string) ?? null,
|
|
addressLocality: (pa.addressLocality as string) ?? null,
|
|
postalCode: (pa.postalCode as string) ?? null,
|
|
streetAddress: (pa.streetAddress as string) ?? null,
|
|
}
|
|
: null,
|
|
}
|
|
: null,
|
|
isRemote: (r.isRemote as boolean) ?? false,
|
|
workplaceType: (r.workplaceType as string) ?? null,
|
|
employmentType: (r.employmentType as string) ?? null,
|
|
isListed: (r.isListed as boolean) ?? false,
|
|
suppressDescriptionOpening: (r.suppressDescriptionOpening as boolean) ?? false,
|
|
suppressDescriptionClosing: (r.suppressDescriptionClosing as boolean) ?? false,
|
|
publishedDate: (r.publishedDate as string) ?? null,
|
|
applicationDeadline: (r.applicationDeadline as string) ?? null,
|
|
externalLink: (r.externalLink as string) ?? null,
|
|
applyLink: (r.applyLink as string) ?? null,
|
|
compensation: comp
|
|
? {
|
|
compensationTierSummary: (comp.compensationTierSummary as string) ?? null,
|
|
summaryComponents: summaryComponents.map((c) => ({
|
|
summary: (c.summary as string) ?? null,
|
|
compensationTypeLabel: (c.compensationTypeLabel as string) ?? null,
|
|
interval: (c.interval as string) ?? null,
|
|
currencyCode: (c.currencyCode as string) ?? null,
|
|
minValue: (c.minValue as number) ?? null,
|
|
maxValue: (c.maxValue as number) ?? null,
|
|
})),
|
|
shouldDisplayCompensationOnJobBoard:
|
|
(comp.shouldDisplayCompensationOnJobBoard as boolean) ?? false,
|
|
}
|
|
: null,
|
|
applicationLimitCalloutHtml: (r.applicationLimitCalloutHtml as string) ?? null,
|
|
updatedAt: (r.updatedAt as string) ?? null,
|
|
job: (r.job as Record<string, unknown>) ?? null,
|
|
},
|
|
}
|
|
},
|
|
|
|
outputs: {
|
|
id: { type: 'string', description: 'Job posting UUID' },
|
|
title: { type: 'string', description: 'Job posting title' },
|
|
descriptionPlain: {
|
|
type: 'string',
|
|
description: 'Full description in plain text',
|
|
optional: true,
|
|
},
|
|
descriptionHtml: {
|
|
type: 'string',
|
|
description: 'Full description in HTML',
|
|
optional: true,
|
|
},
|
|
descriptionSocial: {
|
|
type: 'string',
|
|
description: 'Shortened description for social sharing (max 200 chars)',
|
|
optional: true,
|
|
},
|
|
descriptionParts: {
|
|
type: 'object',
|
|
description: 'Description broken into opening, body, and closing sections',
|
|
optional: true,
|
|
properties: {
|
|
descriptionOpening: {
|
|
type: 'object',
|
|
description: 'Opening (from Job Boards theme settings)',
|
|
optional: true,
|
|
properties: {
|
|
html: { type: 'string', description: 'HTML content', optional: true },
|
|
plain: { type: 'string', description: 'Plain text content', optional: true },
|
|
},
|
|
},
|
|
descriptionBody: {
|
|
type: 'object',
|
|
description: 'Main description body',
|
|
optional: true,
|
|
properties: {
|
|
html: { type: 'string', description: 'HTML content', optional: true },
|
|
plain: { type: 'string', description: 'Plain text content', optional: true },
|
|
},
|
|
},
|
|
descriptionClosing: {
|
|
type: 'object',
|
|
description: 'Closing (from Job Boards theme settings)',
|
|
optional: true,
|
|
properties: {
|
|
html: { type: 'string', description: 'HTML content', optional: true },
|
|
plain: { type: 'string', description: 'Plain text content', optional: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
departmentName: { type: 'string', description: 'Department name', optional: true },
|
|
teamName: { type: 'string', description: 'Team name', optional: true },
|
|
teamNameHierarchy: {
|
|
type: 'array',
|
|
description: 'Hierarchy of team names from root to team',
|
|
items: { type: 'string', description: 'Team name' },
|
|
},
|
|
jobId: { type: 'string', description: 'Associated job UUID', optional: true },
|
|
locationName: { type: 'string', description: 'Primary location name', optional: true },
|
|
locationIds: {
|
|
type: 'object',
|
|
description: 'Primary and secondary location UUIDs',
|
|
optional: true,
|
|
properties: {
|
|
primaryLocationId: {
|
|
type: 'string',
|
|
description: 'Primary location UUID',
|
|
optional: true,
|
|
},
|
|
secondaryLocationIds: {
|
|
type: 'array',
|
|
description: 'Secondary location UUIDs',
|
|
items: { type: 'string', description: 'Location UUID' },
|
|
},
|
|
},
|
|
},
|
|
address: {
|
|
type: 'object',
|
|
description: 'Postal address of the posting location',
|
|
optional: true,
|
|
properties: {
|
|
postalAddress: {
|
|
type: 'object',
|
|
description: 'Structured postal address',
|
|
optional: true,
|
|
properties: {
|
|
addressCountry: { type: 'string', description: 'Country', optional: true },
|
|
addressRegion: { type: 'string', description: 'State or region', optional: true },
|
|
addressLocality: { type: 'string', description: 'City or locality', optional: true },
|
|
postalCode: { type: 'string', description: 'Postal code', optional: true },
|
|
streetAddress: { type: 'string', description: 'Street address', optional: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
isRemote: { type: 'boolean', description: 'Whether the posting is remote' },
|
|
workplaceType: {
|
|
type: 'string',
|
|
description: 'Workplace type (OnSite, Remote, Hybrid)',
|
|
optional: true,
|
|
},
|
|
employmentType: {
|
|
type: 'string',
|
|
description: 'Employment type (FullTime, PartTime, Intern, Contract, Temporary)',
|
|
optional: true,
|
|
},
|
|
isListed: { type: 'boolean', description: 'Whether publicly listed on the job board' },
|
|
suppressDescriptionOpening: {
|
|
type: 'boolean',
|
|
description: 'Whether the theme opening is hidden on this posting',
|
|
},
|
|
suppressDescriptionClosing: {
|
|
type: 'boolean',
|
|
description: 'Whether the theme closing is hidden on this posting',
|
|
},
|
|
publishedDate: { type: 'string', description: 'ISO 8601 published date', optional: true },
|
|
applicationDeadline: {
|
|
type: 'string',
|
|
description: 'ISO 8601 application deadline',
|
|
optional: true,
|
|
},
|
|
externalLink: {
|
|
type: 'string',
|
|
description: 'External link to the job posting',
|
|
optional: true,
|
|
},
|
|
applyLink: {
|
|
type: 'string',
|
|
description: 'Direct apply link',
|
|
optional: true,
|
|
},
|
|
compensation: {
|
|
type: 'object',
|
|
description: 'Compensation details for the posting',
|
|
optional: true,
|
|
properties: {
|
|
compensationTierSummary: {
|
|
type: 'string',
|
|
description: 'Human-readable tier summary',
|
|
optional: true,
|
|
},
|
|
summaryComponents: {
|
|
type: 'array',
|
|
description: 'Structured compensation components',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
summary: { type: 'string', description: 'Component summary', optional: true },
|
|
compensationTypeLabel: {
|
|
type: 'string',
|
|
description: 'Component type label (Salary, Commission, Bonus, Equity, etc.)',
|
|
optional: true,
|
|
},
|
|
interval: {
|
|
type: 'string',
|
|
description: 'Payment interval (e.g. annual, hourly)',
|
|
optional: true,
|
|
},
|
|
currencyCode: {
|
|
type: 'string',
|
|
description: 'ISO 4217 currency code',
|
|
optional: true,
|
|
},
|
|
minValue: { type: 'number', description: 'Minimum value', optional: true },
|
|
maxValue: { type: 'number', description: 'Maximum value', optional: true },
|
|
},
|
|
},
|
|
},
|
|
shouldDisplayCompensationOnJobBoard: {
|
|
type: 'boolean',
|
|
description: 'Whether compensation is shown on the job board',
|
|
},
|
|
},
|
|
},
|
|
applicationLimitCalloutHtml: {
|
|
type: 'string',
|
|
description: 'HTML callout shown when the application limit is reached',
|
|
optional: true,
|
|
},
|
|
updatedAt: {
|
|
type: 'string',
|
|
description: 'ISO 8601 last update timestamp',
|
|
optional: true,
|
|
},
|
|
job: {
|
|
type: 'object',
|
|
description:
|
|
'The expanded job object, only present when the request was made with expandJob=true',
|
|
optional: true,
|
|
},
|
|
},
|
|
}
|