Merge pull request #17 from malkuthro/kflame-runpod-3zone-structure-v7

RunPod v7: address review blockers + finalize 3-zone layout
This commit is contained in:
malkuthro
2026-02-15 12:32:37 +01:00
committed by GitHub
26 changed files with 724 additions and 0 deletions
+12
View File
@@ -17,6 +17,18 @@ 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`
## [0.1.0] - 2026-02-14
+34
View File
@@ -0,0 +1,34 @@
# DECISIONS
## 2026-02-15 — Profile-first RunPod build flow
- We use profile files as inputs and generate lock files as build/runtime truth.
- Profile inputs:
- `Runpod_Comfy/profiles/runpod.<id>.yaml`
- `Runpod_Comfy/profiles/nodes.<id>.yaml`
- `Runpod_Comfy/profiles/models.<id>.yaml`
- Optional `Runpod_Comfy/profiles/urls.<id>.txt`
- Compiler output:
- `Runpod_Comfy/config/comfyui.lock`
- `Runpod_Comfy/config/custom_nodes.lock.json`
- `Runpod_Comfy/config/models.json`
- 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/`.
+7
View File
@@ -22,6 +22,13 @@ The code for the Easy Resize node is inspired by the Resize Image V2 node from C
- **Error Handling**: Fallbacks for missing/invalid JSON in Wan 2.2 Easy Prompt; validation for aspect ratios and divisibility in Easy Resize.
- **Compatibility**: Works with ComfyUI-Manager for easy installation via Git URL. No additional dependencies beyond standard Python and ComfyUI libraries.
## RunPod
A dedicated RunPod setup scaffold is available in `Runpod_Comfy/`.
- Entry guide: `Runpod_Comfy/README.md`
- Deploy notes: `Runpod_Comfy/docs/DEPLOY_RUNPOD.md`
## Release & Stability
For production usage, install a pinned stable tag/commit instead of following moving branch heads.
+10
View File
@@ -0,0 +1,10 @@
# 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`
+23
View File
@@ -0,0 +1,23 @@
ARG BASE_IMAGE=runpod/pytorch:2.6.0-py3.11-cuda12.4.1-devel-ubuntu22.04
FROM ${BASE_IMAGE}
ENV DEBIAN_FRONTEND=noninteractive \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PYTHONUNBUFFERED=1 \
COMFYUI_PORT=8188 \
COMFYUI_WORKDIR=/workspace/ComfyUI \
RUNPOD_VOLUME_ROOT=/workspace
WORKDIR /opt/bootstrap
RUN apt-get update && apt-get install -y --no-install-recommends \
git curl ca-certificates jq \
&& rm -rf /var/lib/apt/lists/*
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
ENTRYPOINT ["/opt/bootstrap/Runpod_Comfy/core/docker/start.sh"]
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT=/opt/bootstrap/Runpod_Comfy
WORKDIR="${COMFYUI_WORKDIR:-/workspace/ComfyUI}"
PORT="${COMFYUI_PORT:-8188}"
"$ROOT/core/scripts/bootstrap.sh"
cd "$WORKDIR"
exec python3 main.py --listen 0.0.0.0 --port "$PORT"
@@ -1,3 +1,7 @@
# 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 !!!
@@ -1,3 +1,7 @@
# 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
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT=/opt/bootstrap/Runpod_Comfy
WORKDIR="${COMFYUI_WORKDIR:-/workspace/ComfyUI}"
mkdir -p "$WORKDIR"
source "$ROOT/image/active/comfyui.lock"
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"
+39
View File
@@ -0,0 +1,39 @@
#!/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 -L --fail "$URL" -o "$DEST"
if [[ -n "$SHA256" ]]; then
echo "$SHA256 $DEST" | sha256sum -c -
fi
done
echo "[models] done"
+42
View File
@@ -0,0 +1,42 @@
#!/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"
+34
View File
@@ -0,0 +1,34 @@
# Deploy on RunPod
## 1) Build and push image
Use GitHub Container Registry or Docker Hub.
Example (GHCR):
```bash
docker build -t ghcr.io/malkuthro/comfyui-koolook:runpod-v1 -f Runpod_Comfy/core/docker/Dockerfile .
docker push ghcr.io/malkuthro/comfyui-koolook:runpod-v1
```
## 2) Create RunPod template
- Container image: `ghcr.io/malkuthro/comfyui-koolook:runpod-v1`
- Exposed HTTP port: `8188`
- Volume mount: `/workspace` (recommended)
## 3) Optional env vars
- `COMFYUI_PORT=8188`
- `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 `config/comfyui.lock`
- custom nodes are installed from `config/custom_nodes.lock.json`
- models are downloaded from `config/models.json` (enabled entries only)
Subsequent starts reuse existing files from volume storage.
+7
View File
@@ -0,0 +1,7 @@
# 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
COMFYUI_REPO=https://github.com/comfyanonymous/ComfyUI.git
COMFYUI_REF=master
@@ -0,0 +1,19 @@
{
"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
@@ -0,0 +1,19 @@
{
"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
+67
View File
@@ -0,0 +1,67 @@
# 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`
### 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-)
docker build --build-arg BASE_IMAGE="$BASE_IMAGE" \
-t comfyui-koolook:${IMAGE_TAG} -f Runpod_Comfy/core/docker/Dockerfile .
```
## 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]
```
@@ -0,0 +1,14 @@
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
@@ -0,0 +1,14 @@
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: []
@@ -0,0 +1,12 @@
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
comfyui:
repo: https://github.com/comfyanonymous/ComfyUI.git
ref: master
@@ -0,0 +1,6 @@
# 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
+164
View File
@@ -0,0 +1,164 @@
#!/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)
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',''))
if n.get('install') in ('',None): n['install']=[]
for m in models:
for req in ('name','url','dest'):
if req not in m: raise ValueError(f'model missing {req}: {m}')
m.setdefault('sha256',''); m.setdefault('enabled',True)
models.extend(url_models)
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"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'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
@@ -0,0 +1,49 @@
#!/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
@@ -0,0 +1,20 @@
# TODO
## NOW
- [ ] Fill real values for RunPod profile files (`Runpod_Comfy/profiles/*.example.yaml` copies)
- [ ] 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/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
@@ -0,0 +1,41 @@
# 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
@@ -0,0 +1,44 @@
# 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.