Agent-first CLI for executing ComfyUI workflows on RunPod serverless
Project description
comfy-gen
An agentic CLI for running ComfyUI workflows on RunPod serverless. Designed to be used by AI coding agents (Claude Code, Cursor, Codex, Windsurf, Gemini CLI) as much as by humans. All output is structured JSON. No interactive prompts. Every command has verbose --help so agents can discover capabilities without documentation.
Table of Contents
- Use with Your Favorite AI Agent
- How It Works
- Installation
- Quick Start
- Commands
- Getting Models onto Your Volume
- Configuration
- Storage
- Workflow Format
- Output Format
- Prerequisites
Use with Your Favorite AI Agent
ComfyGen ships with an agent skill file at SKILL.md in the repo root. Drop it into your agent's skill/tool directory and it can submit workflows, download models, check job status, and manage your RunPod infrastructure — all through natural language.
You: "Submit this workflow with seed 42 and download the output"
Agent: runs comfy-gen submit workflow.json --override 7.seed=42, parses JSON, fetches URL
Works with any agent that can run shell commands and parse JSON. The skill file teaches your agent the full API surface, including workflow analysis, parameter overrides, and error handling.
How It Works
comfy-gen submit workflow.json
- Detects local file references in your workflow and uploads them to S3
- Submits the workflow to your RunPod serverless endpoint
- Polls for completion with real-time progress
- Returns a JSON result with output URLs
Workers spin up on demand, execute the workflow, and shut down. You pay only for execution time.
Development & Testing
pip install -e '.[dev]' # installs pytest + pytest-mock
python3 -m pytest tests/
Tests for the serverless worker (serverless-runtime/) use the dispatch_command fixture in tests/conftest.py, which mirrors the worker's command-dispatch path so routing is exercised end-to-end.
Installation
Requires Python 3.11+.
Recommended: pipx (installs system-wide, no venv needed)
brew install pipx # macOS — or: apt install pipx / pip install pipx
pipx ensurepath # adds pipx bin dir to PATH (restart your shell after)
pipx install comfy-gen
Alternative: pip
python -m pip install comfy-gen
Development install
git clone https://github.com/Hearmeman24/ComfyGen.git
cd ComfyGen
python -m pip install -e '.[dev]'
After installation, comfy-gen is available system-wide as a CLI command.
Windows users: Make sure "Add Python to PATH" was checked during Python installation, or
comfy-genwon't be found. If you missed it, add Python'sScriptsdirectory to your PATH manually (e.g.C:\Users\<you>\AppData\Local\Programs\Python\Python3xx\Scripts).
Quick Start
# 1. Run the setup wizard (creates RunPod endpoint + configures storage)
comfy-gen init
# 2. Download models to your network volume
comfy-gen download civitai 456789 --dest loras
comfy-gen download url https://huggingface.co/org/repo/resolve/main/model.safetensors --dest checkpoints
# 3. Submit a workflow
comfy-gen submit workflow.json
# 4. Submit with an input image for a LoadImage node
comfy-gen submit workflow.json --input 193=/path/to/photo.jpg
# 5. Override workflow parameters
comfy-gen submit workflow.json --override 7.seed=42 --override 7.denoise=0.8
# 6. Check job status / cancel
comfy-gen status <job-id>
comfy-gen cancel <job-id>
Commands
comfy-gen submit <workflow.json>
Full pipeline: upload inputs, submit to serverless, poll, return output URLs.
comfy-gen submit workflow.json
comfy-gen submit workflow.json --input 193=/path/to/ref.jpg
comfy-gen submit workflow.json --override 7.seed=42
comfy-gen submit workflow.json --timeout 300
Progress (stderr):
Submitting to RunPod serverless endpoint...
Job submitted: abc-123-def
[3s] IN_QUEUE
[6s] node_check: Checking custom nodes (10%)
[9s] inference: (2/8) Step 1/8 (29%)
[12s] inference: (5/8) Step 4/8 (55%)
[15s] upload: Uploading outputs to S3 (92%)
Completed in 27s (+2s queue). 1 image
Result (stdout):
{
"ok": true,
"output": {
"url": "https://bucket.s3.region.amazonaws.com/comfy-gen/outputs/abc123.png",
"seed": 1027836870258818,
"resolution": {"width": 828, "height": 1248},
"model_hashes": {
"model.safetensors": {"sha256": "240761...", "type": "diffusion_models"},
"lora.safetensors": {"sha256": "2fdc9d...", "type": "loras", "strength": 0.8}
}
},
"job_id": "abc-123-def",
"delay_seconds": 2,
"elapsed_seconds": 27
}
Features:
- Auto-detects
LoadImagenodes referencing local files and uploads them to S3 --input NODE_ID=FILE_PATHfor explicit file mapping (videos, etc.) — repeatable--override NODE_ID.PARAM=VALUEfor parameter overrides — repeatable, auto-coerces numbers- Real-time progress with stage, step count, and percentage
- Output metadata stripped (no embedded workflow data in images)
- Returns model hashes (SHA256), types, and LoRA strengths
comfy-gen status <job-id>
Check the status of a submitted job.
{"job_id": "abc-123", "status": "completed", "ok": true, "output": {"url": "..."}, "elapsed_seconds": 27, "delay_seconds": 2}
comfy-gen cancel <job-id>
Cancel a running or queued job.
comfy-gen download <civitai|url> <target>
Download models to your RunPod network volume via a serverless job. Files land directly on the mounted volume at /runpod-volume/ComfyUI/models/<dest>/.
# Download a LoRA from CivitAI (use model VERSION ID, not model ID)
comfy-gen download civitai 456789 --dest loras
# Download a checkpoint from HuggingFace
comfy-gen download url https://huggingface.co/Comfy-Org/flux1-dev/resolve/main/flux1-dev-fp8.safetensors --dest checkpoints
# Download with a custom filename
comfy-gen download url https://example.com/model.safetensors --dest diffusion_models --filename my_model.safetensors
# Batch download from a JSON file
comfy-gen download --batch models.json
Supported --dest values: checkpoints, loras, vae, clip, diffusion_models, text_encoders, controlnet, upscale_models
Batch file format:
[
{"source": "civitai", "version_id": "456789", "dest": "loras"},
{"source": "url", "url": "https://huggingface.co/.../model.safetensors", "dest": "checkpoints"}
]
Result:
{
"ok": true,
"files": [
{"filename": "model.safetensors", "dest": "loras", "path": "/runpod-volume/ComfyUI/models/loras/model.safetensors", "size_mb": 228.5}
],
"job_id": "abc-123-def",
"elapsed_seconds": 45
}
comfy-gen list [model_type]
List model files installed on the RunPod network volume. Submits a lightweight job to a worker which scans the filesystem.
comfy-gen list loras # List LoRAs (default)
comfy-gen list checkpoints # List checkpoints
comfy-gen list diffusion_models # List diffusion models
Result:
{
"ok": true,
"model_type": "loras",
"files": [
{"filename": "my_lora.safetensors", "path": "/runpod-volume/ComfyUI/models/loras/my_lora.safetensors", "size_mb": 228.5}
],
"search_paths": ["/ComfyUI/models/loras", "/runpod-volume/ComfyUI/models/loras"],
"job_id": "abc-123-def"
}
Getting Models onto Your Volume
There are two ways to populate your RunPod network volume with models:
Option 1: comfy-gen download (individual models)
Download specific models from CivitAI or HuggingFace directly to your volume. Best for adding individual LoRAs, checkpoints, or other files on demand. See the download command above.
Option 2: Pre-populated RunPod Templates (full model sets)
For common workflows, deploy a HearmemanAI RunPod template that comes pre-configured to download entire model sets to your network volume on first boot.
For example, to get all models needed for Wan 2.2 + Wan Animate video generation:
- Deploy the Wan template as a GPU pod attached to your network volume
- Set these environment variables on the pod:
download_wan22=truedownload_wan_animate=true
- Wait for the pod to fully deploy — all Wan 2.2 and Wan Animate models will be downloaded to the volume
- Stop and delete the pod — the models persist on your network volume
- Your serverless endpoint (which mounts the same volume) can now run Wan workflows
This is the fastest way to get started with a specific workflow type. You only need to deploy the template once per model set.
comfy-gen config
Manage persistent configuration stored at ~/.comfy-gen/config.json.
comfy-gen config # Show all config
comfy-gen config --set runpod_api_key=rpa_abc123 # Set a value
comfy-gen config --get endpoint_id # Get a single value
Configuration
Config is read from multiple sources with this priority order:
config.json > .env file > environment variables > defaults
| Key | Description | Env Var | Default |
|---|---|---|---|
runpod_api_key |
RunPod API key | RUNPOD_API_KEY |
— |
endpoint_id |
RunPod serverless endpoint ID | RUNPOD_ENDPOINT_ID |
— |
aws_access_key_id |
S3 access key | AWS_ACCESS_KEY_ID |
— |
aws_secret_access_key |
S3 secret key | AWS_SECRET_ACCESS_KEY |
— |
s3_bucket |
S3 bucket name | S3_BUCKET |
— |
s3_region |
S3 region | S3_REGION |
eu-west-2 |
s3_endpoint_url |
Custom S3 endpoint (for R2/B2/MinIO) | S3_ENDPOINT_URL |
— |
civitai_token |
CivitAI API token for model downloads | CIVITAI_TOKEN |
— |
timeout_seconds |
Max wait for completion | COMFY_GEN_TIMEOUT |
600 |
poll_interval_seconds |
Status poll interval | COMFY_GEN_POLL_INTERVAL |
3 |
You can also put these in a .env file in your project directory.
Storage
ComfyGen requires S3-compatible storage for transferring input files (images, videos) to workers and receiving output files. The setup wizard (comfy-gen init) configures storage and verifies it works before creating your endpoint.
| Service | Config |
|---|---|
| AWS S3 | Set aws_access_key_id, aws_secret_access_key, s3_bucket, s3_region |
| Cloudflare R2 | Same as above + s3_endpoint_url=https://<account-id>.r2.cloudflarestorage.com, s3_region=auto |
| Backblaze B2 | Same as above + s3_endpoint_url=https://s3.<region>.backblazeb2.com |
| MinIO | Same as above + s3_endpoint_url=http://your-minio:9000 |
| DigitalOcean Spaces | Same as above + s3_endpoint_url=https://<region>.digitaloceanspaces.com |
Uploads are content-addressed (MD5 hash key) — identical files are never re-uploaded.
Workflow Format
Workflows must be in ComfyUI API format — the node-ID-keyed JSON with class_type and inputs fields. Export from ComfyUI via Save (API Format) or enable Dev Mode first.
{
"7": {
"inputs": {"seed": 42, "steps": 20, "cfg": 7.0, "model": ["10", 0]},
"class_type": "KSampler"
},
"10": {
"inputs": {"ckpt_name": "model.safetensors"},
"class_type": "CheckpointLoaderSimple"
}
}
Output Format
All commands output JSON to stdout. Human-readable progress goes to stderr. This makes comfy-gen composable with jq, shell scripts, and AI agents.
# Extract just the output URL
comfy-gen submit workflow.json 2>/dev/null | jq -r '.output.url'
# Save output URL to a variable
URL=$(comfy-gen submit workflow.json 2>/dev/null | jq -r '.output.url')
Success exits with code 0:
{"ok": true, "output": {"url": "...", "seed": 42, "resolution": {"width": 1024, "height": 1024}}, "job_id": "...", "elapsed_seconds": 30}
Error exits with code 1:
{"status": "error", "error": "No RunPod API key configured. Set via: comfy-gen config --set runpod_api_key=rpa_..."}
Prerequisites
You need:
- A RunPod account —
comfy-gen initcreates the serverless endpoint for you - S3-compatible storage — AWS S3, Cloudflare R2, Backblaze B2, or any S3-compatible service (see Storage)
- Python 3.11+
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file comfy_gen-0.2.0.tar.gz.
File metadata
- Download URL: comfy_gen-0.2.0.tar.gz
- Upload date:
- Size: 78.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91f886a4604f8785df5d6d0dc6cb63114094a0fcc52a5d7463570701c85bbf82
|
|
| MD5 |
d55a6c24fd0caad30188b20c7df6134d
|
|
| BLAKE2b-256 |
3d9bb45b7d0f613db939f9bd1a34eaec75adb6d69bde8e25581bcd0e2295370b
|
Provenance
The following attestation bundles were made for comfy_gen-0.2.0.tar.gz:
Publisher:
publish-pypi.yml on Hearmeman24/ComfyGen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
comfy_gen-0.2.0.tar.gz -
Subject digest:
91f886a4604f8785df5d6d0dc6cb63114094a0fcc52a5d7463570701c85bbf82 - Sigstore transparency entry: 1674506128
- Sigstore integration time:
-
Permalink:
Hearmeman24/ComfyGen@7bf63fdbd193fc548f9bd730162d0c3f4f8d91c5 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Hearmeman24
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@7bf63fdbd193fc548f9bd730162d0c3f4f8d91c5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file comfy_gen-0.2.0-py3-none-any.whl.
File metadata
- Download URL: comfy_gen-0.2.0-py3-none-any.whl
- Upload date:
- Size: 53.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
686c9f6b440efe890fec5a5a65be0d82ef4878eb8a7f035c1d9f6fd98bcba874
|
|
| MD5 |
45c3c3cad7f673723e0c8a2f7c6b0067
|
|
| BLAKE2b-256 |
4d817336b50b9ed1cf179a22a849e3b63d11e36f6595ca683ab1752555f673a5
|
Provenance
The following attestation bundles were made for comfy_gen-0.2.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on Hearmeman24/ComfyGen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
comfy_gen-0.2.0-py3-none-any.whl -
Subject digest:
686c9f6b440efe890fec5a5a65be0d82ef4878eb8a7f035c1d9f6fd98bcba874 - Sigstore transparency entry: 1674506135
- Sigstore integration time:
-
Permalink:
Hearmeman24/ComfyGen@7bf63fdbd193fc548f9bd730162d0c3f4f8d91c5 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Hearmeman24
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@7bf63fdbd193fc548f9bd730162d0c3f4f8d91c5 -
Trigger Event:
push
-
Statement type: