npm package: @koda-sl/baker-cli
Description: AI-agent-first CLI for interacting with Baker
Latest version: 0.5.1

# @koda-sl/baker-cli

AI-agent-first CLI for interacting with Baker. Designed for programmatic use by AI agents with structured JSON output, schema introspection, and input hardening. Supports images and videos.

## Installation

```bash
npm install @koda-sl/baker-cli
# or
pnpm add @koda-sl/baker-cli
```

## Authentication

Set two environment variables:

```bash
export BAKER_API_KEY="bk_your_api_key_here"
export BAKER_API_URL="https://your-baker-instance.convex.site"
```

- `BAKER_API_KEY` must start with `bk_`
- `BAKER_API_URL` is your Convex site URL

## Output Format

All commands return a JSON envelope:

```json
// Success
{ "ok": true, "data": { ... } }

// Error
{ "ok": false, "error": { "code": "NOT_FOUND", "message": "Image not found" } }

// Dry-run
{ "ok": true, "dryRun": true, "operation": "images.delete", "params": { "id": "abc123" } }
```

Use `--output` to change format:

| Format  | Description                        | Best for        |
|---------|------------------------------------|-----------------|
| `json`  | Structured JSON envelope (default) | AI agents       |
| `files` | Tab-separated, one row per result  | Piping / shell  |
| `md`    | Markdown table                     | Human reading   |

## Commands

### `baker status`

Check API key validity and connection health.

```bash
baker status
# { "ok": true, "data": { "authenticated": true, "apiUrl": "https://..." } }
```

### `baker images search <query>`

Semantic search using hybrid BM25 + vector + reranking. Only returns ready images.

```bash
# Basic search
baker images search "hero banner"

# Filter by aspect ratio and tags
baker images search "logo" --aspect-ratio 1:1 --tags logo

# Limit results and set minimum relevance
baker images search "team photo" --limit 5 --min-score 0.3

# Full metadata with markdown output
baker images search "product shot" --full --output md
```

**Flags:**

| Flag             | Description                                                              |
|------------------|--------------------------------------------------------------------------|
| `--limit`        | Max results (default 5)                                                 |
| `--min-score`    | Minimum relevance score, 0-1                                             |
| `--aspect-ratio` | Filter by aspect ratio (1:1, 16:9, 9:16, 2:3, 3:4, 1:2, 2:1, 4:5, 3:2, 4:3) |
| `--tags`         | Comma-separated tag names to filter by                                   |
| `--output`       | Output format: `json` \| `files` \| `md`                                |
| `--fields`       | Comma-separated field names to include                                   |
| `--full`         | Include full metadata                                                    |

### `baker images get <id>`

Get a single image by ID.

```bash
baker images get j571abc123def

# With full metadata
baker images get j571abc123def --full

# Alternative flag syntax
baker images get --image-id j571abc123def
```

**Flags:**

| Flag         | Description                                  |
|--------------|----------------------------------------------|
| `--image-id` | Image ID (alternative to positional arg)     |
| `--output`   | Output format: `json` \| `files` \| `md`    |
| `--fields`   | Comma-separated field names to include       |
| `--full`     | Include full metadata                        |

### `baker images upload <file>`

Upload an image file. Content type is auto-detected from the file extension.

```bash
# Upload with auto-detected content type
baker images upload ./logo.png

# Specify source
baker images upload ./banner.jpg --source uploaded

# Override content type
baker images upload ./image.bin --content-type image/webp

# Preview what would happen without uploading
baker images upload ./logo.png --dry-run
```

**Flags:**

| Flag             | Description                                    |
|------------------|------------------------------------------------|
| `--source`       | Source identifier                               |
| `--content-type` | MIME type (auto-detected from extension)        |
| `--dry-run`      | Preview the operation without executing         |

Supported extensions: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.svg`, `.avif`

### `baker images delete <id>`

Delete an image by ID.

```bash
# Preview deletion
baker images delete j571abc123def --dry-run
# { "ok": true, "dryRun": true, "operation": "images.delete", "params": { "id": "j571abc123def" } }

# Execute deletion
baker images delete j571abc123def
```

**Flags:**

| Flag         | Description                                  |
|--------------|----------------------------------------------|
| `--image-id` | Image ID (alternative to positional arg)     |
| `--dry-run`  | Preview the operation without executing       |

### `baker videos search <query>`

Semantic search videos using hybrid BM25 + vector + reranking.

```bash
# Basic search
baker videos search "product demo"

