Skip to content
← Public packages

@kody/agent-mail

AgentMail inboxes for agents: create/list inboxes, messages, threads, and Svix webhook ingress.

src/core.ts

75 lines · 2.3 KB · TypeScript
const agentmailApiKey = '{{secret:agentmailApiKey|scope=user}}'
const agentmailApiBase = 'https://api.agentmail.to'

export function clean(value: unknown) {
	return String(value ?? '').trim()
}

export function requireConfirmOrDryRun(input: {
	confirm?: boolean
	dryRun?: boolean
}, action: string) {
	if (input.dryRun === true) return 'dryRun' as const
	if (input.confirm === true) return 'confirm' as const
	throw new Error(
		`Pass confirm: true to ${action}, or dryRun: true to preview`,
	)
}

export async function parseJsonResponse(response: Response, label = 'AgentMail API') {
	const text = await response.text()
	let body: unknown = null
	try {
		body = text ? JSON.parse(text) : null
	} catch {
		body = { raw: text }
	}
	if (!response.ok) {
		throw new Error(
			label + ' HTTP ' + response.status + ': ' + JSON.stringify(body).slice(0, 800),
		)
	}
	return body
}

export type AgentMailRequestInput = {
	method?: string
	path: string
	query?: Record<string, unknown>
	body?: unknown
	headers?: Record<string, string>
	label?: string
}

/**
 * Make one authenticated AgentMail REST request with agentmailApiKey.
 * Prefer the typed exports over calling this directly.
 */
export async function agentMailRequest(input: AgentMailRequestInput) {
	const path = clean(input.path)
	if (!path.startsWith('/')) throw new Error('path must start with /')
	const url = new URL(path, agentmailApiBase)
	for (const [key, value] of Object.entries(input.query || {})) {
		if (value === undefined || value === null || value === '') continue
		url.searchParams.set(key, String(value))
	}
	const method = clean(input.method || 'GET').toUpperCase() || 'GET'
	const headers: Record<string, string> = {
		Authorization: 'Bearer ' + agentmailApiKey,
		Accept: 'application/json',
		...(input.headers || {}),
	}
	let body: string | undefined
	if (input.body !== undefined && method !== 'GET' && method !== 'HEAD') {
		headers['Content-Type'] = 'application/json'
		body = JSON.stringify(input.body)
	}
	const response = await fetch(url, { method, headers, body })
	return await parseJsonResponse(response, input.label || 'AgentMail API')
}

export function encodePathSegment(value: unknown, label: string) {
	const id = clean(value)
	if (!id) throw new Error(label + ' is required')
	return encodeURIComponent(id)
}