feat: add skill
This commit is contained in:
parent
24cec4d4e0
commit
1826c23826
|
|
@ -36,7 +36,8 @@
|
|||
"mcp__chrome-devtools__take_snapshot",
|
||||
"mcp__chrome-devtools__upload_file",
|
||||
"mcp__chrome-devtools__wait_for",
|
||||
"WebFetch(domain:banatie.app)"
|
||||
"WebFetch(domain:banatie.app)",
|
||||
"Bash(npx skills:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
name: gen-image
|
||||
description: Generate images via Banatie API — text-to-image with optional reference images, aspect ratios, and enhancement templates
|
||||
---
|
||||
|
||||
# Image Generation Skill
|
||||
|
||||
Generate images using the Banatie API. Parses user arguments, validates inputs, and runs the generation script.
|
||||
|
||||
## Arguments
|
||||
|
||||
Parse these from the user's message. Use `AskUserQuestion` for any missing required arguments.
|
||||
|
||||
| Argument | Required | Default | Description |
|
||||
|----------|----------|---------|-------------|
|
||||
| **Prompt** | Yes | — | Image description |
|
||||
| **Output path** | Yes | — | Where to save the file (e.g. `assets/icons/star.png`) |
|
||||
| **Aspect ratio** | No | `1:1` | `1:1`, `16:9`, `9:16`, `3:2`, `4:3`, `3:4`, `21:9` |
|
||||
| **Reference images** | No | — | Local file paths or `@alias` names (max 3) |
|
||||
| **Enhancement template** | No | `general` | `general`, `photorealistic`, `illustration`, `minimalist`, `sticker`, `product`, `comic` |
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Parse arguments** from the user's message. Extract prompt, output path, aspect ratio, references, and template inline where provided.
|
||||
|
||||
2. **Fill missing required arguments** using `AskUserQuestion`. Suggest an output path based on context (e.g. `assets/backgrounds/` for backgrounds, `assets/icons/` for icons).
|
||||
|
||||
3. **Validate** that any referenced local files exist before proceeding.
|
||||
|
||||
4. **Read API docs** from `docs/` subfolder when the user needs advanced features (references, flows, aliases). The docs are:
|
||||
- `docs/image-generation.md` — basic generation, aspect ratios, prompt enhancement, templates
|
||||
- `docs/image-generation-advanced.md` — reference images, aliases, flows, regeneration
|
||||
- `docs/images-upload.md` — image upload, alias management
|
||||
|
||||
5. **Run generation**:
|
||||
```bash
|
||||
node .claude/skills/gen-image/banatie-gen.mjs --prompt "<prompt>" --output <path> [--aspect-ratio <ratio>] [--ref <file_or_alias>]...
|
||||
```
|
||||
|
||||
6. **Report results**: output file path, image dimensions, and the full command for reproducibility.
|
||||
|
||||
## Environment
|
||||
|
||||
The script reads `BANATIE_KEY` from `.env` in the project root. Rate limit: 100 requests/hour.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { writeFileSync, mkdirSync, readFileSync, existsSync } from 'fs';
|
||||
import { resolve, dirname, basename } from 'path';
|
||||
|
||||
const envPath = resolve(dirname(new URL(import.meta.url).pathname), '../../.env');
|
||||
const envPath = resolve(process.cwd(), '.env');
|
||||
try {
|
||||
const envContent = readFileSync(envPath, 'utf-8');
|
||||
for (const line of envContent.split('\n')) {
|
||||
|
|
@ -155,6 +155,6 @@ const args = parseArgs(process.argv.slice(2));
|
|||
if (args.prompt && args.output) {
|
||||
generateImage(args);
|
||||
} else if (process.argv.length > 2) {
|
||||
console.error('Usage: node banatie.mjs --prompt "<description>" --output <path> [--aspect-ratio <ratio>] [--ref <file|id>]...');
|
||||
console.error('Usage: node banatie-gen.mjs --prompt "<description>" --output <path> [--aspect-ratio <ratio>] [--ref <file|@alias>]...');
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
@ -0,0 +1,448 @@
|
|||
# Advanced Image Generation
|
||||
|
||||
Advanced generation features: reference images, aliases, flows, and regeneration. For basic generation, see [image-generation.md](image-generation.md).
|
||||
|
||||
All endpoints require Project Key authentication via `X-API-Key` header.
|
||||
|
||||
---
|
||||
|
||||
## Reference Images
|
||||
|
||||
Use existing images as style or content references for generation.
|
||||
|
||||
### Using References
|
||||
|
||||
Add `referenceImages` array to your generation request:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "A product photo with the logo in the corner",
|
||||
"referenceImages": ["@brand-logo", "@product-style"]
|
||||
}
|
||||
```
|
||||
|
||||
References can be:
|
||||
- **Project aliases**: `@logo`, `@brand-style`
|
||||
- **Flow aliases**: `@hero` (with flowId context)
|
||||
- **Technical aliases**: `@last`, `@first`, `@upload`
|
||||
- **Image UUIDs**: `550e8400-e29b-41d4-a716-446655440000`
|
||||
|
||||
### Auto-Detection from Prompt
|
||||
|
||||
Aliases in the prompt are automatically detected and used as references:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "Create a banner using @brand-logo with blue background"
|
||||
}
|
||||
// @brand-logo is auto-detected and added to referenceImages
|
||||
```
|
||||
|
||||
### Reference Limits
|
||||
|
||||
| Constraint | Limit |
|
||||
|------------|-------|
|
||||
| Max references | 3 images |
|
||||
| Max file size | 5MB per image |
|
||||
| Supported formats | PNG, JPEG, WebP |
|
||||
|
||||
### Response with References
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "550e8400-...",
|
||||
"prompt": "Create a banner using @brand-logo",
|
||||
"referencedImages": [
|
||||
{ "imageId": "7c4ccf47-...", "alias": "@brand-logo" }
|
||||
],
|
||||
"referenceImages": [
|
||||
{
|
||||
"id": "7c4ccf47-...",
|
||||
"storageUrl": "http://...",
|
||||
"alias": "@brand-logo"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Alias Assignment
|
||||
|
||||
Assign aliases to generated images for easy referencing.
|
||||
|
||||
### Project-Scoped Alias
|
||||
|
||||
Use `alias` parameter to assign a project-wide alias:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "A hero banner image",
|
||||
"alias": "@hero-banner"
|
||||
}
|
||||
```
|
||||
|
||||
The output image will be accessible via `@hero-banner` anywhere in the project.
|
||||
|
||||
### Flow-Scoped Alias
|
||||
|
||||
Use `flowAlias` parameter to assign a flow-specific alias:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "A hero image variation",
|
||||
"flowId": "550e8400-...",
|
||||
"flowAlias": "@best"
|
||||
}
|
||||
```
|
||||
|
||||
The alias `@best` is only accessible within this flow's context.
|
||||
|
||||
### Alias Format
|
||||
|
||||
| Rule | Description |
|
||||
|------|-------------|
|
||||
| Prefix | Must start with `@` |
|
||||
| Characters | Alphanumeric, underscore, hyphen |
|
||||
| Pattern | `@[a-zA-Z0-9_-]+` |
|
||||
| Max length | 50 characters |
|
||||
| Examples | `@logo`, `@hero-bg`, `@image_1` |
|
||||
|
||||
### Reserved Aliases
|
||||
|
||||
These aliases are computed automatically and cannot be assigned:
|
||||
|
||||
| Alias | Description |
|
||||
|-------|-------------|
|
||||
| `@last` | Most recently generated image in flow |
|
||||
| `@first` | First generated image in flow |
|
||||
| `@upload` | Most recently uploaded image in flow |
|
||||
|
||||
### Override Behavior
|
||||
|
||||
When assigning an alias that already exists:
|
||||
- The **new image gets the alias**
|
||||
- The **old image loses the alias** (alias set to null)
|
||||
- The old image is **not deleted**, just unlinked
|
||||
|
||||
---
|
||||
|
||||
## 3-Tier Alias Resolution
|
||||
|
||||
Aliases are resolved in this order of precedence:
|
||||
|
||||
### 1. Technical Aliases (Highest Priority)
|
||||
|
||||
Computed on-the-fly, require flow context:
|
||||
|
||||
```
|
||||
GET /api/v1/images/@last?flowId=550e8400-...
|
||||
```
|
||||
|
||||
| Alias | Returns |
|
||||
|-------|---------|
|
||||
| `@last` | Last generated image in flow |
|
||||
| `@first` | First generated image in flow |
|
||||
| `@upload` | Last uploaded image in flow |
|
||||
|
||||
### 2. Flow Aliases
|
||||
|
||||
Stored in flow's `aliases` JSONB field:
|
||||
|
||||
```
|
||||
GET /api/v1/images/@hero?flowId=550e8400-...
|
||||
```
|
||||
|
||||
Different flows can have the same alias pointing to different images.
|
||||
|
||||
### 3. Project Aliases (Lowest Priority)
|
||||
|
||||
Stored in image's `alias` column:
|
||||
|
||||
```
|
||||
GET /api/v1/images/@logo
|
||||
```
|
||||
|
||||
Global across the project, unique per project.
|
||||
|
||||
### Resolution Example
|
||||
|
||||
```
|
||||
// Request with flowId
|
||||
GET /api/v1/images/@hero?flowId=abc-123
|
||||
|
||||
// Resolution order:
|
||||
// 1. Is "@hero" a technical alias? No
|
||||
// 2. Does flow abc-123 have "@hero" in aliases? Check flows.aliases JSONB
|
||||
// 3. Does any image have alias = "@hero"? Check images.alias column
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Flow Integration
|
||||
|
||||
Flows organize related generations into chains.
|
||||
|
||||
### Lazy Flow Creation
|
||||
|
||||
When `flowId` is not provided, a pending flow ID is generated:
|
||||
|
||||
```json
|
||||
// Request
|
||||
{
|
||||
"prompt": "A red car"
|
||||
// No flowId
|
||||
}
|
||||
|
||||
// Response
|
||||
{
|
||||
"data": {
|
||||
"id": "gen-123",
|
||||
"flowId": "flow-456" // Auto-generated, flow record not created yet
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The flow record is created when:
|
||||
- A second generation uses the same `flowId`
|
||||
- A `flowAlias` is assigned to any generation in the flow
|
||||
|
||||
### Eager Flow Creation
|
||||
|
||||
When `flowAlias` is provided, the flow is created immediately:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "A hero banner",
|
||||
"flowAlias": "@hero-flow"
|
||||
}
|
||||
```
|
||||
|
||||
### No Flow Association
|
||||
|
||||
To explicitly create without flow association:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "A standalone image",
|
||||
"flowId": null
|
||||
}
|
||||
```
|
||||
|
||||
### flowId Behavior Summary
|
||||
|
||||
| Value | Behavior |
|
||||
|-------|----------|
|
||||
| `undefined` (not provided) | Auto-generate pendingFlowId, lazy creation |
|
||||
| `null` (explicitly null) | No flow association |
|
||||
| `"uuid-string"` | Use provided ID, create flow if doesn't exist |
|
||||
|
||||
---
|
||||
|
||||
## Regeneration
|
||||
|
||||
### Regenerate Generation
|
||||
|
||||
Recreate an image using the exact same parameters:
|
||||
|
||||
```
|
||||
POST /api/v1/generations/:id/regenerate
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- Uses exact same prompt, aspect ratio, references
|
||||
- **Preserves** output image ID and URL
|
||||
- Works regardless of current status
|
||||
- No request body needed
|
||||
|
||||
**Response:** Same as original generation with new image
|
||||
|
||||
### Update and Regenerate
|
||||
|
||||
Use PUT to modify parameters with smart regeneration:
|
||||
|
||||
```
|
||||
PUT /api/v1/generations/:id
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "A blue car instead",
|
||||
"aspectRatio": "1:1"
|
||||
}
|
||||
```
|
||||
|
||||
**Smart Behavior:**
|
||||
|
||||
| Changed Field | Triggers Regeneration |
|
||||
|---------------|----------------------|
|
||||
| `prompt` | Yes |
|
||||
| `aspectRatio` | Yes |
|
||||
| `flowId` | No (metadata only) |
|
||||
| `meta` | No (metadata only) |
|
||||
|
||||
### Flow Regenerate
|
||||
|
||||
Regenerate the most recent generation in a flow:
|
||||
|
||||
```
|
||||
POST /api/v1/flows/:id/regenerate
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- Finds the most recent generation in flow
|
||||
- Regenerates with exact same parameters
|
||||
- Returns error if flow has no generations
|
||||
|
||||
---
|
||||
|
||||
## Flow Management
|
||||
|
||||
### List Flows
|
||||
|
||||
```
|
||||
GET /api/v1/flows
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `limit` | number | `20` | Results per page (max: 100) |
|
||||
| `offset` | number | `0` | Pagination offset |
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "flow-456",
|
||||
"projectId": "project-123",
|
||||
"aliases": { "@hero": "img-789", "@best": "img-abc" },
|
||||
"generationCount": 5,
|
||||
"imageCount": 7,
|
||||
"createdAt": "2025-11-28T10:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"pagination": { "limit": 20, "offset": 0, "total": 3, "hasMore": false }
|
||||
}
|
||||
```
|
||||
|
||||
### Get Flow
|
||||
|
||||
```
|
||||
GET /api/v1/flows/:id
|
||||
```
|
||||
|
||||
Returns flow with computed counts and aliases.
|
||||
|
||||
### List Flow Generations
|
||||
|
||||
```
|
||||
GET /api/v1/flows/:id/generations
|
||||
```
|
||||
|
||||
Returns all generations in the flow, ordered by creation date (newest first).
|
||||
|
||||
### List Flow Images
|
||||
|
||||
```
|
||||
GET /api/v1/flows/:id/images
|
||||
```
|
||||
|
||||
Returns all images in the flow (generated and uploaded).
|
||||
|
||||
### Update Flow Aliases
|
||||
|
||||
```
|
||||
PUT /api/v1/flows/:id/aliases
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"aliases": {
|
||||
"@hero": "image-id-123",
|
||||
"@best": "image-id-456"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior:** Merges with existing aliases (does not replace all).
|
||||
|
||||
### Remove Flow Alias
|
||||
|
||||
```
|
||||
DELETE /api/v1/flows/:id/aliases/:alias
|
||||
```
|
||||
|
||||
Example: `DELETE /api/v1/flows/flow-456/aliases/@hero`
|
||||
|
||||
### Delete Flow
|
||||
|
||||
```
|
||||
DELETE /api/v1/flows/:id
|
||||
```
|
||||
|
||||
**Cascade Behavior:**
|
||||
- Flow record is **hard deleted**
|
||||
- All generations in flow are **hard deleted**
|
||||
- Images **without** project alias: **hard deleted** with MinIO cleanup
|
||||
- Images **with** project alias: **kept**, but `flowId` set to null
|
||||
|
||||
---
|
||||
|
||||
## Full Request Example
|
||||
|
||||
```json
|
||||
// POST /api/v1/generations
|
||||
{
|
||||
"prompt": "A professional product photo using @brand-style and @product-template",
|
||||
"aspectRatio": "1:1",
|
||||
"autoEnhance": true,
|
||||
"enhancementOptions": { "template": "product" },
|
||||
"flowId": "campaign-flow-123",
|
||||
"alias": "@latest-product",
|
||||
"flowAlias": "@hero",
|
||||
"meta": { "campaign": "summer-2025" }
|
||||
}
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. `@brand-style` and `@product-template` resolved and used as references
|
||||
2. Prompt enhanced using "product" template
|
||||
3. Generation created in flow `campaign-flow-123`
|
||||
4. Output image assigned project alias `@latest-product`
|
||||
5. Output image assigned flow alias `@hero` in the flow
|
||||
6. Custom metadata stored
|
||||
|
||||
---
|
||||
|
||||
## Response Fields (Additional)
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `flowId` | string | Associated flow UUID |
|
||||
| `alias` | string | Project-scoped alias (on outputImage) |
|
||||
| `referencedImages` | array | Resolved references: `[{ imageId, alias }]` |
|
||||
| `referenceImages` | array | Full image details of references |
|
||||
|
||||
---
|
||||
|
||||
## Error Codes
|
||||
|
||||
| HTTP Status | Code | Description |
|
||||
|-------------|------|-------------|
|
||||
| 400 | `ALIAS_FORMAT_CHECK` | Alias must start with @ |
|
||||
| 400 | `RESERVED_ALIAS` | Cannot use technical alias |
|
||||
| 404 | `ALIAS_NOT_FOUND` | Referenced alias doesn't exist |
|
||||
| 404 | `FLOW_NOT_FOUND` | Flow does not exist |
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Basic Generation](image-generation.md) - Simple generation
|
||||
- [Image Upload](images-upload.md) - Upload with aliases
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
# Image Generation API
|
||||
|
||||
Basic image generation using AI. For advanced features like references, aliases, and flows, see [image-generation-advanced.md](image-generation-advanced.md).
|
||||
|
||||
All endpoints require Project Key authentication via `X-API-Key` header.
|
||||
|
||||
---
|
||||
|
||||
## Create Generation
|
||||
|
||||
```
|
||||
POST /api/v1/generations
|
||||
```
|
||||
|
||||
Generate an AI image from a text prompt.
|
||||
|
||||
**Request Body:**
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `prompt` | string | Yes | - | Text description of the image to generate |
|
||||
| `aspectRatio` | string | No | `"1:1"` | Image aspect ratio |
|
||||
| `autoEnhance` | boolean | No | `true` | Enable AI prompt enhancement |
|
||||
| `enhancementOptions` | object | No | - | Enhancement configuration |
|
||||
| `enhancementOptions.template` | string | No | `"general"` | Enhancement template |
|
||||
| `meta` | object | No | `{}` | Custom metadata |
|
||||
|
||||
**Example Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "a red sports car on a mountain road",
|
||||
"aspectRatio": "16:9",
|
||||
"autoEnhance": true,
|
||||
"enhancementOptions": {
|
||||
"template": "photorealistic"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** `201 Created`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
|
||||
"prompt": "A photorealistic establishing shot of a sleek red sports car...",
|
||||
"originalPrompt": "a red sports car on a mountain road",
|
||||
"autoEnhance": true,
|
||||
"aspectRatio": "16:9",
|
||||
"status": "pending",
|
||||
"outputImageId": null,
|
||||
"processingTimeMs": null,
|
||||
"createdAt": "2025-11-28T10:00:00.000Z",
|
||||
"updatedAt": "2025-11-28T10:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Aspect Ratios
|
||||
|
||||
Supported aspect ratios for image generation:
|
||||
|
||||
| Aspect Ratio | Use Case |
|
||||
|--------------|----------|
|
||||
| `1:1` | Square images, social media posts, profile pictures |
|
||||
| `16:9` | Landscape, hero banners, video thumbnails |
|
||||
| `9:16` | Portrait, mobile screens, stories |
|
||||
| `3:2` | Photography standard, print |
|
||||
| `21:9` | Ultra-wide banners, cinematic |
|
||||
|
||||
---
|
||||
|
||||
## Prompt Enhancement
|
||||
|
||||
By default, prompts are automatically enhanced by AI to produce better results.
|
||||
|
||||
### How It Works
|
||||
|
||||
When `autoEnhance: true` (default):
|
||||
- Your original prompt is preserved in `originalPrompt`
|
||||
- AI enhances it with style details, lighting, composition
|
||||
- The enhanced version is stored in `prompt` and used for generation
|
||||
|
||||
When `autoEnhance: false`:
|
||||
- Both `prompt` and `originalPrompt` contain your original text
|
||||
- No AI enhancement is applied
|
||||
|
||||
### Enhancement Templates
|
||||
|
||||
> **Roadmap**: Template selection is planned for a future release. Currently all enhanced prompts use the default `general` style regardless of the `template` parameter.
|
||||
|
||||
Use `enhancementOptions.template` to guide the enhancement style:
|
||||
|
||||
| Template | Description | Best For |
|
||||
|----------|-------------|----------|
|
||||
| `general` | Balanced enhancement (default) | Most use cases |
|
||||
| `photorealistic` | Photography terms, lighting, camera details | Realistic photos |
|
||||
| `illustration` | Art style, composition, color palette | Artwork, drawings |
|
||||
| `minimalist` | Clean, simple, essential elements | Logos, icons |
|
||||
| `sticker` | Bold outlines, limited colors, vector style | Stickers, emojis |
|
||||
| `product` | Studio lighting, materials, lifestyle context | E-commerce |
|
||||
| `comic` | Action lines, expressions, panel composition | Comics, manga |
|
||||
|
||||
### Example: With Enhancement
|
||||
|
||||
```json
|
||||
// Request
|
||||
{
|
||||
"prompt": "a cat",
|
||||
"autoEnhance": true,
|
||||
"enhancementOptions": { "template": "photorealistic" }
|
||||
}
|
||||
|
||||
// Response
|
||||
{
|
||||
"prompt": "A photorealistic close-up portrait of a domestic cat with soft fur, captured with an 85mm lens at f/1.8, natural window lighting creating soft shadows, detailed whiskers and expressive eyes, shallow depth of field with creamy bokeh background",
|
||||
"originalPrompt": "a cat",
|
||||
"autoEnhance": true
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Without Enhancement
|
||||
|
||||
```json
|
||||
// Request
|
||||
{
|
||||
"prompt": "a cat sitting on a windowsill",
|
||||
"autoEnhance": false
|
||||
}
|
||||
|
||||
// Response
|
||||
{
|
||||
"prompt": "a cat sitting on a windowsill",
|
||||
"originalPrompt": "a cat sitting on a windowsill",
|
||||
"autoEnhance": false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Generation Status
|
||||
|
||||
Generations go through these status stages:
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| `pending` | Generation created, waiting to start |
|
||||
| `processing` | AI is generating the image |
|
||||
| `success` | Image generated successfully |
|
||||
| `failed` | Generation failed (see `errorMessage`) |
|
||||
|
||||
Poll the generation endpoint to check status:
|
||||
|
||||
```
|
||||
GET /api/v1/generations/:id
|
||||
```
|
||||
|
||||
When `status: "success"`, the `outputImageId` field contains the generated image ID.
|
||||
|
||||
---
|
||||
|
||||
## List Generations
|
||||
|
||||
```
|
||||
GET /api/v1/generations
|
||||
```
|
||||
|
||||
List all generations with optional filters.
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `status` | string | - | Filter by status: `pending`, `processing`, `success`, `failed` |
|
||||
| `limit` | number | `20` | Results per page (max: 100) |
|
||||
| `offset` | number | `0` | Pagination offset |
|
||||
| `includeDeleted` | boolean | `false` | Include soft-deleted records |
|
||||
|
||||
**Example:**
|
||||
|
||||
```
|
||||
GET /api/v1/generations?status=success&limit=10
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"prompt": "A photorealistic establishing shot...",
|
||||
"originalPrompt": "a red sports car",
|
||||
"autoEnhance": true,
|
||||
"aspectRatio": "16:9",
|
||||
"status": "success",
|
||||
"outputImageId": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"processingTimeMs": 8500,
|
||||
"createdAt": "2025-11-28T10:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"limit": 10,
|
||||
"offset": 0,
|
||||
"total": 42,
|
||||
"hasMore": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Get Generation
|
||||
|
||||
```
|
||||
GET /api/v1/generations/:id
|
||||
```
|
||||
|
||||
Get a single generation with full details.
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
|
||||
"prompt": "A photorealistic establishing shot of a sleek red sports car...",
|
||||
"originalPrompt": "a red sports car on a mountain road",
|
||||
"autoEnhance": true,
|
||||
"aspectRatio": "16:9",
|
||||
"status": "success",
|
||||
"outputImageId": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"outputImage": {
|
||||
"id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"storageKey": "default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"storageUrl": "https://cdn.banatie.app/default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"mimeType": "image/png",
|
||||
"width": 1792,
|
||||
"height": 1024,
|
||||
"fileSize": 1909246
|
||||
},
|
||||
"processingTimeMs": 8500,
|
||||
"retryCount": 0,
|
||||
"errorMessage": null,
|
||||
"meta": {},
|
||||
"createdAt": "2025-11-28T10:00:00.000Z",
|
||||
"updatedAt": "2025-11-28T10:00:08.500Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Delete Generation
|
||||
|
||||
```
|
||||
DELETE /api/v1/generations/:id
|
||||
```
|
||||
|
||||
Delete a generation and its output image.
|
||||
|
||||
**Response:** `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Generation deleted"
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- Generation record is hard deleted
|
||||
- Output image is hard deleted (unless it has a project alias)
|
||||
|
||||
---
|
||||
|
||||
## Response Fields
|
||||
|
||||
### Generation Response
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | string | Generation UUID |
|
||||
| `projectId` | string | Project UUID |
|
||||
| `prompt` | string | Prompt used for generation (enhanced if applicable) |
|
||||
| `originalPrompt` | string | Original user input |
|
||||
| `autoEnhance` | boolean | Whether enhancement was applied |
|
||||
| `aspectRatio` | string | Image aspect ratio |
|
||||
| `status` | string | Generation status |
|
||||
| `outputImageId` | string | Output image UUID (when successful) |
|
||||
| `outputImage` | object | Output image details (when successful) |
|
||||
| `processingTimeMs` | number | Generation time in milliseconds |
|
||||
| `retryCount` | number | Number of retry attempts |
|
||||
| `errorMessage` | string | Error details (when failed) |
|
||||
| `meta` | object | Custom metadata |
|
||||
| `createdAt` | string | ISO timestamp |
|
||||
| `updatedAt` | string | ISO timestamp |
|
||||
|
||||
### Output Image
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | string | Image UUID (same as filename in storage) |
|
||||
| `storageKey` | string | Storage path: `{org}/{project}/img/{imageId}` |
|
||||
| `storageUrl` | string | CDN URL: `https://cdn.banatie.app/{org}/{project}/img/{imageId}` |
|
||||
| `mimeType` | string | Image MIME type |
|
||||
| `width` | number | Image width in pixels |
|
||||
| `height` | number | Image height in pixels |
|
||||
| `fileSize` | number | File size in bytes |
|
||||
|
||||
> **Note:** The image filename in storage equals `image.id` (UUID). No file extension is used - Content-Type is stored in object metadata.
|
||||
|
||||
---
|
||||
|
||||
## Error Codes
|
||||
|
||||
| HTTP Status | Code | Description |
|
||||
|-------------|------|-------------|
|
||||
| 400 | `VALIDATION_ERROR` | Invalid parameters |
|
||||
| 401 | `UNAUTHORIZED` | Missing or invalid API key |
|
||||
| 404 | `GENERATION_NOT_FOUND` | Generation does not exist |
|
||||
| 429 | `RATE_LIMIT_EXCEEDED` | Too many requests |
|
||||
| 500 | `GENERATION_FAILED` | AI generation failed |
|
||||
|
||||
---
|
||||
|
||||
## Rate Limits
|
||||
|
||||
- **100 requests per hour** per API key
|
||||
- Rate limit headers included in response:
|
||||
- `X-RateLimit-Limit`: Maximum requests
|
||||
- `X-RateLimit-Remaining`: Remaining requests
|
||||
- `X-RateLimit-Reset`: Seconds until reset
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Advanced Generation](image-generation-advanced.md) - References, aliases, flows
|
||||
- [Image Upload](images-upload.md) - Upload and manage images
|
||||
|
|
@ -0,0 +1,383 @@
|
|||
# Image Upload & Management API
|
||||
|
||||
Upload images and manage your image library. For generation, see [image-generation.md](image-generation.md).
|
||||
|
||||
All endpoints require Project Key authentication via `X-API-Key` header.
|
||||
|
||||
---
|
||||
|
||||
## Upload Image
|
||||
|
||||
```
|
||||
POST /api/v1/images/upload
|
||||
```
|
||||
|
||||
Upload an image file with optional alias and flow association.
|
||||
|
||||
**Content-Type:** `multipart/form-data`
|
||||
|
||||
**Form Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `file` | file | Yes | Image file (PNG, JPEG, WebP) |
|
||||
| `alias` | string | No | Project-scoped alias (e.g., `@logo`) |
|
||||
| `flowId` | string | No | Flow UUID to associate with |
|
||||
| `flowAlias` | string | No | Flow-scoped alias (requires flowId) |
|
||||
| `meta` | string | No | JSON string with custom metadata |
|
||||
|
||||
**File Constraints:**
|
||||
|
||||
| Constraint | Limit |
|
||||
|------------|-------|
|
||||
| Max file size | 5MB |
|
||||
| Supported formats | PNG, JPEG, JPG, WebP |
|
||||
| MIME types | `image/png`, `image/jpeg`, `image/webp` |
|
||||
|
||||
**Example Request (curl):**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/v1/images/upload \
|
||||
-H "X-API-Key: YOUR_PROJECT_KEY" \
|
||||
-F "file=@logo.png" \
|
||||
-F "alias=@brand-logo" \
|
||||
-F 'meta={"tags": ["logo", "brand"]}'
|
||||
```
|
||||
|
||||
**Response:** `201 Created`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
|
||||
"flowId": null,
|
||||
"storageKey": "default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"storageUrl": "https://cdn.banatie.app/default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"mimeType": "image/png",
|
||||
"fileSize": 45678,
|
||||
"width": 512,
|
||||
"height": 512,
|
||||
"source": "uploaded",
|
||||
"alias": "@brand-logo",
|
||||
"focalPoint": null,
|
||||
"meta": { "tags": ["logo", "brand"] },
|
||||
"createdAt": "2025-11-28T10:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** The `id` field equals the filename in storage (UUID). Original filename is preserved in object metadata.
|
||||
|
||||
### flowId Behavior
|
||||
|
||||
| Value | Behavior |
|
||||
|-------|----------|
|
||||
| Not provided | Auto-generate `pendingFlowId`, lazy flow creation |
|
||||
| `null` | No flow association |
|
||||
| `"uuid"` | Associate with specified flow |
|
||||
|
||||
### Upload with Flow
|
||||
|
||||
```bash
|
||||
# Associate with existing flow
|
||||
curl -X POST .../images/upload \
|
||||
-F "file=@reference.png" \
|
||||
-F "flowId=flow-123" \
|
||||
-F "flowAlias=@reference"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## List Images
|
||||
|
||||
```
|
||||
GET /api/v1/images
|
||||
```
|
||||
|
||||
List all images with filtering and pagination.
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `flowId` | string | - | Filter by flow UUID |
|
||||
| `source` | string | - | Filter by source: `generated`, `uploaded` |
|
||||
| `alias` | string | - | Filter by exact alias match |
|
||||
| `limit` | number | `20` | Results per page (max: 100) |
|
||||
| `offset` | number | `0` | Pagination offset |
|
||||
| `includeDeleted` | boolean | `false` | Include soft-deleted records |
|
||||
|
||||
**Example:**
|
||||
|
||||
```
|
||||
GET /api/v1/images?source=uploaded&limit=10
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "7c4ccf47-...",
|
||||
"storageUrl": "http://...",
|
||||
"source": "uploaded",
|
||||
"alias": "@brand-logo",
|
||||
"width": 512,
|
||||
"height": 512,
|
||||
"createdAt": "2025-11-28T10:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"limit": 10,
|
||||
"offset": 0,
|
||||
"total": 25,
|
||||
"hasMore": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Get Image
|
||||
|
||||
```
|
||||
GET /api/v1/images/:id_or_alias
|
||||
```
|
||||
|
||||
Get a single image by UUID or alias.
|
||||
|
||||
**Path Parameter:**
|
||||
- `id_or_alias` - Image UUID or `@alias`
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `flowId` | string | Flow context for alias resolution |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```
|
||||
# By UUID
|
||||
GET /api/v1/images/7c4ccf47-41ce-4718-afbc-8c553b2c631a
|
||||
|
||||
# By project alias
|
||||
GET /api/v1/images/@brand-logo
|
||||
|
||||
# By technical alias (requires flowId)
|
||||
GET /api/v1/images/@last?flowId=flow-123
|
||||
|
||||
# By flow alias
|
||||
GET /api/v1/images/@hero?flowId=flow-123
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
|
||||
"flowId": null,
|
||||
"storageKey": "default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"storageUrl": "https://cdn.banatie.app/default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a",
|
||||
"mimeType": "image/png",
|
||||
"fileSize": 45678,
|
||||
"width": 512,
|
||||
"height": 512,
|
||||
"source": "uploaded",
|
||||
"alias": "@brand-logo",
|
||||
"focalPoint": null,
|
||||
"fileHash": null,
|
||||
"generationId": null,
|
||||
"meta": { "tags": ["logo", "brand"] },
|
||||
"createdAt": "2025-11-28T10:00:00.000Z",
|
||||
"updatedAt": "2025-11-28T10:00:00.000Z",
|
||||
"deletedAt": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Update Image Metadata
|
||||
|
||||
```
|
||||
PUT /api/v1/images/:id_or_alias
|
||||
```
|
||||
|
||||
Update image metadata (focal point, custom metadata).
|
||||
|
||||
**Request Body:**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `focalPoint` | object | Focal point: `{ x: 0.0-1.0, y: 0.0-1.0 }` |
|
||||
| `meta` | object | Custom metadata |
|
||||
|
||||
**Example:**
|
||||
|
||||
```json
|
||||
// PUT /api/v1/images/@brand-logo
|
||||
{
|
||||
"focalPoint": { "x": 0.5, "y": 0.3 },
|
||||
"meta": {
|
||||
"description": "Updated brand logo",
|
||||
"tags": ["logo", "brand", "2025"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** Updated image object.
|
||||
|
||||
> **Note:** Alias assignment has its own dedicated endpoint.
|
||||
|
||||
---
|
||||
|
||||
## Assign Alias
|
||||
|
||||
```
|
||||
PUT /api/v1/images/:id_or_alias/alias
|
||||
```
|
||||
|
||||
Assign or remove a project-scoped alias.
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
// Assign alias
|
||||
{ "alias": "@new-logo" }
|
||||
|
||||
// Remove alias
|
||||
{ "alias": null }
|
||||
```
|
||||
|
||||
**Override Behavior:**
|
||||
- If another image has this alias, it loses the alias
|
||||
- The new image gets the alias
|
||||
- Old image is preserved, just unlinked
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl -X PUT http://localhost:3000/api/v1/images/7c4ccf47-.../alias \
|
||||
-H "X-API-Key: YOUR_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"alias": "@primary-logo"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Delete Image
|
||||
|
||||
```
|
||||
DELETE /api/v1/images/:id_or_alias
|
||||
```
|
||||
|
||||
Permanently delete an image and its storage file.
|
||||
|
||||
**Behavior:**
|
||||
- **Hard delete** - image record permanently removed
|
||||
- Storage file deleted from MinIO
|
||||
- Cascading updates:
|
||||
- Related generations: `outputImageId` set to null
|
||||
- Flow aliases: image removed from flow's aliases
|
||||
- Referenced images: removed from generation's referencedImages
|
||||
|
||||
**Response:** `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Image deleted"
|
||||
}
|
||||
```
|
||||
|
||||
> **Warning:** This cannot be undone. The image file is permanently removed.
|
||||
|
||||
---
|
||||
|
||||
## Image Response Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | string | Image UUID (same as filename in storage) |
|
||||
| `projectId` | string | Project UUID |
|
||||
| `flowId` | string | Associated flow UUID (null if none) |
|
||||
| `storageKey` | string | Storage path: `{org}/{project}/img/{imageId}` |
|
||||
| `storageUrl` | string | CDN URL: `https://cdn.banatie.app/{org}/{project}/img/{imageId}` |
|
||||
| `mimeType` | string | Image MIME type |
|
||||
| `fileSize` | number | File size in bytes |
|
||||
| `width` | number | Image width in pixels |
|
||||
| `height` | number | Image height in pixels |
|
||||
| `source` | string | `"generated"` or `"uploaded"` |
|
||||
| `alias` | string | Project-scoped alias (null if none) |
|
||||
| `focalPoint` | object | `{ x, y }` coordinates (0.0-1.0) |
|
||||
| `fileHash` | string | SHA-256 hash for deduplication |
|
||||
| `generationId` | string | Source generation UUID (if generated) |
|
||||
| `meta` | object | Custom metadata |
|
||||
| `createdAt` | string | ISO timestamp |
|
||||
| `updatedAt` | string | ISO timestamp |
|
||||
| `deletedAt` | string | Soft delete timestamp (null if active) |
|
||||
|
||||
### Accessing Images
|
||||
|
||||
Use `storageUrl` for direct CDN access:
|
||||
|
||||
```html
|
||||
<!-- Direct access via UUID -->
|
||||
<img src="https://cdn.banatie.app/default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a" />
|
||||
|
||||
<!-- Access via alias (goes through API) -->
|
||||
<img src="https://cdn.banatie.app/default/my-project/img/@brand-logo" />
|
||||
```
|
||||
|
||||
> **Note:** UUID URLs are served directly from MinIO (fast, cacheable). Alias URLs require API resolution.
|
||||
|
||||
---
|
||||
|
||||
## Storage Organization
|
||||
|
||||
Images are stored in MinIO with a simplified path structure:
|
||||
|
||||
```
|
||||
bucket/
|
||||
{orgSlug}/
|
||||
{projectSlug}/
|
||||
img/
|
||||
{imageId} # UUID, no file extension
|
||||
{imageId} # Content-Type in object metadata
|
||||
...
|
||||
```
|
||||
|
||||
**Key points:**
|
||||
- **Filename = Image ID (UUID)** - No file extensions
|
||||
- **Content-Type** stored in MinIO object metadata
|
||||
- **Original filename** preserved in metadata for reference
|
||||
- **Single `img/` directory** for all images (generated + uploaded)
|
||||
|
||||
---
|
||||
|
||||
## Error Codes
|
||||
|
||||
| HTTP Status | Code | Description |
|
||||
|-------------|------|-------------|
|
||||
| 400 | `VALIDATION_ERROR` | Invalid parameters |
|
||||
| 400 | `FILE_TOO_LARGE` | File exceeds 5MB limit |
|
||||
| 400 | `UNSUPPORTED_FILE_TYPE` | Not PNG, JPEG, or WebP |
|
||||
| 400 | `ALIAS_FORMAT_CHECK` | Alias must start with @ |
|
||||
| 401 | `UNAUTHORIZED` | Missing or invalid API key |
|
||||
| 404 | `IMAGE_NOT_FOUND` | Image or alias doesn't exist |
|
||||
| 404 | `ALIAS_NOT_FOUND` | Alias doesn't resolve to any image |
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Basic Generation](image-generation.md) - Generate images
|
||||
- [Advanced Generation](image-generation-advanced.md) - References, aliases, flows
|
||||
35
CLAUDE.md
35
CLAUDE.md
|
|
@ -15,12 +15,7 @@ pnpm pdf -- <file> # Convert HTML file to PDF
|
|||
pnpm remove-bg -- <file|dir> # Remove white background from PNG icons
|
||||
```
|
||||
|
||||
Generate images via Banatie API:
|
||||
```bash
|
||||
node src/scripts/banatie.mjs --prompt "forest theme" --output assets/backgrounds/forest.png --aspect-ratio 9:16
|
||||
node src/scripts/banatie.mjs --prompt "golden star" --output assets/icons/star.png
|
||||
node src/scripts/banatie.mjs --prompt "similar gem" --output assets/icons/gem.png --ref assets/icons/sample.png
|
||||
```
|
||||
Generate images via `/gen-image` skill (uses Banatie API, reads `BANATIE_KEY` from `.env`).
|
||||
|
||||
## Directory Structure
|
||||
|
||||
|
|
@ -31,7 +26,6 @@ src/
|
|||
examples/space-worksheet2.html — Finished 3-page example (output reference)
|
||||
scripts/
|
||||
generate-pdf.mjs — HTML → PDF via Puppeteer
|
||||
banatie.mjs — Banatie API client for image generation
|
||||
remove-bg.mjs — Remove white background from PNGs (flood fill)
|
||||
tasks/ — JSON task definition files
|
||||
assets/
|
||||
|
|
@ -149,32 +143,9 @@ When generating HTML worksheets:
|
|||
- **Images in PDF:** Use local file paths (not URLs). Puppeteer resolves `file://` protocol
|
||||
- **Embed images** as base64 data URIs when possible for reliable PDF rendering
|
||||
|
||||
## Banatie API
|
||||
## Image Generation
|
||||
|
||||
REST API at `https://api.banatie.app` for generating images. Auth via `X-API-Key` header (reads `BANATIE_KEY` from `.env`).
|
||||
|
||||
**POST `/api/v1/generations`** — create generation:
|
||||
- `prompt` (string, required) — image description
|
||||
- `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: `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).
|
||||
|
||||
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 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.
|
||||
Use the `/gen-image` skill to generate images via the Banatie API. The skill has its own script and full API documentation in `.claude/skills/gen-image/`. Auth via `BANATIE_KEY` in `.env`. Rate limit: 100 requests/hour.
|
||||
|
||||
## Background Removal
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue