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
124 changes: 124 additions & 0 deletions .agents/skills/bunny-cli/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
---
name: bunny-cli
description: Manage bunny.net resources from the command line (databases, DNS, Edge Storage, Edge Scripts, static sites, sandboxes, Stream video libraries and imports, authentication, and raw API requests) and query a Bunny Database from application code with `@bunny.net/database-client`. Use when working with bunny.net (pullzones, DNS zones/records, databases, Edge Storage zones and files, Edge Scripts, Magic Containers, static-site hosting/deploys, cloud sandboxes, Stream video libraries, uploads, collections, captions, and transcription, moving a video library into Bunny Stream from Vimeo, S3, Wistia, Mux, Cloudflare Stream, JW Player, or Brightcove), invoking the `bunny` CLI, or making authenticated API calls to api.bunny.net.
---

# bunny.net CLI Skill

The bunny.net CLI (`bunny`) manages bunny.net resources from the command line. Use `bunny <command> --help` for full flag details on any command.

## Critical: Authentication

Commands require an API key. Authenticate first with `bunny login`, which opens a browser-based auth flow and stores the key in a local profile. Alternatively, set `BUNNYNET_API_KEY` as an environment variable or pass `--api-key` directly.

Config is stored in (first match wins):

- `$XDG_CONFIG_HOME/bunnynet.json`
- `~/.config/bunnynet.json`
- `~/.bunnynet.json`
- `/etc/bunnynet.json`

**When something goes wrong, check auth first**: run a quick `bunny api GET /user` to verify your key works. If using profiles, confirm the right one is active with `--profile`.

## Quick Start

```bash
# authenticate
bunny login

# install this skill for AI coding tools (AGENTS.md + .claude/skills; --global for ~/.claude/skills)
bunny skills install

# make a raw API request
bunny api GET /pullzone
bunny api GET /user

# manage databases
bunny db create
bunny db list
bunny db shell
bunny db migrations apply # run pending migrations/*.sql files
bunny db migrations apply --pattern "*/migration.sql" # opt into a nested ORM layout

# manage Edge Scripts
bunny scripts init
bunny scripts deploy dist/index.js
bunny scripts list

# manage cloud sandboxes (isolated containers with Claude Code inside)
bunny sandbox create my-sandbox -e ANTHROPIC_API_KEY=sk-ant-...
bunny sandbox exec my-sandbox -- bun install
bunny sandbox url add my-sandbox 3000

# manage Edge Storage
bunny storage zones create my-zone --region DE --pull-zone
bunny storage files upload ./photo.png --to images/
bunny storage zones credentials my-zone --connection s3

# manage DNS
bunny dns zones create example.com
bunny dns zones nameservers example.com # is the registrar delegated to bunny yet?
bunny dns records add example.com api A 198.51.100.1
bunny dns records preset google-workspace example.com # apply a preset record set
bunny dns records list example.com

# host a static site
bunny sites create my-site # provision (served at sites-my-site-<suffix>.b-cdn.net)
bunny sites deploy ./dist # deploy a directory and publish it as the live site
bunny sites domains add example.com --wait # custom production domain
bunny sites deployments publish --previous --force # instant rollback

# manage Bunny Stream video
bunny stream libraries create my-library --link --save-env --no-add-videos # link this directory, write .env
bunny stream videos upload ./video.mp4 --title "Launch demo"
bunny stream videos list

# import a video library into Bunny Stream
bunny stream import list --source vimeo # browse folders first
bunny stream import --library 12345 --source vimeo --dry-run # plan first
bunny stream import --library 12345 --source vimeo --force # hand every video to Bunny
bunny stream import status --library 12345 # encoding progress
```

## Decision Tree

Use this to route to the correct reference file:

