← Public packages
@kentcdodds/jev
Call TypeSafe Jev evaluations via Cloudflare AI Gateway (same token/gateway as @kentcdodds/ai).
src/resolve.ts
126 lines · 3.6 KB · TypeScriptimport { packageStorage, packages } from 'kody:runtime'
import {
DEFAULT_GATEWAY_ID,
DEFAULT_JEV_MODEL,
} from './types.ts'
const settingNames = {
accountId: 'cloudflareAccountId',
gatewayId: 'cloudflareAiGatewayId',
model: 'cloudflareJevModel',
} as const
export const cloudflareApiTokenPlaceholder = '{{secret:cloudflareApiToken}}'
export const secretsNewUrl =
'https://kody.codes/account/secrets/new?name=cloudflareApiToken&description=Cloudflare%20API%20token%20for%20Workers%20AI%20%2F%20AI%20Gateway&allowedHosts=api.cloudflare.com%2Cgateway.ai.cloudflare.com&scope=user'
function trimString(value: unknown) {
return typeof value === 'string' ? value.trim() : ''
}
async function readLocal(name: string) {
const storage = packageStorage()
const stored = await storage.get(name)
return trimString(stored)
}
/**
* Read account/gateway from @kentcdodds/ai in *that* package's runtime.
* Static `kody:@` imports stamp into this package and would see empty local
* storage — invoke is required for cross-package packageStorage reads.
*/
async function readAiFallback(): Promise<{
accountId: string
gatewayId: string
model: string
}> {
if (typeof packages?.invoke !== 'function') {
return { accountId: '', gatewayId: '', model: '' }
}
try {
const settings = (await packages.invoke('kody:@kentcdodds/ai', {
exportName: './settings',
params: {},
})) as { accountId?: unknown; gatewayId?: unknown; model?: unknown } | null
return {
accountId: trimString(settings?.accountId),
gatewayId: trimString(settings?.gatewayId),
model: trimString(settings?.model),
}
} catch {
return { accountId: '', gatewayId: '', model: '' }
}
}
export async function resolveJevConfig(overrides: {
accountId?: string
gatewayId?: string
model?: string
} = {}) {
const localAccountId = await readLocal(settingNames.accountId)
const localGatewayId = await readLocal(settingNames.gatewayId)
const localModel = await readLocal(settingNames.model)
let accountId = trimString(overrides.accountId) || localAccountId
let gatewayId = trimString(overrides.gatewayId) || localGatewayId
let model = trimString(overrides.model) || localModel
if (!accountId || !gatewayId) {
const fromAi = await readAiFallback()
if (!accountId && fromAi.accountId) {
accountId = fromAi.accountId
// Cache into jev storage so later calls stay local
await packageStorage().set(settingNames.accountId, accountId)
}
if (!gatewayId) {
gatewayId = fromAi.gatewayId || DEFAULT_GATEWAY_ID
if (fromAi.gatewayId) {
await packageStorage().set(settingNames.gatewayId, gatewayId)
}
}
}
if (!gatewayId) gatewayId = DEFAULT_GATEWAY_ID
if (!model) model = DEFAULT_JEV_MODEL
return {
accountId,
gatewayId,
model,
sources: {
accountId: trimString(overrides.accountId)
? 'override'
: localAccountId
? 'jev-storage'
: accountId
? 'ai-settings'
: 'missing',
gatewayId: trimString(overrides.gatewayId)
? 'override'
: localGatewayId
? 'jev-storage'
: fromAiGatewaySource(gatewayId),
model: trimString(overrides.model)
? 'override'
: localModel
? 'jev-storage'
: 'default',
},
}
}
function fromAiGatewaySource(gatewayId: string) {
return gatewayId ? 'fallback' : 'missing'
}
export { settingNames, readLocal }
export function missingSetup(accountId: string) {
if (accountId) return undefined
return {
secretsNewUrl,
settingsHint:
'Save cloudflareApiToken (user scope, hosts api.cloudflare.com). Then set accountId/gatewayId via kody:@kentcdodds/jev/settings — or leave them empty to reuse values already stored on @kentcdodds/ai/settings (gateway defaults to kody).',
}
}