npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@melius-ai/cli

v0.7.0

Published

The Melius command-line interface — create canvases and run AI image/video generations from your terminal

Readme

Mel CLI

The programmatic interface to the Melius platform. Built for AI agents running in sandboxed environments — every command returns structured JSON, uses deterministic exit codes, and requires zero interactive input.

Quick start

npm install -g @melius-ai/cli
# or with Homebrew (macOS/Linux): brew install melius-ai/tap/mel

# Authenticate (see "Get an API key" below to obtain a key)
mel auth login --api-key mel_<your-key>

# Build a canvas
mel project list
mel canvas create <projectId> --title "My canvas"
mel node create <canvasId> --type image --prompt "A sunset over the ocean"
mel run start <nodeId> --canvas-id <canvasId>
mel run wait <runId>

Get an API key

  1. Sign up at app.melius.com/signup if you don't have a Melius account yet.
  2. In the Melius app, go to Team settings → Integrations and create an API key (it starts with mel_ and is shown once — copy it immediately).
  3. Authenticate the CLI with it: mel auth login --api-key mel_<your-key>.

Upgrading

Check your installed version with mel --version. Upgrade with the same tool you installed with:

# npm
npm install -g @melius-ai/cli@latest

# Homebrew (macOS/Linux)
brew update && brew upgrade mel

To see the latest published version, run npm view @melius-ai/cli version (or visit the package page on npm). Releases follow semantic versioning — a major version bump (for example 1.x2.x) can include breaking changes, so review what changed before upgrading across one.

Sandbox setup

In the Claude Code sandbox, only two environment variables are needed:

export MEL_API_KEY=mel_...
export MEL_BASE_URL=https://api.melius.com

The API key's team is auto-injected

Commands

Authentication

| Command | Description | | ----------------------------------- | ----------------------------------------- | | mel auth login --api-key <key> | Store and validate an API key | | mel auth logout | Clear stored credentials | | mel auth whoami | Show current user (id, name, email) | | mel auth create-key --name <n> | Create a new API key (raw key shown once) | | mel auth list-keys | List keys for the current team | | mel auth revoke-key --key-id <id> | Revoke an API key |

Projects

| Command | Description | | -------------------------------- | --------------------------------- | | mel project list | List projects in the current team | | mel project get <id> | Get a project by ID | | mel project create --title <t> | Create a new project |

Canvases

| Command | Description | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | mel canvas create <projectId> | Create a canvas (returns id for all subsequent commands) | | mel canvas list <projectId> | List canvases in a project | | mel canvas get <canvasId> | Get canvas metadata | | mel canvas content <canvasId> | Primary read — returns live CRDT state; add --node-id and --include-all-versions to inspect one node plus its incident edges | | mel canvas plan-layout <canvasId> | Compute positions for planned nodes (read-only, no writes) | | mel canvas delete <canvasId> | Delete a canvas and all its content |

Nodes

All node mutations write directly to the CRDT document. Changes are synced to connected browser clients in real-time.

| Command | Description | | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | mel node create <canvasId> --type <t> | Add a node. Types: text, image, video, file, group | | mel node bulk-create <canvasId> --json - | Create multiple nodes + edges in one call | | mel node update <canvasId> <nodeId> | Update node fields (prompt, title, geometry, model) | | mel node delete <canvasId> <nodeId> | Remove a node and its orphaned edges | | mel node get <canvasId> <nodeId> | Legacy persisted-node read; prefer mel canvas content <canvasId> --node-id <nodeId> --include-all-versions for live inspection | | mel node set-version <nodeId> --version-id <id> | Switch the active output version | | mel node group <canvasId> --node-ids <ids> | Group existing nodes (computes bounding box, reparents) | | mel node ungroup <canvasId> <groupId> | Ungroup — reparents children to the parent canvas/group and deletes the group |

Node create options:

--type <type>           REQUIRED. text, image, video, file, group
--id <uuid>             Auto-generated if omitted
--title <title>         Defaults to type name ("Text", "Image", etc.)
--prompt <text>         Text prompt (maps to textPrompt)
--model <m>             Model name (requires --variant)
--variant <v>           Model variant (requires --model)
--aspect-ratio <r>      21:9, 16:9, 4:3, 3:2, 1:1, 2:3, 3:4, 9:16, 9:21
--duration <n>          Output duration in seconds (video only)
--x, --y, --w, --h     Geometry (defaults per type: text 380x200, image 520x293)
--z-index <n>           Stacking order
--group-id <uuid>       Parent group