- **Authenticate or switch profiles** -> `references/auth.md`
- **Database management (create, list, show, link, delete, shell, studio, migrations, regions, tokens)** -> `references/database.md`
- **Querying a database from application code (`@bunny.net/database-client`: connect, prepare, bind, batch, types, errors)** -> `references/database-client.md`
- **DNS (zones, delegation checks, records, presets, BIND import/export, DNSSEC, logging, Scriptable DNS scripts)** -> `references/dns.md`
- **Edge Scripts (init, create, deploy, link, stats, deployments/rollback, env vars, custom domains)** -> `references/scripts.md`
- **Static sites (create, deploy, rollback, custom domains, GitHub Actions)** -> `references/sites.md`
- **Edge Storage (zones, files, connection credentials, S3/FTP/HTTP, custom domains)** -> `references/storage.md`
- **Sandboxes (create, exec, ssh, files list/cp, public URLs, persistent env vars, Claude Code auth)** -> `references/sandbox.md`
- **Bunny Stream (libraries, player/security settings, video upload/fetch/embed/cleanup, captions, collections, encoding, transcription, smart titles/chapters)** -> `references/stream.md`
- **Import a video library into Bunny Stream (Vimeo, S3, Wistia, Mux, Cloudflare Stream, JW Player, Brightcove; browse, dry run, resume, status)** -> `references/stream-import.md`
- **Make raw API requests** -> `references/api.md`
- **CLI doesn't have a command for it** -> use `bunny api` as a fallback (see `references/api.md`)

## Global Flags

Available on every command:

| Flag | Short | Default | Description |
| ----------- | ----- | --------- | --------------------------------------------------------- |
| `--profile` | `-p` | `default` | Configuration profile to use |
| `--verbose` | `-v` | `false` | Enable verbose/debug output |
| `--output` | `-o` | `text` | Output format: `text`, `json`, `table`, `csv`, `markdown` |
| `--api-key` | | | API key (takes priority over profile and env) |

## Environment Variables

| Variable | Description |
| ------------------------ | ----------------------------------------------------------------------------- |
| `BUNNYNET_API_KEY` | API key (overrides profile) |
| `BUNNYNET_API_URL` | API base URL (default: `https://api.bunny.net`) |
| `BUNNYNET_DASHBOARD_URL` | Dashboard URL for browser-based auth flow (default: `https://dash.bunny.net`) |
| `NO_COLOR` | Disable colored output |

## Anti-Patterns

- **Forgetting to authenticate**: Run `bunny login` first. Without it, commands fail with a missing API key error. Use `bunny api GET /user` to verify.
- **Hardcoding API keys in scripts**: Use `BUNNYNET_API_KEY` env var or `--api-key` flag instead of embedding keys. Better yet, use `bunny login` profiles.
- **Relying on prompts in automation**: Prompts require an interactive terminal; piped answers are not supported. In scripts, CI, and agent sessions, pass every value as a flag and add `--force` to skip confirmations. A command that would need a prompt without a terminal does not wait: it fails fast with exit code 1 and names the flag to pass, so treat that as "retry with flags", never as success.
79 changes: 79 additions & 0 deletions .agents/skills/bunny-cli/references/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# API Command

## `bunny api` — Make raw authenticated API requests

**Use `bunny api` when a CLI command doesn't exist for what you need.** Provides direct access to the bunny.net REST API with automatic authentication.

```bash
bunny api GET /user # get current user
bunny api GET /pullzone # list pull zones
bunny api GET /database/v2/databases # list databases
bunny api POST /database/v2/databases --body '{"name":"test","storage_region":"DE","primary_regions":["DE"]}'
bunny api DELETE /dnszone/12345 # delete a resource
```

## Syntax

```
bunny api METHOD PATH [--body BODY]
```

### Positional Arguments

| Argument | Required | Description |
| -------- | -------- | --------------------------------------------------------------- |
| `METHOD` | Yes | HTTP method: `GET`, `POST`, `PUT`, `PATCH`, `DELETE` |
| `PATH` | Yes | API endpoint path (e.g., `/pullzone`, `/database/v2/databases`) |

### Flags

| Flag | Short | Description |
| -------- | ----- | ----------------- |
| `--body` | `-b` | JSON request body |

## Request Body

The body can come from three sources (in priority order):

1. `--body` flag: `bunny api POST /path --body '{"key":"value"}'`
2. STDIN (if not a TTY and method is not GET): `echo '{"key":"value"}' | bunny api POST /path`
3. No body (default for GET requests)

The body must be valid JSON — the command validates it before sending.

## Headers

All requests automatically include:

- `AccessKey: {apiKey}` — from profile or `--api-key` flag
- `User-Agent: bunny-cli/{VERSION}`
- `Accept: application/json`
- `Content-Type: application/json` (when body is provided)

## Output

- Always outputs JSON (pretty-printed with 2-space indent)
- On error: extracts message from response fields (`detail`, `Message`, `title`) or falls back to HTTP status text
- In verbose mode (`-v`): logs the request method, URL, body, and response status