# Filter by tags and limit results
baker videos search "tutorial" --tags explainer,ugc --limit 5

# Full metadata with markdown output
baker videos search "testimonial" --full --output md
```

**Flags:**

| Flag       | Description                              |
|------------|------------------------------------------|
| `--limit`  | Max results (default 20)                 |
| `--tags`   | Comma-separated tags to filter by        |
| `--output` | Output format: `json` \| `files` \| `md` |
| `--fields` | Comma-separated field names to include   |
| `--full`   | Include full metadata                    |

### `baker videos get <id>`

Get a single video by ID.

```bash
baker videos get j571abc123def

# With full metadata
baker videos get j571abc123def --full

# Alternative flag syntax
baker videos get --video-id j571abc123def
```

### `baker videos upload <file>`

Upload a video file via Mux direct upload. Content type is auto-detected from the file extension.

```bash
# Upload with auto-detected content type
baker videos upload ./demo.mp4

# Override content type
baker videos upload ./video.bin --content-type video/mp4

# Preview what would happen without uploading
baker videos upload ./demo.mp4 --dry-run
```

**Flags:**

| Flag             | Description                                    |
|------------------|------------------------------------------------|
| `--content-type` | MIME type (auto-detected from extension)        |
| `--dry-run`      | Preview the operation without executing        |

Supported extensions: `.mp4`, `.mov`, `.webm`, `.avi`, `.mkv`

### `baker videos delete <id>`

Delete a video by ID.

```bash
# Preview deletion
baker videos delete j571abc123def --dry-run

# Execute deletion
baker videos delete j571abc123def
```

**Flags:**

| Flag        | Description                              |
|-------------|------------------------------------------|
| `--dry-run` | Preview the operation without executing  |

### `baker schema [command]`

Inspect command argument schemas for AI agent introspection.

```bash
# List all available commands
baker schema
# { "ok": true, "data": { "commands": ["images.search", "images.get", ...] } }

# Get schema for a specific command
baker schema images.search
# { "ok": true, "data": { "command": "images.search", "args": { ... } } }
```

## Compact vs Full Output

By default, responses include only essential fields to minimize token usage:

**Images:**

| Mode      | Fields                                                     |
|-----------|------------------------------------------------------------|
| Default   | `_id`, `name`, `imageUrl`, `description`, `tags`           |
| `--full`  | All fields: width, height, aspectRatio, dominantColor, imagePalette, source, createdAt |

**Videos:**

| Mode      | Fields                                    |
|-----------|-------------------------------------------|
| Default   | `_id`, `name`, `thumbnailUrl`, `description`, `tags`, `status` |
| `--full`  | All fields: duration, muxPlaybackId, source, timestamps |

## Error Codes

| Code               | Description                          |
|--------------------|--------------------------------------|
| `UNAUTHORIZED`     | Invalid or missing API key           |
| `NOT_FOUND`        | Resource not found                   |
| `VALIDATION_ERROR` | Invalid input (bad ID, missing args) |
| `RATE_LIMITED`     | Too many requests                    |
| `NETWORK_ERROR`    | Connection failed                    |
| `INTERNAL_ERROR`   | Unexpected server error              |

## Available Image Tags

Use these with `--tags` to filter image search results. Multiple tags can be comma-separated.

| Tag                | Description                                                  |
|--------------------|--------------------------------------------------------------|
| `logo`             | Singular company, product, or organizational logo            |
| `profile-picture`  | Portrait-style image featuring a person's face               |
| `illustration`     | Stylized, drawn, or vector artwork                           |
| `software`         | UI, dashboards, app interfaces, or software captures         |
| `product-shot`     | Focused depiction of a tangible product                      |
| `team-photo`       | Group of people posed together, such as company team imagery |
| `environment`      | Backgrounds or scenes showcasing locations, offices, spaces  |
| `iconography`      | Icons, symbols, or small graphical elements                  |
| `textures`         | Patterns, textures, or abstract visual surfaces              |

## For AI Agents

This CLI is designed for AI agent consumption. Key patterns:

1. **Start with `baker schema`** to discover available commands and their arguments
2. **Use `--dry-run`** on mutations to preview before executing
3. **Use `--fields`** to request only the data you need (context window discipline)
4. **Parse the JSON envelope** — check `ok` field before accessing `data`
5. **Use `--output files`** for compact, pipe-friendly output
6. **Check `baker status`** first to verify connectivity