Node update: same options, all optional. Geometry must be updated as a complete set (--x --y --w --h together).

Edges

| Command | Description | | ------------------------------------------------------------- | ---------------------------------------------------------- | | mel edge create <canvasId> --src <id> --dst <id> --type <t> | Connect two nodes. Types: text, image, video, file | | mel edge bulk-create <canvasId> --json - | Create multiple edges at once (preferred for workflows) | | mel edge delete <canvasId> <edgeId> | Remove an edge |

Edge type determines data flow: text passes text output, image passes image output. ID is auto-generated if --id is omitted.

Runs (single node)

| Command | Description | | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | mel run start <nodeId> --canvas-id <canvasId> | Start a generation. Returns { id } | | mel run get <runId> | Check run status: pending, running, finished, failed | | mel run wait <runId> | Block until done. Exit 0 = finished, 1 = failed, 4 = timeout | | mel run latest <canvasId> | Latest run status for every node on a canvas | | mel run download <runId> | Presigned ZIP URL for a finished run's output | | mel run magic-resize <sourceNodeId> --canvas-id <canvasId> --ratios <list> | Resize an image into multiple aspect ratios in one call. Returns { nodeId, nodeRunId, creditCost } |

Magic resize: mel run magic-resize creates a magic-resize node, wires the source image in, and runs it — all in one call. The source node must already have a finished image asset. --ratios is comma-separated (1–5): presets 21:9, 16:9, 4:3, 1:1, 3:4, 9:16, 9:21 render at 1K, or pass custom WxH pixel sizes (each side 64–4096, e.g. 1080x1350). Pass the returned nodeId to mel run wait to block until the variants are ready.

Auto-model resolution: when --model/--variant are omitted, mel run start automatically resolves the correct model from the platform defaults. --canvas-id is required so the CLI can verify the node is on the canvas and the backend can do edge-aware detection (picks image-to-image when the node has incoming image edges). Always source the node ID from the latest mel canvas content output or the current-turn idMap returned by mel node bulk-create.

# Auto-resolve (recommended)
mel run start <nodeId> --canvas-id <canvasId>

# Explicit model
mel run start <nodeId> --canvas-id <canvasId> --model flux-pro --variant text-to-image

# With overrides
mel run start <nodeId> --canvas-id <canvasId> --prompt "Override prompt" --variations 3

Bulk runs (entire canvas)

| Command | Description | | ----------------------------------- | -------------------------------------------------------------------- | | mel bulk-run start <canvasId> | Run all nodes in dependency order. Returns BulkNodeRunResponse | | mel bulk-run get <bulkRunId> | Check bulk run status and per-node progress | | mel bulk-run wait <bulkRunId> | Block until done. Exit 0 = finished, 1 = failed, 4 = timeout | | mel bulk-run download <bulkRunId> | Download ZIP of leaf node outputs. Returns { url, filename, size } |

Nodes are executed tier-by-tier: downstream nodes wait for their parents to finish. If a parent fails, its dependents are skipped but independent branches continue. When you pass --node-ids, use UUIDs from the latest mel canvas content output or the idMap returned by mel node bulk-create.

# Run all nodes and wait for completion (recommended)
mel bulk-run start <canvasId> --wait

# Run specific nodes only
mel bulk-run start <canvasId> --node-ids <id1>,<id2>,<id3>

# Start without blocking, then check later
mel bulk-run start <canvasId>
mel bulk-run get <bulkRunId>
mel bulk-run wait <bulkRunId> --timeout 120

# Get per-node status
mel bulk-run get <bulkRunId> --fields status,completedNodes,failedNodes,skippedNodes

# Download leaf node outputs as ZIP after completion
mel bulk-run download <bulkRunId>

Presets

Presets are pre-built canvas templates with nodes, edges, and groups already configured.

