import { kody } from 'kody:runtime'
import {
KODY_ID,
OFFICIAL_PACKAGE_ID,
PACKAGE_NAME,
PUBLIC_URL,
asRecord,
invocationUrl,
optionalString,
tokenSetupUrl,
} from './helpers.ts'
export type GuideInput = {
packageId?: string
username?: string
}
/**
* Agent setup: prefilled invocation-token URL and HTTP invocation shape.
*
* This package does not use account secrets. External clients mint a package
* invocation token in the browser and store the raw bearer in Raycast
* preferences (or another local secret store). Never paste the token in chat.
*
* @example
* import guide from 'kody:@kody/raycast/guide'
* const setup = await guide()
*/
export default async function guide(input: GuideInput = {}) {
const record = asRecord(input)
const username = optionalString(record, 'username') ?? 'YOUR_USERNAME'
let packageId = optionalString(record, 'packageId')
if (!packageId) {
const listed = await kody.package_list({})
packageId =
listed.packages.find(pkg => pkg.kody_id === KODY_ID)?.package_id ??
OFFICIAL_PACKAGE_ID
}
const setupUrl = tokenSetupUrl(packageId)
return {
name: PACKAGE_NAME,
kodyId: KODY_ID,
publicUrl: PUBLIC_URL,
packageId,
auth: 'package-invocation-token',
secrets: [],
secretUrls: {},
tokenSetupUrl: setupUrl,
invocation: {
method: 'POST',
listPackagesUrl: invocationUrl(username, 'list-packages'),
getPackageUrl: invocationUrl(username, 'get-package'),
invokeUrl: invocationUrl(username, 'invoke'),
guideUrl: invocationUrl(username, 'guide'),
source: 'raycast',
},
steps: [
'Share https://kody.codes/@kody/raycast — not a /community/{listing_id} URL.',
'For a personal launcher, fork or install this listing into the user account so list-packages sees their packages. Live @kody/raycast lists official kody-scope packages.',
`Open the prefilled token form: ${setupUrl}`,
'Leave the raw token out of the URL. Generate or paste it in the form, then store it only in Raycast preferences or a local secret store.',
`POST https://kody.codes/@${username}/api/package-invocations/raycast/<export> with Authorization: Bearer <token>.`,
'Call ./invoke to run another owned package. The bearer stays scoped to raycast; packages.invoke enters the target runtime.',
'Mutations on ./invoke support dryRun: true (preview, no target call). This package declares no jobs.',
'Never paste invocation tokens or other secrets into chat.',
],
}
}