Merge pull request #25 from malkuthro/kflame-public-cleanup-runpod-move-v2

Cleanup v2: remove RunPod management scaffold from public repo
This commit is contained in:
malkuthro
2026-02-15 16:12:34 +01:00
committed by GitHub
32 changed files with 14 additions and 918 deletions
+3 -1
View File
@@ -1,2 +1,4 @@
_safe*
.venv/*
.venv/*
Runpod_Comfy/
PLAN.md
+1 -12
View File
@@ -17,18 +17,7 @@ The format is inspired by Keep a Changelog and SemVer.
- `.github/PULL_REQUEST_TEMPLATE.md`
- `.github/ISSUE_TEMPLATE/release_checklist.md`
- `docs/RELEASE_NOTES_TEMPLATE.md`
- RunPod_Comfy implementation scaffold:
- `Runpod_Comfy/README.md`
- `Runpod_Comfy/setup/USER_README.md`
- `Runpod_Comfy/core/docker/Dockerfile`
- `Runpod_Comfy/core/docker/start.sh`
- `Runpod_Comfy/core/scripts/bootstrap.sh`
- `Runpod_Comfy/core/scripts/install_custom_nodes.sh`
- `Runpod_Comfy/core/scripts/download_models.sh`
- `Runpod_Comfy/image/active/comfyui.lock`
- `Runpod_Comfy/image/active/custom_nodes.lock.json`
- `Runpod_Comfy/image/active/models.json`
- `Runpod_Comfy/image/DEPLOY_RUNPOD.md`
- RunPod_Comfy scaffold was previously added during iteration and has since been moved to a private management repository.
## [0.1.0] - 2026-02-14
-35
View File
@@ -1,35 +0,0 @@
# DECISIONS
## 2026-02-15 — Profile-first RunPod build flow
- We use profile files as inputs and generate lock files as build/runtime truth.
- Profile/recipe inputs:
- `Runpod_Comfy/setup/recipes/<id>/runpod.yaml`
- `Runpod_Comfy/setup/recipes/<id>/nodes.yaml`
- `Runpod_Comfy/setup/recipes/<id>/models.yaml`
- Optional `Runpod_Comfy/setup/recipes/<id>/urls.txt`
- Compiler output:
- `Runpod_Comfy/image/builds/<id>/comfyui.lock`
- `Runpod_Comfy/image/builds/<id>/custom_nodes.lock.json`
- `Runpod_Comfy/image/builds/<id>/models.json`
- Activated to `Runpod_Comfy/image/active/*`
- Docker base image is taken from generated `comfyui.lock` (`BASE_IMAGE` build arg).
- Goal: normal updates (new node repo/model URL/version bumps) should require editing config files only, not Python/Docker logic.
- Expected operator UX:
- Edit 3–4 simple profile files with real values.
- Run compiler.
- Build/push image.
- Deploy and smoke-test on RunPod.
## 2026-02-15 — Work continuity model
- Planning/serious execution in OpenClaw web dashboard.
- Telegram for quick pings.
- Repo files (`TODO/WORKLOG/DECISIONS/WORKFLOW`) are continuity anchor across devices.
## 2026-02-15 — Recipe architecture for long-term readability
- Use a 3-zone layout inside `Runpod_Comfy/`:
- `setup/` (operator-facing edits/tools; keep lightweight)
- `image/` (generated outputs + active locks + deploy runbook)
- `core/` (runtime internals + technical references)
- Keep one recipe folder per image variant under `setup/recipes/<id>/`.
- Compiler supports explicit args (`--id`, `--out`, `--image-tag`, `--no-activate`).
- Default outputs go to `image/builds/<recipe-id>/` and can be activated to `image/active/`.
+3 -3
View File
@@ -24,10 +24,10 @@ The code for the Easy Resize node is inspired by the Resize Image V2 node from C
## RunPod
A dedicated RunPod setup scaffold is available in `Runpod_Comfy/`.
RunPod management has been moved to a separate private repository for active operations and iteration.
- Entry guide: `Runpod_Comfy/setup/USER_README.md`
- Deploy notes: `Runpod_Comfy/image/DEPLOY_RUNPOD.md`
- Runtime/deployment scaffolding is no longer maintained in this public repo.
- This public repo remains focused on the ComfyUI nodes/plugin code.
## Release & Stability
-10
View File
@@ -1,10 +0,0 @@
# Runpod_Comfy
This module is organized in three top-level folders:
- `setup/` → operator-facing inputs and helper tools
- `image/` → generated build artifacts, active lock set, and deploy runbook
- `core/` → runtime internals (docker/scripts) and technical references
Start here:
- `Runpod_Comfy/setup/USER_README.md`
-32
View File
@@ -1,32 +0,0 @@
ARG BASE_IMAGE=runpod/pytorch:2.6.0-py3.11-cuda12.4.1-devel-ubuntu22.04
ARG ENABLE_JUPYTER=true
ARG ENABLE_FILE_TOOLS=true
FROM ${BASE_IMAGE}
ENV DEBIAN_FRONTEND=noninteractive \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PYTHONUNBUFFERED=1 \
COMFYUI_PORT=8188 \
JUPYTER_PORT=8888 \
COMFYUI_WORKDIR=/workspace/ComfyUI \
RUNPOD_VOLUME_ROOT=/workspace
WORKDIR /opt/bootstrap
ARG ENABLE_JUPYTER
ARG ENABLE_FILE_TOOLS
RUN apt-get update && apt-get install -y --no-install-recommends \
git curl ca-certificates jq \
&& if [ "$ENABLE_FILE_TOOLS" = "true" ]; then apt-get install -y --no-install-recommends zip unzip p7zip-full rsync; fi \
&& rm -rf /var/lib/apt/lists/*
RUN if [ "$ENABLE_JUPYTER" = "true" ]; then python3 -m pip install --no-cache-dir jupyterlab; fi
COPY Runpod_Comfy /opt/bootstrap/Runpod_Comfy
RUN chmod +x /opt/bootstrap/Runpod_Comfy/core/scripts/*.sh /opt/bootstrap/Runpod_Comfy/core/docker/start.sh
EXPOSE 8188 8888
ENTRYPOINT ["/opt/bootstrap/Runpod_Comfy/core/docker/start.sh"]
-48
View File
@@ -1,48 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT=/opt/bootstrap/Runpod_Comfy
WORKDIR="${COMFYUI_WORKDIR:-/workspace/ComfyUI}"
PORT="${COMFYUI_PORT:-8188}"
JUPYTER_PORT="${JUPYTER_PORT:-8888}"
JUPYTER_TOKEN="${JUPYTER_TOKEN:-}"
"$ROOT/core/scripts/bootstrap.sh"
LOCK="$ROOT/image/active/comfyui.lock"
ENABLE_JUPYTER_LOCK="true"
if [[ -f "$LOCK" ]]; then
ENABLE_JUPYTER_LOCK=$(grep -E '^ENABLE_JUPYTER=' "$LOCK" | head -n1 | cut -d= -f2-)
[[ -z "$ENABLE_JUPYTER_LOCK" ]] && ENABLE_JUPYTER_LOCK="true"
fi
if [[ "$ENABLE_JUPYTER_LOCK" == "true" ]]; then
if ! command -v jupyter >/dev/null 2>&1; then
echo "[jupyter] WARNING: ENABLE_JUPYTER=true but jupyter binary not found (likely built with ENABLE_JUPYTER=false)"
else
if [[ -z "$JUPYTER_TOKEN" ]]; then
JUPYTER_TOKEN="$(python3 -c 'import secrets; print(secrets.token_hex(16))')"
echo "[jupyter] generated token: $JUPYTER_TOKEN"
fi
echo "[jupyter] starting on 0.0.0.0:${JUPYTER_PORT}"
jupyter lab \
--ip=0.0.0.0 \
--port "$JUPYTER_PORT" \
--no-browser \
--allow-root \
--IdentityProvider.token="$JUPYTER_TOKEN" \
--ServerApp.root_dir="$WORKDIR" \
>/tmp/jupyter.log 2>&1 &
JUPYTER_PID=$!
sleep 2
if ! kill -0 "$JUPYTER_PID" 2>/dev/null; then
echo "[jupyter] WARNING: failed to start, check /tmp/jupyter.log"
fi
fi
fi
cd "$WORKDIR"
exec python3 main.py --listen 0.0.0.0 --port "$PORT"
@@ -1,9 +0,0 @@
# RunPod Container Requirements
> Note: moved to `Runpod_Comfy/core/docs/features/` during the 3-zone refactor (`setup/image/core`) so technical references live under `core/`.
# Requirments:
1. Cuda 12.8 - 12.9 - should work with RTX pro 6000 and B200 gpus !!!
2. Python 12.xx - for compability with sageattention and triton
3. Models and custom nodes - CUSTOM list (predownloaded at stratup) - for each docker TEMPLATE - there will be 2 options at least
@@ -1,19 +0,0 @@
# GitHub and Reference Links
> Note: moved to `Runpod_Comfy/core/docs/references/` during the 3-zone refactor (`setup/image/core`) so technical references live under `core/`.
# Github - Links
### Runpod.io official - documentation
Info:
- https://www.runpod.io/articles/guides/deploying-models-with-docker-containers
Github repo:
- https://github.com/runpod/containers/tree/main
### Exemple of CUSTOM docker containers we can reference (dockerhub)
General usage:
- https://hub.docker.com/r/runpod/pytorch
Training LORAs:
- https://hub.docker.com/r/hearmeman/diffusion-pipe
Upscale setup Wan 22 - T2V
- https://hub.docker.com/r/hearmeman/comfyui-wan-template
-39
View File
@@ -1,39 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT=/opt/bootstrap/Runpod_Comfy
WORKDIR="${COMFYUI_WORKDIR:-/workspace/ComfyUI}"
LOCK="$ROOT/image/active/comfyui.lock"
mkdir -p "$WORKDIR"
lock_get() {
local key="$1"
grep -E "^${key}=" "$LOCK" | head -n1 | cut -d= -f2-
}
COMFYUI_REPO="$(lock_get COMFYUI_REPO)"
COMFYUI_REF="$(lock_get COMFYUI_REF)"
if [[ -z "$COMFYUI_REPO" || -z "$COMFYUI_REF" ]]; then
echo "[bootstrap] invalid lock file: missing COMFYUI_REPO or COMFYUI_REF"
exit 1
fi
if [[ ! -f "$WORKDIR/main.py" ]]; then
echo "[bootstrap] Cloning ComfyUI from $COMFYUI_REPO"
git clone "$COMFYUI_REPO" "$WORKDIR"
fi
cd "$WORKDIR"
echo "[bootstrap] Checking out ComfyUI ref: $COMFYUI_REF"
git fetch --all --tags || true
git checkout "$COMFYUI_REF"
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt
"$ROOT/core/scripts/install_custom_nodes.sh"
"$ROOT/core/scripts/download_models.sh"
echo "[bootstrap] done"
@@ -1,48 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT=/opt/bootstrap/Runpod_Comfy
WORKDIR="${COMFYUI_WORKDIR:-/workspace/ComfyUI}"
LOCK="$ROOT/image/active/models.json"
if [[ ! -f "$LOCK" ]]; then
echo "[models] lock file not found: $LOCK"
exit 0
fi
COUNT=$(jq '.models | length' "$LOCK")
for ((i=0; i<COUNT; i++)); do
ENABLED=$(jq -r ".models[$i].enabled // true" "$LOCK")
[[ "$ENABLED" != "true" ]] && continue
NAME=$(jq -r ".models[$i].name" "$LOCK")
URL=$(jq -r ".models[$i].url" "$LOCK")
DEST_REL=$(jq -r ".models[$i].dest" "$LOCK")
SHA256=$(jq -r ".models[$i].sha256 // \"\"" "$LOCK")
DEST="$WORKDIR/$DEST_REL"
mkdir -p "$(dirname "$DEST")"
if [[ -f "$DEST" ]]; then
echo "[models] exists: $NAME ($DEST_REL), skipping"
continue
fi
echo "[models] downloading: $NAME"
CURL_ARGS=(-L --fail)
if [[ "$URL" == *"huggingface.co"* ]] && [[ -n "${HF_TOKEN:-}" ]]; then
CURL_ARGS+=(-H "Authorization: Bearer ${HF_TOKEN}")
fi
if [[ "$URL" == *"civitai.com"* ]] && [[ -n "${CIVITAI_TOKEN:-}" ]]; then
CURL_ARGS+=(-H "Authorization: Bearer ${CIVITAI_TOKEN}")
fi
curl "${CURL_ARGS[@]}" "$URL" -o "$DEST"
if [[ -n "$SHA256" ]]; then
echo "$SHA256 $DEST" | sha256sum -c -
fi
done
echo "[models] done"
@@ -1,42 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT=/opt/bootstrap/Runpod_Comfy
WORKDIR="${COMFYUI_WORKDIR:-/workspace/ComfyUI}"
NODES_DIR="$WORKDIR/custom_nodes"
LOCK="$ROOT/image/active/custom_nodes.lock.json"
mkdir -p "$NODES_DIR"
if [[ ! -f "$LOCK" ]]; then
echo "[custom_nodes] lock file not found: $LOCK"
exit 0
fi
COUNT=$(jq '.nodes | length' "$LOCK")
for ((i=0; i<COUNT; i++)); do
REPO=$(jq -r ".nodes[$i].repo" "$LOCK")
REF=$(jq -r ".nodes[$i].ref" "$LOCK")
TARGET=$(jq -r ".nodes[$i].target_dir" "$LOCK")
[[ -z "$TARGET" || "$TARGET" == "null" ]] && TARGET=$(basename "$REPO" .git)
DST="$NODES_DIR/$TARGET"
if [[ ! -d "$DST/.git" ]]; then
echo "[custom_nodes] cloning $REPO -> $DST"
git clone "$REPO" "$DST"
fi
pushd "$DST" >/dev/null
git fetch --all --tags || true
git checkout "$REF"
if [[ -f requirements.txt ]]; then
python3 -m pip install -r requirements.txt || true
fi
popd >/dev/null
done
echo "[custom_nodes] done"
-49
View File
@@ -1,49 +0,0 @@
# Deploy on RunPod
## 1) Build and push image
Use GitHub Container Registry or Docker Hub.
Example (GHCR):
```bash
BASE_IMAGE=$(grep '^BASE_IMAGE=' Runpod_Comfy/image/active/comfyui.lock | cut -d= -f2-)
IMAGE_NAME=$(grep '^IMAGE_NAME=' Runpod_Comfy/image/active/comfyui.lock | cut -d= -f2-)
IMAGE_TAG=$(grep '^RUNPOD_IMAGE_TAG=' Runpod_Comfy/image/active/comfyui.lock | cut -d= -f2-)
ENABLE_JUPYTER=$(grep '^ENABLE_JUPYTER=' Runpod_Comfy/image/active/comfyui.lock | cut -d= -f2-)
ENABLE_FILE_TOOLS=$(grep '^ENABLE_FILE_TOOLS=' Runpod_Comfy/image/active/comfyui.lock | cut -d= -f2-)
docker build \
--build-arg BASE_IMAGE="$BASE_IMAGE" \
--build-arg ENABLE_JUPYTER="$ENABLE_JUPYTER" \
--build-arg ENABLE_FILE_TOOLS="$ENABLE_FILE_TOOLS" \
-t ${IMAGE_NAME}:${IMAGE_TAG} \
-f Runpod_Comfy/core/docker/Dockerfile .
docker push ${IMAGE_NAME}:${IMAGE_TAG}
```
## 2) Create RunPod template
- Container image: `ghcr.io/malkuthro/comfyui-koolook:runpod-v1`
- Exposed HTTP ports: `8188` (ComfyUI), `8888` (JupyterLab)
- Volume mount: `/workspace` (recommended)
## 3) Optional env vars
- `COMFYUI_PORT=8188`
- `JUPYTER_PORT=8888`
- `JUPYTER_TOKEN=...` (recommended to set explicitly; if unset, container auto-generates and logs a token)
- `COMFYUI_WORKDIR=/workspace/ComfyUI`
- `HF_TOKEN=...` (if needed)
- `CIVITAI_TOKEN=...` (if needed)
## 4) First boot expectations
On first start:
- ComfyUI is cloned at pinned ref from `image/active/comfyui.lock`
- custom nodes are installed from `image/active/custom_nodes.lock.json`
- models are downloaded from `image/active/models.json` (enabled entries only)
- JupyterLab starts automatically when `ENABLE_JUPYTER=true`
Subsequent starts reuse existing files from volume storage.
-9
View File
@@ -1,9 +0,0 @@
# Generated by Runpod_Comfy/setup/tools/compile_profile.sh
RECIPE_ID=example
BASE_IMAGE=runpod/pytorch:2.6.0-py3.11-cuda12.4.1-devel-ubuntu22.04
IMAGE_NAME=ghcr.io/your-user/comfyui-koolook
RUNPOD_IMAGE_TAG=upscaler-v1.0.0
ENABLE_JUPYTER=true
ENABLE_FILE_TOOLS=true
COMFYUI_REPO=https://github.com/comfyanonymous/ComfyUI.git
COMFYUI_REF=master
@@ -1,19 +0,0 @@
{
"version": 1,
"nodes": [
{
"id": "comfyui-koolook",
"repo": "https://github.com/malkuthro/ComfyUI-Koolook.git",
"ref": "main",
"target_dir": "ComfyUI-Koolook",
"install": []
},
{
"id": "comfyui-manager",
"repo": "https://github.com/ltdrdata/ComfyUI-Manager.git",
"ref": "main",
"target_dir": "ComfyUI-Manager",
"install": []
}
]
}
-19
View File
@@ -1,19 +0,0 @@
{
"version": 1,
"models": [
{
"name": "example_sdxl_checkpoint",
"url": "https://example.com/path/to/model.safetensors",
"dest": "models/checkpoints/model.safetensors",
"sha256": "",
"enabled": false
},
{
"name": "example_vae",
"url": "https://example.com/path/to/vae.safetensors",
"dest": "models/vae/vae.safetensors",
"sha256": "",
"enabled": false
}
]
}
View File
-85
View File
@@ -1,85 +0,0 @@
# Runpod_Comfy Setup Guide
This package is split into 3 readable zones:
- `setup/` → what you edit (recipes + helper tools)
- `image/` → generated outputs (build artifacts + active locks) + deploy runbook
- `core/` → runtime internals (Docker + bootstrap scripts + technical references)
## Quick Start
### 1) Create a recipe
```bash
Runpod_Comfy/setup/tools/new_profile.sh upscaler-v1
```
Creates:
- `Runpod_Comfy/setup/recipes/upscaler-v1/runpod.yaml`
- `Runpod_Comfy/setup/recipes/upscaler-v1/nodes.yaml`
- `Runpod_Comfy/setup/recipes/upscaler-v1/models.yaml`
- `Runpod_Comfy/setup/recipes/upscaler-v1/urls.txt`
In `runpod.yaml`, feature defaults are ON:
- `features.enable_jupyter: true`
- `features.enable_file_tools: true`
Set either to `false` to disable.
Path rules (important):
- Node `target_dir` is relative to `ComfyUI/custom_nodes` (do not prefix `ComfyUI/...`).
- Model `dest` is relative to `ComfyUI` root (use `models/...`, not `ComfyUI/models/...`).
### 2) Compile recipe
```bash
Runpod_Comfy/setup/tools/compile_profile.sh --id upscaler-v1
```
Generates into:
- `Runpod_Comfy/image/builds/upscaler-v1/comfyui.lock`
- `Runpod_Comfy/image/builds/upscaler-v1/custom_nodes.lock.json`
- `Runpod_Comfy/image/builds/upscaler-v1/models.json`
By default compile also **activates** to:
- `Runpod_Comfy/image/active/comfyui.lock`
- `Runpod_Comfy/image/active/custom_nodes.lock.json`
- `Runpod_Comfy/image/active/models.json`
### 3) Build image
```bash
BASE_IMAGE=$(grep '^BASE_IMAGE=' Runpod_Comfy/image/active/comfyui.lock | cut -d= -f2-)
IMAGE_TAG=$(grep '^RUNPOD_IMAGE_TAG=' Runpod_Comfy/image/active/comfyui.lock | cut -d= -f2-)
ENABLE_JUPYTER=$(grep '^ENABLE_JUPYTER=' Runpod_Comfy/image/active/comfyui.lock | cut -d= -f2-)
ENABLE_FILE_TOOLS=$(grep '^ENABLE_FILE_TOOLS=' Runpod_Comfy/image/active/comfyui.lock | cut -d= -f2-)
docker build \
--build-arg BASE_IMAGE="$BASE_IMAGE" \
--build-arg ENABLE_JUPYTER="$ENABLE_JUPYTER" \
--build-arg ENABLE_FILE_TOOLS="$ENABLE_FILE_TOOLS" \
-t comfyui-koolook:${IMAGE_TAG} \
-f Runpod_Comfy/core/docker/Dockerfile .
```
By default both `ENABLE_JUPYTER` and `ENABLE_FILE_TOOLS` are `true`.
## Iterate safely
- Minor tweaks: keep same recipe id, bump `image_tag` in `runpod.yaml`
- Major changes: clone to new id (`upscaler-v2`, `wan22-v1`)
- Optional no-activate compile:
```bash
Runpod_Comfy/setup/tools/compile_profile.sh --id upscaler-v1 --no-activate
```
## Deploy
RunPod deploy runbook:
- `Runpod_Comfy/image/DEPLOY_RUNPOD.md`
## Compiler args
```bash
Runpod_Comfy/setup/tools/compile_profile.sh --id <recipe-id> [--out <dir>] [--image-tag <tag>] [--no-activate]
```
@@ -1,14 +0,0 @@
profile_id: Hires-FIX-upscaler_v01
models:
- name: wan22_t2v_a14b_low_fp8_scaled_kj
url: https://huggingface.co/Kijai/WanVideo_comfy_fp8_scaled/resolve/main/T2V/Wan2_2-T2V-A14B-LOW_fp8_e4m3fn_scaled_KJ.safetensors
dest: models/diffusion_models/Wan2_2-T2V-A14B-LOW_fp8_e4m3fn_scaled_KJ.safetensors
sha256: ""
enabled: true
- name: lightx2v_t2v_14b_cfg_step_distill_v2_lora_rank128_bf16
url: https://huggingface.co/Kijai/WanVideo_comfy/resolve/d4c3006fda29c47a51d07b7ea77495642cf9359f/Lightx2v/lightx2v_T2V_14B_cfg_step_distill_v2_lora_rank128_bf16.safetensors
dest: models/loras/lightx2v_T2V_14B_cfg_step_distill_v2_lora_rank128_bf16.safetensors
sha256: ""
enabled: true
@@ -1,14 +0,0 @@
profile_id: Hires-FIX-upscaler_v01
nodes:
- id: comfyui-koolook
repo: https://github.com/malkuthro/ComfyUI-Koolook.git
ref: main
target_dir: ComfyUI-Koolook
install: []
- id: comfyui-manager
repo: https://github.com/ltdrdata/ComfyUI-Manager.git
ref: main
target_dir: ComfyUI-Manager
install: []
@@ -1,16 +0,0 @@
profile_id: Hires-FIX-upscaler_v01
# Docker build base
base_image: runpod/pytorch:2.6.0-py3.11-cuda12.4.1-devel-ubuntu22.04
# Optional image metadata used by compiler summary/lock file
image_name: ghcr.io/malkuthro/comfyui-koolook
image_tag: hires-fix-upscaler-v01
features:
enable_jupyter: true
enable_file_tools: true
comfyui:
repo: https://github.com/comfyanonymous/ComfyUI.git
ref: master
@@ -1,6 +0,0 @@
# Optional quick model list parser.
# Format (pipe-separated):
# url | dest | sha256(optional) | enabled(optional,true/false) | name(optional)
#
# Example:
# https://example.com/model.safetensors | models/checkpoints/model.safetensors | | false | my_model
@@ -1,14 +0,0 @@
profile_id: example
models:
- name: example_sdxl_checkpoint
url: https://example.com/path/to/model.safetensors
dest: models/checkpoints/model.safetensors
sha256: ""
enabled: false
- name: example_vae
url: https://example.com/path/to/vae.safetensors
dest: models/vae/vae.safetensors
sha256: ""
enabled: false
@@ -1,14 +0,0 @@
profile_id: example
nodes:
- id: comfyui-koolook
repo: https://github.com/malkuthro/ComfyUI-Koolook.git
ref: main
target_dir: ComfyUI-Koolook
install: []
- id: comfyui-manager
repo: https://github.com/ltdrdata/ComfyUI-Manager.git
ref: main
target_dir: ComfyUI-Manager
install: []
@@ -1,16 +0,0 @@
profile_id: example
# Docker build base
base_image: runpod/pytorch:2.6.0-py3.11-cuda12.4.1-devel-ubuntu22.04
# Optional image metadata used by compiler summary/lock file
image_name: ghcr.io/your-user/comfyui-koolook
image_tag: upscaler-v1.0.0
features:
enable_jupyter: true
enable_file_tools: true
comfyui:
repo: https://github.com/comfyanonymous/ComfyUI.git
ref: master
@@ -1,6 +0,0 @@
# Optional quick model list parser.
# Format (pipe-separated):
# url | dest | sha256(optional) | enabled(optional,true/false) | name(optional)
#
# Example:
# https://example.com/model.safetensors | models/checkpoints/model.safetensors | | false | my_model
-185
View File
@@ -1,185 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
RECIPE_ID=""
RECIPES_DIR="$ROOT/setup/recipes"
OUT_DIR=""
ACTIVATE=true
IMAGE_TAG_OVERRIDE=""
usage() {
cat <<EOF
Usage:
$0 --id <recipe-id> [--recipes-dir <dir>] [--out <dir>] [--image-tag <tag>] [--no-activate]
$0 <recipe-id> # backward-compatible
Defaults:
recipes-dir: Runpod_Comfy/setup/recipes
out: Runpod_Comfy/image/builds/<recipe-id>
activate: true (copies generated files to Runpod_Comfy/image/active)
EOF
}
if [[ $# -eq 1 && "$1" != --* ]]; then
RECIPE_ID="$1"
else
while [[ $# -gt 0 ]]; do
case "$1" in
--id) RECIPE_ID="$2"; shift 2 ;;
--recipes-dir) RECIPES_DIR="$2"; shift 2 ;;
--out) OUT_DIR="$2"; shift 2 ;;
--image-tag) IMAGE_TAG_OVERRIDE="$2"; shift 2 ;;
--no-activate) ACTIVATE=false; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown arg: $1"; usage; exit 1 ;;
esac
done
fi
[[ -z "$RECIPE_ID" ]] && { usage; exit 1; }
[[ -z "$OUT_DIR" ]] && OUT_DIR="$ROOT/image/builds/$RECIPE_ID"
RECIPE_DIR="$RECIPES_DIR/$RECIPE_ID"
RUNPOD_PROFILE="$RECIPE_DIR/runpod.yaml"
NODES_PROFILE="$RECIPE_DIR/nodes.yaml"
MODELS_PROFILE="$RECIPE_DIR/models.yaml"
URLS_FILE="$RECIPE_DIR/urls.txt"
CONFIG_DIR="$ROOT/image/active"
for f in "$RUNPOD_PROFILE" "$NODES_PROFILE" "$MODELS_PROFILE"; do
[[ -f "$f" ]] || { echo "Missing: $f"; exit 1; }
done
mkdir -p "$OUT_DIR"
python3 - "$RUNPOD_PROFILE" "$NODES_PROFILE" "$MODELS_PROFILE" "$URLS_FILE" "$OUT_DIR" "$RECIPE_ID" "$IMAGE_TAG_OVERRIDE" <<'PY'
import json, pathlib, re, sys
runpod_path = pathlib.Path(sys.argv[1]); nodes_path = pathlib.Path(sys.argv[2]); models_path = pathlib.Path(sys.argv[3]); urls_path = pathlib.Path(sys.argv[4]); out_dir = pathlib.Path(sys.argv[5]); recipe_id = sys.argv[6]; image_tag_override = sys.argv[7]
def scalar(v):
v=v.strip()
if not v: return ""
if v.lower()=="true": return True
if v.lower()=="false": return False
if (v.startswith('"') and v.endswith('"')) or (v.startswith("'") and v.endswith("'")): return v[1:-1]
if re.fullmatch(r"-?\d+", v): return int(v)
return v
def parse(path):
lines=path.read_text(encoding='utf-8').splitlines(); root={}; i=0
while i<len(lines):
raw=lines[i]; s=raw.strip(); i+=1
if not s or s.startswith('#'): continue
if ':' not in raw: raise ValueError(f"Invalid line: {raw}")
k,v=raw.split(':',1); k=k.strip(); v=v.strip()
if v:
root[k]=scalar(v); continue
block=[]
while i<len(lines):
r=lines[i]; st=r.strip()
if not st or st.startswith('#'): i+=1; continue
if (len(r)-len(r.lstrip(' ')))<2: break
block.append(r); i+=1
if block and block[0].lstrip().startswith('- '):
items=[]; cur=None
for b in block:
st=b.strip()
if st.startswith('- '):
if cur is not None: items.append(cur)
cur={}; payload=st[2:].strip()
if payload:
kk,vv=payload.split(':',1); cur[kk.strip()]=scalar(vv.strip())
else:
kk,vv=st.split(':',1); cur[kk.strip()]=scalar(vv.strip())
if cur is not None: items.append(cur)
root[k]=items
else:
d={}
for b in block:
kk,vv=b.strip().split(':',1); d[kk.strip()]=scalar(vv.strip())
root[k]=d
return root
def parse_urls(path):
if not path.exists(): return []
out=[]
for line in path.read_text(encoding='utf-8').splitlines():
s=line.strip()
if not s or s.startswith('#'): continue
p=[x.strip() for x in s.split('|')]
if len(p)<2: raise ValueError(f"Bad urls entry: {line}")
url,dest=p[0],p[1]; sha=p[2] if len(p)>2 else ""; enabled=(p[3].lower()!='false') if len(p)>3 and p[3] else True; name=p[4] if len(p)>4 and p[4] else pathlib.Path(dest).stem
out.append({"name":name,"url":url,"dest":dest,"sha256":sha,"enabled":enabled})
return out
runpod=parse(runpod_path); nodes_data=parse(nodes_path); models_data=parse(models_path)
base_image=runpod.get('base_image'); comfy=runpod.get('comfyui',{}) if isinstance(runpod.get('comfyui'),dict) else {}
if not base_image: raise ValueError('base_image is required')
image_name=runpod.get('image_name','')
image_tag= image_tag_override or runpod.get('image_tag', recipe_id)
features = runpod.get('features', {}) if isinstance(runpod.get('features'), dict) else {}
enable_jupyter = bool(features.get('enable_jupyter', True))
enable_file_tools = bool(features.get('enable_file_tools', True))
nodes=nodes_data.get('nodes',[]); models=models_data.get('models',[])
url_models=parse_urls(urls_path)
for n in nodes:
if 'repo' not in n or 'ref' not in n: raise ValueError(f'node requires repo/ref: {n}')
n.setdefault('target_dir', pathlib.Path(str(n['repo']).rstrip('/')).name.replace('.git',''))
target = str(n.get('target_dir', '')).strip()
if target.startswith('/'):
raise ValueError(f"node target_dir must be relative (not absolute): {target}")
if target.startswith('ComfyUI/'):
raise ValueError(f"node target_dir should be relative to custom_nodes (remove 'ComfyUI/...'): {target}")
if '..' in pathlib.PurePosixPath(target).parts:
raise ValueError(f"node target_dir cannot contain '..': {target}")
if n.get('install') in ('',None): n['install']=[]
models.extend(url_models)
for m in models:
for req in ('name','url','dest'):
if req not in m: raise ValueError(f'model missing {req}: {m}')
dest = str(m.get('dest', '')).strip()
if dest.startswith('/'):
raise ValueError(f"model dest must be relative to ComfyUI root (not absolute): {dest}")
if dest.startswith('ComfyUI/'):
raise ValueError(f"model dest should not include 'ComfyUI/' prefix: {dest}")
if '..' in pathlib.PurePosixPath(dest).parts:
raise ValueError(f"model dest cannot contain '..': {dest}")
m.setdefault('sha256',''); m.setdefault('enabled',True)
comfy_lock='\n'.join([
'# Generated by Runpod_Comfy/setup/tools/compile_profile.sh',
f'RECIPE_ID={recipe_id}',
f'BASE_IMAGE={base_image}',
f'IMAGE_NAME={image_name}',
f'RUNPOD_IMAGE_TAG={image_tag}',
f'ENABLE_JUPYTER={str(enable_jupyter).lower()}',
f'ENABLE_FILE_TOOLS={str(enable_file_tools).lower()}',
f"COMFYUI_REPO={comfy.get('repo','https://github.com/comfyanonymous/ComfyUI.git')}",
f"COMFYUI_REF={comfy.get('ref','master')}",
''
])
(out_dir/'comfyui.lock').write_text(comfy_lock, encoding='utf-8')
(out_dir/'custom_nodes.lock.json').write_text(json.dumps({'version':1,'nodes':nodes},indent=2)+'\n',encoding='utf-8')
(out_dir/'models.json').write_text(json.dumps({'version':1,'models':models},indent=2)+'\n',encoding='utf-8')
print('=== compile summary ===')
print(f'Recipe ID : {recipe_id}')
print(f'Base image : {base_image}')
print(f'Image name : {image_name or "(unset)"}')
print(f'Image tag : {image_tag}')
print(f'ComfyUI ref : {comfy.get("ref","master")}')
print(f'Jupyter : {enable_jupyter}')
print(f'File tools : {enable_file_tools}')
print(f'Custom nodes : {len(nodes)}')
print(f'Models : {len(models)} (urls.txt additions: {len(url_models)})')
print(f'Output dir : {out_dir}')
PY
if [[ "$ACTIVATE" == true ]]; then
mkdir -p "$CONFIG_DIR"
cp "$OUT_DIR/comfyui.lock" "$CONFIG_DIR/comfyui.lock"
cp "$OUT_DIR/custom_nodes.lock.json" "$CONFIG_DIR/custom_nodes.lock.json"
cp "$OUT_DIR/models.json" "$CONFIG_DIR/models.json"
echo "Activated build into: $CONFIG_DIR"
fi
echo "[ok] compile complete"
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <recipe-id>"
echo "Example: $0 upscaler-v1"
exit 1
fi
ID="$1"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
RECIPES="$ROOT/setup/recipes"
EXAMPLES="$RECIPES/examples"
TARGET="$RECIPES/$ID"
mkdir -p "$TARGET"
for kind in runpod nodes models; do
src="$EXAMPLES/${kind}.example.yaml"
dst="$TARGET/${kind}.yaml"
if [[ -f "$dst" ]]; then
echo "[skip] exists: $dst"
else
cp "$src" "$dst"
python3 - "$dst" "$ID" <<'PY'
from pathlib import Path
import re, sys
p=Path(sys.argv[1]); rid=sys.argv[2]
t=p.read_text(encoding='utf-8')
t=re.sub(r'^profile_id:\s*.*$', f'profile_id: {rid}', t, flags=re.M)
p.write_text(t, encoding='utf-8')
PY
echo "[new] $dst"
fi
done
src_urls="$EXAMPLES/urls.example.txt"
dst_urls="$TARGET/urls.txt"
if [[ -f "$dst_urls" ]]; then
echo "[skip] exists: $dst_urls"
else
cp "$src_urls" "$dst_urls"
echo "[new] $dst_urls"
fi
echo
echo "Next:"
echo " 1) edit $TARGET/{runpod.yaml,nodes.yaml,models.yaml,urls.txt}"
echo " 2) Runpod_Comfy/setup/tools/compile_profile.sh --id $ID"
-20
View File
@@ -1,20 +0,0 @@
# TODO
## NOW
- [ ] Fill real values for RunPod recipe files (`Runpod_Comfy/setup/recipes/<id>/*.yaml`)
- [ ] Compile selected profile and verify generated lock files
- [ ] Build and push first RunPod image
## NEXT
- [ ] Deploy image on RunPod and run smoke test workflow
- [ ] Add helper script: `Runpod_Comfy/setup/tools/build_from_lock.sh`
- [ ] Add profile validation checks (schema-lite)
## BLOCKED
- [ ] Final production model URLs and checksums (waiting on selection)
## DONE
- [x] Add profile compiler scaffold (`compile_profile.sh`)
- [x] Add example profile files (runpod/nodes/models + urls format)
- [x] Wire Dockerfile to configurable base image via build-arg
- [x] Generate lock files from sample profile and sanity-check JSON
-41
View File
@@ -1,41 +0,0 @@
# Workflow (Cristian + kflame)
This is the operating flow for this repo.
## 1) Where planning happens
- Primary workspace: OpenClaw web dashboard session.
- Telegram: quick pings only.
## 2) Source of truth
- Tasks: `TODO.md`
- Progress/handoff: `WORKLOG.md`
- Decisions: `DECISIONS.md`
- Code/config truth: repo files + commits
## 3) Start of each work block
1. Read `TODO.md`
2. Read latest entry in `WORKLOG.md`
3. Read `DECISIONS.md`
4. Pick one task and execute
## 4) End of each work block
1. Update `WORKLOG.md` (done/next/blockers)
2. Update `TODO.md` statuses
3. If a meaningful choice was made, update `DECISIONS.md`
4. Commit changes with clear message
## 5) Task status model
- `NOW`: actively working
- `NEXT`: queued next
- `BLOCKED`: waiting on external dependency
- `DONE`: complete
## 6) Resume from any device
Use this order:
1. `WORKLOG.md` latest entry
2. `TODO.md` NOW/NEXT items
3. `DECISIONS.md`
4. Git history (`git log --oneline -n 10`)
## 7) Rule of thumb
If it matters later, write it to file now.
-44
View File
@@ -1,44 +0,0 @@
# WORKLOG
## 2026-02-15
### Done
- Implemented profile-driven scaffold for RunPod Comfy builds.
- Added:
- `Runpod_Comfy/profiles/runpod.example.yaml`
- `Runpod_Comfy/profiles/nodes.example.yaml`
- `Runpod_Comfy/profiles/models.example.yaml`
- `Runpod_Comfy/profiles/urls.txt`
- `Runpod_Comfy/tools/compile_profile.sh`
- Updated `Runpod_Comfy/docker/Dockerfile` to use `ARG BASE_IMAGE` + `FROM ${BASE_IMAGE}`.
- Updated `Runpod_Comfy/README.md` with compile/build flow.
- Ran sample compile: `Runpod_Comfy/tools/compile_profile.sh example`.
- Validated generated JSON structure via Python loader.
- Commit: `af028bf`.
### Next (top 3)
1. Create real profile files for Cristian's RunPod target.
2. Compile and inspect generated lock files.
3. Build + push image, deploy pod, smoke test.
### Thread recap (copied from Telegram for continuity)
- Agreed division of work: assistant builds production-ready profile compiler scaffold; Cristian fills real values next session.
- Delivered scaffold scope:
- Config layer with profile files for runpod/nodes/models (+ optional urls parser)
- Compiler that generates `config/comfyui.lock`, `config/custom_nodes.lock.json`, `config/models.json`
- Docker wired to consume generated lock data
- Sanity checks + dry docker build command validation
- Tomorrow execution plan:
1. Edit 3–4 profile files with real CUDA/image/nodes/models.
2. Run compiler + docker build + docker push.
3. Deploy image on RunPod and run smoke test.
### Blockers
- Real model URLs/checksums not finalized.
### Additional context imported from Telegram (extended)
- Desired abstraction confirmed: config-driven workflow where adding node repos/model URLs and bumping versions does not require code edits.
- Strong emphasis on "production-ready" compiler/scaffold with placeholders now and real values tomorrow.
- Tomorrow checklist reaffirmed:
1. Fill real profile values (CUDA/image/nodes/models).
2. Compile profile, build image, push image.
3. Deploy on RunPod and validate.
+7
View File
@@ -0,0 +1,7 @@
# RunPod Management Notice
RunPod setup, image build/deploy scaffolding, and recipe iteration were moved to a private repository.
This public repository stays focused on ComfyUI custom node/plugin code.
If you need RunPod runtime operations, use the private management repository.