| Command | Description | | ---------------------------------------- | ------------------------------------------------------------------------ | | mel preset list | List available presets with node/edge configurations | | mel preset apply <canvasId> <presetId> | Apply a preset — creates all nodes, edges, and groups with proper layout |

mel preset apply replicates the frontend layout algorithm: single-group presets get column-based layout, multi-group presets use their saved positions centered on --center-x/--center-y.

# Discover presets
mel preset list
mel preset list --fields id,title,icon

# Apply a preset and run all nodes
mel preset apply <canvasId> <presetId>
mel bulk-run start <canvasId> --wait

Models

| Command | Description | | ------------------------------------------------ | ------------------------------------------------------------- | | mel model list --category <text\|image\|video> | Discover available models, modes, and supported aspect ratios | | mel model defaults | Show auto-model defaults for each node type |

Fonts

| Command | Description | | --------------- | -------------------------------------------------------------------- | | mel font list | List the team's uploaded custom fonts for use with better-font-32b |

Use a familyName from mel font list with the better-font-32b image model by setting font: { source: "custom", familyName } in the node's model config. For non-custom typography, use a Google font family with source: "google" instead. Never guess custom font family names.

Comments

| Command | Description | | ------------------------------------------------------------- | ----------------------------------- | | mel comment list <canvasId> | List all comment threads | | mel comment create <canvasId> --body <text> --x <n> --y <n> | Start a thread at a canvas position | | mel comment reply <threadId> --body <text> | Reply to a thread | | mel comment resolve <threadId> | Mark a thread as resolved |

Configuration

| Command | Description | | ------------------------------ | ------------------------------------------------------- | | mel config get [key] | Show config (or a specific key) | | mel config set <key> <value> | Set a config value. Keys: apiKey, baseUrl, teamId | | mel config path | Print config file path (~/.mel/config.json) |

Config priority: MEL_API_KEY / MEL_BASE_URL / MEL_TEAM_ID env vars > config file > defaults.

Upload

| Command | Description | | ---------------------------------------- | ------------------------------------------ | | mel upload <filePath> | Upload a local image to Melius | | mel upload <filePath> --node-id <id> | Upload and associate with an existing node | | mel upload <filePath> --canvas-id <id> | Upload and associate with a canvas |

Supported formats: PNG, JPEG, GIF, WebP, AVIF.

Returns { assetId, s3Bucket, s3Key, url }. Use the returned url as --src when creating a file node, or pass --node-id to link the upload directly to an existing node.

# Upload and create a file node from the result
mel upload ./photo.png
mel upload ./photo.png --node-id <nodeId> --canvas-id <canvasId>

Assets (Files / DAM library)

Requires the Pro or Enterprise plan.

| Command | Description | | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | | mel asset search [query] | Search the team's Files/DAM library (uploads + past generations) by meaning; exact matches rank first. Returns up to 20 matches per page | | mel asset folders | List the library's folders (id, name, path, fileCount) so you can resolve a folder name to an id for --folder-id | | mel asset place <canvasId> <assetIds...> | Place existing library assets onto a canvas by id — references the original asset (no duplicate) |

mel asset search searches by meaning (semantic), with exact filename/title matches ranked first, so query with a filename or a description of the content. It returns { query, total, results: [{ assetId, filename, fileType, previewUrl, ... }] }. Filter with --file-type <image|video|audio|pdf|text> and --limit <1-20>; page deeper with --offset <n> (skip the first N matches — page until the running count reaches total); scope to one canvas with --canvas-id (omit to search the whole team library).

Scope to a folder with --folder-id (from mel asset folders): with a query it filters within that folder and its subfolders; omit the query to bring in the whole folder (every asset in it). mel asset folders returns each folder's root-relative path (e.g. Brand / Logos) to disambiguate same-named folders.

mel asset place creates a file node per asset (writes to CRDT, no re-upload) and returns { placed: [{ assetId, nodeId, mediaType }] }. Wire the returned nodeIds into generations with mel edge create. This is the correct way to reuse an existing library asset — do not re-upload it.

# Find an asset, then bring it onto a canvas and wire it
mel asset search "black jacket shot" --file-type image
mel asset place <canvasId> <assetId1> <assetId2>

