Codex et ComfyUI Desktop pour illustrer le portfolio
Piloter en sécurité ComfyUI Desktop sous Windows depuis WSL2, puis générer avec Flux Schnell des couvertures WebP cohérentes pour le portfolio.
[TUTO-11] Codex et ComfyUI Desktop pour illustrer le portfolio
Temps estimé : 45 min
Résultat final : Codex, depuis le projet portfolio ouvert sous WSL2, envoie un workflow Flux Schnell à ComfyUI Desktop sous Windows. La couverture 16:9 est convertie en WebP, contrôlée visuellement et enregistrée sous public/tutos/covers/<slug>.webp. Claude Code peut exécuter la même procédure comme variante compatible.
Environnement de référence actuel :
- hôte : Windows 11 ;
- environnement de développement : WSL2, réseau
mirrored; - GPU : AMD Radeon RX 7900 XT, 20 Go de VRAM ;
- génération : ComfyUI Desktop sous Windows, avec PyTorch/ROCm natif Windows ;
- workflow principal opérationnel : Flux Schnell ;
- variante : Flux Dev BF16, limitée par les 20 Go de VRAM et susceptible de décharger une partie du modèle en RAM ;
- pilote des projets personnels : Codex ; Claude Code reste compatible, mais n’est plus le pilote unique.
La version exacte de ComfyUI Desktop, du pilote AMD, de PyTorch/ROCm et les mesures de VRAM sont à vérifier sur la machine. Ce tutoriel ne donne donc aucun temps de génération ni benchmark.
Objectif
Mettre en place ce flux :
Codex ou Claude Code (WSL2)
→ API locale ComfyUI (Windows)
→ workflow API Flux Schnell
→ PNG temporaire 16:9
→ conversion WebP sans métadonnées
→ public/tutos/covers/<slug>.webp
La connexion recommandée reste sur la boucle locale grâce au mode réseau WSL mirrored. Le pont netsh portproxy n’est documenté qu’en solution de repli pour une installation WSL en mode NAT.
Prérequis
- ComfyUI Desktop installé sous Windows et lancé au moins une fois ;
- workflow Flux Schnell fonctionnel manuellement dans ComfyUI ;
- Codex ouvert sur le projet personnel
portfoliosous WSL2 ; - Python 3 et
curldisponibles dans WSL2 ; - accès au frontmatter de l’article pour récupérer son
slug.
Vérifier WSL et les outils locaux :
wsl.exe --version
python3 --version
curl --version
La distribution utilisée et la version exacte de WSL sont à vérifier sur la machine. La configuration de référence actuelle contient :
# C:\Users\<utilisateur>\.wslconfig
[wsl2]
networkingMode=mirrored
Après une modification de .wslconfig, appliquer la configuration depuis PowerShell :
wsl.exe --shutdown
Cette commande arrête toutes les distributions WSL et leurs processus en cours. Enregistrer le travail avant de l’exécuter.
Étape 1 : Vérifier le workflow et les modèles Flux
Le dépôt confirme les noms suivants :
| Usage | Nom présent dans le dépôt | État |
|---|---|---|
| Workflow principal | flux1-schnell.safetensors | Flux Schnell opérationnel ; chemin et hash à vérifier sur la machine |
| VAE | ae.safetensors | utilisé par les workflows Flux |
| Encodeur CLIP | clip_l.safetensors | utilisé par les workflows Flux |
| Encodeur T5 historique | t5xxl_fp16.safetensors | référencé dans .claude/flux_dev.json |
| Modèle secondaire | flux1-dev.safetensors | Flux Dev BF16, limité par la VRAM |
La documentation ComfyUI actuelle propose t5xxl_fp8_e4m3fn.safetensors pour réduire l’empreinte mémoire de Flux Schnell. Le nom réellement sélectionné dans le workflow opérationnel n’est pas versionné dans ce dépôt : il est à vérifier sur la machine dans le nœud DualCLIPLoader.
Le dépôt mentionne encore models/unet/flux1-schnell.safetensors, alors que la documentation ComfyUI actuelle utilise models/diffusion_models/flux1-schnell.safetensors. Ne déplace pas un modèle qui fonctionne : le chemin réel de ComfyUI Desktop et les dossiers qu’il indexe sont à vérifier sur la machine.
Vérifier les fichiers depuis le dossier de modèles réellement configuré dans ComfyUI Desktop :
sha256sum \
models/diffusion_models/flux1-schnell.safetensors \
models/vae/ae.safetensors \
models/text_encoders/clip_l.safetensors
Adapte uniquement les chemins confirmés par l’installation. Les valeurs SHA256 attendues ne sont pas renseignées dans le dépôt : compare-les aux fiches des fichiers téléchargés depuis les sources officielles avant toute nouvelle utilisation.
⚠️ Sécurité : un fichier de modèle non vérifié est une dépendance non fiable. Télécharge uniquement depuis la source officielle, préfère
.safetensorset n’exécute jamais un script fourni avec un modèle sans l’avoir audité.
Dans ComfyUI, charger le workflow Flux Schnell et produire une image manuellement avant de poursuivre. Contrôler dans les nœuds de chargement :
- le nom exact du modèle de diffusion ;
- le VAE ;
- les deux encodeurs de texte ;
- le nombre d’étapes enregistré dans le workflow ;
- l’absence de custom node ou d’appel cloud non nécessaire.
Flux Dev BF16 reste une variante : ne l’utilise pas comme workflow par défaut sur cette RX 7900 XT si son exécution provoque un manque de VRAM ou un offload important. Le comportement exact est à vérifier sur la machine.
Étape 2 : Connecter WSL2 à ComfyUI sans exposer l’API
Avec networkingMode=mirrored, WSL2 peut joindre un service Windows limité à la boucle locale. Laisse ComfyUI Desktop sur son adresse locale, puis teste d’abord le port de référence :
export COMFYUI_URL="http://127.0.0.1:8000"
curl --fail --silent "$COMFYUI_URL/system_stats" \
| python3 -m json.tool \
| head -20
⚠️ Sécurité :
/system_statsexpose des informations sur le système, les périphériques et la VRAM. Le tutoriel ne configure aucune authentification pour l’API locale ComfyUI ; n’exécute ce test que via127.0.0.1ou une liaison explicitement filtrée.
Si le test échoue, vérifier dans ComfyUI Desktop le port réellement affiché dans les journaux. Le port 8000 est celui documenté dans ce dépôt, mais il est à vérifier sur la machine.
Ne configure pas cette option en mode réseau mirrored :
--listen 0.0.0.0
⚠️ Sécurité :
--listen 0.0.0.0fait écouter ComfyUI sur toutes les interfaces IPv4. Selon le pare-feu et le profil réseau Windows, l’interface, l’API, la file d’attente, les informations système et les fichiers servis par/viewpeuvent alors devenir accessibles depuis le réseau local sans authentification.
Si l’option est déjà présente dans Settings → Server Config → Extra Arguments, la retirer, redémarrer ComfyUI Desktop, puis refaire le test sur 127.0.0.1.
Étape 3 : Utiliser netsh portproxy uniquement en repli NAT
Cette étape est inutile avec le mode mirrored. Elle ne s’applique que si WSL2 fonctionne en NAT et ne peut pas atteindre le service Windows via 127.0.0.1.
Depuis WSL2, relever l’adresse de Windows vue par WSL et l’adresse de la distribution :
WINDOWS_HOST_IP=$(ip route show default | awk '{print $3}')
WSL_IP=$(hostname -I | awk '{print $1}')
printf 'Windows=%s\nWSL=%s\n' "$WINDOWS_HOST_IP" "$WSL_IP"
Ouvrir ensuite PowerShell en tant qu’administrateur et recopier les deux valeurs :
$WindowsHostIp = "<adresse Windows affichée dans WSL>"
$WslIp = "<adresse WSL affichée dans WSL>"
netsh interface portproxy add v4tov4 `
listenport=8188 `
listenaddress=$WindowsHostIp `
connectport=8000 `
connectaddress=127.0.0.1 `
protocol=tcp
⚠️ Sécurité : une règle
portproxyest persistante et peut rester active après la fin du test. Ici,listenaddressdoit être l’adresse virtuelle précise vue par WSL, jamais une adresse générique. Vérifie la règle après chaque changement d’adresse WSL.
Créer une règle de pare-feu limitée à cette paire d’adresses :
New-NetFirewallRule `
-DisplayName "ComfyUI-WSL2-Bridge" `
-Direction Inbound `
-Action Allow `
-Protocol TCP `
-LocalAddress $WindowsHostIp `
-LocalPort 8188 `
-RemoteAddress $WslIp `
-Profile Any
⚠️ Sécurité : n’autorise pas
8188pourAnyen adresse distante. Une règle limitée seulement au port, ou à tout le sous-réseau local, peut exposer l’API ComfyUI à d’autres machines. Le profilAnyn’est acceptable ici que parce queLocalAddressetRemoteAddresssont tous deux restreints.
Cette variante souvent copiée sur Internet est à proscrire :
netsh interface portproxy add v4tov4 `
listenport=8188 `
listenaddress=0.0.0.0 `
connectport=8000 `
connectaddress=127.0.0.1
⚠️ Sécurité :
listenaddress=0.0.0.0accepte les connexions sur toutes les interfaces IPv4 de Windows. Une règle de pare-feu absente, trop large, désactivée ou associée au mauvais profil suffit alors à rendre l’API accessible sur le LAN, sans authentification.
Auditer le pont et le pare-feu :
netsh interface portproxy show v4tov4
Get-NetTCPConnection -State Listen -LocalPort 8188
Get-NetFirewallRule -DisplayName "ComfyUI-WSL2-Bridge" |
Get-NetFirewallAddressFilter
Depuis WSL2, utiliser uniquement l’adresse relevée :
export COMFYUI_URL="http://${WINDOWS_HOST_IP}:8188"
curl --fail --silent "$COMFYUI_URL/system_stats" \
| python3 -m json.tool \
| head -20
⚠️ Sécurité : ce test confirme aussi que l’endpoint non authentifié est joignable. Si une autre machine du LAN peut obtenir une réponse, supprimer immédiatement le pont et la règle de pare-feu, puis corriger le filtrage avant de continuer.
Supprimer le repli dès qu’il n’est plus nécessaire :
netsh interface portproxy delete v4tov4 `
listenport=8188 `
listenaddress=$WindowsHostIp `
protocol=tcp
Remove-NetFirewallRule -DisplayName "ComfyUI-WSL2-Bridge"
Étape 4 : Exporter le workflow au format API
ComfyUI distingue le workflow destiné à l’interface et le workflow destiné à l’API.
- Ouvrir
http://127.0.0.1:8000sous Windows. - Charger le workflow Flux Schnell déjà validé.
- Si l’option n’apparaît pas, activer les options de développement dans les paramètres.
- Utiliser File → Export (API).
- Enregistrer le fichier dans le projet portfolio sous
workflows/flux-schnell-api.json.
⚠️ Sécurité : relis le JSON avant de le versionner. Des custom nodes peuvent y enregistrer un prompt privé, un chemin local, une URL distante, un identifiant ou une clé. Le format API n’est pas une garantie d’absence de secrets.
Valider le JSON et afficher les noms de modèles réellement exportés :
python3 -m json.tool workflows/flux-schnell-api.json > /dev/null
rg -n '"(unet_name|vae_name|clip_name1|clip_name2|ckpt_name)"' \
workflows/flux-schnell-api.json
Afficher les IDs, types et entrées des nœuds :
python3 - workflows/flux-schnell-api.json <<'PY'
import json
import sys
workflow = json.load(open(sys.argv[1], encoding="utf-8"))
for node_id, node in workflow.items():
inputs = ", ".join(node.get("inputs", {}).keys())
print(f"{node_id:>8} {node.get('class_type', '?'):<28} {inputs}")
PY
Les IDs de .claude/flux_dev.json (56:47, 56:48, etc.) appartiennent au workflow Flux Dev historique. Ne les recopie pas dans Flux Schnell. Le JSON exporté sur la machine est la source de vérité.
Étape 5 : Ajouter un lanceur de couverture au portfolio
Le script existant scripts/generate-covers.mjs produit les couvertures SVG de repli. Ne l’écrase pas : le lanceur ComfyUI porte un autre nom et produit le PNG temporaire destiné au WebP prioritaire.
Créer scripts/comfyui-cover.py dans le projet portfolio avec ce contenu :
#!/usr/bin/env python3
import argparse
import json
import os
from pathlib import Path
import random
import re
import time
from urllib import error, parse, request
def api_json(base_url, path, method="GET", payload=None):
data = None if payload is None else json.dumps(payload).encode("utf-8")
headers = {"Content-Type": "application/json"} if data else {}
req = request.Request(
f"{base_url.rstrip('/')}{path}",
data=data,
headers=headers,
method=method,
)
try:
with request.urlopen(req, timeout=30) as response:
return json.load(response)
except error.HTTPError as exc:
body = exc.read().decode("utf-8", errors="replace")
raise SystemExit(f"ComfyUI HTTP {exc.code}: {body}") from exc
def choose_node(workflow, explicit_id, label, predicate):
if explicit_id:
if explicit_id not in workflow:
raise SystemExit(f"{label}: nœud {explicit_id!r} absent du workflow")
return explicit_id, workflow[explicit_id]
matches = [
(node_id, node)
for node_id, node in workflow.items()
if predicate(node)
]
if len(matches) != 1:
ids = ", ".join(node_id for node_id, _ in matches) or "aucun"
raise SystemExit(
f"{label}: détection ambiguë ({ids}). "
f"Indique l'ID avec l'option correspondante."
)
return matches[0]
parser = argparse.ArgumentParser()
parser.add_argument("--workflow", required=True)
parser.add_argument("--slug", required=True)
parser.add_argument("--prompt", required=True)
parser.add_argument("--output", required=True)
parser.add_argument(
"--base-url",
default=os.environ.get("COMFYUI_URL", "http://127.0.0.1:8000"),
)
parser.add_argument("--width", type=int, default=1280)
parser.add_argument("--height", type=int, default=720)
parser.add_argument("--seed", type=int)
parser.add_argument("--prompt-node")
parser.add_argument("--size-node")
parser.add_argument("--seed-node")
parser.add_argument("--save-node")
parser.add_argument("--timeout", type=int, default=300)
args = parser.parse_args()
if not re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", args.slug):
raise SystemExit("Slug invalide : utiliser uniquement a-z, 0-9 et des tirets")
if args.width * 9 != args.height * 16:
raise SystemExit("La couverture doit respecter exactement le ratio 16:9")
workflow = json.loads(Path(args.workflow).read_text(encoding="utf-8"))
prompt_id, prompt_node = choose_node(
workflow,
args.prompt_node,
"Prompt",
lambda node: (
node.get("class_type") == "CLIPTextEncode"
and "text" in node.get("inputs", {})
),
)
size_id, size_node = choose_node(
workflow,
args.size_node,
"Dimensions",
lambda node: (
node.get("class_type") in {"EmptySD3LatentImage", "EmptyLatentImage"}
and {"width", "height"} <= node.get("inputs", {}).keys()
),
)
seed_id, seed_node = choose_node(
workflow,
args.seed_node,
"Seed",
lambda node: (
"seed" in node.get("inputs", {})
or "noise_seed" in node.get("inputs", {})
),
)
save_id, save_node = choose_node(
workflow,
args.save_node,
"Sortie",
lambda node: node.get("class_type") == "SaveImage",
)
seed = args.seed if args.seed is not None else random.SystemRandom().randrange(2**63)
prompt_node["inputs"]["text"] = args.prompt
size_node["inputs"]["width"] = args.width
size_node["inputs"]["height"] = args.height
for node in workflow.values():
if node.get("class_type") == "ModelSamplingFlux":
inputs = node.get("inputs", {})
if "width" in inputs and "height" in inputs:
inputs["width"] = args.width
inputs["height"] = args.height
seed_input = "seed" if "seed" in seed_node["inputs"] else "noise_seed"
seed_node["inputs"][seed_input] = seed
save_node["inputs"]["filename_prefix"] = f"portfolio/{args.slug}"
queued = api_json(
args.base_url,
"/prompt",
method="POST",
payload={"prompt": workflow, "client_id": f"portfolio-{args.slug}"},
)
job_id = queued.get("prompt_id")
if not job_id:
raise SystemExit(f"Workflow refusé : {json.dumps(queued, ensure_ascii=False)}")
deadline = time.monotonic() + args.timeout
image_info = None
while time.monotonic() < deadline:
history = api_json(args.base_url, f"/history/{job_id}")
entry = history.get(job_id)
if entry:
status = entry.get("status", {})
if status.get("status_str") == "error":
raise SystemExit(f"Échec ComfyUI : {json.dumps(status, ensure_ascii=False)}")
images = entry.get("outputs", {}).get(save_id, {}).get("images", [])
if images:
image_info = images[0]
break
time.sleep(2)
if image_info is None:
raise SystemExit(f"Timeout après {args.timeout}s pour le prompt {job_id}")
query = parse.urlencode(
{
"filename": image_info["filename"],
"subfolder": image_info.get("subfolder", ""),
"type": image_info.get("type", "output"),
}
)
output = Path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
with request.urlopen(
f"{args.base_url.rstrip('/')}/view?{query}",
timeout=30,
) as response:
output.write_bytes(response.read())
print(f"Image : {output}")
print(f"Seed : {seed}")
print(
f"Nœuds : prompt={prompt_id}, dimensions={size_id}, "
f"seed={seed_id}, sortie={save_id}"
)
Le script détecte les nœuds simples par leur class_type. Si plusieurs nœuds correspondent, il s’arrête au lieu de modifier le mauvais. Relance-le alors avec --prompt-node, --size-node, --seed-node ou --save-node, en utilisant les IDs affichés à l’étape précédente.
L’automatisation ne reconstruit pas le workflow et ne duplique pas les noms des modèles : elle modifie uniquement le prompt, les dimensions, la seed et le préfixe de sortie du workflow API exporté.
Étape 6 : Appliquer la convention de prompt Macchiato bleu
Pour garder une série cohérente, conserve le même workflow Flux Schnell et commence chaque prompt par le même contrat visuel :
Editorial technical illustration for a software engineering portfolio,
dark Catppuccin Macchiato color palette,
deep backgrounds #24273a, #1e2030 and #181926,
dominant blue #8aadf4 with sapphire #7dc4e4 and sky #91d7e3 accents,
clean geometric composition, subtle blue glow, restrained fine grain,
high contrast, one clear focal subject, generous negative space,
no readable text, no letters, no numbers, no logo, no watermark,
16:9 landscape composition.
Ajoute ensuite seulement :
- le sujet concret de l’article ;
- deux ou trois éléments visuels maximum ;
- la relation entre ces éléments ;
- la zone à laisser calme pour le recadrage responsive.
Exemple pour ce tutoriel :
Editorial technical illustration for a software engineering portfolio,
dark Catppuccin Macchiato color palette,
deep backgrounds #24273a, #1e2030 and #181926,
dominant blue #8aadf4 with sapphire #7dc4e4 and sky #91d7e3 accents,
clean geometric composition, subtle blue glow, restrained fine grain,
high contrast, one clear focal subject, generous negative space,
no readable text, no letters, no numbers, no logo, no watermark,
16:9 landscape composition.
An abstract AMD GPU on the Windows side connected through a narrow local bridge
to a workflow graph inside a Linux terminal, flowing into a single image frame,
calm space on the left, precise editorial vector-like rendering.
Flux Schnell n’utilise pas nécessairement un negative prompt dans le workflow officiel. Les exclusions importantes sont donc formulées directement dans le prompt principal. Leur respect reste à contrôler visuellement.
Étape 7 : Générer et nommer une couverture 16:9 depuis Codex
Depuis la racine du projet portfolio, récupérer le slug du frontmatter. Adapter ARTICLE au fichier concerné :
ARTICLE="src/content/tutorials/TUTO-11-comfyui-wsl2-generation-images.md"
SLUG=$(sed -n 's/^slug::space:*//p' "$ARTICLE" | head -1 | tr -d "\"'")
if ! [[ "$SLUG" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
printf 'Slug invalide : %s\n' "$SLUG" >&2
exit 1
fi
printf 'Couverture cible : public/tutos/covers/%s.webp\n' "$SLUG"
Dans Codex, demander :
Dans le projet portfolio, lis le frontmatter de l'article ciblé et
workflows/flux-schnell-api.json. Prépare un prompt conforme au contrat visuel
Macchiato bleu du TUTO-11. Exécute scripts/comfyui-cover.py en 1280x720 avec
le slug exact de l'article. Ne modifie ni le workflow source ni un autre fichier.
Arrête-toi et montre les IDs candidats si la détection d'un nœud est ambiguë.
Codex peut alors exécuter la commande suivante avec le prompt validé :
export COMFYUI_URL="http://127.0.0.1:8000"
PROMPT=$(cat <<'PROMPT_EOF'
Editorial technical illustration for a software engineering portfolio,
dark Catppuccin Macchiato color palette, deep background #24273a,
dominant blue #8aadf4 with sapphire #7dc4e4 and sky #91d7e3 accents,
clean geometric composition, subtle blue glow, restrained fine grain,
no readable text, no logo, no watermark, 16:9 landscape composition.
An abstract AMD GPU connected through a narrow local bridge to a workflow graph,
flowing into a single image frame, calm space on the left.
PROMPT_EOF
)
python3 scripts/comfyui-cover.py \
--workflow workflows/flux-schnell-api.json \
--slug "$SLUG" \
--prompt "$PROMPT" \
--width 1280 \
--height 720 \
--output "public/tutos/covers/${SLUG}.png"
⚠️ Sécurité : cette commande appelle
/prompt,/history/<prompt_id>et/view, sans authentification dans cette configuration locale. N’utilise jamais une URL ComfyUI provenant du LAN, d’Internet ou d’un tiers sans transport sécurisé, contrôle d’accès et authentification explicitement vérifiés.
La seed affichée par le script doit être conservée dans les notes de travail si l’image doit pouvoir être régénérée. En cas de manque de VRAM avec Flux Schnell, tester 1024x576, toujours en 16:9. La nécessité de cette réduction est à vérifier sur la machine.
Ne bascule pas automatiquement vers Flux Dev BF16 : il est documenté comme variante limitée par la VRAM, pas comme solution de repli plus légère.
Étape 8 : Utiliser la même automatisation depuis Claude Code
Claude Code reste compatible : il doit exécuter le même script et le même workflow API, sans maintenir un second payload ComfyUI.
L’ancien skill ~/.claude/skills/comfyui-image-gen peut être conservé comme variante historique, à condition de :
- remplacer l’IP fixe
172.24.16.1parCOMFYUI_URL; - supprimer le payload Flux Dev et ses IDs codés en dur ;
- appeler
scripts/comfyui-cover.pyavec le slug et le prompt ; - conserver Codex comme procédure principale pour les projets personnels.
Prompt compatible Claude Code :
Utilise scripts/comfyui-cover.py et workflows/flux-schnell-api.json.
Génère une couverture 16:9 pour cet article avec son slug exact et la convention
Macchiato bleu du TUTO-11. N'expose pas ComfyUI et n'invente aucun ID de nœud.
Étape 9 : Convertir en WebP et supprimer les métadonnées
Installer l’encodeur WebP dans WSL2 si nécessaire :
sudo apt update
sudo apt install -y webp libimage-exiftool-perl
Convertir le PNG temporaire. cwebp ne copie aucune métadonnée par défaut ; -metadata none rend cette intention explicite :
PNG="public/tutos/covers/${SLUG}.png"
WEBP="public/tutos/covers/${SLUG}.webp"
cwebp \
-preset picture \
-q 82 \
-m 6 \
-mt \
-metadata none \
"$PNG" \
-o "$WEBP"
⚠️ Sécurité : les PNG produits par ComfyUI peuvent contenir le prompt, le workflow, des chemins ou des informations sur l’outil. Ne publie pas le PNG intermédiaire. Publie uniquement le WebP réencodé avec
-metadata none, après contrôle.
La valeur 82 est un réglage éditorial de départ, pas un benchmark. Comparer visuellement le WebP au PNG et ajuster si des aplats, lignes ou dégradés deviennent visibles.
Vérifier le nom, le type et les métadonnées potentiellement sensibles :
test -s "$WEBP"
file "$WEBP"
if exiftool -G1 -a -s "$WEBP" \
| grep -Eai 'EXIF|XMP|GPS|Comment|Author|Software|Prompt|Workflow|Parameters'; then
echo "Métadonnée potentiellement sensible détectée" >&2
exit 1
else
echo "Aucune métadonnée sensible détectée"
fi
file et ExifTool affichent toujours des informations techniques comme le nom, la taille ou les dimensions du fichier. Le contrôle porte sur les champs publiables susceptibles de révéler le prompt, le workflow, un chemin, un logiciel, une identité ou une géolocalisation.
Étape 10 : Vérifier visuellement et valider la destination
Ouvrir le WebP depuis WSL2 :
explorer.exe "$(wslpath -w "$WEBP")"
Contrôler manuellement :
- ratio 16:9 et cadrage lisible sur grand et petit écran ;
- sujet principal immédiatement compréhensible ;
- palette dominée par
#8aadf4,#7dc4e4et#91d7e3sur fonds Macchiato sombres ; - absence de texte illisible, pseudo-logo, watermark, visage ou donnée non souhaitée ;
- absence d’artefacts gênants ;
- cohérence avec les autres couvertures du portfolio.
La destination finale doit être exactement :
public/tutos/covers/<slug>.webp
Le portfolio cherche actuellement les couvertures dans l’ordre webp, png, puis svg. Le WebP portant le slug exact devient donc automatiquement la couverture prioritaire.
Vérifier la présence du fichier :
test -f "public/tutos/covers/${SLUG}.webp"
git status --short -- "public/tutos/covers/${SLUG}.webp"
Ne supprime le PNG intermédiaire qu’après validation visuelle et confirmation que le WebP est la seule version à publier.
Dépannage
curl: (7) Failed to connect en mode mirrored
grep -n "networkingMode" /mnt/c/Users/*/.wslconfig 2>/dev/null
curl -v http://127.0.0.1:8000/system_stats
⚠️ Sécurité : avec
curl -v, vérifie que la destination reste127.0.0.1. Ne remplace pas temporairement l’adresse par l’IP LAN de Windows et n’ajoute pas--listen 0.0.0.0pour contourner le diagnostic.
Vérifier ensuite que ComfyUI Desktop est démarré, que son port correspond à celui du tuto, puis exécuter wsl.exe --shutdown si .wslconfig vient d’être modifié.
Le repli portproxy ne répond plus
Les adresses WSL peuvent changer après un redémarrage :
netsh interface portproxy show v4tov4
Get-NetFirewallRule -DisplayName "ComfyUI-WSL2-Bridge" |
Get-NetFirewallAddressFilter
Get-Service iphlpsvc
Supprimer l’ancienne règle et la recréer avec les adresses exactes. Ne l’élargis pas à 0.0.0.0 pour éviter cette maintenance.
node_errors dans la réponse de /prompt
Le workflow, un nom de modèle ou un ID de nœud ne correspond pas à l’installation :
python3 -m json.tool workflows/flux-schnell-api.json > /dev/null
rg -n '"class_type"|"unet_name"|"vae_name"|"clip_name' \
workflows/flux-schnell-api.json
Réexporter le workflow Flux Schnell fonctionnel depuis la même instance ComfyUI. Ne remplace pas ses IDs par ceux du workflow Flux Dev historique.
La détection du script est ambiguë
Afficher les nœuds comme à l’étape 4, identifier les nœuds effectivement reliés à SaveImage, puis passer leurs IDs :
python3 scripts/comfyui-cover.py \
--workflow workflows/flux-schnell-api.json \
--slug "$SLUG" \
--prompt "$PROMPT" \
--output "public/tutos/covers/${SLUG}.png" \
--prompt-node "<ID>" \
--size-node "<ID>" \
--seed-node "<ID>" \
--save-node "<ID>"
⚠️ Sécurité : les IDs ne sont pas un mécanisme de confiance. Relis le
class_typeet les connexions du nœud avant de l’autoriser ; un custom node peut appeler un service externe ou écrire ailleurs sur le disque.
Manque de VRAM
- confirmer que le workflow chargé est Flux Schnell ;
- conserver un seul batch ;
- essayer
1024x576; - vérifier le T5 réellement sélectionné dans
DualCLIPLoader; - fermer les autres charges GPU ;
- consulter
/system_statset les journaux ComfyUI ; - ne tester Flux Dev BF16 qu’en connaissance de sa limite VRAM.
Les réglages efficaces, la quantité d’offload et la version PyTorch/ROCm sont à vérifier sur la machine.
Métadonnées détectées dans le WebP
Réencoder depuis le PNG original avec cwebp -metadata none, puis relancer ExifTool. Ne publie pas le fichier tant qu’un champ contient un prompt, un workflow, un chemin local, un nom d’utilisateur, une géolocalisation ou une information d’outil inutile.
Références
- ComfyUI — routes du serveur local
- ComfyUI — exemple API officiel
- ComfyUI — workflow Flux.1 Schnell et noms de modèles
- Black Forest Labs — FLUX.1 Schnell
- Microsoft — accès aux applications réseau avec WSL
- Microsoft —
netsh interface portproxy - AMD — prérequis ROCm sous Windows
- Google —
cwebp - TUTO-09