import type { BackendContext } from '@jaspers-ai/sdk' import { AGENT_ID, agentIdOf, parseAgent, serializeAgent, type Agent, type AgentInput } from './agents.ts' import { errorText } from './errors.ts' // Agents on disk, in the plugin's data folder's defs/, one Markdown file each. The list is the live // value `agents`, which the view reads, published again whenever the folder changes. const FOLDER = 'defs' export async function loadAgents(ctx: BackendContext): Promise { const agents: Agent[] = [] for (const entry of await ctx.files.list(FOLDER)) { const id = entry.dir ? null : agentIdOf(entry.path) if (id === null) continue try { agents.push(parseAgent(id, await ctx.files.read(entry.path))) } catch (err) { // Gone between the listing and the read, or unreadable: listed with why, not a broken list. agents.push({ ...parseAgent(id, ''), error: `could not be read: ${errorText(err, 'unreadable')}` }) } } return agents.sort((a, b) => a.name.localeCompare(b.name) || a.id.localeCompare(b.id)) } export async function publishAgents(ctx: BackendContext): Promise { const agents = await loadAgents(ctx) ctx.live.set('agents', agents) return agents } /** Writes one agent, refusing what would not read back as a working one. */ export async function saveAgent(ctx: BackendContext, input: AgentInput): Promise { const text = serializeAgent(input) const checked = parseAgent(input.id, text) if (checked.error) throw new Error(`Not saved: ${checked.error}.`) await ctx.files.write(`${FOLDER}/${input.id}.md`, text) } export async function deleteAgent(ctx: BackendContext, id: string): Promise { if (!AGENT_ID.test(id)) throw new Error(`No agent ${id}.`) await ctx.files.remove(`${FOLDER}/${id}.md`) } export function watchAgents(ctx: BackendContext): void { ctx.files.watch(FOLDER, () => { publishAgents(ctx).catch((err: unknown) => console.warn(`agents: ${errorText(err, 'failed')}`)) }) }