3.6 KiB
3.6 KiB
AGENTS.md
Bun + TypeScript single-file server that exposes an OpenAI-compatible image generation endpoint and serves a small vanilla HTML/JS playground.
Runtime
- Bun, not Node. See
CLAUDE.mdfor the full Bun-vs-Node cheatsheet (preferBun.serve,Bun.file,bun:test,Bun.sql, etc.). Do not adddotenv— Bun loads.envautomatically. - Bun version baseline:
1.3.13(perREADME.md).
Commands
- Install:
bun install - Dev (HMR):
bun run dev→bun --hot ./index.ts - Start:
bun run start→bun ./index.ts - Typecheck: no script defined. Use
bunx tsc --noEmit(tsconfig already setsnoEmit: true, so plainbunx tscworks too). - Tests / lint / formatter: none configured. If adding tests, use
bun test.
The server binds 0.0.0.0 (see index.ts:61), so it is reachable from other
hosts on the network when running locally — be mindful when entering API keys.
Architecture
index.ts— the entire backend. OneBun.serveinstance with:/servesindex.htmlvia Bun's HTML import (import index from "./index.html").POST /api/generateaccepts{ baseURL, apiKey, model, prompt, size, referenceImages? }. It returns{ images: string[] }where each entry is adata:URL (base64).- Two code paths inside the handler:
- No
referenceImages→ uses@ai-sdk/openai-compatible+generateImagefromai. referenceImagespresent → hand-rolledmultipart/form-dataPOST to${baseURL}/images/edits(seegenerateWithReference). The AI SDK does not currently expose image edits for OpenAI-compatible providers, so this path bypasses it on purpose. The edits endpoint is gpt-image series only (see UI hint inindex.html).
- No
index.html— self-contained UI: inline CSS, plain DOM JS, no build step. Text fields (baseURL,apiKey,model,size,prompt) persist inlocalStorageunder theaip:<field>prefix. Reference images are kept in an in-memoryrefImagesarray as base64 data URLs and are not persisted — refreshing the page drops them. There is no React code despitereact/react-dom/@types/react*being inpackage.json— treat those deps as latent. Do not invent a React frontend unless asked.- No router, no DB, no auth. API key is supplied per-request by the browser and never stored server-side.
TypeScript conventions
tsconfig.json is strict with bundler-mode resolution:
strict,noUncheckedIndexedAccess,noImplicitOverride,noFallthroughCasesInSwitchare on — array/object index access isT | undefinedand must be narrowed.verbatimModuleSyntax+moduleDetection: "force"— useimport typefor type-only imports; every file is a module.allowImportingTsExtensionsis on;.tsextensions in imports are fine.jsx: "react-jsx"is set but unused (see frontend note above).
When extending the API
- Add new routes inside the
routesobject inindex.ts; keep the{ POST: async (req) => … }shape used by/api/generate. - Return JSON with
Response.json(...). Validate the request body shape explicitly — the existing handler asserts required fields and returns 400 before calling the model. - The AI SDK image type is loose; the current handler casts to
{ mediaType?: string; base64?: string }. Mirror that pattern rather than trusting field presence. - For anything the AI SDK does not cover (e.g. image edits, masks, variations),
follow
generateWithReference: buildFormDatawithBlobs decoded from the incoming data URLs andfetchthe upstream endpoint directly with the caller'sAuthorization: Bearer <apiKey>.