## Examples

```bash
# List all pull zones
bunny api GET /pullzone

# Get a specific pull zone
bunny api GET /pullzone/12345

# Create a DNS zone
bunny api POST /dnszone --body '{"Domain":"example.com"}'

# Pipe body from a file
cat payload.json | bunny api POST /database/v2/databases

# Use with a specific profile
bunny api GET /user -p staging

# Verbose mode to debug requests
bunny api GET /pullzone -v
```
127 changes: 127 additions & 0 deletions .agents/skills/bunny-cli/references/auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Authentication

## `bunny login` — Browser-based login

Opens a browser to authenticate with bunny.net and stores the API key in a local profile.

```bash
bunny login # interactive login, saves to "default" profile
bunny login -p staging # save to "staging" profile
bunny login --force # overwrite existing profile without confirmation
bunny login --api-key KEY # no browser: verify and save the key directly
```

### How it works

1. Starts a local HTTP server on a random port
2. Opens `https://dash.bunny.net/auth/login` in the default browser
3. Waits up to 5 minutes for the auth callback
4. Extracts the API key and saves it to the profile
5. Fetches user details and prints a welcome message

### Flags

| Flag | Default | Description |
| ----------- | ------- | ----------------------------------------------------- |
| `--force` | `false` | Overwrite existing profile without confirmation |
| `--api-key` | - | Skip the browser and save this key after verifying it |

### Notes

- If the browser doesn't open automatically, the URL is printed to the terminal
- Exits with an error if the profile already exists (use `--force` to overwrite)
- Uses `BUNNYNET_DASHBOARD_URL` env var if set (default: `https://dash.bunny.net`)

### Headless and remote machines

The browser flow needs a browser the user can see. `bunny login` detects when
that is impossible (SSH session, CI, container, or a Unix host with no display
server) and does not open one.

- **With a terminal**: it warns why, then offers to take a pasted API key at a masked prompt, or to print the login URL plus the `ssh -L` port forward the callback needs.
- **Without a terminal** (agents, CI, piped stdin): it exits immediately with a hint rather than hanging for 5 minutes on a callback nobody can reach.

When running unattended, do not call bare `bunny login`. Either pass the key:

```bash
bunny login --api-key "$BUNNYNET_API_KEY"
```

or skip login entirely and export `BUNNYNET_API_KEY`, which takes priority over
any stored profile and needs no config file. Keys given to `bunny login` are
checked against `/user` first, so a bad key fails there instead of on the next
command.

With `--output json`, login prints `{"authenticated": true, "profile": "...",
"name": "..."}` on stdout and nothing else, so the result is parseable.

---

## `bunny auth logout` — Remove a profile

Deletes the stored API key for a profile.

```bash
bunny auth logout # remove "default" profile (with confirmation)
bunny auth logout -p staging # remove "staging" profile
bunny auth logout --force # skip confirmation prompt
```

### Flags

| Flag | Default | Description |
| --------- | ------- | ------------------------ |
| `--force` | `false` | Skip confirmation prompt |

### Notes

- Errors if the profile doesn't exist
- Prompts "Are you sure?" before deleting (bypassed with `--force`)

---

## Custom API Endpoint

The CLI defaults to `https://api.bunny.net` but supports custom endpoints for staging or internal environments.

**Via environment variable** (takes priority over profile config):

```bash
BUNNYNET_API_URL=https://api.staging.bunny.net bunny api GET /user
```

**Via profile config** (set `api_url` in `~/.config/bunnynet.json`):

```json
{
"profiles": {
"staging": {
"api_key": "...",
"api_url": "https://api.staging.bunny.net"
}
}
}
```

```bash
bunny --profile staging api GET /user
```

**Precedence**: `BUNNYNET_API_URL` env var always wins over the profile's `api_url` field.

---

## Config Resolution Precedence

Authentication is resolved in this order (first wins):

1. `--api-key` flag
2. `BUNNYNET_API_KEY` environment variable
3. Profile from config file (via `--profile`, default: `default`)
4. Built-in defaults (empty API key — commands will fail)

The API base URL is resolved separately:

1. `BUNNYNET_API_URL` environment variable
2. `api_url` from the active profile
3. Default: `https://api.bunny.net`
Loading
Loading