An agent harness that uses a small Typer CLI to submit ComfyUI API prompts, stream async progress via WebSocket, and download generated outputs.
What it does
- Reads ComfyUI server URL from
config.json - Submits prompt JSON to
POST /prompt - Supports runtime overrides for key fields:
- positive text prompt
- mesh seed
- target face count
- file name prefix
- texture seed
- Streams progress via
GET /ws?clientId=...when a client ID is available - Waits for completion using
GET /history/{prompt_id}(with queue polling fallback) - Auto-downloads
.glboutput viaGET /view
Requirements
- Python +
uv - ComfyUI server reachable from this machine
- A ComfyUI build with required nodes/models installed and running at
server_url, such as:michaelgold/comfy3d, or- another ComfyUI setup that includes qwen-image-2512 and Trellis2
Quick start
cd /Users/mg/.openclaw/workspace/comfy-prompt-cli uv sync uv run comfy-prompt-cli config init --force
Default config.json:
{
"server_url": "http://localhost:8188/"
}Commands
1) Health check
uv run comfy-prompt-cli health
2) Text to image (qwen_image_2512)
uv run comfy-prompt-cli text-to-image \ --prompt "A cinematic portrait of a fox in rain"
3) Image + text to image (qwen_image_edit_2511)
uv run comfy-prompt-cli image-text-to-image \ --image path/to/input.png \ --prompt "Put this character in a futuristic city at sunset"
T-pose safe framing
For strict T-pose characters, keep every fingertip visible with roughly 15–20% empty margin beyond each hand. Create a deterministic 65% centered resize on a white square canvas:
uv run comfy-prompt-cli pad-tpose-image \ --image path/to/tpose.png \ --subject-scale 0.65 \ --output downloads/tpose_padded.png
Or apply the same preprocessing directly before image-to-glb:
uv run comfy-prompt-cli image-to-glb \ --image path/to/tpose.png \ --subject-scale 0.65
image-to-glb defaults to --subject-scale 1.0, preserving existing behavior unless padding is requested.
4) Image to GLB (img_to_trellis2)
uv run comfy-prompt-cli image-to-glb \ --image path/to/input.png \ --mesh-seed 12345 \ --target-face-num 80000 \ --filename-prefix my_mesh \ --texture-seed 67890
5) Rig GLB (rig_glb_mia)
uv run comfy-prompt-cli rig-glb \ --mesh wrestler_multi_trellis.glb \ --glb-name rigged
By default, rig-glb uses MIA to create the humanoid armature, then runs an
isolated Blender post-stage that:
- merges coincident triangle vertices (
1e-6by default), - removes the MIA skin weights,
- applies Blender Armature Deform → With Automatic Weights,
- reimports and validates the resulting GLB.
The canonical worker ships inside the comfy_prompt_cli.workers package, so
notebook and installed-CLI users get the same first-party implementation without
a source checkout or a worker-path override. It runs directly when local Python
provides bpy; the CLI can otherwise execute that same packaged worker in the
configured Comfy3D container.
The expected downloaded filename remains the final auto-skinned GLB. Remesher
also preserves the pre-postprocess MIA artifact as *.mia_raw.glb and writes an
*.autoskin.json report with weld and weighted/unweighted vertex counts. The
final GLB is published first and the report is published last as the commit
marker; output_sha256 must match the final GLB before consumers treat the pair
as complete. Neither public artifact is mutated after publication. The pipeline
fails if Blender assigns no weights or leaves more than 0.5% of the
welded vertices unweighted before the nearest-bone completion pass. Before bone
heat, disconnected components of at most 128 vertices are deleted only when
their combined size is no more than 0.5% of the welded mesh.
Use --no-auto-skin to keep the previous MIA-weight output unchanged. Advanced
controls include --auto-skin-weld-distance, --max-unweighted-fraction,
--auto-skin-worker, and --bpy-container. Atomic no-replace publication is
supported on Linux (renameat2) and Windows; unsupported platforms fail cleanly
with ENOTSUP, where --no-auto-skin remains available.
6) Text to GLB (end-to-end)
uv run comfy-prompt-cli text-to-glb \ --prompt "A stylized wrestler character, full body, neutral pose"
7) Text to Rigged GLB (end-to-end)
uv run comfy-prompt-cli text-to-rigged-glb \ --prompt "A stylized wrestler character, full body, neutral pose"
text-to-rigged-glb uses the same Blender automatic-weight post-stage by
default and supports the same opt-out and tuning flags.
8) Submit prompt JSON
uv run comfy-prompt-cli send path/to/prompt_api.json
9) Submit with overrides
uv run comfy-prompt-cli send path/to/prompt_api.json \ --prompt "A 3d cartoon astronaut in a t-pose" \ --mesh-seed 12345 \ --target-face-num 80000 \ --filename-prefix astronaut \ --texture-seed 67890
10) Wait for completion + download GLB
uv run comfy-prompt-cli wait <prompt_id> --out-dir downloads
If you want live /ws progress for an already-submitted prompt, pass the same client_id used when submitting:
uv run comfy-prompt-cli wait <prompt_id> --client-id <client_id> --out-dir downloads
11) One-shot full pass (submit + wait + download)
uv run comfy-prompt-cli run path/to/prompt_api.json \ --prompt "A 3d cartoon astronaut in a t-pose" \ --mesh-seed 12345 \ --target-face-num 80000 \ --filename-prefix astronaut \ --texture-seed 67890 \ --out-dir downloads
12) Dry run (build payload only)
uv run comfy-prompt-cli send path/to/prompt_api.json --dry-run
Typical workflow
# Text -> image uv run comfy-prompt-cli text-to-image --prompt "A 3d cartoon astronaut in a t-pose" # Image + text -> image uv run comfy-prompt-cli image-text-to-image \ --image path/to/input.png \ --prompt "Make this look like a fashion editorial" # Image -> GLB uv run comfy-prompt-cli image-to-glb \ --image path/to/input.png \ --mesh-seed 12345 \ --target-face-num 80000 \ --filename-prefix astronaut \ --texture-seed 67890
Input format notes
send expects ComfyUI API prompt JSON.
Accepted:
- direct API prompt object (
{"node_id": {...}}), or - wrapper with top-level
promptkey ({"prompt": {...}})
Rejected:
- UI workflow export format with top-level
nodes+links
If you pass workflow export JSON, CLI will show a clear error telling you to export/copy API prompt JSON.
Image-based commands (image-text-to-image, image-to-glb) accept a local image path.
The CLI uploads that image to ComfyUI input storage before submitting the workflow.
Examples included
examples/qwen_image_2512.json
Text-to-image API prompt workflowexamples/qwen_image_edit_2511.json
Image+text editing API prompt workflowexamples/img_to_trellis2.json
Image-to-GLB API prompt workflowexamples/qwen_to_trellis2.json
Text-to-GLB workflow template
Troubleshooting
- Connection error: verify
config.jsonserver_url, host reachability, and ComfyUI port. - Upload error for image commands: verify your image path exists and ComfyUI supports
POST /upload/image. - No GLB found: workflow may not output
.glb; check/history/{prompt_id}outputs. - Large GLB can’t be sent over Telegram: Telegram may reject with
413 Request Entity Too Large; use local path or reduce mesh/texture settings.