Skip to content

byteowlz/cmfy

Repository files navigation

cmfy — A Flexible ComfyUI CLI

cmfy is a fast, flexible command‑line tool to run ComfyUI workflows. It loads JSON workflows from your filesystem, applies template variables and targeted overrides, submits them to a running ComfyUI server, and saves the generated outputs locally.

Features

  • Simple commands: run, batch run (JSONL mixed workflows), workflows (list/show/inspect/assign/ssh-list/ssh-import), queue, job (status/wait/cancel), server ping, config (init/path), version.
  • Works with local workflow JSONs; supports both raw prompt maps and { "prompt": { ... } } wrappers.
  • Configurable workflows_dir and output_dir in a TOML config stored under $XDG_CONFIG_HOME/cmfy/config.toml.
  • Rich templating via ${KEY} placeholders across string inputs.
  • Precise overrides with --set <nodeID>.inputs.<name>=<value> (coerces ints/floats/bools; quoted strings preserved).
  • Asset uploads for images/masks/inputs; exposes ${IMAGE}, ${MASK}, ${INPUT} and enumerated variants.
  • First‑class sampler/refiner flags (--sampler, --scheduler, --steps, --cfg, --denoise, etc.).
  • Music prompt convenience flags: --tags and --lyrics map to ${TAGS} and ${LYRICS}.
  • Standard workflow aliases (e.g., txt2img, img2img, …) via [standard_workflows] and path mappings via [standard_workflows_params.<alias>].

