Skip to content

src/setup-launcher-token.ts

43 lines · 1.6 KB · TypeScript
Raw
import { packageContext } from 'kody:runtime'
import { storeLauncherToken } from './launcher-auth.ts'

function randomToken() {
	const bytes = new Uint8Array(32)
	crypto.getRandomValues(bytes)
	let out = ''
	for (const b of bytes) out += b.toString(16).padStart(2, '0')
	return `kody_raycast_${out}`
}

/**
 * Mint (or rotate) the optional Raycast launcher bearer token for the package app.
 * Stores only a SHA-256 hash in package storage. Returns the plaintext once so an
 * agent can write `~/.config/kody/raycast.json` on the owner's machine — never
 * paste the token into chat.
 *
 * Prefer minted `list-commands` / `run` webhooks for Raycast. Package apps on
 * `*.kody.run` need a Kody browser handoff, so webhooks are the external-client path.
 *
 * @returns token (once), hint, and hosted API base when available
 *
 * @example
 * import setupLauncherToken from 'kody:@kody/raycast/setup-launcher-token'
 * const { token, apiBaseUrl } = await setupLauncherToken()
 */
export default async function setupLauncherToken(input: { token?: string } = {}) {
	const token = (input.token?.trim() || randomToken()).trim()
	const stored = await storeLauncherToken(token)
	const apiBaseUrl =
		typeof packageContext?.hostedUrl === 'string' && packageContext.hostedUrl
			? packageContext.hostedUrl.replace(/\/+$/, '')
			: null
	return {
		ok: true as const,
		token,
		hint: stored.hint,
		apiBaseUrl,
		configPath: '~/.config/kody/raycast.json',
		preferred: 'minted-webhooks',
		note: 'Prefer listCommandsWebhookUrl + runWebhookUrl in configPath. Optional apiBaseUrl + token is package-app only (needs browser handoff). Never paste token into chat.',
	}
}