From 905d92b36af8aaf43dbc4a65109867220276f60e Mon Sep 17 00:00:00 2001 From: Oleg Proskurin Date: Tue, 24 Feb 2026 01:21:27 +0700 Subject: [PATCH] feat: references --- CLAUDE.md | 13 +++++++----- src/scripts/banatie.mjs | 46 +++++++++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f1f1814..6e64073 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -155,13 +155,14 @@ REST API at `https://api.banatie.app` for generating images. Auth via `X-API-Key **POST `/api/v1/generations`** — create generation: - `prompt` (string, required) — image description -- `aspectRatio` — `1:1`, `16:9`, `9:16`, `3:2`, `21:9` (default: `1:1`) -- `referenceImages` (string[]) — image IDs as references +- `aspectRatio` — `1:1`, `16:9`, `9:16`, `3:2`, `4:3`, `3:4`, `21:9` (default: `1:1`) +- `referenceImages` (string[]) — `@alias` names of reference images +- `flowId` (string) — associate with a flow (for flow-scoped alias resolution) - `autoEnhance` (boolean) — prompt enhancement (default: true) **POST `/api/v1/images/upload`** — upload image (for use as reference): -- Multipart form data, field `file` (up to 5MB, JPEG/PNG/WebP) -- Returns image ID in `data.id` +- Multipart form data: `file` (up to 5MB, JPEG/PNG/WebP), `alias` (@name), `flowId` +- Auto-creates a flow, returns `flowId` in response Response returns JSON with `data.outputImage.storageUrl` (CDN URL to download). @@ -169,7 +170,9 @@ Script `src/scripts/banatie.mjs` CLI: ```bash node src/scripts/banatie.mjs --prompt "description" --output path.png [--aspect-ratio 1:1] [--ref file.png]... ``` -- `--ref` accepts local file paths (auto-uploaded) or existing image IDs (passed as-is). Can be repeated. +- `--ref` accepts local file paths or `@alias`. Can be repeated +- Local files are uploaded with auto-generated `@alias` into a shared flow +- The flow's `flowId` is passed to generation for alias resolution Rate limit: 100 requests/hour per API key. diff --git a/src/scripts/banatie.mjs b/src/scripts/banatie.mjs index 345e375..3ac0b6c 100644 --- a/src/scripts/banatie.mjs +++ b/src/scripts/banatie.mjs @@ -16,7 +16,7 @@ try { const API_BASE = 'https://api.banatie.app/api/v1'; const API_KEY = process.env.BANATIE_KEY || ''; -async function uploadImage(filePath) { +async function uploadImage(filePath, { flowId, alias }) { const absolutePath = resolve(filePath); const fileBuffer = readFileSync(absolutePath); const fileName = basename(absolutePath); @@ -27,8 +27,10 @@ async function uploadImage(filePath) { const form = new FormData(); form.append('file', new Blob([fileBuffer], { type: mime }), fileName); + form.append('alias', alias); + if (flowId) form.append('flowId', flowId); - console.log(`Uploading reference: ${filePath}...`); + console.log(`Uploading: ${filePath} as ${alias}${flowId ? ` (flow: ${flowId})` : ''}...`); const response = await fetch(`${API_BASE}/images/upload`, { method: 'POST', @@ -48,22 +50,37 @@ async function uploadImage(filePath) { process.exit(1); } - console.log(`Uploaded: ${result.data.id}`); - return result.data.id; + console.log(`Uploaded: ${alias} (${result.data.id})`); + return result.data; +} + +function makeAlias(filePath, index) { + const name = basename(filePath).replace(/\.[^.]+$/, '').replace(/[^a-z0-9-]/gi, '-').toLowerCase(); + return `@ref-${index + 1}-${name}`; } async function resolveRefs(refs) { if (!refs || refs.length === 0) return undefined; - const ids = []; - for (const ref of refs) { - if (existsSync(ref)) { - ids.push(await uploadImage(ref)); + const aliases = []; + let flowId = null; + + for (let i = 0; i < refs.length; i++) { + const ref = refs[i]; + if (ref.startsWith('@')) { + aliases.push(ref); + } else if (existsSync(ref)) { + const alias = makeAlias(ref, i); + const data = await uploadImage(ref, { flowId, alias }); + if (!flowId) flowId = data.flowId; + aliases.push(alias); } else { - ids.push(ref); + console.error(`Reference not found: ${ref}`); + process.exit(1); } } - return ids; + + return { referenceImages: aliases, flowId }; } export async function generateImage({ prompt, output, aspectRatio = '1:1', refs }) { @@ -72,11 +89,14 @@ export async function generateImage({ prompt, output, aspectRatio = '1:1', refs process.exit(1); } - const referenceImages = await resolveRefs(refs); + const resolved = await resolveRefs(refs); const body = { prompt, aspectRatio }; - if (referenceImages) body.referenceImages = referenceImages; + if (resolved) { + body.referenceImages = resolved.referenceImages; + if (resolved.flowId) body.flowId = resolved.flowId; + } - console.log(`Generating: "${prompt}" (${body.aspectRatio})${referenceImages ? ` with ${referenceImages.length} ref(s)` : ''}...`); + console.log(`Generating: "${prompt}" (${body.aspectRatio})${resolved ? ` with ${resolved.referenceImages.length} ref(s)` : ''}...`); const response = await fetch(`${API_BASE}/generations`, { method: 'POST',