import { createHash } from "node:crypto"; import { fromManifest, type SkillSource } from "agents/skills"; import * as Effect from "effect/Effect"; import type { InstalledSkills } from "./installed-skills"; // The public SDK source interface lets SQLite supply discovery while R2 supplies // package bytes lazily. Native fromManifest owns content/resource projection. export function installedSkillSource(store: InstalledSkills): SkillSource { const id = "flarebot-installed"; const visible = store.list().filter((skill) => skill.enabled); const source = (name: string) => Effect.gen(function* () { const expected = visible.find((skill) => skill.name === name); if (!expected) return null; const loaded = yield* store.load(name); if ( !loaded?.enabled || loaded.revision !== expected.revision || loaded.fingerprint !== expected.fingerprint ) return null; return fromManifest({ id, fingerprint: loaded.fingerprint, skills: [loaded.entry], }); }); return { id, fingerprint: createHash("sha256") .update( JSON.stringify( visible.map((skill) => [ skill.name, skill.fingerprint, skill.revision, ]), ), ) .digest("hex"), list() { return Promise.resolve( visible.map((skill) => ({ name: skill.name, description: skill.description, version: skill.version, sourceId: id, })), ); }, load(name) { return Effect.runPromise( source(name).pipe( Effect.flatMap((native) => native ? Effect.promise(() => native.load(name)) : Effect.succeed(null), ), ), ); }, readResource(name, path) { return Effect.runPromise( source(name).pipe( Effect.flatMap((native) => native ? Effect.promise(() => native.readResource!(name, path)) : Effect.succeed(null), ), ), ); }, }; }