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.', } }