- Add .comfyignore so `comfy node publish` ships only the runtime
package: __init__.py, kikotools/, web/, examples/, README, LICENSE,
pyproject.toml, requirements.txt
- Exclude tests/, scripts/, .github/, dev tooling configs, contributor
docs, and the stray example.py reference file (not imported anywhere)
- Registry scanner flagged 1.1.1 on five info-level pattern matches;
two were in tests/ and the historical Model.eval matches came from
example.py, none of which should be in the published archive
- Bump version to 1.1.2
- 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
Combines #56-#63:
- torch >=2.11.0, pillow >=12.2.0, mypy >=1.20.0, pytest-mock >=3.15.1,
pre-commit >=4.5.1 in both pyproject.toml and requirements-dev.txt
(Dependabot only edited pyproject, but CI installs requirements-dev.txt)
- actions/checkout v7, actions/cache v6, softprops/action-gh-release v3
across all workflows, including the new test/coverage/js jobs
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.
The image loader's config.json/selections.json (one user's saved folders
and selections) and the Gemini model cache were committed, so every
install shipped them as defaults, and running from a symlinked checkout
dirtied the tree. Untrack them and ignore them, plus gemini_config.json
which would hold an API key. All loaders already handle the files being
absent.
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)
tests.yml only ran inline smoke scripts on Ubuntu, never the pytest
suite, so broken tests went unnoticed on main. Mirror ComfyUI_PromptManager:
- pytest on ubuntu/windows/macos x Python 3.10-3.13 (fail-fast off)
- separate coverage job enforcing fail_under = 85
- syntax check every web/*.js as an ES module
- CPU-only torch on Linux to avoid the ~2.5 GB CUDA wheel
- Groups have no node-menu hook, so the menu-hook refactor dropped their
custom color entry; add it via getCanvasMenuItems using the group under
the mouse, as the frontend's own Comfy.GroupOptions does
- The "Auto-shade Title" setting was read but never applied
- Share one hidden color input instead of three
- Don't mark the canvas dirty while merely building the node menu
Replace monkey-patches of LGraphCanvas.onMenuNodeColors and the
canvas/node menu getters (applied after a 500ms timeout, and scraping
.litecontextmenu DOM for "No color") with the frontend's
getNodeMenuItems/getCanvasMenuItems extension hooks. Custom colors move
into a "Custom Colors" submenu; follow-execution state moves to module
scope so repeated setup() calls no longer stack wrappers.
The backend now 403s folders outside the saved paths. Show the full path
and add it only on an explicit click; server-side refusals (e.g. a
too-broad folder) are shown in the status line.
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)
- Delete kikotools/core (registry/settings) and batch_prompts/node_simple
which nothing imports
- pytest.ini used the [tool:pytest] header and was ignored; make it the
single pytest config (drop the pyproject duplicate), keep
--strict-markers
- Add pytest-asyncio to dev deps; enforce 85% coverage via
[tool.coverage.report] fail_under
- 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
- Escape file names, paths and image metadata interpolated into
innerHTML in the local image loader and save image viewer (XSS via
crafted file names or a shared workflow's filename_prefix)
- Save image viewer: bind document drag listeners once and remove them
in disconnectedCallback instead of adding two per run; drop debug
logging and the window.kikoSaveImageTest helper
- Local image loader: check response.ok and surface 400/403 errors,
disconnect the ResizeObserver on node removal
- Gemini prompt: restore the user's node color after the run flash
- Workflow timer: chain onRemoved, merge properties in onConfigure
- Seed history: build entries with textContent
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.