mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-02 02:01:47 +00:00
fix(skills/comfyui): bug fixes, cloud parity, expanded coverage, examples, tests
The audit of v4.1 surfaced ~70 issues across the five scripts and three
reference docs — most user-visible (silent file overwrites, status-error
misclassified as success, X-API-Key leaked to S3 on /api/view redirect,
Cloud endpoints that 404 because they were renamed). v5.0.0 fixes those
and fills the gaps that previously forced users to write their own glue
(WebSocket monitoring, batch/sweep, img2img upload helper, dep auto-fix,
log fetch, health check, example workflows).
Critical fixes
- run_workflow.py: poll_status now checks status_str==error BEFORE
completed:true, so a failed run no longer reports success
- run_workflow.py: download_output streams to disk via safe_path_join,
preserves server subfolder structure (no silent overwrites), and
retries with exponential backoff
- run_workflow.py: refuses to overwrite a link with a literal in
inject_params (would silently break wiring)
- _common.py: _StripSensitiveOnRedirectSession (subclasses
requests.Session.rebuild_auth) drops X-API-Key/Cookie on cross-host
redirects — fixes a real key-leak path through Cloud's signed-URL
download flow. Tested
- Cloud routing (verified live): /history → /history_v2,
/models/<f> → /experiment/models/<f>, plus folder aliases for the
unet ↔ diffusion_models and clip ↔ text_encoders rename
- check_deps.py: distinguishes 200/empty vs 404 folder_not_found vs
403 free-tier; emits concrete fix_command per missing dep
- extract_schema.py: prompt vs negative_prompt determined by tracing
KSampler.{positive,negative} connections (incl. through Reroute /
Primitive nodes) instead of meta-title heuristic; symmetric
duplicate-name resolution; cycle-safe trace_to_node
- hardware_check.py: multi-GPU pick-best, Apple variant detection,
Rosetta detection, WSL2, ROCm --json, disk-space check, optional
PyTorch probe; powershell preferred over deprecated wmic
- comfyui_setup.sh: prefers pipx → uvx → pip --user (with PEP-668
fallback); idempotent — skips relaunch if server already up;
configurable port/workspace; persistent log; SIGINT trap
New scripts
- run_batch.py — count or sweep (cartesian product), parallel up to
cloud tier limit
- ws_monitor.py — real-time WebSocket viewer; saves preview frames
- auto_fix_deps.py — runs comfy node install / model download for
whatever check_deps reports missing (with --dry-run)
- health_check.py — single command that runs the verification checklist
(comfy-cli + server + checkpoints + optional smoke test that cancels
itself to avoid burning compute)
- fetch_logs.py — pull traceback / status messages for a prompt_id
Coverage expansion
- Param patterns now cover Flux (BasicScheduler, BasicGuider,
RandomNoise, ModelSamplingFlux), SD3, Wan/Hunyuan/LTX video,
IPAdapter, rgthree, easy-use, AnimateDiff
- Embedding refs in CLIPTextEncode strings extracted as model deps
- ckpt_name / vae_name / lora_name / unet_name now controllable so
workflows can be retargeted per run
Examples
- workflows/{sd15,sdxl,flux_dev}_txt2img.json
- workflows/sdxl_{img2img,inpaint}.json
- workflows/upscale_4x.json
- workflows/{animatediff_video,wan_video_t2v}.json + README
Tests
- 117 tests (105 unit + 8 cloud integration + 4 cross-host security)
- Cloud tests auto-skip without COMFY_CLOUD_API_KEY; verified end-to-end
against live cloud API
Backwards compatibility
- All existing CLI flags continue to work; new behavior is opt-in
(--ws, --input-image, --randomize-seed, --flat-output, etc.)
This commit is contained in:
parent
7d48a16f14
commit
a7780fe05f
32 changed files with 6117 additions and 1372 deletions
|
|
@ -1,113 +1,263 @@
|
|||
#!/usr/bin/env bash
|
||||
# ComfyUI Setup — Install, launch, and verify using the official comfy-cli.
|
||||
# Usage: bash scripts/comfyui_setup.sh [--nvidia|--amd|--m-series|--cpu]
|
||||
#
|
||||
# If no flag is passed, runs hardware_check.py to detect the right one
|
||||
# automatically, and refuses to install locally when the verdict is "cloud"
|
||||
# (no usable GPU, too little VRAM, Intel Mac, etc.) — pointing the user
|
||||
# at Comfy Cloud instead.
|
||||
# Improvements over v1:
|
||||
# - Prefers `pipx` / `uvx` over global `pip install` (avoids polluting system Python)
|
||||
# - Idempotent: detects already-running server and skips re-launch
|
||||
# - Configurable port via --port=N (default 8188)
|
||||
# - Configurable workspace via --workspace=PATH
|
||||
# - Persistent log file in /tmp/comfyui_setup.<pid>.log for debugging
|
||||
# - SIGINT trap cleans up partial state
|
||||
# - Refuses local install when hardware_check.py verdict is "cloud"
|
||||
# - Forwards extra flags to comfy-cli (e.g. --cuda-version=12.4)
|
||||
#
|
||||
# Prerequisites: Python 3.10+, pip
|
||||
# What it does:
|
||||
# 0. Hardware check (skipped if a flag was passed explicitly)
|
||||
# 1. Installs comfy-cli (if not present)
|
||||
# 2. Disables analytics tracking
|
||||
# 3. Installs ComfyUI + ComfyUI-Manager
|
||||
# 4. Launches server in background
|
||||
# 5. Verifies server is reachable
|
||||
# Usage:
|
||||
# bash scripts/comfyui_setup.sh
|
||||
# (auto-detects GPU; uses recommendation from hardware_check.py)
|
||||
# bash scripts/comfyui_setup.sh --nvidia
|
||||
# bash scripts/comfyui_setup.sh --m-series --port=8190
|
||||
# bash scripts/comfyui_setup.sh --amd --workspace=/data/comfy
|
||||
#
|
||||
# Flags:
|
||||
# --nvidia | --amd | --m-series | --cpu GPU selection (skips hw check)
|
||||
# --port=N HTTP port (default 8188)
|
||||
# --workspace=PATH ComfyUI install location
|
||||
# --skip-launch Install only, don't start server
|
||||
# --force-cloud-override Install locally even if hw says cloud
|
||||
# -- Pass remaining args to `comfy install`
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
HARDWARE_CHECK="$SCRIPT_DIR/hardware_check.py"
|
||||
LOG_FILE="/tmp/comfyui_setup.$$.log"
|
||||
PORT=8188
|
||||
WORKSPACE=""
|
||||
GPU_FLAG=""
|
||||
SKIP_LAUNCH=0
|
||||
FORCE_CLOUD_OVERRIDE=0
|
||||
EXTRA_INSTALL_ARGS=()
|
||||
|
||||
# Step 0: Hardware check (auto-detect GPU flag when none was provided)
|
||||
if [ $# -ge 1 ]; then
|
||||
GPU_FLAG="$1"
|
||||
echo "==> GPU flag: $GPU_FLAG (user-supplied, skipping hardware check)"
|
||||
else
|
||||
cleanup() {
|
||||
local exit_code=$?
|
||||
if [ $exit_code -ne 0 ]; then
|
||||
echo "==> Setup exited with status $exit_code. Log: $LOG_FILE" >&2
|
||||
fi
|
||||
exit $exit_code
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
log() { echo "==> $*" | tee -a "$LOG_FILE" >&2; }
|
||||
err() { echo "ERROR: $*" | tee -a "$LOG_FILE" >&2; }
|
||||
|
||||
# --- Argument parsing ---
|
||||
PASSTHROUGH=0
|
||||
for arg in "$@"; do
|
||||
if [ "$PASSTHROUGH" -eq 1 ]; then
|
||||
EXTRA_INSTALL_ARGS+=("$arg")
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
--nvidia|--amd|--m-series|--cpu)
|
||||
GPU_FLAG="$arg"
|
||||
;;
|
||||
--port=*)
|
||||
PORT="${arg#*=}"
|
||||
;;
|
||||
--workspace=*)
|
||||
WORKSPACE="${arg#*=}"
|
||||
;;
|
||||
--skip-launch)
|
||||
SKIP_LAUNCH=1
|
||||
;;
|
||||
--force-cloud-override)
|
||||
FORCE_CLOUD_OVERRIDE=1
|
||||
;;
|
||||
--)
|
||||
PASSTHROUGH=1
|
||||
;;
|
||||
--help|-h)
|
||||
# Print the leading comment block, stripping the `# ` prefix.
|
||||
# Stops at the first blank line which separates docs from code.
|
||||
awk '
|
||||
NR == 1 { next } # skip shebang
|
||||
/^[^#]/ { exit } # stop at first non-comment line
|
||||
/^$/ { exit } # ...or first blank line
|
||||
{ sub(/^# ?/, ""); print }
|
||||
' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
err "Unknown argument: $arg"
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log "Logging to $LOG_FILE"
|
||||
|
||||
# --- Step 0: Hardware check (skipped if user gave an explicit GPU flag) ---
|
||||
if [ -z "$GPU_FLAG" ]; then
|
||||
if [ ! -f "$HARDWARE_CHECK" ]; then
|
||||
echo "==> hardware_check.py not found, defaulting to --nvidia"
|
||||
log "hardware_check.py not found — defaulting to --nvidia"
|
||||
GPU_FLAG="--nvidia"
|
||||
else
|
||||
echo "==> Running hardware check..."
|
||||
log "Running hardware check…"
|
||||
set +e
|
||||
HW_JSON="$(python3 "$HARDWARE_CHECK" --json)"
|
||||
HW_JSON="$(python3 "$HARDWARE_CHECK" --json 2>>"$LOG_FILE")"
|
||||
HW_EXIT=$?
|
||||
set -e
|
||||
echo "$HW_JSON"
|
||||
echo ""
|
||||
|
||||
if [ -z "$HW_JSON" ]; then
|
||||
err "hardware_check.py produced no output (exit $HW_EXIT). Pass an explicit flag."
|
||||
exit 1
|
||||
fi
|
||||
echo "$HW_JSON" | tee -a "$LOG_FILE" >&2
|
||||
|
||||
VERDICT="$(echo "$HW_JSON" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("verdict",""))')"
|
||||
FLAG="$(echo "$HW_JSON" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("comfy_cli_flag") or "")')"
|
||||
|
||||
if [ "$VERDICT" = "cloud" ]; then
|
||||
echo ""
|
||||
echo "==> Hardware check: this machine is not suitable for local ComfyUI."
|
||||
echo " Recommended: Comfy Cloud — https://platform.comfy.org"
|
||||
echo ""
|
||||
echo " If you want to override and install anyway, re-run with an"
|
||||
echo " explicit flag: bash $0 --nvidia|--amd|--m-series|--cpu"
|
||||
if [ "$VERDICT" = "cloud" ] && [ "$FORCE_CLOUD_OVERRIDE" -ne 1 ]; then
|
||||
log ""
|
||||
log "Hardware check: this machine is not suitable for local ComfyUI."
|
||||
log "Recommended: Comfy Cloud — https://platform.comfy.org"
|
||||
log ""
|
||||
log "To override and force a local install, re-run with --force-cloud-override"
|
||||
log "or pass an explicit GPU flag (--nvidia|--amd|--m-series|--cpu)."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ "$VERDICT" = "marginal" ]; then
|
||||
log "Hardware check: verdict is MARGINAL."
|
||||
log " SD1.5 should work; SDXL/Flux may be slow or OOM."
|
||||
log " Consider Comfy Cloud for heavier workflows: https://platform.comfy.org"
|
||||
fi
|
||||
|
||||
if [ -z "$FLAG" ]; then
|
||||
echo "==> Hardware check couldn't pick a comfy-cli flag. Defaulting to --nvidia."
|
||||
echo " (For Intel Arc or unsupported hardware, use the manual install path.)"
|
||||
log "hardware_check could not pick a comfy-cli flag. Defaulting to --nvidia."
|
||||
log "(For Intel Arc or unsupported hardware, use the manual install path.)"
|
||||
GPU_FLAG="--nvidia"
|
||||
else
|
||||
GPU_FLAG="$FLAG"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$VERDICT" = "marginal" ]; then
|
||||
echo "==> Hardware check: verdict is MARGINAL."
|
||||
echo " SD1.5 should work; SDXL/Flux may be slow or OOM."
|
||||
echo " Consider Comfy Cloud for heavier workflows: https://platform.comfy.org"
|
||||
echo ""
|
||||
log "GPU flag: $GPU_FLAG"
|
||||
log "Port: $PORT"
|
||||
[ -n "$WORKSPACE" ] && log "Workspace: $WORKSPACE"
|
||||
[ "${#EXTRA_INSTALL_ARGS[@]}" -gt 0 ] && log "Extra install args: ${EXTRA_INSTALL_ARGS[*]}"
|
||||
|
||||
# --- Step 1: Install comfy-cli (prefer pipx / uvx over global pip) ---
|
||||
COMFY_BIN=""
|
||||
if command -v comfy >/dev/null 2>&1; then
|
||||
COMFY_BIN="comfy"
|
||||
log "comfy-cli already on PATH: $(comfy -v 2>/dev/null || echo 'unknown version')"
|
||||
elif command -v uvx >/dev/null 2>&1; then
|
||||
log "Using uvx (no install needed)"
|
||||
COMFY_BIN="uvx --from comfy-cli comfy"
|
||||
elif command -v pipx >/dev/null 2>&1; then
|
||||
log "Installing comfy-cli via pipx…"
|
||||
pipx install comfy-cli >>"$LOG_FILE" 2>&1
|
||||
COMFY_BIN="comfy"
|
||||
# pipx adds shims to ~/.local/bin which may need to be on PATH
|
||||
if ! command -v comfy >/dev/null 2>&1; then
|
||||
if [ -x "$HOME/.local/bin/comfy" ]; then
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
COMFY_BIN="$HOME/.local/bin/comfy"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log "Neither pipx nor uvx found. Falling back to pip install --user…"
|
||||
log " (Recommend installing pipx: https://pipx.pypa.io)"
|
||||
if ! pip install --user comfy-cli >>"$LOG_FILE" 2>&1; then
|
||||
# macOS: PEP 668 externally-managed-environment may block --user
|
||||
log "pip install --user failed. Retrying with --break-system-packages…"
|
||||
pip install --user --break-system-packages comfy-cli >>"$LOG_FILE" 2>&1 || {
|
||||
err "Could not install comfy-cli. Install pipx or uv first."
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
# Resolve the actual `comfy` script — pip --user puts it in:
|
||||
# Linux: ~/.local/bin/comfy
|
||||
# macOS: ~/Library/Python/<ver>/bin/comfy OR ~/.local/bin/comfy
|
||||
COMFY_BIN=""
|
||||
for candidate in "$HOME/.local/bin/comfy" \
|
||||
"$HOME/Library/Python/3.13/bin/comfy" \
|
||||
"$HOME/Library/Python/3.12/bin/comfy" \
|
||||
"$HOME/Library/Python/3.11/bin/comfy" \
|
||||
"$HOME/Library/Python/3.10/bin/comfy"; do
|
||||
if [ -x "$candidate" ]; then
|
||||
COMFY_BIN="$candidate"
|
||||
export PATH="$(dirname "$candidate"):$PATH"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$COMFY_BIN" ]; then
|
||||
if command -v comfy >/dev/null 2>&1; then
|
||||
COMFY_BIN="comfy"
|
||||
else
|
||||
err "Installed comfy-cli but couldn't find the 'comfy' script."
|
||||
err "Add the right Python user-bin directory to PATH and retry."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "==> ComfyUI Setup"
|
||||
echo " GPU flag: $GPU_FLAG"
|
||||
echo ""
|
||||
# --- Step 2: Disable analytics tracking (avoid interactive prompt) ---
|
||||
log "Disabling analytics tracking…"
|
||||
$COMFY_BIN --skip-prompt tracking disable >>"$LOG_FILE" 2>&1 || true
|
||||
|
||||
# Step 1: Install comfy-cli
|
||||
if command -v comfy >/dev/null 2>&1; then
|
||||
echo "==> comfy-cli already installed: $(comfy -v 2>/dev/null || echo 'unknown version')"
|
||||
else
|
||||
echo "==> Installing comfy-cli..."
|
||||
pip install comfy-cli
|
||||
# --- Step 3: Install ComfyUI ---
|
||||
WORKSPACE_ARG=()
|
||||
if [ -n "$WORKSPACE" ]; then
|
||||
WORKSPACE_ARG=(--workspace "$WORKSPACE")
|
||||
fi
|
||||
|
||||
# Step 2: Disable tracking (avoid interactive prompt)
|
||||
echo "==> Disabling analytics tracking..."
|
||||
comfy --skip-prompt tracking disable 2>/dev/null || true
|
||||
|
||||
# Step 3: Install ComfyUI
|
||||
if comfy which 2>/dev/null | grep -q "ComfyUI"; then
|
||||
echo "==> ComfyUI already installed at: $(comfy which 2>/dev/null)"
|
||||
if $COMFY_BIN "${WORKSPACE_ARG[@]}" which 2>/dev/null | grep -q "ComfyUI"; then
|
||||
EXISTING_WS="$($COMFY_BIN "${WORKSPACE_ARG[@]}" which 2>/dev/null || true)"
|
||||
log "ComfyUI already installed at: $EXISTING_WS"
|
||||
else
|
||||
echo "==> Installing ComfyUI ($GPU_FLAG)..."
|
||||
comfy --skip-prompt install $GPU_FLAG
|
||||
log "Installing ComfyUI ($GPU_FLAG)…"
|
||||
if ! $COMFY_BIN "${WORKSPACE_ARG[@]}" --skip-prompt install "$GPU_FLAG" "${EXTRA_INSTALL_ARGS[@]}" >>"$LOG_FILE" 2>&1; then
|
||||
err "Install failed. Tail of log:"
|
||||
tail -20 "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Step 4: Launch in background
|
||||
echo "==> Launching ComfyUI in background..."
|
||||
comfy launch --background 2>/dev/null || {
|
||||
echo "==> Background launch failed. Trying foreground check..."
|
||||
echo " You may need to run: comfy launch"
|
||||
if [ "$SKIP_LAUNCH" -eq 1 ]; then
|
||||
log "Setup complete (--skip-launch). Run \`$COMFY_BIN launch --background -- --port $PORT\` when ready."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Step 4: Detect already-running server ---
|
||||
if curl -fsS "http://127.0.0.1:$PORT/system_stats" >/dev/null 2>&1; then
|
||||
log "Server already running on port $PORT — skipping launch."
|
||||
log "Stop with \`$COMFY_BIN stop\` if you want a fresh start."
|
||||
curl -fsS "http://127.0.0.1:$PORT/system_stats" | python3 -m json.tool 2>/dev/null || true
|
||||
log "Done."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Step 5: Launch ---
|
||||
log "Launching ComfyUI in background on port $PORT…"
|
||||
LAUNCH_EXTRAS=("--" "--port" "$PORT")
|
||||
if ! $COMFY_BIN "${WORKSPACE_ARG[@]}" launch --background "${LAUNCH_EXTRAS[@]}" >>"$LOG_FILE" 2>&1; then
|
||||
err "Background launch failed. Tail of log:"
|
||||
tail -20 "$LOG_FILE" >&2
|
||||
err "Try foreground launch to see real-time errors: $COMFY_BIN launch -- --port $PORT"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
# Step 5: Wait for server to be ready
|
||||
echo "==> Waiting for server..."
|
||||
MAX_WAIT=30
|
||||
# --- Step 6: Wait for server ---
|
||||
log "Waiting for server…"
|
||||
MAX_WAIT=60
|
||||
ELAPSED=0
|
||||
while [ $ELAPSED -lt $MAX_WAIT ]; do
|
||||
if curl -s http://127.0.0.1:8188/system_stats >/dev/null 2>&1; then
|
||||
echo "==> Server is running!"
|
||||
curl -s http://127.0.0.1:8188/system_stats | python3 -m json.tool 2>/dev/null || true
|
||||
if curl -fsS "http://127.0.0.1:$PORT/system_stats" >/dev/null 2>&1; then
|
||||
log "Server is running!"
|
||||
curl -fsS "http://127.0.0.1:$PORT/system_stats" | python3 -m json.tool 2>/dev/null || true
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
|
|
@ -115,17 +265,22 @@ while [ $ELAPSED -lt $MAX_WAIT ]; do
|
|||
done
|
||||
|
||||
if [ $ELAPSED -ge $MAX_WAIT ]; then
|
||||
echo "==> Server did not start within ${MAX_WAIT}s."
|
||||
echo " Check logs with: comfy launch (foreground) to see errors."
|
||||
err "Server did not start within ${MAX_WAIT}s."
|
||||
err "Inspect log: $LOG_FILE"
|
||||
err "Or run foreground: $COMFY_BIN launch -- --port $PORT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "==> Setup complete!"
|
||||
echo " Server: http://127.0.0.1:8188"
|
||||
echo " Web UI: http://127.0.0.1:8188 (open in browser)"
|
||||
echo " Stop: comfy stop"
|
||||
echo ""
|
||||
echo " Next steps:"
|
||||
echo " - Download a model: comfy model download --url <URL> --relative-path models/checkpoints"
|
||||
echo " - Run a workflow: python3 scripts/run_workflow.py --workflow <file.json> --args '{...}'"
|
||||
log ""
|
||||
log "Setup complete!"
|
||||
log " Server: http://127.0.0.1:$PORT"
|
||||
log " Web UI: http://127.0.0.1:$PORT (open in browser)"
|
||||
log " Stop: $COMFY_BIN stop"
|
||||
log " Log: $LOG_FILE (kept until shell closes)"
|
||||
log ""
|
||||
log "Next steps:"
|
||||
log " - Download a model: $COMFY_BIN model download --url <URL> --relative-path models/checkpoints"
|
||||
log " - Run a workflow: python3 $SCRIPT_DIR/run_workflow.py --workflow <file.json> --args '{...}'"
|
||||
|
||||
# Disable trap on success path
|
||||
trap - EXIT
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue