- Split run_name on "/" and "\" and reject empty, "." and ".." parts, so
a Windows-style "..\foo" can no longer resolve outside model_compare
and let the index-0 archive step rename an unrelated output folder
- Reject drive-letter names and require the resolved directory to sit
strictly below output/model_compare after symlinks are followed
- Nested run names such as project/date/sweep still work
- Checkpoint Folder Iterator loads one checkpoint per queued run from a
folder (relative to models/checkpoints or absolute), so a whole folder
can be swept with only one model in memory at a time
- Model Compare Sheet saves each run's image as a tile under
output/model_compare/<run_name>/ and rebuilds a labeled contact sheet
from disk every run, so the grid survives between independent prompts
- Starting a sweep at index 0 archives stale tiles to a timestamped folder
- Running past the last checkpoint raises a clear error instead of
wrapping, so an over-queued batch never restarts and archives a
finished sweep
- Checkpoints load from the scanned absolute path, so folders outside the
configured roots work as the tooltip promises
- Extract the LoRA Folder Batch folder-scanning, natural-sort and regex
filter helpers into kikotools/base/model_folders.py so both tools share
one implementation
- google-generativeai is deprecated and emits a FutureWarning at node
load; google-genai is Google's supported replacement
- Image analysis now uses genai.Client().models.generate_content
instead of the module-level configure/GenerativeModel API
- Model listing reads supported_actions, the new SDK's name for
supported_generation_methods
- Install instructions, requirements.txt, and README updated to the
new package name
Drop RETURN_NAMES so ComfyUI shows the output as STRING, matching the original Project File Path Generator. Links connect by slot index, so existing workflows are unaffected.
Builds a save prefix like MyProject/2026-10-13/images/image for a save
node's filename_prefix, replacing djz-nodes' Project File Path Generator
with a fresh implementation:
- Same inputs, root defaults to empty (prefixes are already relative to
the output folder, so "output" nested as output/output/...)
- Optional date folder; IS_CHANGED tracks the date instead of forcing a
re-run on every queue
- Components are split on / and \ with '..' segments dropped, so a path
can't climb out of the output folder
- Only real file extensions are stripped (render_v1.5 keeps its dot)
- The separator option is applied last instead of being undone by
normpath; an empty filename raises a clear error
A Windows console on a legacy code page (cp1252, or cp437 in cmd)
raises UnicodeEncodeError on emoji. The startup banner printed a 🫶
prefix and display names like "Model Downloader 🌐", which aborts
loading the whole node pack. Same approach as ComfyUI_PromptManager:
- Banner prints ASCII-only display names (emoji stay in the UI names)
- Preset validation messages print "1024x1024" instead of "1024×1024"
- Explicit UTF-8 for text files (Windows defaults to cp1252)
- Regression test imports the package with a strict cp1252/cp437 stdout
- Close PIL images and NamedTemporaryFile handles before deleting the
files; Windows refuses to delete open files (WinError 32)
- Set USERPROFILE alongside HOME: expanduser ignores HOME on Windows
- Compare realpaths in the nested save-prefix test; Windows temp dirs
use 8.3 short names (RUNNER~1)
save_paths is reachable without auth, so saving "/" made every media
file on the machine servable. Reject filesystem/drive roots, the home
directory and its ancestors (realpath-resolved, so symlinks count), and
ignore such entries already in config.json.
ComfyUI registers custom_nodes in folder_names_and_paths, so treating
every registered folder as a download root let a queued prompt drop a
.py file there that runs on the next restart.
- Skip custom_nodes when collecting allowed roots
- save_response only writes model/data extensions (.safetensors, .gguf,
.ckpt, .pth, .json, ...), so any future code-holding folder is still
protected
- KikoSaveImage: reject absolute or '..' filename_prefix directories that
resolved outside the output folder
- Film grain: use a local torch.Generator instead of reseeding the global
RNG; strength 0 no longer skips the toe adjustment
- Purge VRAM: IS_CHANGED returns NaN so caching never skips the purge
- Resolution calculator: clamp outputs to >= 8
- Plot sampler params: font size floor of 1 for images under 32px wide
- Gemini prompt: drop Image.fromarray mode= argument (removed in Pillow)
- Reject filenames with path components; reduce server-derived names
(Content-Disposition, CivitAI redirect, URL) to a safe basename
- save_path must resolve inside models_dir or a registered model folder
(including extra_model_paths.yaml roots); relative paths resolve
against ComfyUI's base path
- Only http/https for user URLs and redirects; drop the Authorization
header on cross-host redirects
- Stream to <file>.part and rename on success, removing the partial file
on any failure, so a dropped connection no longer leaves a truncated
model that later runs treat as complete
- 60s timeout on every request; shared streaming loop in BaseDownloader
- Custom downloader honours Content-Disposition before the default name
/view served any readable file (e.g. ~/.ssh/id_rsa) to anyone who could
reach the server; the only guard was a '..' substring check.
- Resolve paths with realpath and require commonpath containment in the
saved paths or ComfyUI input/output directories
- /view serves image/video/audio only, /thumbnail and load_media images
only; the extension check applies to the resolved path, so symlinks
can't smuggle other files
- /images 403s outside the roots; page/per_page validated and clamped
- /list_directories (folder picker) returns directory names only
- load_media refuses disallowed paths from a tampered selections.json
- Route handlers moved to module level so they are unit tested
- Update stale tests to the (image, info) return contract
- LoRA strength strings: step <= 0 looped forever and reversed ranges
returned [] (ZeroDivisionError later); now raise ValueError, and cap
generated values at 1000
- Flux sampler params: unparseable steps/guidance/shift/denoise/seed
tokens raise with the field name instead of being dropped, and an
empty LoRA folder raises instead of returning (None, [])
- Detect Flux schnell via unet_config guidance_embed (the ModelType.FLOW
check never matched Flux)
- IS_CHANGED: re-run Flux params when a seed is '?'; LoRA folder batch
hashes file list + mtimes instead of always re-executing
- LoRA folder scan uses commonpath containment across all lora roots
- Prompt separators must be whole lines; failed encodes raise
Previously split_latent_batch and join_latent_batch only handled the
"samples" key, silently dropping metadata like noise_mask or batch_index.
This broke inpaint and masked workflows after a round-trip conversion.
Add 4 utility nodes for converting between batched tensors and lists,
eliminating the dependency on Impact Pack for this common operation:
- ImageBatchToImageList (OUTPUT_IS_LIST)
- ImageListToImageBatch (INPUT_IS_LIST)
- LatentBatchToLatentList (OUTPUT_IS_LIST)
- LatentListToLatentBatch (INPUT_IS_LIST)
JS Math.random() only has 53 bits of integer precision, making the
64-bit range produce non-uniform values. 32-bit (0xFFFFFFFF) is the
standard ComfyUI seed range and works correctly in both Python and JS.
Also removes the redundant mode input from SeedHistoryNode, relying on
ComfyUI's built-in control_after_generate widget instead.