Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "pixelvault",
"description": "Upload, manage, and serve images via PixelVault — agent-first image hosting for developers and AI coding agents.",
"version": "1.9.0",
"version": "1.10.0",
"author": {
"name": "PixelVault",
"email": "hello@pixelvault.dev"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Agent skill for image hosting: upload an image and get a permanent CDN URL back
| `/pixelvault-setup` | Install CLI and configure API key |
| `/pixelvault-list` | List recently uploaded images |
| `/pixelvault-transform` | Resize, convert, remove backgrounds, add effects or a watermark — via URL params |
| `/pixelvault-generate <prompt>` | Generate an image with an AI model (via OpenRouter) and host it — permanent CDN URL back |
| `/pixelvault-generate <prompt>` | Generate an image from a prompt and host it. Built-in by default (`pixelvault generate`, only your PixelVault key, monthly credits); OpenRouter for other models |
| `/pixelvault-email-images` | Host the local images in an email template (Resend, react-email) and rewrite them to permanent CDN URLs |
| `/pixelvault-desktop` | Get a local screenshot/clipboard image into a remote/cloud agent as a URL (via the PixelVault desktop app) |
| `/pixelvault-account` | Check quota/usage before bulk uploads; list, create, rotate, and revoke API keys; start a plan upgrade or open the billing portal (needs `pixelvault-cli` 0.6.0+, 0.7.0+ for upgrade/billing) |
Expand Down
113 changes: 99 additions & 14 deletions skills/pixelvault-generate/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,115 @@
---
name: pixelvault-generate
description: Generate an image with an AI model (via OpenRouter — e.g. Gemini image / "Nano Banana") and host it on PixelVault, getting a permanent CDN URL back. Use when you need to CREATE an image and then embed it somewhere durable — a blog hero, an OG/social card, a README diagram, a docs illustration — and a temporary model URL or a base64 blob won't do. Prompt in, permanent https://img.pixelvault.dev/... URL out.
description: Generate an image from a text prompt and host it on PixelVault, getting a CDN URL back. By default it uses PixelVault's built-in generation (one command, only your PixelVault key, Cloudflare Workers AI models, monthly credits); for a specific provider model (Gemini / "Nano Banana", etc.) it falls back to OpenRouter. Use when you need to CREATE an image and then embed it somewhere durable - a blog hero, an OG/social card, a README diagram, a docs illustration - and a temporary model URL or a base64 blob won't do. Prompt in, https://img.pixelvault.dev/... URL out.
args:
- name: prompt
description: What image to generate (e.g. "flat vector blog hero, a robot writing at a laptop, soft gradient, 16:9, no text")
description: What image to generate (e.g. "flat vector blog hero, a robot writing at a laptop, soft gradient, no text")
required: true
- name: model
description: Optional OpenRouter image-model slug (default google/gemini-3.1-flash-image)
description: Optional. Built-in path - a PixelVault model id (default flux-1-schnell; sdxl-lightning, sdxl-base, lucid-origin, phoenix-1.0). OpenRouter path - an OpenRouter image-model slug (default google/gemini-3.1-flash-image)
required: false
---

# PixelVault Generate

Generate an image with an AI model, then host it on PixelVault so what you embed
is a **permanent** URL — not a model link that expires in an hour, and not a
base64 blob you have to carry around.

```
OpenRouter (image model) → base64 → PixelVault → https://img.pixelvault.dev/…
```
Generate an image, then host it on PixelVault so what you embed is a **stable
CDN URL** - not a model link that expires in an hour, and not a base64 blob you
have to carry around.

**When it triggers:** "make a hero image for this post and host it", "generate an
OG card", "create a diagram and give me a URL", or any task that needs a *new*
image at a durable link.

## Prerequisites
## Pick a path

| | **Path A: built-in (default)** | **Path B: OpenRouter** |
|---|---|---|
| Needs | Your PixelVault key (secret key, verified email) | `OPENROUTER_API_KEY` + your PixelVault key |
| One command | `pixelvault generate "<prompt>"` | several steps (generate, decode, upload) |
| Models | FLUX.1 schnell (default), SDXL, Leonardo (open Cloudflare Workers AI models) | Any OpenRouter image model, e.g. Gemini / "Nano Banana" |
| Cost | Plan credits: free 10 a month, Starter 300, Pro 1,500 | Your OpenRouter spend |
| Good at | Illustrations, backgrounds, abstract art, hero images | Whatever the chosen model is best at, e.g. text in images |

**Use Path A unless** the user names a provider or model that only Path B has, or
needs something Path A is weak at (accurate text rendered inside the image, photoreal
faces), or Path A is out of credits and the user has an OpenRouter key. If you are
unsure, try Path A first: it is one command and a failed call is refunded.

## Path A: built-in generation (default)

Needs `pixelvault-cli` 0.10.0 or newer, configured with a **secret** key whose
account has a verified email.

1. **Preflight** - fail early, before spending a credit:

```bash
command -v pixelvault >/dev/null || npm install -g pixelvault-cli@latest
pixelvault generate --help >/dev/null 2>&1 \
|| { echo "pixelvault-cli is older than 0.10.0 - run: npm install -g pixelvault-cli@latest"; exit 1; }
pixelvault whoami --json >/dev/null 2>&1 \
|| { echo "PixelVault not authenticated - run /pixelvault-setup"; exit 1; }
pixelvault usage # the "AI images" line shows credits left this month
```

2. **Generate.** stdout is only the hosted URL; the credits used and left go to
stderr, so capture stdout:

```bash
URL="$(pixelvault generate "flat vector blog hero, a robot writing at a laptop, soft gradient, no text")"
echo "$URL"
# -> https://img.pixelvault.dev/proj_abc/img_xyz.jpg
# stderr: Generated with flux-1-schnell: 1 credit used, 9 left this month.
```

Options: `--model <id>`, `--folder <name>`, `--optimized` (print the WebP/AVIF
URL, usually far smaller, best for pages and emails), `--delete-after 1h`
(auto-delete the image), `--private` (signed link, with `--expires 12h`), `--json`
(full response).

3. **Report** the URL. Need an OG card or a thumbnail? Add transform params to the
same URL, no re-upload: `...img_xyz.jpg?w=1200&h=630&fit=cover&fmt=auto` (social card),
`...img_xyz.jpg?w=700&fmt=auto&q=auto` (inline). See `/pixelvault-transform`.

### Models and credits

| Model | Credits per image | Plans |
|---|---|---|
| `flux-1-schnell` (default) | 1 | All, including Free |
| `sdxl-lightning`, `sdxl-base` (beta) | 1 | Starter, Pro |
| `lucid-origin`, `phoenix-1.0` (Leonardo) | 20 | Starter, Pro |

Credits reset on the 1st of each month (UTC). A failed generation is refunded,
deleting an image does not give its credit back, and a generated image also counts
toward your normal upload and storage limits.

### Errors (Path A)

| Error | Action |
|-------|--------|
| `command not found: pixelvault` / unknown command `generate` | `npm install -g pixelvault-cli@latest` (needs 0.10.0+) |
| `generation_credits_exhausted` (402) | Out of credits until the 1st. **Do not retry.** Tell the user, offer an upgrade, or switch to Path B |
| `model_requires_paid_plan` (402) | The free plan can only use `flux-1-schnell`; drop `--model` or upgrade |
| `email_verification_required` (403) | The account must verify its email first (`/pixelvault-setup`) |
| `generation_forbidden` (403) | A publishable key was used; generation needs a **secret** key |
| `prompt_rejected` (422) | Prompts that clearly ask for adult content are refused; reword |
| `rate_limit_exceeded` (429) | 60 generations per hour per account, or the day's capacity: wait and retry later |
| `generation_failed` / `generation_unavailable` / `generation_timeout` (502/503/504) | The credit is refunded; safe to retry once |
| `401` / not authenticated | Run `/pixelvault-setup` or set `PIXELVAULT_API_KEY` |

The first result is a draft: models do not follow every instruction literally, so
re-run or reword the prompt when it matters. Keep the prompt in version control so
the art is reproducible. Each model has its own license, so check the terms before
using output commercially (Cloudflare's model catalog lists them).

## Path B: OpenRouter (other models)

Use this when you need a provider model Path A doesn't have.

```
OpenRouter (image model) -> base64 -> PixelVault -> https://img.pixelvault.dev/...
```

### Prerequisites

1. **`OPENROUTER_API_KEY`** — for image generation. Get one at
<https://openrouter.ai/keys> and export it in the environment.
Expand All @@ -36,7 +121,7 @@ image at a durable link.
These are **authoring-time** credentials — they belong in your shell/`.env`, never
in a deployed site or client bundle. What ships is only the resulting CDN URL.

## Steps
### Steps

Run these in order. Each step fails loudly so an agent never proceeds with a
missing key or a broken file.
Expand Down Expand Up @@ -104,7 +189,7 @@ missing key or a broken file.
(social card), `…img_xyz.png?w=700&fmt=auto&q=auto` (inline). See
`/pixelvault-transform`.

## Error Handling
### Error handling

| Error | Action |
|-------|--------|
Expand All @@ -118,7 +203,7 @@ missing key or a broken file.
| PixelVault `413 Payload Too Large` | Image exceeds the plan limit (5 MB free) — downscale, or use a paid plan |
| PixelVault `415 Unsupported Media Type` | Decoded file isn't a supported image — the generation step likely failed |

## Notes
### Notes

- **Prompts are non-deterministic.** The same prompt yields a different image each
run. Keep the prompt in version control (frontmatter, a manifest, a comment) so
Expand Down