# Bring in a whole folder: resolve the folder, then search it with no query
mel asset folders                                   # → [{ id, name, path, fileCount }]
mel asset search --folder-id <folderId>             # every asset in the folder
mel asset search "shark" --folder-id <folderId>     # or filter within it

Shell completion

mel completion <shell> prints a tab-completion script for bash, zsh, or fish. The script is generated from the live command tree, so it never drifts from the actual commands.

# bash — add to ~/.bashrc
echo 'eval "$(mel completion bash)"' >> ~/.bashrc

# zsh — add to ~/.zshrc
echo 'eval "$(mel completion zsh)"' >> ~/.zshrc

# fish
mel completion fish > ~/.config/fish/completions/mel.fish

Agent workflow

A typical agent session:

# 1. Discover the workspace
mel project list
mel canvas list <projectId>

# 2. Read current state
mel canvas content <canvasId>

# 3. Discover models
mel model list --category image
mel model defaults

# 4a. Build a generation pipeline from scratch
TEXT=$(mel node create <canvasId> --type text --prompt "Describe a product photo")
IMAGE=$(mel node create <canvasId> --type image --prompt "Professional product shot" --aspect-ratio 1:1)
mel edge create <canvasId> --src $(echo $TEXT | jq -r .id) --dst $(echo $IMAGE | jq -r .id) --type text

# 4b. Or do it all in one call (nodes + edges via combined JSON)
mel node bulk-create <canvasId> --json - <<'EOF'
{"nodes":[{"id":"brief","type":"text","prompt":"Describe a product photo"},{"id":"hero","type":"image","prompt":"Professional product shot","aspectRatio":"1:1"}],"edges":[{"src":"brief","dst":"hero","type":"text"}]}
EOF

# 4c. Or apply a preset template (creates all nodes, edges, and groups)
mel preset list
mel preset apply <canvasId> <presetId>

# 5a. Run a single node and wait
RUN=$(mel run start $(echo $IMAGE | jq -r .id) --canvas-id <canvasId>)
mel run wait $(echo $RUN | jq -r .id)

# 5b. Or run all nodes at once (dependency-aware)
mel bulk-run start <canvasId> --wait

# 6. Inspect results
mel canvas content <canvasId> --node-id $(echo $IMAGE | jq -r .id) --include-all-versions

# 7. Organize — group related nodes together
mel node group <canvasId> --node-ids <id1>,<id2>,<id3> --label "Hero section"

# 8. Leave feedback
mel comment create <canvasId> --body "Generation complete" --x 0 --y 0

Output contract

All commands follow the same contract:

  • Success: structured JSON on stdout, exit code 0
  • Errors: structured JSON on stderr, deterministic exit code

Exit codes

| Code | Meaning | Agent action | | ---- | ---------------------- | ----------------------------- | | 0 | Success | Parse stdout | | 1 | API error (4xx/5xx) | Read error message | | 2 | Usage error (bad args) | Fix command | | 3 | Auth error (401/403) | Re-authenticate or check team | | 4 | Timeout | Retry or increase --timeout | | 5 | Network error | Check connectivity |

Error shape

{
    "error": {
        "code": "UNAUTHORIZED",
        "message": "Invalid or expired API key",
        "status": 401,
        "suggestion": "Sign up at https://app.melius.com/signup, then create an API key in the Melius app under Team settings → Integrations. Then run: mel auth login --api-key mel_..."
    }
}

Every error includes a machine-readable code, a human-readable message, and often a suggestion with the exact command to fix the issue.

Rate limiting

The API is rate limited per plan tier. On a 429 the CLI prints a Rate limited — waiting Ns… notice to stderr and automatically backs off and retries — honoring the Retry-After header, otherwise exponential backoff — so polling commands like mel run wait ride through a limit instead of failing. stdout stays clean for parsing. After repeated 429s it gives up with a structured RATE_LIMITED error (exit 1).

Global flags

--json             JSON output (default)
--text             Tab-separated plain text
--fields <csv>     Select specific fields: --fields id,title,status
--quiet            Suppress output, exit code only
--verbose          Log request/response metadata to stderr

How it works

The CLI is a thin, stateless wrapper around the Melius REST API. Node and edge mutations write directly to the CRDT document via the backend's direct mutation service — changes sync to connected browser clients in real-time via WebSocket.