Requirements

  • Go 1.21+ to build.
  • A running ComfyUI server (HTTP API) reachable at server_url (default http://127.0.0.1:8188).

Install / Build

# From repository root
go build -o cmfy ./cmd/cmfy
./cmfy version

Or use just:

just help
just build
just test
just check

Configuration

Config is stored at $XDG_CONFIG_HOME/cmfy/config.toml. If XDG_CONFIG_HOME is empty, falls back to ~/.config/cmfy/config.toml.

By default, output_dir is ./outputs (relative to the current working directory where you run cmfy).

Initialize a default config:

./cmfy config init
./cmfy config path  # prints the path

Config keys:

"$schema" = "https://raw.githubusercontent.com/byteowlz/schemas/refs/heads/main/cmfy/cmfy.config.schema.json"

server_url = "http://127.0.0.1:8188"
output_dir = "./outputs"
workflows_dir = "workflows"
default_workflow = ""       # optional fallback when -w is omitted
default_width = 768
default_height = 768
default_steps = 28

[vars]
# Global template vars available to all workflows
# MODEL = "sdxl.safetensors"
# CFG = "4.5"

[workflows.my_custom.vars]
# Per-workflow defaults (workflow name = file name without extension)
# PROMPT = "a cute cat"
# STEPS = "20"

[standard_workflows]
# Map well-known aliases to workflow names or absolute/relative JSON paths.
# If unset but a matching file exists (e.g., <workflows_dir>/txt2img.json), it is used implicitly.
txt2img = ""
img2img = ""
canny2img = ""
depth2img = ""
img2vid = ""
txt2vid = ""
txt2music = ""
txt2img_lora = ""
img2img_inpainting = ""

# Optional: precise paths for first-class flags for a given alias
[standard_workflows_params.txt2img]
# sampler_name = "12.inputs.sampler_name"
# scheduler    = "12.inputs.scheduler"
# steps        = "12.inputs.steps"
# cfg          = "12.inputs.cfg"
# denoise      = "12.inputs.denoise"

[standard_workflows_params.txt2img_lora]
# sampler_name         = "28.inputs.sampler_name"
# refiner.sampler_name = "42.inputs.sampler_name"
# refiner.steps        = "42.inputs.steps"

# Optional: named remote servers for SSH workflow discovery/import
[remote_servers.local_gpu]
ssh_config_host = "local-gpu"
workflows_dir = "~/ComfyUI/user/default/workflows"
# host = "192.168.1.20"
# user = "agent"
# port = 22
# key_path = "~/.ssh/id_ed25519"

Workflows

  • Place JSON workflows under workflows_dir (default ./workflows).
  • Supported formats:
    • Raw prompt map: a JSON object with numeric keys for nodes: { "0": { ... }, "1": { ... }, ... }.
    • Wrapper with prompt key: { "prompt": { "0": { ... } } }.
  • Optional workflow metadata keys:
    • variables: variable defaults/descriptions.
    • prompt_guidelines (or guidelines): authoring hints for prompt/tag writing.

Inspect a workflow to discover node IDs, class types, and inputs:

./cmfy workflows inspect txt2img
./cmfy workflows inspect txt2music --guidelines  # prints optional prompting guidance
./cmfy workflows show txt2img      # prints JSON with prompt map
./cmfy workflows list              # lists available names in workflows_dir

# From SSH-configured remote servers in config.toml
./cmfy workflows ssh-list local_gpu
./cmfy workflows ssh-list local_gpu flux --json
./cmfy workflows ssh-import local_gpu Flux_upscale.json

Running a Workflow

Basic run:

# By name (resolved in workflows_dir)
./cmfy run -w txt2img --prompt "a cozy cabin in the woods" --width 768 --height 768 --steps 30

# By path (absolute or relative)
./cmfy run -w ./workflows/my_flow.json --prompt "a sketch of an owl"

# Async submit (returns immediately; prints prompt ID)
./cmfy run -w txt2img --prompt "a sketch of an owl" --async

# txt2music convenience variables
./cmfy run -w txt2music --tags "lofi chill, warm keys" --lyrics "Late night city lights"

Using standard and custom aliases:

# Assign (CLI writes config.toml [standard_workflows])
./cmfy workflows assign txt2img my_txt2img
./cmfy workflows assign ltx23 ~/cmfy/workflows/image_to_video_ltx2_3_i2v_with_sound.json

# Then run directly by alias
./cmfy txt2img --prompt "sunlit woodland" --steps 28 --cfg 5.5 --sampler euler
./cmfy ltx23 --image input.png --var PROMPT="cinematic close-up" --async

You can also define aliases manually in config:

[standard_workflows]
ltx23 = "~/cmfy/workflows/image_to_video_ltx2_3_i2v_with_sound.json"

Asset uploads:

./cmfy run -w img2img \
  --image input.png \
  --mask mask.png \
  --input controlnet_hint.png

# Exposes variables: ${IMAGE}, ${IMAGE1}, ${MASK}, ${INPUT}, etc.
# Use these variables in your workflow node string inputs.

Targeted overrides:

# Set any input on any node using <nodeID>.inputs.<name>=<value>
./cmfy run -w txt2img --set 12.inputs.steps=25 --set 12.inputs.cfg=5.0
./cmfy run -w img2img --set 8.inputs.denoise=0.6 --set 9.inputs.seed=1337

# Values are coerced when possible: ints, floats, bools; quoted strings stay strings

First‑class sampler/refiner flags:

./cmfy txt2img \
  --sampler euler --scheduler normal \
  --steps 30 --cfg 5.5 --denoise 0.2

./cmfy txt2img_lora \
  --sampler dpmpp_2m --refiner-sampler euler \
  --refiner-steps 15 --refiner-cfg 4.0

How first‑class flags are applied:

  • If [standard_workflows_params.<alias>] mappings exist, flags (e.g., steps, cfg, sampler_name) are written to those exact paths.
  • Otherwise, cmfy uses heuristics:
    • Base flags set the first node that has a matching input name.
    • refiner.* flags target the second occurrence (common in two‑stage flows).
  • Use workflows inspect to find stable node IDs and add precise mappings in config.

Outputs:

  • Saved to output_dir (default ./outputs).
  • Override per run with --output/-o or --output-dir (all workflow commands, including aliases like cmfy txt2img).
  • Filenames are taken from the server’s /view endpoint responses.

Template Variables

  • Any string input in the workflow that contains ${KEY} will be replaced with the value of KEY.
  • Sources (precedence):
    1. Global [vars] in config
    2. [workflows.<name>.vars] in config
    3. Convenience flags (--prompt, --tags, --lyrics, --seed, --width, --height, --steps, --cfg)
    4. --var KEY=VAL on CLI
    5. Uploaded assets populate ${IMAGE}, ${MASK}, ${INPUT} and enumerated variants
  • Numeric inputs generally require --set unless you model them as strings in the workflow and template them.

Optional prompting guidelines in workflow JSON:

{
  "prompt_guidelines": {
    "summary": "Write cinematic, high-contrast prompts.",
    "dos": ["Use strong nouns", "Include mood and pacing"],
    "donts": ["Avoid contradictory styles"],
    "examples": ["epic trailer score, dark tension, rising strings"]
  }
}

Show them with:

./cmfy workflows inspect txt2music --guidelines

Batch Mode (JSONL)

Submit multiple jobs from a JSONL file. Each line can target a different workflow.

# Print example JSONL to stdout
./cmfy batch run example --mode mixed-workflows

# Run batch file
./cmfy batch run --file jobs.jsonl --async

# Throttle submission rate (submission delay only, not Comfy execution concurrency)
./cmfy batch run --file jobs.jsonl --submit-delay 500ms

# Machine-readable result summary
./cmfy batch run --file jobs.jsonl --json

JSONL line schema (core fields):

  • workflow (required): alias, name, or workflow path
  • id (optional): user tracking ID
  • vars (optional): map for ${KEY} substitutions
  • set (optional): map for <nodeID>.inputs.<name>=value
  • image / mask / input (optional): arrays of file paths
  • server (optional): override server URL
  • async (optional): per-line async override
  • timeout (optional): per-line wait timeout (e.g. 30m)

Server Utilities

./cmfy server ping      # checks connectivity to server_url
./cmfy queue            # show running/pending queue
./cmfy queue --json     # machine-readable queue status
./cmfy job status <id>  # check a prompt by ID
./cmfy job wait <id>    # wait for completion
./cmfy job cancel <id>  # try to remove from queue
./cmfy version          # prints CLI version

API Endpoints Used

  • POST /upload — uploads files (form-data) as type input.
  • POST /prompt — submits a prompt graph, returns prompt_id.
  • GET /history/<prompt_id> — tracks prompt execution and outputs.
  • GET /queue — queue state for running/pending prompts.
  • POST /queue — queue manipulation (used for cancel attempts).
  • GET /view?filename=...&subfolder=...&type=... — downloads generated assets.
  • GET /system_stats — used for server ping.

Tips & Troubleshooting

  • If cmfy run reports “no workflow specified”, set default_workflow in config or pass -w.
  • If an alias is not set, cmfy will still run it implicitly if <workflows_dir>/<alias>.json exists.
  • Use workflows inspect to discover inputs; then use --set or add mappings under [standard_workflows_params.<alias>] for stable behavior.
  • The bundled TOML parser supports keys/strings/ints/floats/bools/arrays and simple sections. If you need advanced TOML features, open an issue to switch to a full TOML library.

Roadmap

  • Optional WebSocket progress and live preview support
  • Built‑in templates for popular workflows
  • More first‑class flags (e.g., clip_skip, scheduler params)
  • Richer templating with type hints (e.g., ${WIDTH:int})

Contributing

Issues and PRs are welcome. Please keep changes focused and consistent with the existing style. If you add new flags or config keys, update this README accordingly.