Skip to content
← Public packages

@kentcdodds/raycast

Raycast launcher: stored execute commands via webhooks (canonical; do not use raycast-shortcuts).

src/run.ts

125 lines · 3.6 KB · TypeScript
import { kody } from 'kody:runtime'
import { getCommandRecord } from './commands-store.ts'

export type RunInput = {
	/** Run a stored command by id. */
	commandId?: string
	/** Ad-hoc ESM module (same shape as MCP execute). Ignored when commandId is set. */
	code?: string
	/** Passed to the module default export. */
	params?: Record<string, unknown>
	idempotencyKey?: string
}

function isPlainObject(value: unknown): value is Record<string, unknown> {
	return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
}

/**
 * Minted `run` URLs are also registered on `kentcdodds/kody-raycast-hooks` so the
 * Mac can read them via `gh api` without pasting secrets. GitHub then POSTs real
 * hook events at those URLs. Those payloads must not throw (5xx → retries + Activity noise).
 *
 * Catch more than repo-scoped deliveries: installation/meta/org/advisory shapes often
 * omit top-level `repository` and used to fall through to the commandId/code throw.
 */
function isGithubWebhookNoise(input: Record<string, unknown>): boolean {
	if (typeof input.commandId === 'string' && input.commandId.trim()) return false
	if (typeof input.code === 'string' && input.code.trim()) return false

	// Ping: { zen, hook_id, hook, ... }
	if (typeof input.zen === 'string') return true

	// Hook CRUD / meta (often no zen)
	if ('hook_id' in input || isPlainObject(input.hook)) return true

	// App / org level (often no repository)
	if (isPlainObject(input.installation)) return true
	if (
		'repositories_added' in input ||
		'repositories_removed' in input ||
		'repository_selection' in input
	) {
		return true
	}
	if (isPlainObject(input.organization) && isPlainObject(input.sender)) return true

	// Other GitHub-only top-level keys
	if (
		isPlainObject(input.security_advisory) ||
		isPlainObject(input.marketplace_purchase) ||
		isPlainObject(input.sponsorship)
	) {
		return true
	}

	// Classic repo (and most check/workflow) deliveries
	if (isPlainObject(input.repository)) return true

	// Broad leftover: sender.login + action without Raycast run fields
	const sender = input.sender
	if (
		isPlainObject(sender) &&
		typeof sender.login === 'string' &&
		typeof input.action === 'string'
	) {
		return true
	}

	return false
}

/**
 * Run a stored Raycast command or ad-hoc execute code via `kody.execute`.
 * HTTP callers use the minted `run` webhook; agents can import this export too.
 *
 * @param input.commandId - Stored command id
 * @param input.code - Ad-hoc ESM when not using commandId
 * @param input.params - Params for the default export
 * @returns The execute result payload (ok, result, error, logs, …)
 *
 * @example
 * import run from 'kody:@kentcdodds/raycast/run'
 * const out = await run({ commandId: 'ping-discord-general' })
 */
export default async function run(input: RunInput = {}) {
	const record = input as Record<string, unknown>
	if (isGithubWebhookNoise(record)) {
		return {
			ok: true,
			ignored: true,
			reason: 'github-webhook-event',
			commandId: null,
			title: null,
		}
	}

	const commandId = typeof input.commandId === 'string' ? input.commandId.trim() : ''
	let code = typeof input.code === 'string' ? input.code : ''
	let title: string | null = null

	if (commandId) {
		const command = await getCommandRecord(commandId)
		if (!command) {
			throw new Error(`Unknown Raycast command id: ${commandId}`)
		}
		code = command.code
		title = command.title
	}

	if (!code.trim()) {
		throw new Error('Provide commandId or code (ESM with export default).')
	}

	const executed = await kody.execute({
		code,
		params: input.params ?? {},
		idempotencyKey: input.idempotencyKey,
	})

	return {
		commandId: commandId || null,
		title,
		...executed,
	}
}