@browserlessapi/inferlo 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -3
- package/dist/adonisjs.d.ts +33 -0
- package/dist/adonisjs.js +30 -0
- package/dist/adonisjs.js.map +1 -0
- package/dist/ai/index.d.ts +22 -8
- package/dist/ai/index.js +107 -15
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/providers.js +5 -4
- package/dist/ai/providers.js.map +1 -1
- package/dist/ai/public.d.ts +21 -0
- package/dist/ai/public.js +17 -0
- package/dist/ai/public.js.map +1 -0
- package/dist/ai/tool.d.ts +47 -0
- package/dist/ai/tool.js +85 -0
- package/dist/ai/tool.js.map +1 -0
- package/dist/ai-client.d.ts +3 -0
- package/dist/ai-client.js +8 -0
- package/dist/ai-client.js.map +1 -0
- package/dist/api-version.d.ts +32 -0
- package/dist/api-version.js +45 -0
- package/dist/api-version.js.map +1 -0
- package/dist/app.d.ts +117 -5
- package/dist/app.js +667 -156
- package/dist/app.js.map +1 -1
- package/dist/audit.d.ts +50 -0
- package/dist/audit.js +92 -0
- package/dist/audit.js.map +1 -0
- package/dist/cache.d.ts +92 -0
- package/dist/cache.js +245 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli/main.js +45 -5
- package/dist/cli/main.js.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.js +101 -0
- package/dist/config.js.map +1 -0
- package/dist/db/table.d.ts +31 -0
- package/dist/db/table.js +140 -1
- package/dist/db/table.js.map +1 -1
- package/dist/deprecation.d.ts +23 -0
- package/dist/deprecation.js +70 -0
- package/dist/deprecation.js.map +1 -0
- package/dist/describe.d.ts +68 -5
- package/dist/describe.js +38 -3
- package/dist/describe.js.map +1 -1
- package/dist/elysia.d.ts +49 -0
- package/dist/elysia.js +10 -0
- package/dist/elysia.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.js +5 -4
- package/dist/env.js.map +1 -1
- package/dist/express.d.ts +38 -0
- package/dist/express.js +18 -0
- package/dist/express.js.map +1 -0
- package/dist/fastify.d.ts +52 -0
- package/dist/fastify.js +65 -0
- package/dist/fastify.js.map +1 -0
- package/dist/h3.d.ts +76 -0
- package/dist/h3.js +61 -0
- package/dist/h3.js.map +1 -0
- package/dist/hapi.d.ts +45 -0
- package/dist/hapi.js +57 -0
- package/dist/hapi.js.map +1 -0
- package/dist/hono.d.ts +26 -0
- package/dist/hono.js +10 -0
- package/dist/hono.js.map +1 -0
- package/dist/index.d.ts +27 -5
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +24 -0
- package/dist/jobs.js +35 -6
- package/dist/jobs.js.map +1 -1
- package/dist/koa.d.ts +51 -0
- package/dist/koa.js +45 -0
- package/dist/koa.js.map +1 -0
- package/dist/lambda.d.ts +110 -0
- package/dist/lambda.js +169 -0
- package/dist/lambda.js.map +1 -0
- package/dist/logging.d.ts +23 -0
- package/dist/logging.js +87 -0
- package/dist/logging.js.map +1 -0
- package/dist/mcp.d.ts +1 -7
- package/dist/mcp.js +18 -4
- package/dist/mcp.js.map +1 -1
- package/dist/metrics.d.ts +48 -0
- package/dist/metrics.js +184 -0
- package/dist/metrics.js.map +1 -0
- package/dist/module.d.ts +52 -0
- package/dist/module.js +152 -0
- package/dist/module.js.map +1 -0
- package/dist/nestjs.d.ts +43 -0
- package/dist/nestjs.js +47 -0
- package/dist/nestjs.js.map +1 -0
- package/dist/nextjs.d.ts +35 -0
- package/dist/nextjs.js +33 -0
- package/dist/nextjs.js.map +1 -0
- package/dist/openapi.d.ts +3 -1
- package/dist/openapi.js +16 -3
- package/dist/openapi.js.map +1 -1
- package/dist/permissions.d.ts +26 -0
- package/dist/permissions.js +78 -0
- package/dist/permissions.js.map +1 -0
- package/dist/react-router.d.ts +41 -0
- package/dist/react-router.js +42 -0
- package/dist/react-router.js.map +1 -0
- package/dist/redact.d.ts +19 -0
- package/dist/redact.js +150 -0
- package/dist/redact.js.map +1 -0
- package/dist/request-matcher.d.ts +4 -0
- package/dist/request-matcher.js +14 -0
- package/dist/request-matcher.js.map +1 -0
- package/dist/route.d.ts +54 -1
- package/dist/route.js +24 -0
- package/dist/route.js.map +1 -1
- package/dist/secure-headers.d.ts +8 -0
- package/dist/secure-headers.js +61 -0
- package/dist/secure-headers.js.map +1 -0
- package/dist/serve.d.ts +72 -1
- package/dist/serve.js +201 -61
- package/dist/serve.js.map +1 -1
- package/dist/sessions.d.ts +72 -0
- package/dist/sessions.js +171 -0
- package/dist/sessions.js.map +1 -0
- package/dist/sveltekit.d.ts +72 -0
- package/dist/sveltekit.js +68 -0
- package/dist/sveltekit.js.map +1 -0
- package/dist/tracing.d.ts +118 -0
- package/dist/tracing.js +204 -0
- package/dist/tracing.js.map +1 -0
- package/dist/trpc.d.ts +57 -0
- package/dist/trpc.js +142 -0
- package/dist/trpc.js.map +1 -0
- package/dist/tsed.d.ts +32 -0
- package/dist/tsed.js +13 -0
- package/dist/tsed.js.map +1 -0
- package/llms.txt +15 -5
- package/package.json +99 -3
- package/templates/app/AGENTS.md +12 -6
- package/templates/app/_env.example +2 -0
- package/templates/app/src/app.ts +15 -8
- package/templates/app/src/config.ts +17 -0
- package/templates/app/src/server.ts +9 -9
- package/templates/app/src/routes/health.ts +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.js","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAwC3C,4FAA4F;AAC5F,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB,0FAA0F;AAC1F,MAAM,YAAY,GAAG;IACnB,GAAG,CAAC,OAAgB,EAAE,GAAW;QAC/B,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;IACvC,CAAC;IACD,IAAI,CAAC,OAAgB;QACnB,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7B,CAAC;CACF,CAAC;AAEF,MAAM,aAAa,GAAG,kCAAkC,CAAC;AAOzD,oGAAoG;AACpG,MAAM,UAAU,YAAY,CAAC,MAAe,EAAE,KAA+B;IAC3E,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,SAAS,CAAC;IAC/D,MAAM,MAAM,GAAG,MAAuB,CAAC;IACvC,MAAM,GAAG,GAAG,MAAM,EAAE,GAA0B,CAAC;IAC/C,MAAM,KAAK,GACT,MAAM,KAAK,IAAI;QACf,OAAO,MAAM,KAAK,QAAQ;QAC1B,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,KAAK,UAAU;QAC3C,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,UAAU;QACvC,OAAO,GAAG,CAAC,OAAO,EAAE,IAAI,KAAK,UAAU;QACvC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,UAAU;QACxC,OAAO,GAAG,CAAC,WAAW,EAAE,OAAO,KAAK,UAAU;QAC9C,OAAO,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,QAAQ;QACxC,OAAO,GAAG,CAAC,cAAc,EAAE,KAAK,KAAK,QAAQ;QAC7C,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/G,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,YAAY,CAAC,QAAQ,EAAE,4EAA4E,EAAE;YAC7G,GAAG,EAAE,mIAAmI,KAAK,+BAA+B;SAC7K,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,cAAc,CAAC,MAAe,EAAE,OAAe,EAAE,UAAkB;IACjF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;IACvB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,IAAI,OAAO,EAAE,UAAU,CAAC,CAAC;IAC7E,OAAO;QACL,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACd,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YACxF,MAAM,UAAU,GAAe;gBAC7B,qBAAqB,EAAE,MAAM;gBAC7B,UAAU,EAAE,GAAG,CAAC,QAAQ;gBACxB,YAAY,EAAE,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC5C,gBAAgB,EAAE,GAAG,CAAC,QAAQ;aAC/B,CAAC;YACF,IAAI,GAAG,CAAC,IAAI;gBAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC3D,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC5C,IAAI,KAAK;gBAAE,UAAU,CAAC,qBAAqB,CAAC,GAAG,KAAK,CAAC;YACrD,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,CAAC;YACzF,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3E,CAAC;KACF,CAAC;AACJ,CAAC;AAcD,MAAM,UAAU,aAAa,CAAC,MAAqB,EAAE,WAAmB,EAAE,OAAgB;IACxF,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;IACvB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,IAAI,WAAW,EAAE,OAAO,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,IAAI,WAAW,CAAC;IAChD,MAAM,IAAI,GAAG,CAAC,IAAc,EAAE,KAAc,EAAE,EAAE;QAC9C,IAAI,CAAC,eAAe,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACrE,MAAM,IAAI,GAAI,KAA4B,EAAE,IAAI,CAAC;QACjD,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;;YAC/D,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACpF,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,GAAG,EAAE,CAAC;IACb,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,UAAsB,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3H,OAAO;QACL,KAAK;QACL,IAAI;QACJ,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE;YAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YACrC,IAAI,MAAM,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YACjG,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAClB,MAAM,KAAK,CAAC;YACd,CAAC;YACD,IAAI,CAAC,GAAG,EAAE,CAAC;YACX,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,MAAM;YACJ,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC;YACpC,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YACvD,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,KAAK,aAAa,CAAC;QAChF,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,aAAa,GAAG,eAAe,CAAC;AAEtC;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,OAAsB;IACnE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IAC/E,MAAM,MAAM,GAAG,CAAI,SAAiB,EAAE,KAAyB,EAAE,KAAyB,EAAE,EAAoB,EAAc,EAAE;QAC9H,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YAAE,OAAO,EAAE,EAAE,CAAC;QACnC,MAAM,UAAU,GAAe,EAAE,gBAAgB,EAAE,MAAM,EAAE,mBAAmB,EAAE,SAAS,EAAE,CAAC;QAC5F,IAAI,KAAK;YAAE,UAAU,CAAC,oBAAoB,CAAC,GAAG,KAAK,CAAC;QACpD,IAAI,KAAK;YAAE,UAAU,CAAC,eAAe,CAAC,GAAG,KAAK,CAAC;QAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;IAClF,CAAC,CAAC;IACF,MAAM,OAAO,GAAY;QACvB,GAAG,OAAO;QACV,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACjG,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACrG,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QACnH,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACrG,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;KACnG,CAAC;IACF,IAAI,OAAO,CAAC,WAAW;QAAE,OAAO,CAAC,WAAW,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,WAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnH,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACpJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,WAAW,CAAC,IAA0B;IACpD,MAAM,OAAO,GAAG,IAAI,EAAE,WAAW,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,KAAK,aAAa;QAAE,OAAO,EAAE,CAAC;IAC7D,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;AAC9D,CAAC;AAQD,MAAM,aAAa,GAAG;IACpB,GAAG,CAAC,OAAwB,EAAE,GAAW;QACvC,OAAO,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAClF,CAAC;IACD,IAAI,CAAC,OAAwB;QAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;CACF,CAAC;AAEF,MAAM,WAAW,GAAG,qDAAqD,CAAC;AAS1E,MAAM,UAAU,UAAU,CAAC,MAAqB,EAAE,WAAmB,EAAE,OAAgB;IACrF,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;IACvB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,IAAI,WAAW,EAAE,OAAO,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,IAAI,aAAa,CAAC;IACpD,OAAO;QACL,OAAO;YACL,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM;gBAAE,OAAO,EAAE,CAAC;YACvC,MAAM,OAAO,GAA2B,EAAE,CAAC;YAC3C,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;YACtD,MAAM,KAAK,GAAoB,EAAE,CAAC;YAClC,IAAI,OAAO,OAAO,CAAC,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrF,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;gBACxC,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,IAAI,GAAG;oBAAE,KAAK,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;YACxH,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE;YACf,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACvI,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAC3B,WAAW,GAAG,CAAC,IAAI,EAAE,EACrB;gBACE,IAAI;gBACJ,UAAU,EAAE;oBACV,kBAAkB,EAAE,SAAS;oBAC7B,0BAA0B,EAAE,SAAS;oBACrC,4BAA4B,EAAE,GAAG,CAAC,IAAI;oBACtC,sBAAsB,EAAE,GAAG,CAAC,EAAE;oBAC9B,qBAAqB,EAAE,GAAG,CAAC,QAAQ;iBACpC;aACF,EACD,MAAM,CACP,CAAC;YACF,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,EAAE;gBAC9B,IAAI,CAAC,eAAe,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBACrE,MAAM,IAAI,GAAI,KAA4B,EAAE,IAAI,CAAC;gBACjD,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBACjH,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC;YACrD,CAAC,CAAC;YACF,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;gBACxF,IAAI,CAAC,YAAY,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;gBAClD,OAAO,OAAO,CAAC;YACjB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,KAAK,CAAC,CAAC;gBACZ,MAAM,KAAK,CAAC;YACd,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/trpc.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inferlo inside tRPC (11.0 and later), without a runtime dependency on tRPC:
|
|
3
|
+
*
|
|
4
|
+
* import { initTRPC } from "@trpc/server";
|
|
5
|
+
* import { createAI, anthropic, t } from "@browserlessapi/inferlo/ai";
|
|
6
|
+
* import { inferloAI, trpcTools } from "@browserlessapi/inferlo/trpc";
|
|
7
|
+
*
|
|
8
|
+
* const trpc = initTRPC.context<Context>().create();
|
|
9
|
+
* const aiProcedure = trpc.procedure.use(inferloAI(ai)); // ctx.ai in every procedure, typed
|
|
10
|
+
*
|
|
11
|
+
* export const appRouter = trpc.router({
|
|
12
|
+
* summarize: aiProcedure.input(t.object({ text: t.string() })) // Inferlo schemas work as tRPC inputs
|
|
13
|
+
* .mutation(async ({ ctx, input }) => (await ctx.ai.generate({ model: "fast", prompt: input.text })).text),
|
|
14
|
+
* chat: aiProcedure.input(t.object({ prompt: t.string() }))
|
|
15
|
+
* .subscription(async function* ({ ctx, input, signal }) {
|
|
16
|
+
* yield* ctx.ai.stream({ model: "fast", prompt: input.prompt, signal }); // text chunks
|
|
17
|
+
* }),
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Procedures as AI tools, called as the current user through a caller:
|
|
21
|
+
* const tools = trpcTools(appRouter, {
|
|
22
|
+
* caller: appRouter.createCaller(ctx),
|
|
23
|
+
* procedures: { "tickets.create": "Create a support ticket for the current user." },
|
|
24
|
+
* });
|
|
25
|
+
* await ai.generate({ model: "fast", prompt, tools, maxSteps: 5 });
|
|
26
|
+
*
|
|
27
|
+
* This entry point never loads the router, database or jobs code.
|
|
28
|
+
*/
|
|
29
|
+
import type { MiddlewareFunction } from "@trpc/server/unstable-core-do-not-import";
|
|
30
|
+
import type { AI } from "./ai/index.ts";
|
|
31
|
+
import { type ToolDef } from "./ai/tool.ts";
|
|
32
|
+
import { ObjectSchema, type Shape } from "./schema.ts";
|
|
33
|
+
/**
|
|
34
|
+
* A tRPC middleware that puts an AI client (createAI(...) or an Inferlo app's app.ai) on the context:
|
|
35
|
+
* const aiProcedure = trpc.procedure.use(inferloAI(ai)), then ctx.ai in its procedures.
|
|
36
|
+
*/
|
|
37
|
+
export declare function inferloAI(ai: AI): MiddlewareFunction<any, any, any, {
|
|
38
|
+
ai: AI;
|
|
39
|
+
}, any>;
|
|
40
|
+
/** A procedure offered as a tool: its description, or { description, input } for procedures whose input is not a t.object. */
|
|
41
|
+
export type TrpcToolProcedure = string | {
|
|
42
|
+
description: string;
|
|
43
|
+
input: ObjectSchema<Shape>;
|
|
44
|
+
};
|
|
45
|
+
export interface TrpcToolsOptions {
|
|
46
|
+
/** Runs the procedures: appRouter.createCaller(ctx) or createCallerFactory(appRouter)(ctx), with the current user's context. */
|
|
47
|
+
caller: object;
|
|
48
|
+
/** Procedure paths ("tickets.create") and what each does. The model reads the description to decide when to call it. */
|
|
49
|
+
procedures: Record<string, TrpcToolProcedure>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Makes AI tools from tRPC queries and mutations, for ai.generate({ tools }). Each tool is named after its
|
|
53
|
+
* path with "." replaced by "_" ("tickets.create" becomes tickets_create), takes the procedure's t.object input,
|
|
54
|
+
* and runs through the caller, so the procedure's middleware, auth and validation apply. TRPCErrors such as
|
|
55
|
+
* NOT_FOUND or FORBIDDEN go back to the model as failed tool results; server errors reject ai.generate.
|
|
56
|
+
*/
|
|
57
|
+
export declare function trpcTools(router: object, options: TrpcToolsOptions): ToolDef[];
|
package/dist/trpc.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { tool } from "./ai/tool.js";
|
|
2
|
+
import { assertAIClient } from "./ai-client.js";
|
|
3
|
+
import { httpError, InferloError } from "./errors.js";
|
|
4
|
+
import { ObjectSchema, t } from "./schema.js";
|
|
5
|
+
/**
|
|
6
|
+
* A tRPC middleware that puts an AI client (createAI(...) or an Inferlo app's app.ai) on the context:
|
|
7
|
+
* const aiProcedure = trpc.procedure.use(inferloAI(ai)), then ctx.ai in its procedures.
|
|
8
|
+
*/
|
|
9
|
+
export function inferloAI(ai) {
|
|
10
|
+
assertAIClient(ai, 'Pass createAI({ providers, models }) from "@browserlessapi/inferlo/ai", or an Inferlo app\'s app.ai: trpc.procedure.use(inferloAI(ai)).');
|
|
11
|
+
return ({ next }) => next({ ctx: { ai } });
|
|
12
|
+
}
|
|
13
|
+
/** HTTP status of each tRPC error code a model can correct (the others reject ai.generate). */
|
|
14
|
+
const CLIENT_ERRORS = {
|
|
15
|
+
BAD_REQUEST: 400,
|
|
16
|
+
PARSE_ERROR: 400,
|
|
17
|
+
UNAUTHORIZED: 401,
|
|
18
|
+
PAYMENT_REQUIRED: 402,
|
|
19
|
+
FORBIDDEN: 403,
|
|
20
|
+
NOT_FOUND: 404,
|
|
21
|
+
METHOD_NOT_SUPPORTED: 405,
|
|
22
|
+
TIMEOUT: 408,
|
|
23
|
+
CONFLICT: 409,
|
|
24
|
+
PRECONDITION_FAILED: 412,
|
|
25
|
+
PAYLOAD_TOO_LARGE: 413,
|
|
26
|
+
UNSUPPORTED_MEDIA_TYPE: 415,
|
|
27
|
+
UNPROCESSABLE_CONTENT: 422,
|
|
28
|
+
TOO_MANY_REQUESTS: 429,
|
|
29
|
+
CLIENT_CLOSED_REQUEST: 499,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Makes AI tools from tRPC queries and mutations, for ai.generate({ tools }). Each tool is named after its
|
|
33
|
+
* path with "." replaced by "_" ("tickets.create" becomes tickets_create), takes the procedure's t.object input,
|
|
34
|
+
* and runs through the caller, so the procedure's middleware, auth and validation apply. TRPCErrors such as
|
|
35
|
+
* NOT_FOUND or FORBIDDEN go back to the model as failed tool results; server errors reject ai.generate.
|
|
36
|
+
*/
|
|
37
|
+
export function trpcTools(router, options) {
|
|
38
|
+
const problems = [];
|
|
39
|
+
const procedures = router?._def?.procedures;
|
|
40
|
+
if (!procedures || typeof procedures !== "object")
|
|
41
|
+
problems.push("the first argument must be a tRPC router (trpc.router({ ... }))");
|
|
42
|
+
if (!options || typeof options !== "object") {
|
|
43
|
+
problems.push("options must be { caller, procedures }");
|
|
44
|
+
throw invalid(problems);
|
|
45
|
+
}
|
|
46
|
+
if (!options.caller || (typeof options.caller !== "object" && typeof options.caller !== "function"))
|
|
47
|
+
problems.push("caller must be appRouter.createCaller(ctx)");
|
|
48
|
+
const entries = options.procedures && typeof options.procedures === "object" ? Object.entries(options.procedures) : [];
|
|
49
|
+
if (!entries.length)
|
|
50
|
+
problems.push('procedures must name at least one procedure: { "tickets.create": "Create a support ticket." }');
|
|
51
|
+
if (problems.length)
|
|
52
|
+
throw invalid(problems);
|
|
53
|
+
const tools = [];
|
|
54
|
+
for (const [path, entry] of entries) {
|
|
55
|
+
const procedure = procedures[path];
|
|
56
|
+
if (!procedure) {
|
|
57
|
+
const known = Object.keys(procedures);
|
|
58
|
+
problems.push(`"${path}" is not a procedure of this router${known.length ? ` (procedures: ${known.join(", ")})` : ""}`);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const type = procedure._def?.type;
|
|
62
|
+
if (type !== "query" && type !== "mutation") {
|
|
63
|
+
problems.push(`"${path}" is a ${type ?? "unknown"} procedure; only queries and mutations can be tools`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const description = typeof entry === "string" ? entry : entry?.description;
|
|
67
|
+
if (typeof description !== "string" || !description.trim()) {
|
|
68
|
+
problems.push(`"${path}" needs a description of what it does`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const inputs = procedure._def?.inputs ?? [];
|
|
72
|
+
let input;
|
|
73
|
+
if (typeof entry === "object" && entry !== null && entry.input !== undefined) {
|
|
74
|
+
if (!(entry.input instanceof ObjectSchema)) {
|
|
75
|
+
problems.push(`"${path}" input must be a t.object({ ... }) schema`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
input = entry.input;
|
|
79
|
+
}
|
|
80
|
+
else if (inputs.every((parser) => parser instanceof ObjectSchema)) {
|
|
81
|
+
const shape = {};
|
|
82
|
+
for (const parser of inputs)
|
|
83
|
+
Object.assign(shape, parser.shape);
|
|
84
|
+
input = t.object(shape);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
problems.push(`"${path}" has an input that is not a t.object schema from Inferlo; pass { description, input: t.object({ ... }) } for it`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const name = path.replace(/\./g, "_");
|
|
91
|
+
const hasInput = inputs.length > 0;
|
|
92
|
+
const call = resolve(options.caller, path);
|
|
93
|
+
try {
|
|
94
|
+
tools.push(tool(name, { description, input }).handle(async ({ input: args }) => {
|
|
95
|
+
try {
|
|
96
|
+
return await call(hasInput ? args : undefined);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw toToolError(error);
|
|
100
|
+
}
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error instanceof InferloError)
|
|
105
|
+
problems.push(`"${path}" does not make a valid tool name (${name})`);
|
|
106
|
+
else
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (problems.length)
|
|
111
|
+
throw invalid(problems);
|
|
112
|
+
return tools;
|
|
113
|
+
}
|
|
114
|
+
/** caller.tickets.create, looked up when the tool runs (callers are proxies). */
|
|
115
|
+
function resolve(caller, path) {
|
|
116
|
+
return async (input) => {
|
|
117
|
+
let target = caller;
|
|
118
|
+
for (const key of path.split("."))
|
|
119
|
+
target = target[key];
|
|
120
|
+
return target(input);
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** TRPCErrors a model can correct become httpErrors (failed tool results); anything else is rethrown. */
|
|
124
|
+
function toToolError(error) {
|
|
125
|
+
if (error instanceof InferloError)
|
|
126
|
+
return error;
|
|
127
|
+
const candidate = error;
|
|
128
|
+
if (!candidate || typeof candidate !== "object" || candidate.name !== "TRPCError" || typeof candidate.code !== "string")
|
|
129
|
+
return error;
|
|
130
|
+
if (candidate.cause instanceof InferloError && candidate.cause.status < 500)
|
|
131
|
+
return candidate.cause;
|
|
132
|
+
const status = CLIENT_ERRORS[candidate.code];
|
|
133
|
+
if (status === undefined)
|
|
134
|
+
return error;
|
|
135
|
+
return httpError(status, typeof candidate.message === "string" ? candidate.message : candidate.code, { code: candidate.code });
|
|
136
|
+
}
|
|
137
|
+
function invalid(problems) {
|
|
138
|
+
return new InferloError("IL5017", `Invalid trpcTools() options: ${problems.join("; ")}.`, {
|
|
139
|
+
fix: 'Use trpcTools(appRouter, { caller: appRouter.createCaller(ctx), procedures: { "tickets.create": "Create a support ticket." } }) with queries and mutations whose input is a t.object({ ... }).',
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=trpc.js.map
|
package/dist/trpc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trpc.js","sourceRoot":"","sources":["../src/trpc.ts"],"names":[],"mappings":"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"}
|
package/dist/tsed.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inferlo inside Ts.ED (8 and later), without a runtime dependency on Ts.ED:
|
|
3
|
+
*
|
|
4
|
+
* import { InferloAI, inferloAI } from "@browserlessapi/inferlo/tsed";
|
|
5
|
+
*
|
|
6
|
+
* @Configuration({ imports: [inferloAI(ai)] }) // or { token: InferloAI, useFactory: () => createAI(...) }
|
|
7
|
+
* export class Server {}
|
|
8
|
+
*
|
|
9
|
+
* @Injectable()
|
|
10
|
+
* export class TicketsService {
|
|
11
|
+
* constructor(private readonly ai: InferloAI) {} // or: ai = inject(InferloAI)
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* A whole Inferlo app mounts with app.use("/api", toNodeHandler(inferloApp)) in $beforeRoutesInit on the
|
|
15
|
+
* Express platform. This entry point never loads the router, database or jobs code.
|
|
16
|
+
*/
|
|
17
|
+
import type { AI } from "./ai/index.ts";
|
|
18
|
+
/**
|
|
19
|
+
* Injection token and type of the AI client in Ts.ED: constructor(private readonly ai: InferloAI) {},
|
|
20
|
+
* @Inject(InferloAI) ai: InferloAI, or inject(InferloAI). It has every method of AI.
|
|
21
|
+
*/
|
|
22
|
+
export declare abstract class InferloAI {
|
|
23
|
+
}
|
|
24
|
+
export interface InferloAI extends AI {
|
|
25
|
+
}
|
|
26
|
+
/** A Ts.ED provider that registers an AI client as InferloAI, for @Configuration({ imports }) and PlatformTest.create({ imports }). */
|
|
27
|
+
export interface InferloAIProvider {
|
|
28
|
+
token: typeof InferloAI;
|
|
29
|
+
use: AI;
|
|
30
|
+
}
|
|
31
|
+
/** Registers an AI client (createAI(...) or an Inferlo app's app.ai) for injection: @Configuration({ imports: [inferloAI(ai)] }). */
|
|
32
|
+
export declare function inferloAI(ai: AI): InferloAIProvider;
|
package/dist/tsed.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { assertAIClient } from "./ai-client.js";
|
|
2
|
+
/**
|
|
3
|
+
* Injection token and type of the AI client in Ts.ED: constructor(private readonly ai: InferloAI) {},
|
|
4
|
+
* @Inject(InferloAI) ai: InferloAI, or inject(InferloAI). It has every method of AI.
|
|
5
|
+
*/
|
|
6
|
+
export class InferloAI {
|
|
7
|
+
}
|
|
8
|
+
/** Registers an AI client (createAI(...) or an Inferlo app's app.ai) for injection: @Configuration({ imports: [inferloAI(ai)] }). */
|
|
9
|
+
export function inferloAI(ai) {
|
|
10
|
+
assertAIClient(ai, 'Pass createAI({ providers, models }) from "@browserlessapi/inferlo/ai", or an Inferlo app\'s app.ai: @Configuration({ imports: [inferloAI(ai)] }).');
|
|
11
|
+
return { token: InferloAI, use: ai };
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=tsed.js.map
|
package/dist/tsed.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tsed.js","sourceRoot":"","sources":["../src/tsed.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAgB,SAAS;CAAG;AASlC,qIAAqI;AACrI,MAAM,UAAU,SAAS,CAAC,EAAM;IAC9B,cAAc,CAAC,EAAE,EAAE,oJAAoJ,CAAC,CAAC;IACzK,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;AACvC,CAAC"}
|
package/llms.txt
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
# Inferlo
|
|
2
2
|
|
|
3
|
-
> Inferlo is
|
|
3
|
+
> Inferlo is an enterprise-ready, batteries-included TypeScript backend framework (routes, schemas, database with PostgreSQL and migrations, auth, rate limiting, background jobs, cron, OpenAPI) with AI models built in (Anthropic, OpenAI, Gemini, Ollama and OpenAI-compatible providers with fallback, structured output, streaming, tool calling, embeddings and usage tracking) and an MCP server, with zero runtime dependencies. Runs on Node.js 22.18+, Bun, Deno and edge runtimes. Package: @browserlessapi/inferlo.
|
|
4
4
|
|
|
5
5
|
Key rules: routes are private unless `auth: "public"`; validate input with `t.*` schemas; use `db.<table>` methods (never string-built SQL); every model has id, createdAt, updatedAt automatically; relative imports end in `.ts`; run `inferlo describe --json` to see an app and `inferlo check --json` after changes.
|
|
6
6
|
|
|
7
7
|
## Docs
|
|
8
8
|
|
|
9
9
|
- [Getting started](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/getting-started.md): install, project layout, minimal app
|
|
10
|
+
- [Configuration](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/config.md): config({ schema, secrets, environments }), typed settings validated at startup, secrets redacted
|
|
10
11
|
- [Schemas](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/schema.md): t.* types, modifiers, validation, JSON Schema
|
|
11
12
|
- [Models and database](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/database.md): model(), queries, adapters, db push, transactions
|
|
12
|
-
- [Routes](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/routes.md): options, handler context, return values, errors, middleware
|
|
13
|
-
- [Auth](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/auth.md): auth.resolve, roles, bearer API keys, passwords
|
|
13
|
+
- [Routes](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/routes.md): options, handler context, return values, errors, middleware, deprecated routes (Deprecation and Sunset headers), API versions with apiVersion("v1", routes) and an OpenAPI document per version
|
|
14
|
+
- [Auth](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/auth.md): auth.resolve, roles, permissions() with can() and authorize() and the route permission option, bearer API keys, passwords
|
|
14
15
|
- [AI](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/ai.md): providers, aliases and fallback, structured output, streaming, embeddings, usage and budgets
|
|
16
|
+
- [Modules](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/modules.md): module("billing", { imports, models, routes, jobs, provides }) splits a large app into feature modules with explicit dependencies, createApp({ modules }), checked at startup and shown in describe
|
|
15
17
|
- [MCP, OpenAPI and describe](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/mcp.md)
|
|
16
18
|
- [CLI](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/cli.md)
|
|
17
19
|
- [Testing](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/testing.md)
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
+
- [Use with other frameworks](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/integrations.md): the AI module in any framework, toNodeHandler and fromNodeRequest to mount an app in Express, Fastify, NestJS or node:http (fallthrough: true passes unmatched requests to the host), inferloAI() middleware for Hono (c.var.ai), Express (req.ai), Koa (ctx.ai), Elysia (ai) and tRPC (ctx.ai), toKoaMiddleware() to mount an app in Koa, inferloPlugin for Fastify (request.ai, app under a prefix), InferloModule for NestJS, an AdonisJS provider (ctx.ai, config/ai.ts), InferloAI and inferloAI() for Ts.ED injection, trpcTools() for tRPC procedures as AI tools, toNextHandlers() for Next.js App Router route handlers, an Encore.ts raw endpoint serving the app with toNodeHandler(app, { prefix }) and createAI in Encore services
|
|
21
|
+
- [Migrate from Express](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/migrate-from-express.md): move an Express app to Inferlo route by route at the same URLs, with a concept mapping table, shared auth and tests
|
|
22
|
+
- [Migrate from NestJS](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/migrate-from-nestjs.md): move a NestJS app to Inferlo route by route at the same URLs with toNodeHandler(app, { fallthrough: true }), Nest modules, providers, guards, pipes and DTOs mapped to Inferlo modules, plain functions, auth and schemas
|
|
23
|
+
- [Migrate from Hono](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/migrate-from-hono.md): move a Hono app to Inferlo route by route at the same URLs with hono.all("*", (c) => inferlo.fetch(c.req.raw)), Hono validators, middleware, sub-apps and basePath mapped to Inferlo schemas, createApp options, modules and apiVersion
|
|
24
|
+
- [Migrate from Koa](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/migrate-from-koa.md): move a Koa app to Inferlo route by route at the same URLs with toKoaMiddleware(app, { fallthrough: true }) before the Koa router, @koa/router routes, ctx.throw, body parsers and Koa middleware mapped to Inferlo routes, schemas, errors and createApp options
|
|
25
|
+
- [Security](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/security.md): secure defaults and security headers (createApp({ secureHeaders }))
|
|
26
|
+
- [Observability](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/observability.md): request ids (X-Request-Id, ctx.requestId), request logs and JSON logs with createApp({ logging: { format: "json", requests: true } }), your own logger, Prometheus metrics with createApp({ metrics: { token } })
|
|
27
|
+
- [Deployment](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/deploy.md): Node, Bun, Deno, edge, health and readiness endpoints, graceful shutdown with app.close()
|
|
28
|
+
- [Support policy](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/support.md): semantic versioning, which versions get fixes, LTS after 1.0, deprecation rules and supported runtime, database and framework versions
|
|
29
|
+
- [Error codes](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/errors.md): every ILxxxx code with its fix
|
|
20
30
|
|
|
21
31
|
## Full text
|
|
22
32
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserlessapi/inferlo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The TypeScript backend framework with built
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "The enterprise-ready TypeScript backend framework, with AI models built in: routing, validation, PostgreSQL and migrations, auth, rate limiting, jobs, OpenAPI and MCP, plus Anthropic, OpenAI, Gemini, Ollama and OpenAI-compatible models.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "BrowserlessAPI",
|
|
@@ -35,6 +35,70 @@
|
|
|
35
35
|
"types": "./dist/index.d.ts",
|
|
36
36
|
"default": "./dist/index.js"
|
|
37
37
|
},
|
|
38
|
+
"./adonisjs": {
|
|
39
|
+
"types": "./dist/adonisjs.d.ts",
|
|
40
|
+
"default": "./dist/adonisjs.js"
|
|
41
|
+
},
|
|
42
|
+
"./ai": {
|
|
43
|
+
"types": "./dist/ai/public.d.ts",
|
|
44
|
+
"default": "./dist/ai/public.js"
|
|
45
|
+
},
|
|
46
|
+
"./elysia": {
|
|
47
|
+
"types": "./dist/elysia.d.ts",
|
|
48
|
+
"default": "./dist/elysia.js"
|
|
49
|
+
},
|
|
50
|
+
"./express": {
|
|
51
|
+
"types": "./dist/express.d.ts",
|
|
52
|
+
"default": "./dist/express.js"
|
|
53
|
+
},
|
|
54
|
+
"./fastify": {
|
|
55
|
+
"types": "./dist/fastify.d.ts",
|
|
56
|
+
"default": "./dist/fastify.js"
|
|
57
|
+
},
|
|
58
|
+
"./h3": {
|
|
59
|
+
"types": "./dist/h3.d.ts",
|
|
60
|
+
"default": "./dist/h3.js"
|
|
61
|
+
},
|
|
62
|
+
"./hapi": {
|
|
63
|
+
"types": "./dist/hapi.d.ts",
|
|
64
|
+
"default": "./dist/hapi.js"
|
|
65
|
+
},
|
|
66
|
+
"./hono": {
|
|
67
|
+
"types": "./dist/hono.d.ts",
|
|
68
|
+
"default": "./dist/hono.js"
|
|
69
|
+
},
|
|
70
|
+
"./lambda": {
|
|
71
|
+
"types": "./dist/lambda.d.ts",
|
|
72
|
+
"default": "./dist/lambda.js"
|
|
73
|
+
},
|
|
74
|
+
"./koa": {
|
|
75
|
+
"types": "./dist/koa.d.ts",
|
|
76
|
+
"default": "./dist/koa.js"
|
|
77
|
+
},
|
|
78
|
+
"./nextjs": {
|
|
79
|
+
"types": "./dist/nextjs.d.ts",
|
|
80
|
+
"default": "./dist/nextjs.js"
|
|
81
|
+
},
|
|
82
|
+
"./nestjs": {
|
|
83
|
+
"types": "./dist/nestjs.d.ts",
|
|
84
|
+
"default": "./dist/nestjs.js"
|
|
85
|
+
},
|
|
86
|
+
"./react-router": {
|
|
87
|
+
"types": "./dist/react-router.d.ts",
|
|
88
|
+
"default": "./dist/react-router.js"
|
|
89
|
+
},
|
|
90
|
+
"./sveltekit": {
|
|
91
|
+
"types": "./dist/sveltekit.d.ts",
|
|
92
|
+
"default": "./dist/sveltekit.js"
|
|
93
|
+
},
|
|
94
|
+
"./tsed": {
|
|
95
|
+
"types": "./dist/tsed.d.ts",
|
|
96
|
+
"default": "./dist/tsed.js"
|
|
97
|
+
},
|
|
98
|
+
"./trpc": {
|
|
99
|
+
"types": "./dist/trpc.d.ts",
|
|
100
|
+
"default": "./dist/trpc.js"
|
|
101
|
+
},
|
|
38
102
|
"./postgres": {
|
|
39
103
|
"types": "./dist/db/postgres.d.ts",
|
|
40
104
|
"default": "./dist/db/postgres.js"
|
|
@@ -57,7 +121,7 @@
|
|
|
57
121
|
"scripts": {
|
|
58
122
|
"build": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/cli/main.js",
|
|
59
123
|
"prepare": "npm run build",
|
|
60
|
-
"typecheck": "tsc --noEmit && tsc --noEmit -p examples/helpdesk",
|
|
124
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p examples/helpdesk && tsc --noEmit -p examples/with-hono && tsc --noEmit -p examples/with-express && tsc --noEmit -p examples/with-fastify && tsc --noEmit -p examples/with-elysia && tsc --noEmit -p examples/with-nestjs && tsc --noEmit -p examples/with-trpc && tsc --noEmit -p examples/with-adonisjs && tsc --noEmit -p examples/with-tsed && tsc --noEmit -p examples/with-encore && tsc --noEmit -p examples/with-koa && tsc --noEmit -p examples/with-nextjs && tsc --noEmit -p examples/with-nitro && tsc --noEmit -p examples/with-sveltekit && tsc --noEmit -p examples/with-react-router && tsc --noEmit -p examples/with-lambda && tsc --noEmit -p examples/with-hapi",
|
|
61
125
|
"test": "node --test --test-reporter=spec \"test/**/*.test.ts\" \"examples/*/test/*.test.ts\"",
|
|
62
126
|
"test:bun": "bun test test/",
|
|
63
127
|
"llms": "node scripts/build-llms.ts",
|
|
@@ -65,17 +129,49 @@
|
|
|
65
129
|
"bench": "node bench/run.ts"
|
|
66
130
|
},
|
|
67
131
|
"peerDependencies": {
|
|
132
|
+
"@opentelemetry/api": ">=1.3.0",
|
|
68
133
|
"pg": ">=8"
|
|
69
134
|
},
|
|
70
135
|
"peerDependenciesMeta": {
|
|
136
|
+
"@opentelemetry/api": {
|
|
137
|
+
"optional": true
|
|
138
|
+
},
|
|
71
139
|
"pg": {
|
|
72
140
|
"optional": true
|
|
73
141
|
}
|
|
74
142
|
},
|
|
75
143
|
"devDependencies": {
|
|
144
|
+
"@adonisjs/core": "^6.21.0",
|
|
145
|
+
"@hapi/hapi": "^21.4.13",
|
|
146
|
+
"@nestjs/common": "^12.3.3",
|
|
147
|
+
"@nestjs/core": "^12.3.3",
|
|
148
|
+
"@nestjs/platform-express": "^12.3.3",
|
|
149
|
+
"@opentelemetry/api": "^1.10.0",
|
|
150
|
+
"@sveltejs/kit": "^2.70.3",
|
|
151
|
+
"@trpc/server": "^11.24.0",
|
|
152
|
+
"@tsed/core": "^8.45.0",
|
|
153
|
+
"@tsed/di": "^8.45.0",
|
|
154
|
+
"@tsed/platform-express": "^8.45.0",
|
|
155
|
+
"@tsed/platform-http": "^8.45.0",
|
|
156
|
+
"@tsed/platform-params": "^8.45.0",
|
|
157
|
+
"@tsed/schema": "^8.45.0",
|
|
158
|
+
"@types/express": "^5.0.6",
|
|
159
|
+
"@types/koa": "^3.0.3",
|
|
76
160
|
"@types/node": "^22.18.0",
|
|
77
161
|
"@types/pg": "^8.15.0",
|
|
162
|
+
"elysia": "^1.4.30",
|
|
163
|
+
"encore.dev": "^1.58.8",
|
|
164
|
+
"express": "^5.3.0",
|
|
165
|
+
"fastify": "^5.12.8",
|
|
166
|
+
"h3": "^2.0.8",
|
|
167
|
+
"hono": "^4.13.16",
|
|
168
|
+
"koa": "^3.3.0",
|
|
169
|
+
"next": "^16.4.5",
|
|
78
170
|
"pg": "^8.16.0",
|
|
171
|
+
"react": "^19.3.1",
|
|
172
|
+
"react-router": "^8.5.1",
|
|
173
|
+
"reflect-metadata": "^0.2.2",
|
|
174
|
+
"rxjs": "^7.8.2",
|
|
79
175
|
"typescript": "^5.9.3"
|
|
80
176
|
},
|
|
81
177
|
"publishConfig": {
|
package/templates/app/AGENTS.md
CHANGED
|
@@ -22,6 +22,7 @@ Do not finish until `inferlo check` passes.
|
|
|
22
22
|
|---|---|
|
|
23
23
|
| `src/models.ts` | Every model (`model("table_name", { ... })`) and the `models` array |
|
|
24
24
|
| `src/routes/<feature>.ts` | Routes for one feature, each one an exported `const` |
|
|
25
|
+
| `src/config.ts` | `settings`: every environment variable, validated at startup with `config({ schema, secrets })` |
|
|
25
26
|
| `src/app.ts` | `createApp({ models, routes, db, auth, ai })`. Import route modules with `import * as x` |
|
|
26
27
|
| `src/server.ts` | Starts the server. Keep it small |
|
|
27
28
|
| `test/<feature>.test.ts` | Tests using `app.request(path, { method, json, as: user })` |
|
|
@@ -29,19 +30,24 @@ Do not finish until `inferlo check` passes.
|
|
|
29
30
|
## Rules
|
|
30
31
|
|
|
31
32
|
1. **One way to do each thing.** Routes: `route.get/post/put/patch/delete(path, options).handle(fn)`. Validation: `t.*` schemas. Database: `db.<table>.<method>`. AI: `ai.generate` / `ai.stream` / `ai.embed`.
|
|
32
|
-
2. **Routes are private by default.** A route without `auth` needs a signed-in user. Use `auth: "public"` only for routes anyone may call, and `auth: "<role>"` for role checks.
|
|
33
|
+
2. **Routes are private by default.** A route without `auth` needs a signed-in user. Use `auth: "public"` only for routes anyone may call, and `auth: "<role>"` for role checks. Browser sign-in uses cookie sessions: `export const session = sessions({ secret: settings.SESSION_SECRET, user: ({ userId }, { db }) => ... })`, `createApp({ auth: session })`, `await session.signIn(ctx, user.id)` in the login route and `session.signOut(ctx)` in logout; browsers send the CSRF token in `X-CSRF-Token` or a `_csrf` form field on POST, PUT, PATCH and DELETE (403 IL3005 without it).
|
|
33
34
|
3. **Validate every input with a schema.** `body`, `query` and `params` options. Reuse model schemas: `Ticket.pick("title", "body")`, `Ticket.omit("ownerId")`, `.partial()` for PATCH.
|
|
34
|
-
4. **Check ownership.** Fetch with `db.x.get(id)` (throws 404) and compare `ownerId` to `user.id`; throw `notFound()` when it does not match, so other users' ids are not revealed.
|
|
35
|
-
5. **Related rows:** a `t.ref` field named `<name>Id` is a relation: `db.notes.findMany({ include: { owner: true } })` adds `note.owner` with one extra query, and `db.users.findMany({ include: { notes: true } })` adds `user.notes` (the rows that point to each user). Never load related rows in a loop.
|
|
35
|
+
4. **Check ownership.** Fetch with `db.x.get(id)` (throws 404) and compare `ownerId` to `user.id`; throw `notFound()` when it does not match, so other users' ids are not revealed. When more than one rule decides access (owners, admins, teams), define named permissions once in `src/permissions.ts` with `export const access = permissions({ "notes.update": ({ user, note }) => note.ownerId === user.id || (user.roles ?? []).includes("admin") })`, pass `permissions: access` to `createApp`, and call `await access.authorize("notes.update", { user, note })` in routes and jobs (403 IL3004 when denied). Permissions that need only the user go on the route: `{ permission: "reports.view" }`; add `permissions: typeof access` to `Register` (with `import type`) so permission names are type-checked. When changes must be on record (who changed what), add `AuditLog` to the models and call `await audit("notes.update", { model: "notes", before: note, after: updated })` after the change, in routes and jobs (jobs pass `userId`).
|
|
36
|
+
5. **Related rows:** a `t.ref` field named `<name>Id` is a relation: `db.notes.findMany({ include: { owner: true } })` adds `note.owner` with one extra query, and `db.users.findMany({ include: { notes: true } })` adds `user.notes` (the rows that point to each user). Never load related rows in a loop. For lists clients page through, use `db.notes.paginate({ where, orderBy: { createdAt: "desc" }, cursor: query.cursor, limit: query.limit })`, which returns `{ items, nextCursor }`.
|
|
36
37
|
6. **Never write raw SQL with string concatenation.** Use the table methods. If you really need SQL, use `db.$sql(text, params)` with `$1` placeholders.
|
|
37
38
|
7. **Model ids, timestamps:** every model already has `id`, `createdAt`, `updatedAt`. Never define them or send them as input.
|
|
38
39
|
8. **New field?** Add it to the model in `src/models.ts`, then run `npx inferlo db push` in development, or `npx inferlo db migrate <name>` to write a migration file for production (applied with `npx inferlo db migrate --apply`). Use `.optional()` or `.default()` for new fields on existing tables. Never edit `migrations/snapshot.json`, and never edit a migration file that has been applied (`--apply` warns with IL4018 and does not run the change): put changes in a new migration. Removing a model or field needs `npx inferlo db migrate <name> --allow-drop` (it deletes that data); never use it for a rename. Changing a field's type or default, making it required or optional, or changing an enum's values needs `--allow-change`; give a newly required field a `.default()` so existing NULL rows get a value, and before removing an enum value add an `UPDATE` in the new migration file (before its `ADD CONSTRAINT`) that moves rows off it (IL4021).
|
|
39
40
|
9. **New resource?** `npx inferlo add resource <plural_name>` generates the model and CRUD routes. Then edit them.
|
|
40
|
-
10. **AI calls** use aliases from `src/app.ts` (`model: "fast"`), never hard-coded provider model ids in routes. Use `output: schema` for structured data and `for: user.id` so usage is tracked per user. To let the model act, pass routes as tools: `ai.generate({ prompt, tools: [createNote, getNote], for: user.id })`. Each tool call runs as the current user with that route's auth and validation. To send images: `ai.generate({ prompt, images: [{ url }] })` or `[{ base64, mediaType: "image/png" }]`. Give routes that call AI models, send email or check passwords a `rateLimit: { max, per, by }` (IL2005 is the 429 it returns).
|
|
41
|
+
10. **AI calls** use aliases from `src/app.ts` (`model: "fast"`), never hard-coded provider model ids in routes. Use `output: schema` for structured data and `for: user.id` so usage is tracked per user. To let the model act, pass routes as tools: `ai.generate({ prompt, tools: [createNote, getNote], for: user.id })`. Each tool call runs as the current user with that route's auth and validation. For work that is not a route (an external API, a calculation), make a function tool: `tool("getWeather", { description, input: t.object({ ... }) }).handle(async ({ input }) => ...)`. To send images: `ai.generate({ prompt, images: [{ url }] })` or `[{ base64, mediaType: "image/png" }]`. Give routes that call AI models, send email or check passwords a `rateLimit: { max, per, by }` (IL2005 is the 429 it returns).
|
|
41
42
|
11. **Errors:** throw `notFound()`, `forbidden()`, `badRequest()` or `httpError(status, message)`. Every Inferlo error has a code (ILxxxx), a fix and a docs link: read the fix before changing code.
|
|
42
43
|
12. **Imports:** relative imports end in `.ts` (`import { Note } from "../models.ts"`).
|
|
43
|
-
13. **
|
|
44
|
-
14. **
|
|
44
|
+
13. **Configuration:** read environment variables only through `settings` from `src/config.ts`. A new variable is a new field in its `t.object({ ... })` schema (`.default(...)` or `.optional()` unless the app cannot start without it), and API keys, passwords and connection URLs go in `secrets` so they are never logged. Never read `process.env` in routes.
|
|
45
|
+
14. **Expose to AI agents:** add `tool: true` to a route to make it an MCP tool at `/mcp`. Same auth applies.
|
|
46
|
+
15. **Slow work after the response** (email, slow AI calls) is a background job: `export const x = job("send-email", { input: t.object({ ... }) }).handle(async ({ input, db, ai }) => { ... })` in `src/jobs.ts`, registered with `createApp({ jobs: [jobs] })`, queued with `await ctx.jobs.enqueue(x, { ... })`. Pass ids in the input, not rows. In tests, run queued jobs with `await app.jobs.runDue()`. Jobs run in the server process (`app.jobs.start()` in `src/server.ts`); with `createApp({ jobQueue: { store: "database" } })` they are kept in Postgres and `npm run worker` (`inferlo worker`) runs them in separate processes. Scheduled work is a cron next to its job: `export const nightly = cron("0 3 * * *", cleanupJob)` (UTC; the job's input must be `t.object({})`).
|
|
47
|
+
16. **Security headers and health:** every response already gets security headers (CSP, HSTS over HTTPS, nosniff, frame and referrer policies) and `GET /health` and `GET /ready` are built in (`health: true` in `src/app.ts`). Change a header with `createApp({ secureHeaders: { "Content-Security-Policy": "..." } })` instead of setting it in every route, never turn them all off without a proxy that adds them, and never add your own `/health` route. On SIGTERM, `src/server.ts` calls `app.close()`, which finishes in-flight requests and jobs first. Prometheus metrics (`GET /metrics`: requests, jobs, AI calls, tokens and cost) are served when `METRICS_TOKEN` is set; never add your own `/metrics` route.
|
|
48
|
+
17. **Large apps: feature modules.** When the app grows to several features, group each feature's models, routes and jobs in `src/<feature>/module.ts` with `export const billing = module("billing", { imports: [users], models: [...], routes: [routes], jobs: [jobs], provides: { createInvoice } })` and register it with `createApp({ modules: [users, billing] })`. A module that uses another module's models or `provides` lists it in `imports` (IL6023 and IL6024 tell you when one is missing). Call other modules only through their `provides`; never import their internal files. Keep every model in `Register` in `src/models.ts`.
|
|
49
|
+
18. **Replacing a route:** do not delete a route clients use. Add the new one and mark the old one `deprecated: { since: "YYYY-MM-DD", sunset: "YYYY-MM-DD", link: "https://..." }` in its options; it then sends the standard Deprecation and Sunset headers and is marked in OpenAPI and MCP. Remove it after the sunset date. For a breaking change to many routes, publish a new API version instead: `createApp({ routes: [apiVersion("v1", [notesV1]), apiVersion("v2", [notes])] })` serves them under `/v1` and `/v2`, each with its own OpenAPI document (`/v1/openapi.json`); write route paths without the version prefix.
|
|
50
|
+
19. **Caching:** for results that are slow or expensive to compute, use the built-in cache instead of a module-level `Map`: `` return cache.getOrSet(`notes:stats:${user.id}`, { ttlMs: 60_000, tags: ["notes"] }, () => computeStats(db, user.id)) `` in routes and jobs (`ctx.cache`), and `await cache.invalidate("notes")` after notes change. `ttlMs` is required; put the user id in the key of anything that differs per user; values come back as JSON (dates as strings). To cache a whole GET route instead, add the route option `cache: { ttlMs: 60_000, tags: ["notes"] }` (one response per user by default; `by: "public"` only for data that is the same for everyone) and invalidate the tag where the data changes. With several instances, use `createApp({ cache: { store: "database" } })`.
|
|
45
51
|
|
|
46
52
|
## Testing
|
|
47
53
|
|
package/templates/app/src/app.ts
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import { anthropic, bearer, createApp,
|
|
1
|
+
import { anthropic, bearer, createApp, hashToken, memory, openai } from "@browserlessapi/inferlo";
|
|
2
2
|
import { postgres } from "@browserlessapi/inferlo/postgres";
|
|
3
|
+
import { settings } from "./config.ts";
|
|
3
4
|
import { models } from "./models.ts";
|
|
4
|
-
import * as health from "./routes/health.ts";
|
|
5
5
|
import * as notes from "./routes/notes.ts";
|
|
6
6
|
|
|
7
|
-
const databaseUrl = env("DATABASE_URL", { optional: true });
|
|
8
|
-
|
|
9
7
|
export const app = createApp({
|
|
10
8
|
name: "{{name}}",
|
|
11
9
|
version: "0.1.0",
|
|
12
10
|
models,
|
|
13
11
|
// inferlo:routes
|
|
14
|
-
routes: [
|
|
15
|
-
|
|
12
|
+
routes: [notes],
|
|
13
|
+
// GET /health (liveness) and GET /ready (readiness: database and AI configuration).
|
|
14
|
+
health: true,
|
|
15
|
+
// Config secrets never appear in logs (provider keys, tokens and passwords in URLs are hidden too).
|
|
16
|
+
redact: [settings],
|
|
17
|
+
// One log line per request with its id (also in the X-Request-Id header), status and duration.
|
|
18
|
+
// Use format: "json" for one JSON object per line when a log collector reads the output.
|
|
19
|
+
logging: { requests: true },
|
|
20
|
+
// Prometheus metrics at GET /metrics (requests, jobs, AI calls, tokens and cost) when METRICS_TOKEN is set.
|
|
21
|
+
metrics: settings.METRICS_TOKEN ? { token: settings.METRICS_TOKEN } : false,
|
|
22
|
+
db: settings.DATABASE_URL ? postgres({ url: settings.DATABASE_URL }) : memory(),
|
|
16
23
|
|
|
17
24
|
// Requests send "Authorization: Bearer <api key>". Keys are stored as SHA-256 hashes.
|
|
18
25
|
auth: bearer(async (token, { db }) => {
|
|
@@ -24,8 +31,8 @@ export const app = createApp({
|
|
|
24
31
|
|
|
25
32
|
ai: {
|
|
26
33
|
providers: {
|
|
27
|
-
anthropic: anthropic({ apiKey:
|
|
28
|
-
openai: openai({ apiKey:
|
|
34
|
+
anthropic: anthropic({ apiKey: settings.ANTHROPIC_API_KEY }),
|
|
35
|
+
openai: openai({ apiKey: settings.OPENAI_API_KEY }),
|
|
29
36
|
},
|
|
30
37
|
// Aliases keep model ids in one place. A list means: try in order.
|
|
31
38
|
models: {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { config, t } from "@browserlessapi/inferlo";
|
|
2
|
+
|
|
3
|
+
// Every environment variable the app reads, validated once at startup (IL7003 lists what is wrong).
|
|
4
|
+
// Values come from the environment (inferlo dev reads .env), then from `environments`, then defaults.
|
|
5
|
+
// Secrets are redacted when settings are logged or serialised.
|
|
6
|
+
export const settings = config({
|
|
7
|
+
schema: t.object({
|
|
8
|
+
// Leave empty to use the in-memory database (data resets on restart).
|
|
9
|
+
DATABASE_URL: t.string().optional(),
|
|
10
|
+
PORT: t.int().min(1).max(65535).default(3000),
|
|
11
|
+
ANTHROPIC_API_KEY: t.string().default(""),
|
|
12
|
+
OPENAI_API_KEY: t.string().default(""),
|
|
13
|
+
// Set to serve Prometheus metrics at GET /metrics (scrapers send it as a bearer token).
|
|
14
|
+
METRICS_TOKEN: t.string().min(16).optional(),
|
|
15
|
+
}),
|
|
16
|
+
secrets: ["DATABASE_URL", "ANTHROPIC_API_KEY", "OPENAI_API_KEY", "METRICS_TOKEN"],
|
|
17
|
+
});
|