Enable LLMs to handle webhooks with plaintext files
at main 66 lines 1.7 kB view raw
1import type { LureHandlerOptions, LureHandler } from "./types.js"; 2import type { StandardSchemaV1 } from "@standard-schema/spec"; 3import { LureCache } from "./loader.js"; 4import { watchLures } from "./watcher.js"; 5import { createQueue } from "./queue.js"; 6import type { FSWatcher } from "chokidar"; 7 8export async function createLureHandler<TSchema extends StandardSchemaV1 | undefined = undefined>( 9 options: LureHandlerOptions<TSchema>, 10): Promise<LureHandler> { 11 const { 12 basePath: rawBasePath, 13 luresDir, 14 callback, 15 configSchema, 16 maxAttempts = 1, 17 allowUnverified = true, 18 watch = false, 19 } = options; 20 21 const basePath = "/" + rawBasePath.replace(/^\/+/, "").replace(/\/+$/, ""); 22 23 const cache = new LureCache( 24 luresDir, 25 allowUnverified, 26 configSchema as StandardSchemaV1 | undefined, 27 ); 28 await cache.load(); 29 30 let watcher: FSWatcher | undefined; 31 if (watch) { 32 watcher = watchLures(luresDir, cache); 33 } 34 35 const queue = createQueue({ 36 cache, 37 callback: callback as (prompt: string, config: unknown) => Promise<void>, 38 maxAttempts, 39 }); 40 41 return { 42 handle(req) { 43 if (basePath === "/") { 44 // root basePath: every path matches; lurePath is the full path 45 } else if (req.path !== basePath && !req.path.startsWith(basePath + "/")) { 46 return false; 47 } 48 49 const lurePath = basePath === "/" ? req.path : req.path.slice(basePath.length) || "/"; 50 const lure = cache.get(lurePath); 51 if (lure === undefined) { 52 return false; 53 } 54 55 queue.enqueue(lurePath, req); 56 return true; 57 }, 58 59 async destroy() { 60 if (watcher !== undefined) { 61 await watcher.close(); 62 } 63 await queue.drain(); 64 }, 65 }; 66}