dependabot/github_actions/github-actions-30f7f1d3b7
40
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7050f2328e |
Release v0.5.0 (#283)
* Release v0.5.0: stability fixes for drift status and draft toasts Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tests): stop hardcoding pack version in version-route assertion Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tests): parse pyproject independently in version-route assertion Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
2babbafb6b |
chore(security): pin Actions and harden test lock (#273)
* chore(security): pin Actions and update aiohttp * fix(security): generate universal test lock * fix(security): harden relock safeguards |
||
|
|
24857d5e9c |
Release v0.4.4 (#263)
* Release v0.4.4: funding metadata * docs: add GitHub footer to v0.4.4 notes |
||
|
|
575cfb7f63 | Release v0.4.3: publish latest updates (#256) | ||
|
|
543998e026 |
Release v0.4.2 (#253)
Bump 0.4.1 -> 0.4.2. Ships #251 (Comfy Registry node-list parsing fix) and records #252 (tag-driven automated release pipeline). First release cut via release.yml. |
||
|
|
bda4df576a |
Release v0.4.1 (#250)
* Release v0.4.0: update footer notice * Release v0.4.1 |
||
|
|
88dbfedf86 |
feat(nodes): Easy Image Batch keyframes_insert + insert-over-source +… (#246)
* feat(nodes): Easy Image Batch keyframes_insert + insert-over-source + clean batch - Rename optional input keyframe_batch -> keyframes_insert (node ID unchanged). - Insert-over-source mode: when keyframes_insert AND source_batch are both connected, composite the inserts over the source cut window (instead of the placeholder background). keyframes_insert alone keeps the offset/reconstruct behaviour over placeholder. - No image source connected -> clean placeholder batch sized by new width/height fallback widgets, instead of raising. width/height are appended LAST so older saved workflows keep their positional widgets_values alignment (fixes a mid-list insertion that shifted placeholder_color into source_frames). - alpha_batch marks only inserted/placed positions; source background is not counted as placed. - New pure planner plan_source_base_fill (torch-free) alongside plan_offset_placements; both unit-tested in CI. - Docs: behavior matrix spec, interactive HTML demo, and a generated "all versions" ComfyUI demo workflow under docs/designs/. Not yet implemented (specced as DRAFT in docs/designs, pending maintainer approval + tie-break decision): image1-4 "super-overwrite" layer across all modes, and removal of the Mode 2 unconnected-slot source_batch fallback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(deps): raise aiohttp [test] floor to >=3.14.1 The constraints lock already pins aiohttp 3.14.1 (the CVE-2026-54273..54280 fix line), but pyproject's [test] extra still declared aiohttp>=3.9. Dependabot keys off pyproject, not the lock, so the open declared range kept eight range-based alerts open even though the installed version is patched. Raising the floor to >=3.14.1 aligns the declared range with the lock and clears the alerts once this reaches the default branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(nodes): Easy Image Batch image1-4 super-overwrite + source passthrough Make the four manual slots the top compositing layer in every mode (select and insert): a connected imageN overwrites output frame imageN_frame on top of the sequence/inserts and the background. Higher slot wins a collision (image4 > image1); a slot beats a sequence pick / insert at the same position. Extracted as the torch-free plan_slot_overwrites planner + a shared _apply_slot_overwrites helper used by both code paths. Two model fixes ride along, per the finalised behaviour matrix: - An unconnected slot now contributes nothing -- imageN_frame is only a placement target for a wired image, never a source_batch pick. The old unwired-slot fallback (which surfaced seemingly-random frames) is removed. - source_batch + an empty frame list (no insert) now passes the source cut window straight through instead of a placeholder batch, matching the insert-mode empty-list passthrough. Tests: new plan_slot_overwrites planner cases + fake-tensor behaviour tests covering slot-beats-pick/insert, higher-slot-wins, passthrough-not-placed, and the removed phantom fallback. Docstring, tooltips, user guide, design spec (promoted DRAFT -> canonical), and CHANGELOG updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(nodes): assert selected_image_batch = picks/inserts + slots; drop matrix HTML demo selected_image_batch already packs every placed position -- both the source_frames picks / inserts and the manual-slot overwrites, with slots winning on overlap. Add explicit select- and insert-mode tests asserting the composition and ascending order, locking the behaviour in. Also delete docs/designs/easy-image-batch-matrix.html (stale interactive demo built against an earlier draft) and drop its companion reference from the design spec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(nodes): Easy Image Batch passthrough inpaint mask + frame-range syntax Two refinements to the Easy Image Batch passthrough/list behaviour: - Short-source passthrough is now an extend/inpaint mask. When source_batch is connected and the frame list is empty, the covered source frames are kept content (alpha 0.0) and the uncovered tail becomes the selection (selected_image_batch / selected_frames) with alpha 1.0 -- "keep the first N, generate the rest". Full coverage -> no gap, empty selection. The selection is the gap here (inverse of select mode); insert mode is unchanged. - The source_frames field accepts inclusive ranges: "1-5, 7, 9, 14-17" expands to 1,2,3,4,5,7,9,14,15,16,17, in both select and insert, via a shared torch-free parse_frame_tokens (descending/non-integer tokens warn). Tests cover the gap selection (short/full/cut-start-offset/slot-in-gap), the parser (ranges, bad tokens), and range syntax end-to-end. Docstring, tooltip, user guide, design spec, and CHANGELOG updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(designs): remove draft-era Easy Image Batch demo workflow + builder Delete easy-image-batch-demo-setup.json and build_easy_image_batch_demo.py (the leftover draft-era demo, sibling to the already-removed matrix HTML) and drop the companion reference from the behavior spec. A purpose-built demo will replace them later; the spec matrix + the node remain the canonical reference. Also drop the now-stale "(proposed)" from the spec title (it is canonical). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(user-guide): remove stale v0.1.5 Easy Image Batch screenshot The screenshot predates the node's keyframes_insert / super-overwrite / passthrough rework and is unreferenced. Removing it to be re-created fresh. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: point Easy Image Batch docs at the bundled Select-and-Rebuild demo Note the runnable demo workflow ("Easy Image Batch - Select and Rebuild", bundled into the Kforge Labs Workflows sidebar folder via #248) in both the user guide and the design spec, replacing the generated demo removed earlier. Referenced by path (not a clickable link) since the workflow lands via #248. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(nodes): address PR review — keyframe_batch alias + input guards - Restore keyframe_batch as a deprecated alias of keyframes_insert so pre-rename workflows (origin/main exposes the input) keep loading; an explicit keyframes_insert wins. Follows docs/maintainers/node-versioning.md (alias-then-deprecate); removal deferred to a release boundary. - Warn instead of silently returning a clean batch when source_frames is non-empty but no image source is connected. - Cap frame-list range expansion at 8192 frames/token to guard against typos like 1-99999999. - Docs: reframe the bundled demo-workflow note as forthcoming (the JSON lands via #248, not on this branch); document the alias, cap, and no-source guard in the user guide / design spec / CHANGELOG. Tests: alias routing + precedence, no-source warning, range cap + boundary (46 node tests). Fresh-eyes review: no correctness/compat issues (the IMAGE alias input adds no widget-position shift). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: point Easy Image Batch docs at the now-merged Select-and-Rebuild demo #248 merged to main and is now in this branch (via the main merge), so the demo workflow exists at docs/workflows/kforge-labs-workflows/. Repoint the user guide and design spec at it directly (present tense, real folder link) instead of the earlier "coming soon / pending" framing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: AI Machine <ai.machine@x.baconcph.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
1cf2fd31a2 |
feat(setups): add published setup catalog API (#214)
* feat(setups): add published setup catalog API * fix(setups): report registry storage diagnostics |
||
|
|
8c8546c17c |
Add queue-driven loop status controller (#205)
* Add queue-driven loop status node * Document ComfyUI server API testing * Harden loop queue controller * Guard loop controller HTTP requests |
||
|
|
447465ecd8 |
refactor(nodes): remove superseded LTX Director extra nodes + harden registry (#198) (#199)
* refactor(nodes): remove superseded LTX Director extra nodes + harden registry (#198) Remove the two experimental nodes layered on the WhatDreamsCost fork — KoolookAudioTranscriptTimeline (k_audio_timeline.py) and KoolookTimelineEditor (k_timeline_editor.py) — plus the transcribe_audio_timeline.py CLI and its test. The audio-transcript to Director-timing job now lives inside the fork via the Director's audio_transcript_json input, so the standalone nodes are redundant. Also drops the orphaned [audio]/faster-whisper extra and updates packaging, dev-sync, web, and docs references. Harden the node registry against the #183 failure: __init__.py now imports each node group independently and installs the /koolook/presets/* routes regardless of node-import results, so one missing or broken module degrades to 'that node absent' instead of zeroing every node and 404'ing the sidebar disk routes. A new test keeps every root k_*.py listed in dev-sync's RUNTIME_PATHS. Closes #198. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(nodes): ship loader gates via dev-sync-audio; generalize RUNTIME_PATHS guard (#198) Address the K-Flame review on #199: MEDIUM — the #198 refactor made koolook_versioning.py a single registration gate, but dev-sync-audio shipped the new __init__.py without it (and the k_*.py-glob guard test couldn't see it). Add koolook_versioning.py AND koolook_install_guard.py (whose absolute-import fallback raises uncaught) to AUDIO_PATHS so a scoped sync can never leave the loader pointing at a module the target lacks. Generalize the guard test: parse __init__.py's actual load-time import targets (relative imports + _merge_node_group calls) and assert each is in RUNTIME_PATHS, instead of globbing k_*.py which missed the non-k_ gates. Add an AUDIO_PATHS gate-coverage test. LOW — CHANGELOG now records the [audio]/faster-whisper + transcribe_audio_timeline.py removal as an external-contract break. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8058e17456 |
fix(snapshot): paired drift-corruption fix — duplicate-install guard + boot-time mismatch (#161, #162) (#186)
* fix(snapshot): prevent drift corruption from duplicate installs + boot-time mismatch (#161, #162) Closes #162 and #161 as the paired fix the issues describe — both touch the same data-loss surface and verify against the same dev-sync rig. installs at import time, log critical, register nothing in the alphabetical loser; client-side `window` sentinel as belt-and-suspenders; align .env.example / dev-iteration-loop / sync_to_dev / README at the Registry-derived `koolook/` path. compare against live /userdata + picks, set a drift flag on mismatch; gate periodic + pre-load autosave routing to `_unsaved_autosave/` while drifted; promote status pill to "drifted" precedence over "saved" with a recovery tooltip; auto-clear on the next markStateSaved (Save / Load). 26 new tests (14 install guard, 4 sidebar sentinel, 8 boot drift) — 316 total passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(snapshot): apply review fixes — canonical fingerprint + early loser exit + drift timestamp Review-driven follow-ups on PR #186: 1. **Symmetrize file-side fingerprint (MEDIUM).** `detectBootDrift` now uses a new `_canonicalStateFingerprint()` that normalizes the workflows tree through `normalizeWorkflowsStore` AND deep-sorts keys via `sortJsonValue`. Without this, hand-edited / cross-machine / pre-v0.3 files would false-positive as drifted on first boot (e.g. `archived: "false"` string vs `false` bool, missing `directories: {}`, key insertion-order asymmetry from the `{...wf, archived, module, tags}` spread). `_computeFingerprint` stays unchanged so the localStorage save baseline contract holds for existing users. New regression test verifies a shape-only diff does NOT trigger drift. 2. **Gate fork imports on `_is_winning_install` (LOW).** Non-winning duplicate installs now short-circuit before the heavy torch / PIL / diffusers / aiohttp imports, exiting in ~5 LoC instead of paying full import cost just to discard the mappings. 3. **Drift timestamp uses `driftDetectedAt`, not stale `lastNamedSaveAt` (LOW).** The localStorage save baseline could be from a prior session whose live state was already corrupt — surfacing it as "Last named save" in the drift tooltip would mislead. Replaced with the session-noticed-at timestamp from `_bootDriftDiagnostics`. 352 tests passing (316 prior + 26 from PR #186 + 1 new normalize- symmetry case). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(install-guard): fail safe on unreadable / non-UTF-8 siblings (#162) Re-review follow-up. The import-time duplicate-install scan could crash the entire plugin load (fail-silent — the alphabetical winner vanished too, not just the loser) on two unguarded paths: - detect_duplicate_koolook_installs: the per-entry is_dir()/is_file() over custom_nodes/ siblings was unguarded, so a non-traversable sibling (restrictive perms, broken mount, a special dir) raised PermissionError up into __init__.py. No duplicate even required. Now caught per-entry and skipped. - read_pyproject_version: read_text(encoding="utf-8") raises UnicodeDecodeError (a ValueError, NOT OSError) on a non-UTF-8 / binary pyproject.toml, escaping the `except OSError`. Now degrades to "?" via `except (OSError, ValueError)`. - __init__.py: wrapped the guard call site as a final backstop — any unforeseen guard failure now degrades to register-normally rather than aborting the import. Both paths reproduced before the fix; the first surfaced while running the suite from a /tmp checkout whose parent held a root-owned dir. 3 regression tests added (non-traversable sibling skipped; non-UTF-8 pyproject -> "?"; report survives a non-UTF-8 sibling). Full suite: 452 passed (449 + 3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: kflame99 <261674183+kflame99@users.noreply.github.com> |
||
|
|
201cdf3001 |
Track audio loop card setup metadata (#191)
* feat(audio): surface director flavor and prompt mode * Track audio loop card setup metadata * Fix audio card review issues * Harden audio card metadata capture |
||
|
|
1d9d40ab70 |
Add audio transcript timeline node (#189)
* Add audio transcript timeline node * Hook audio transcripts into Koolook Director * Add modular timeline audio timing workflow * fix(frontend): guard Comfy workflow draft cache * fix(audio): compose transcript timeline prompts * fix(audio): address transcript review findings * fix(ci): keep transcript tests lightweight * fix(timeline): restore image previews from saved paths * fix(timeline): scrub restored preview blobs |
||
|
|
4739dd4203 |
feat: Easy_Utility, Easy_LoadVideo, and preview fixes (#184)
* feat(nodes): add Easy_Utility — INT → padded STRING dispatcher First mode `int_to_padded_string`: takes an INT widget (with up/down arrows) and emits a zero-padded STRING (default `001`, no `v` prefix). The downstream `koolook_versioning.resolve_version_token` prepends `v` and re-pads, so wiring this into `EasyAIPipeline.version` / `Easy_VideoCombine.version` yields the standard `vNNN` token at execution. Designed for extension: the `mode` widget shipped from day one so adding further conversions (string→int, formatting variants, etc.) doesn't rewire saved workflows. Note: the previously-attempted JS preview-resolver refactor that aimed to fix the "preview shows stale version" bug was reverted in this worktree before commit — it didn't actually fix the bug (the real cause is the JS resolver not descending into ComfyUI subgraphs) and it broke shot_name resolution in the process. The preview-resolver work is NOT in this commit; only the Python node + tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(nodes): guard version widget against pysssss .replace crash PR #180 added a STRING `version` widget to EasyAIPipeline and Easy_VideoCombine. Three workflow states leave widget.value undefined or non-string when graphToPrompt iterates widgets: - Pre-#180 workflows replay an INT (e.g. `1`) into the new STRING slot. - Widget-to-input conversion voids the value in newer LiteGraph. - Easy_VideoCombine's dict-based widgets_values omits the new key entirely on workflows saved before #180. With pysssss/ComfyUI-Custom-Scripts installed, presetText.js patches every STRING TextWidget.serializeValue to run callbacks that do `value.replace(...)` for `{variable}` substitution. Any of the states above crashes graphToPrompt and turns Run into a no-op for the whole workflow ("Cannot read properties of undefined (reading 'replace')"). Fix: trap `widget.value` reads/writes on the version widget via `Object.defineProperty` so the property is always coerced to a string regardless of caller. `configurable: true` so downstream code can still override the descriptor if it needs to. Tests: 231 passing; both JS files parse OK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ai-pipeline): resolve subgraph version preview * feat(video): add path-aware load video node * fix(ai-pipeline): preview connected text names * fix(pr184): address review cleanup --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9834c10087 |
feat(nodes): single STRING version input + strict v001 naming (#180)
* feat(nodes): single STRING version input + strict v001 naming - koolook_versioning.py: shared version-token helper (bare number -> vNNN, else verbatim; disable_versioning is the master off-switch) - EasyAIPipeline: version is now ONE STRING field (typed or wired from a global version node), replacing the INT widget - Easy_VideoCombine: new STRING 'version' (replaces keep_silent_intermediate) + enable_overwrite; strict mode strips VHS's _NNNNN counter -> <name>_v001.<ext>; empty keeps the legacy counter - Fix empty JSON sidecar: record prompt graph as 'workflow' fallback when extra_pnginfo is absent (headless/API path) - forks: pin VHS dependency (1.7.7 / 0edce8e) - sync_to_dev: add koolook_versioning.py to RUNTIME_PATHS Tests: 199 passing; preflight_release all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(video-combine): JSON sidecar is a drag-loadable workflow Write the litegraph workflow graph at the top level of the metadata JSON (CreationTime tucked into graph 'extra') so dropping the sidecar onto the ComfyUI canvas reloads the workflow, instead of a non-loadable wrapper. Falls back to {CreationTime, prompt} when no graph is available (headless). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9f063345f7 |
feat(investigations): LTX23 audio-file lip-sync scaffold (#177) (#178)
* feat(investigations): scaffold #177 LTX23 audio-file lip-sync investigation Reorganize docs/investigations/ into numbered topic folders, each self-contained with narrative + upstream pin + patches + per-run snapshots. Add a generic sync helper that mirrors the dev-sync pattern for any investigation's third-party node modifications. Structure (new): docs/investigations/ 00_LTX23-base-1-step/README.md (rename of ltx-director-4k-transitions.md) 01_LTX23-audio-file-lipsync/ README.md -- problem, mechanism, patches, reversion, promotion upstream.json -- repo + version + pinned commit + sync target env + files patches/ -- source of truth for modified upstream files ltx_director.py -- adds `relay_overrides` multiline JSON input prompt_relay.py -- per-segment sigma per Prompt-Relay paper formula runs/ LOOP.md -- iteration protocol log.md -- rolling table (empty header for now) Sync helper: scripts/sync_investigation_patches.py <NN>_<topic> [--dry-run] - Reads upstream.json for target env var + file list - Backs up the target's current state once per day (<file>.bak.<YYYYMMDD>) - Worktree-aware: falls back to MAIN's .env via git rev-parse --git-common-dir - Prints a two-line report matching scripts/sync_to_dev.py shape - Per-investigation trigger phrase via `trigger:` key (sync-audio for #177) Env: .env.example -- documents new KOLOOK_WHATDREAMSCOST_PATH .env -- local path set Glossary updates (docs/reference/glossary.md): + Investigation folder + JSON file folder + Loop + Run + Investigation patches + sync_investigation_patches.py Closes the workflow gap from #177: MAIN is now the source of truth for investigation patches; the live install is a sync destination only. No manual file copying between iterations — `sync-audio` does it. * chore(ruff): exclude docs/investigations from lint The investigation patches under docs/investigations/*/patches/ are vendored upstream source (WhatDreamsCost-ComfyUI v1.3.2) carried so sync_investigation_patches.py can mirror them into the live install and so iterations can be diff'd against the pinned upstream commit. Linting them would force divergence from upstream and defeat that purpose. Mirrors the existing forks/ exclude precedent. Fixes the CI Lint & Smoke failure on PR #178. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: per-task automation modules + WhatDreamsCost Koolook fork Pivot docs/automations/ from "one folder per model" to "one folder per automation module" so the same diffusion model can host several independent tasks side by side. Replace the parallel docs/investigations/ tree with a fork of WhatDreamsCost-ComfyUI under forks/. Automation modules (new layout) - docs/automations/LTX-2.3/base-1step/ the existing single-stage automation, moved from docs/automations/LTX-2.3/ with its README, findings, and handoff-checklist rescoped to the task. The 4k-transitions backstory that produced the module moves here as backstory/4k-transitions.md. The earlier docs/investigations/experiments/ card PNGs/HTML land in example-cards/. - docs/automations/LTX-2.3/audio-lipsync/ the audio-file lip-sync module previously living under docs/investigations/01_…/. README, handoff, findings stub, backstory/, and runs/{LOOP,log}.md, all referencing the new fork. - docs/automations/README.md + CHEATSHEET.md updated to describe the module-per-task pivot and the new tree shape. - docs/automations/CONVENTIONS.md unchanged (cross-cutting loop contract still holds). WhatDreamsCost Koolook fork (new) - forks/whatdreamscost_koolook/versions/v1_3_2/ UPSTREAM_PIN.yaml pinned to upstream e81223a (v1.3.2), GPL-3.0 verified 2026-05-28. ltx_director.py MODIFIED — adds the relay_overrides multiline JSON widget on LTXDirector. Moved from docs/investigations/01_…/patches/. prompt_relay.py MODIFIED — per-segment σ per the Prompt-Relay paper formula σ = (L − w_eff) / (2 · √ln(1/ε)). Moved from docs/investigations/01_…/patches/. patches.py UNMODIFIED — vendored verbatim from upstream because ltx_director.py imports detect_model_type and apply_patches from it (same precedent as radiance_koolook's color_helpers.py). - Registered as LTXDirector__koolook_v1_3_2 ("LTX Director (Koolook v1.3.2)") so it coexists with an installed copy of upstream WhatDreamsCost-ComfyUI. Wired into the root __init__.py NODE_CLASS_MAPPINGS merge. - forks/forks_manifest.yaml + forks/THIRD_PARTY.md updated with the new entry (attribution, change log, license verified date, exposed node IDs). Removed - docs/investigations/ entire tree retired; its contents either moved to automation modules or to forks/. - scripts/sync_investigation_patches.py no longer needed — fork code lives in the Koolook tree and is synced via the standard dev-sync flow. - KOLOOK_WHATDREAMSCOST_PATH in .env.example same reason. - docs/investigations in pyproject.toml's ruff extend-exclude added in |
||
|
|
7d46d1cdf2 |
Release v0.3.7 (#175)
* Release v0.3.7: support docs and fixes * docs: align bug report privacy guidance |
||
|
|
1ae86da0de | Release v0.3.6: workflow draft quota hotfix (#168) | ||
|
|
96d4b51d7b |
Release v0.3.5 (#160)
* Release v0.3.5 prep * fix(sidebar): bypass stale browser cache for snapshot state * test(sidebar): guard no-store snapshot fetches |
||
|
|
b42bd4af77 |
chore(python): add bootstrap scripts + pip-audit + line-ending policy (#158)
Solidify the Python test-env path so any agent (Claude / Codex) or
human can bootstrap with one command, and so the global preflight
skill's pip-audit branch actually runs against this repo.
scripts/bootstrap_test_env.{ps1,sh}: one-liner per platform, idempotent
(no-op if .venv exists, --force / -Force to recreate), upgrades pip,
installs project + test extras.
pyproject.toml: add pip-audit>=2.7 to [project.optional-dependencies]
test so /preflight's Python audit branch lights up.
CLAUDE.md, Hard rules: replace the inline venv-bootstrap command with
a reference to the platform script.
AGENTS.md: short "Test Environment Bootstrap" pointer (mirrors CLAUDE.md
per the Claude/Codex symmetry rule).
.gitattributes: enforce LF for *.sh (otherwise Windows CRLF breaks the
bash shebang on POSIX) and CRLF for *.ps1 (Windows convention).
Triggered by the warmup-skill Python detection added in agent-playbooks
PR #34 — projects now need a clean bootstrap path for the new check to
have a recommended remediation.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
ff79b0f418 |
feat(video): add Easy_VideoCombine (#154)
* feat(video): add Easy_VideoCombine with absolute-path support Adds a Koolook subclass of VHS VideoCombine that bypasses ComfyUI's output-directory sandbox when filename_prefix is an absolute path — the VFX-pipeline missing piece for writing renders into a project tree (e.g. E:/projects/shot01/v003) instead of ComfyUI/output/. Implementation: - k_video_combine.py: ~60 LoC subclass + a pure _resolve_abs_target helper. Scoped-patches folder_paths.get_save_image_path for the duration of one combine_video() call so upstream's encoder, audio mux, batch manager, and progress bar run unchanged. No VHS code copied. Self-skips registration when VHS isn't importable (clean one-line stderr notice). - Discriminator: os.path.isabs(filename_prefix). No new input pin; pattern borrowed from spacepxl/ComfyUI-HQ-Image-Save's SaveEXR. - Optional create_path_if_missing BOOLEAN (default False) so typos surface as FileNotFoundError instead of spawning stray dirs. License audit (2026-05-16): - VHS upstream: GPL-3.0 (verified via raw LICENSE fetch). - HQ-Image-Save pattern source: MIT, copyright 2023 spacepxl. - Koolook: already GPL-3.0; both upstreams are compatible. - No source code from either upstream is incorporated. Verification: 18 pytest cases green (12 existing + 6 new covering relative passthrough, absolute resolution, create-on/off branches, trailing-separator normalization, and empty-basename rejection). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(starter-preset): pre-favourite Easy_VideoCombine Adds the new node to the shipped starter_preset.json picks so fresh installs surface it in the Kforge Labs sidebar's Nodes section without the user having to discover it via Comfy's stock search first. Pairs naturally with the existing LoadEXR / SaveEXRFrames picks from HQ-Image-Save. Existing installs with non-empty picks are unaffected (the seeder skips them by design); they can either Load -> "starter" in the Snapshot dialog or hit "+" once in the Nodes section to pull it in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(dev-sync): include k_video_combine.py in RUNTIME_PATHS Without this entry the new Easy_VideoCombine module is never copied into the dev ComfyUI install, so the node fails to register on restart with a silent ImportError. Caught when the maintainer restarted ComfyUI and couldn't find the node in the search palette. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(starter-preset): include all Koolook nodes in default picks Fresh installs should land on a populated Nodes (favorites) panel that immediately surfaces what Koolook provides — otherwise new users see only third-party picks and have no entry point into the Koolook-native workflow. Adds the 8 currently-registered Koolook node IDs to the shipped starter preset alongside the existing third-party essentials: EasyAIPipeline, EasyResize_Koolook, EasyWan22Prompt, Easy_Pattern, Easy_VideoCombine, Easy_hdr_VAE_decode, Easy_hdr_VAE_encode, KoolookLoadCameraPosesAbsolute, easy_ImageBatch Also adds Easy_VideoCombine to the README's Included Nodes table. Existing installs with non-empty picks are unaffected by the seeder (by design). To pull the Koolook defaults into an existing install, Quick Save first, then Snapshot -> Load -> "starter". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(video): reach VHS_VideoCombine via nodes.NODE_CLASS_MAPPINGS The previous `from videohelpersuite.nodes import VideoCombine` fails inside ComfyUI's loader context because VHS uses relative imports throughout (`from .logger ...`, `from .utils ...`) that only resolve when VHS is loaded through ComfyUI's importlib spec — not through a plain absolute import path. The error surfaced as No module named 'utils.install_util'; 'utils' is not a package at Koolook load time, which caused the node to self-skip with an empty NODE_CLASS_MAPPINGS — invisible in ComfyUI's search palette. Fix: look up VHS_VideoCombine through `nodes.NODE_CLASS_MAPPINGS`, which is the documented cross-pack interface. ComfyUI's loader (custom_nodes/.../__init__.py exec_module → merge into global) runs to completion for each pack before moving to the next, and VHS sorts before Koolook alphabetically in every layout we've seen, so by the time Koolook's __init__.py runs the global registry already has VHS_VideoCombine in it. Verified against ComfyUI's nodes.py load_custom_node() at load_custom_node() lines 2196 (exec_module) and 2225-2228 (merge), which guarantee this ordering. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(video): add output_directory input for split path/name editing Overloading filename_prefix for absolute paths means losing the "just type a name" affordance — once you put the path there, there's nowhere to specify just the file root. Across many renders into the same project tree this becomes tedious: every iteration retypes the whole path. Adds an optional `output_directory` STRING input. When set: - filename_prefix is treated as just the filename root; any path components are stripped (so accidental typos like "sub/clip" don't double-up under the directory). - Absolute output_directory writes directly to that location. - Relative output_directory joins under ComfyUI's output/. When output_directory is empty, the original overloaded filename_prefix behavior is preserved unchanged (back-compat for the one-field flow). Implementation: - New pure helper `_compose_prefix(filename_prefix, output_directory)` combines the two fields into one effective prefix. The existing isabs discrimination then handles sandboxed vs absolute writes uniformly downstream. - 6 new tests cover all combinations: empty/abs/relative directory, path stripping from filename_prefix, trailing-separator trim, empty-name fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(video): coerce frontend 'undefined' STRING values to empty The ComfyUI frontend serializes a STRING widget with empty default as the literal string "undefined" when left untouched (a known long-running quirk). Without defensive normalization, an untouched output_directory was treated as a real directory name and prepended to the user's path: filename_prefix = "E:/renders/clip_v003" (typed by user) output_directory = "undefined" (frontend default, not typed) _compose_prefix(...) -> "undefined/clip_v003" (relative) _resolve_abs_target -> None (not absolute) super().combine_video -> writes to <output>/undefined/clip_v003_* The QuickTime ended up at <ComfyUI>/output/undefined/<name>.mov with a spurious `undefined/` subfolder, instead of at the absolute path the user actually specified. Fix: a `_normalize_text_input()` helper coerces the literal strings "undefined" / "null" / "None" (case-insensitive, post-strip) to "". Applied to both filename_prefix and output_directory at the top of combine_video() before any path composition. 4 new tests cover the sentinels and a regression test for the exact path-composition behavior that produced the bug. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(video-fe): wire format-specific widgets for Easy_VideoCombine VHS's frontend extension (web/js/VHS.core.js:2030) gates its format-widget machinery on `nodeData?.name == "VHS_VideoCombine"` — strict-equality on the registered node ID. Our subclass uses a different ID (`Easy_VideoCombine`), so none of VHS's per-format widget rendering ran. The user saw: [VideoHelperSuite] - WARNING - Missing input for p has been set to hq on every execution (profile defaulting to "hq" for ProRes), and had no way to pick anything other than the default — because the profile dropdown was never rendered. Fix: a minimal frontend extension at web/easy_video_combine.js (~80 LoC) that replicates `addFormatWidgets` for our node name. Hooks the format dropdown's callback to add/remove per-format widgets the same way upstream does. Self-contained — does not import from VHS's module (their helpers aren't exported as ES imports). Verifies via the actual ComfyUI install after dev-sync; static preview servers can't exercise it (depends on LiteGraph's registered_node_types at runtime). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(video): strip 'undefined' from anywhere + one-shot debug log The previous normalization only caught the EXACT literal "undefined" / "null" / "None" at field-level. Adds two stronger defenses for the case where a sentinel still slips through: 1. _normalize_text_input now also strips surrounding quotes (paste- from-Explorer artifact) and handles embedded newlines by taking the first non-empty non-sentinel line (defends against multi-line Text Multiline inputs with trailing blank or junk lines). 2. New _strip_sentinel_components helper drops any path component that is exactly a sentinel string from the composed effective_ prefix — belt-and-suspenders for any path that somehow carries `\undefined\` as a literal segment by the time it reaches us. Also adds a one-shot print() of fp_raw, od_raw, normalized values, and effective_prefix at the top of combine_video. The user is seeing an undefined/ folder under their custom path despite the field-level normalization, and pure code tracing doesn't reproduce it — the log will surface what kwargs actually contains at run time so we can fix the real cause if defensive stripping doesn't already handle it. To be removed in a follow-up once root cause is known. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(video): trim node docstring + tooltips, remove debug print - Class docstring collapsed from ~20 lines to a single sentence (the frontend was showing it as the node description on right-click, cluttering the UI). - Widget tooltips trimmed to one short sentence each — same intent, no walls of text. - Debug print() added in |
||
|
|
f74b30603e | Release v0.3.2 (#149) | ||
|
|
8e489ad37a |
feat(snapshot): redesigned Save/Load dialogs + folder picker (#137) (#144)
* feat(snapshot): row-augment max(periodic, pre_load_*) + pytest infra (#137) Server-side preparation for the snapshot dialogs redesign. The Load dialog's "newer auto-save available" row flag now reflects the freshest recovery file regardless of which mechanism wrote it — before this we only stat'd ``periodic.json``, missing cases where the user's most recent Load wrote a fresher ``pre_load_*.json`` snapshot inside the same autosave subfolder. Extracts ``_latest_autosave_mtime(autosave_dir, named_mtime)`` from the inline block in ``list_presets`` so the scan is unit-testable. The helper walks ``periodic.json`` and every ``pre_load_*.json`` once, returns the max mtime if strictly newer than the named file, else ``None``. Same shape as before, broader source set. Introduces pytest as a first-class test runner: * ``tests/server/test_listing_autosave_mtime.py`` — 5 row-augment test cases (periodic-only, pre_load-only, max-of-both, no-newer, missing-dir). * ``[tool.pytest.ini_options]`` in ``pyproject.toml`` scopes discovery to ``tests/`` and uses ``importmode=importlib``. * ``[project.optional-dependencies] test = [...]`` defines the test extra (pytest + aiohttp, which is a runtime dep of ``koolook_routes`` that must be importable even when only a pure helper is under test). * ``.github/workflows/ci.yml`` gains a ``pytest`` job that installs via ``pip install .[test]`` and runs ``pytest -q``. Guards ``__init__.py``'s relative imports in a ``try / except ImportError / else`` block. Pytest's package collector walks up from test files looking for ``__init__.py`` and tries to import the one at the repo root — but the relative imports inside it only resolve under ComfyUI's custom-node loader, not under a standalone Python import. Without the guard, every pytest run aborts during collection with ``attempted relative import with no known parent package``. The fallback emits a one-line diagnostic and registers empty mappings so the package directory can still be imported by tooling. ComfyUI's loader still hits the ``else`` branch and behaviour is unchanged for real users. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(snapshot): folder picker + agent-driven visual harness (#137) Commit 2 of the snapshot dialogs redesign. Introduces the navigate- into folder picker from mockup section 6 — the picker that opens when the user clicks Save to… / Load from… in the redesigned dialogs (and that the Settings cog will be removed in favour of, once commit 6 lands). Path input is the source of truth: type/paste + Enter navigates, clicking a folder row drills in, ↑ Up climbs one level. Files appear greyed below the directories as a "yes, this is the folder I expected" affordance. End-visible path overflow: the path input uses ``direction: rtl`` + ``unicode-bidi: plaintext`` so when the path exceeds the input width the leading ``/Users/…`` clips on the left, never the trailing folder name. Per mockup section 6. Visual harness — the path the issue called out as "visual verification is a hard gate": * ``.claude/launch.json`` defines ``design-server`` (``python3 -m http.server 8765``). Agent / maintainer can spin this up via ``preview_start`` or directly from a terminal. * ``docs/designs/_harness/folder-picker.html`` mounts the real ``showFolderPicker`` from ``web/sidebar/modals.js`` against a canned filesystem, with one trigger button per mockup-illustrated state (default / new-folder-input / empty / error). Cache-busts module imports so editing modals.js while the harness is open picks up fresh code on reload. * ``docs/maintainers/visual-harness.md`` documents the workflow, when to use the harness vs. dev-sync, and the backtick-in-CSS- comments pitfall that broke this commit twice during development. * ``CLAUDE.md`` now lists the harness as the preferred agent-driven path; dev-sync stays the integration-level gate. Server: ``/koolook/presets/browse`` gains an opt-in ``?files=1`` parameter that returns child JSON files alongside the directories. Default response shape unchanged so the legacy Settings dialog keeps working. ``_list_child_files`` mirrors ``_list_child_dirs``'s swallow-OSError-per-entry policy so a broken symlink mid-listing doesn't sink the whole response. Tests: six new pytest cases in ``tests/server/test_browse_list_children.py`` covering directory sorting, autosave-subfolder hiding, JSON-only filter, case-insensitive extension match, and the empty-result paths. ``pytest`` runs all 11 tests cleanly under the existing CI job from commit 1. What's still verified via dev-sync (not via the harness): * Real Comfy theme tokens (the harness fakes ``--comfy-menu-bg`` etc. with dark-theme defaults). * End-to-end Save → Load round-trip across the new library path. * Picker interaction with ComfyUI's keyboard manager. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(snapshot): modal headers stay title-only; route gloss to tooltip (#137) Follow-up to the folder picker commit. Maintainer flagged that the "Where snapshots are saved and loaded from." subtitle below the title was extra information the user doesn't need at first glance — if the title isn't self-explanatory, fix the title, don't pad it with a sentence. The mockup at docs/designs/snapshot-dialogs.html section 6 shows a title-only header for exactly this reason. Locks the rule in as a project convention so every dialog box behaves the same way going forward: * docs/maintainers/conventions.md — new "Modal headers" section spells out the rule, what counts as a "descriptive subtitle" (remove) vs. a "functional info row" (keep), and how to plumb explanatory copy via a hover tooltip on the title instead. * web/sidebar/modals.js — makeModalShell now accepts ``titleTooltip``, applied as the ``title`` attribute on the modal title element so the gloss is mouseover-discoverable but never visible by default. showFolderPicker drops its ``subtitle`` param (it was added one commit ago, no other callers) in favour of ``titleTooltip``. * docs/designs/_harness/folder-picker.html — harness re-points its explanatory text to ``titleTooltip``. Verified in the harness: the visible header is title-only, the tooltip surfaces on hover. Applies prospectively to the Save / Load dialogs (commits 3 and 4) — they will also be title-only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(snapshot): redesigned Save dialog with inline library row + Save to... + 4-state button (#137) Commit 3 of the snapshot dialogs redesign. The Save dialog is now a single modal (was three: three-way overwrite / input prompt / confirm) that matches mockup section 2: * Inline library-path info row at the top of the body — "Saved to" label on the left, "Open folder ↗" link on the right, full path below. Pure information; every action lives in the bottom command bar. * Bottom command bar with split layout: Save to... on the left, Cancel + Save as new... + Save on the right (spacer in between). * Save to... opens the folder picker from commit 2. On Use this folder, the dialog calls saveSettings() to persist the new library path, refreshes its in-place library row, and flips the primary button into Dirty state. * The primary Save button cycles through the four states from mockup section 5: - Default ("Save") — clean state, primary blue. - Dirty ("Save to new folder") — folder differs from the one the dialog opened with; the label spells out the consequence. - In progress ("Saving...") — disabled, dimmed primary. - Done ("Saved") — disabled, subtle green, brief pause before the dialog closes. * Save as new... still routes through the existing showInputModal plus presetExists guard, but the prompt no longer carries a library-path subtitle — the parent dialog already shows the target. Convention from the previous commit applied. tree.js now passes the new capability functions (saveSettings, browseDirectories, createBrowseDirectory, revealPresetFolder) into showSaveSnapshotDialog so the dialog can invoke the folder picker end-to-end inside the Save flow. Visual harness: docs/designs/_harness/save-dialog.html exercises all five mockup states (default tracked, untracked Save-as-new, dirty, in-progress, done) so the maintainer (or future agent) can re-verify without dev-syncing. Verified all states render correctly in the preview server. Static guard against the recurring backtick-in-CSS-comments trap: tests/sidebar/test_css_template_literal.py asserts that the CSS template literal in web/sidebar/constants.js contains exactly two backticks (open + close) and nothing in between. The trap bit this work three times (commits 2 and 3) — a literal backtick inside a comment closes the template early and silently bricks the entire sidebar. The test runs under the same pytest job introduced in commit 1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(snapshot): redesigned Load dialog with scoped recovery + inline delete (#137) Commit 4 of the snapshot dialogs redesign. The Load dialog is now a single modal matching mockup section 3: * Inline library row at the top, same shape as the Save dialog — "Loaded from" label + Open folder ↗ link + leaf folder name + full path. No 📂 emoji button (the link supersedes it). * Preset rows: click to load. The "Replace current state?" confirm modal is gone — clicking Load is the intent, the dialog title already says Load snapshot, no double-confirm. * Scoped recovery: when a preset has a newer autosave (per the server-side row-augment from commit 1), the first click DOES NOT load. It expands exactly ONE recovery row directly beneath the parent, showing the freshest entry (newest of periodic.json or pre_load_*.json) with its kind badge + meta line. The user then chooses: re-click the named row to load the named version, OR click the recovery row to restore the auto-save. Both options are surfaced inline; the user picks by clicking the one they want. * Inline delete confirm: × outlines the target row red, the bottom Close button transforms to 'Yes — delete "<name>"' (danger styling). A second click commits; Escape cancels and reverts. No second modal. * Bottom command bar matches mockup section 3 split layout: [Load from…] on the left, [Close] on the right. Load from… opens the folder picker from commit 2; on Use this folder it persists via saveSettings() and refreshes the listing. The pre-#137 YES/NO choice modal (``promptApplyChoice``) and the collapsible Recovery auto-saves <details> section are both removed — the scoped-recovery inline row + the click flow above subsume their job. (Commit 5 in the issue plan was specifically for dropping the YES/NO modal, which happens here as a natural consequence of the routing change; commit 5 will be a small follow-up if needed.) Behavior preserved from the pre-#137 implementation: * doNamedLoad — pre-load auto-save backup chain, partial-failure tracker handling, identical toast messages. * doAutosaveRestore — same backup chain, same markStateAutosaved() semantics, same restored-name derivation from subdir. Tree.js plumbs the three new caps (saveSettings, browseDirectories, createBrowseDirectory) into showLoadSnapshotDialog so the dialog can invoke the folder picker end-to-end. Visual harness: docs/designs/_harness/load-dialog.html exercises default, empty, scoped-recovery, and pending-delete states. Verified all four render correctly in the preview server. Net diff: -56 lines in the sidebar source (the redesign deletes more than it adds because the YES/NO modal + collapsible recovery disclosure + confirm modal for plain loads are all gone). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(designs): reconcile snapshot redesign sketch with shipped behaviour (#137) Commit 5 of the snapshot dialogs redesign. The code-level removal of the autosave-newer YES/NO modal happened in commit 4 as a natural consequence of the scoped-recovery inline row taking over its job; this commit reconciles the design docs so they describe what was actually built. docs/designs/snapshot-dialogs-redesign.md: * "What changes" item 4 reworded — the YES/NO modal is dropped; the affordance is now an inline scoped recovery row that opens directly beneath the clicked preset. * "What changes" gains items 2, 3, 5 covering the Save/Load dialog redesigns and the inline delete confirm that landed in commits 3 and 4. * Mermaid flow rewritten to reflect the new routing (preset click → scoped recovery expand → user picks named or recovery row), matching commit 4's onPresetClick implementation. docs/designs/snapshot-dialogs.html "Locked-in decisions": * Updated the autosave-modal item from "modal head reads X" to "modal — dropped" and explains the inline scoped recovery row replacement. * Added the title-only modal header convention (commit 2 follow-up) pointing to docs/maintainers/conventions.md. * Added a pointer to the visual harness workflow added in commit 2. No code changes — the YES/NO modal was already gone after commit 4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(snapshot): remove Settings cog + showSnapshotSettingsDialog + CHANGELOG (#137) Final commit of the snapshot dialogs redesign. The Settings cog disappears from the Snapshot toolbar — the snapshot row is now status indicator | Load | Quick Save | Save, matching mockup section 1. Library-path management now lives inside the Save dialog ("Save to…") and Load dialog ("Load from…"), both of which open the folder picker introduced in commit 2 and persist via the existing `saveSettings` route. Removals: * `showSnapshotSettingsDialog` (~325 lines) — the dialog body, its inline directory browser, and the "save folder location" state machine. The same browse functionality lives in `showFolderPicker` now; the same persistence happens via `saveSettings()` called from inside Save / Load. * `showThreeWayDialog` (~45 lines) — only consumer was the pre-#137 Save flow (overwrite / Save as new / Cancel), which the redesigned Save dialog absorbed into its primary action bar. * `renderLibraryLocation` (~17 lines) — replaced by inline rendering in the Save and Load dialogs' library rows. * Settings cog toolbar button + `openSettingsDialog` wiring in tree.js (~12 lines). * Orphan `.koolook-browse-*`, `.koolook-path-input-row`, `.koolook-load-library-row*`, `.koolook-settings-save-note` CSS rules in constants.js (~16 lines) — only consumed by the removed dialog. `.koolook-settings-folder-name` and `.koolook-settings- folder-path` stay; the new Save / Load library rows reuse them verbatim (git-blame continuity for the visual style). Adds: * CHANGELOG.md [Unreleased] entry summarising the whole #137 feature — picker, redesigned Save/Load dialogs, scoped recovery, inline delete, removed Settings cog, pytest infrastructure. Net diff for this commit: -377 lines. Cumulative PR diff (commits 1–6 + harness/convention follow-ups): all six issue ACs satisfied. The maintainer should dev-sync once before merging to confirm real-Comfy theme tokens render correctly (harness fakes the dark-theme defaults) and end-to-end Save → Load round-trips against the live preset folder behave. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): install pytest + aiohttp directly, not via pip install . (#137) The Pytest job introduced in commit 1 of this PR was failing on CI because ``pip install .[test]`` tries to build a wheel of the entire project. setuptools's flat-layout discovery then trips on the non-Python ``web/`` and ``forks/`` directories at the repo root and errors out with "Multiple top-level packages discovered in a flat- layout: ['web', 'forks']". The project is a ComfyUI custom node loaded directly from its source directory, not a pip-installable package. There's no wheel to build. Install the test deps explicitly instead — the ``[project.optional- dependencies] test`` block in pyproject.toml is the spec, CI mirrors it by hand. Worked locally because the venv install path went straight to ``pip install pytest aiohttp`` and never touched the project. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(snapshot): untracked dirty state + Load dialog Escape listener leak (#137) Two findings from the external review on PR #144: **Blocker — Save dialog's primary action with no tracked preset + folder changed via Save to...** ``updatePrimaryLabel`` was entering the "dirty" state whenever the library path diverged from the original, regardless of whether a tracked preset existed. The primary click handler then routed "dirty" to ``doOverwrite(current)`` — and with ``current === null``, ``sanitizeName(null)`` would throw / silently coerce to an empty string instead of asking the user for a name. Fix: ``isDirty()`` only matters when there is something to overwrite. With no tracked preset, the primary stays in ``default-new`` regardless of folder divergence. The Save to... button still updates the library row, so the user has feedback the destination changed; the eventual primary click then routes to the Save-as-new name prompt and writes into the new folder cleanly. Verified in the visual harness: opening Save as untracked, switching the library to ``/Volumes/Shared/different-library`` via the picker, then inspecting the primary button: * Label stays "Save" (was "Save to new folder" pre-fix) * Tooltip "Choose a name and write the snapshot." (was "Write into /Volumes/Shared/different-library.") * Library row still reflects the new path (saveSettings DID run) **Major — Load dialog leaked a document keydown listener per session** The inline-delete Escape-to-cancel handler was registered with ``document.addEventListener`` but never removed. Each open/close cycle of the Load dialog accumulated another stale handler; long sessions ended up firing the cancelDelete check on every Escape press across every dialog (no-op because ``pendingDelete`` was null in closed dialogs, but the listener was still alive and called). Fix: capture the handler by name, attach on open, tear down on close by wrapping ``overlay.remove`` (mirrors the pattern the modal shell already uses at ``modals.js:52-66`` for its own Escape handler). Verified by instrumenting ``document.{add,remove}EventListener`` and opening + closing the Load dialog five times in a row: every keydown listener added during the cycle was removed before the next cycle opened. No accumulation. CI gates: ruff, compileall, bandit (via preflight equivalent), pytest 12/12, preflight 4/4 — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ea188a4c70 | Release v0.3.1: KForge spotlight onboarding (#126) | ||
|
|
f8f8adf55d |
Use KForge spotlight as onboarding front door (#124)
* docs: use KForge spotlight and refresh guide showcases * docs: address guide showcase review polish * docs: fix auto-save showcase anchor |
||
|
|
70dab39781 |
Release v0.3.0: Snapshot library + Modules + Install-missing + Pages guide (#121)
Headline themes for 0.3.0: - **Snapshot library** — save/load full Kforge Labs state to a configurable on-disk path, per-preset autosave subfolders, Quick Save, status dot, in-UI recovery, atomic writes, YES/NO modal on newer auto-save, library folder browser, 24-hour timestamps with relative-recent tier. - **Modules** — tag a saved workflow `module` (or use the new "Save as module" checkbox on selection saves) and left-click splices the cluster into the live canvas instead of replacing it. Subgraph wrapper nodes carry their `definitions.subgraphs` entries on save. Insert pre-flight toast surfaces both pack-miss and subgraph-miss cases in plain English. - **Install missing for picks** — one-click installation of missing nodes via ComfyUI-Manager, with discovery → confirm → progress → result UX. - **Hover preview + group modes** — node mock card on row hover, segmented Repository / Theme grouping toggle in the Nodes section, flatten-on-search, spotlight-on-add, × removability for any visible favorite, theme-classifier rework. - **GitHub Pages guide** — bundled visual onboarding now also live at https://malkuthro.github.io/ComfyUI-Koolook/web/guide/, replacing the previous unmaintained wiki link in pyproject.toml's Documentation URL. - **Doc accuracy + polish** — refreshed forks/READMEs (v1_0_1 → v2_3_3), retired stale copilot-instructions, fixed namespace-suffix bullet in THIRD_PARTY.md. Tracked in #120. Full detail in CHANGELOG.md `[0.3.0]`. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8bbedfe3f7 |
docs: host visual guide on GitHub Pages, retire wiki URL (#117)
The bundled visual sidebar guide is now also live at https://malkuthro.github.io/ComfyUI-Koolook/web/guide/ — pulled from main via GitHub Pages (legacy mode, source=main/, enabled out-of-band via the repos/.../pages API). README's "get started" and "Learn More" sections, plus pyproject.toml's Documentation URL (used by the Comfy Registry package page), now point at the Pages site instead of the unmaintained wiki. The in-app Tools → Help (`H`) button continues to open the bundled local copy via `import.meta.url`, so the guide still works offline. While here, fixed README's stale "Tools → Info" wording — the actual button has the H help glyph and a tooltip reading "Open the Kforge Labs visual guide" (verified in web/sidebar/tree.js). Closes the wiki-URL part of #114; the rest of #114 (cross-ref polish, glossary, snapshot-schema reference page) stays open. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
87ecba2f4d |
Release v0.2.0: Wan 2.2 sequence fix + cinema color surface + alpha output (#43)
Renames the [0.1.10] CHANGELOG section to [0.2.0] (the rolled-forward work from PR #42 was never published as 0.1.10) and bumps pyproject.toml to match. Minor bump signals the meaningful surface changes: - New encode return tuple shape (LATENT, STRING, IMAGE). - Six new cinema log color spaces + ACES 2065-1 + Rec.2020 Linear. - Compress (Log) HDR mode with per-profile soft-shoulder + log-domain highlight denoise. - New latent_sampling parameter (sample / mean / mode). - Behavior change: exposure now applied in linear space for non-Raw source spaces (was a code-domain multiplier before). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
428d4f538b |
chore(nodes): drop Easy_Version + bump 0.1.9 (#41)
Per maintainer direction: Easy_Version was a one-line node that turned
an INT N into the string `vNNN` (zero-padded). The first trivial node
ever written for the pack, never used in production workflows. Removed
to keep the pack's surface focused on the higher-value nodes.
Anyone needing the exact behavior can either inline `f"v{N:03d}"`
downstream of an INT primitive in their workflow, or pull a more
general string-format node from KJ Nodes / ComfyUI-Custom-Scripts.
BREAKING for any saved workflow that references the `Easy_Version` ID
(node will fail to load on import). Same migration shape as the v0.1.5
__koolook_v1_0_1 cleanup.
Also rolls the [Unreleased] PR #39 LATENT-dict-wrap fix into [0.1.9]
so it ships in the same release.
Touched:
- Deleted: k_easy_version.py
- __init__.py: removed version_mappings / version_display imports + merges
- README.md: removed node-table row, Pipeline category list update,
Features bullet, Usage section, Node Code Overview line, install
category list — Easy Version no longer mentioned anywhere
- pyproject.toml: 0.1.8 -> 0.1.9
- CHANGELOG.md: [0.1.9] entry covering the removal + the carried-over
PR #39 fix
Net effect: pack drops from 8 nodes to 7. Koolook/Pipeline subfolder
now has only EasyAIPipeline.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
b7a17b570b |
chore(categories): split Koolook/VFX into Pipeline / Image / VAE + fix VAE display names (#38)
Three small UX fixes from the v0.1.7 post-release inspection:
1. Categories: the catch-all `Koolook/VFX` subfolder is replaced with
three more granular ones:
- Easy_Version, EasyAIPipeline -> Koolook/Pipeline
- easy_ImageBatch -> Koolook/Image (joins EasyResize)
- Easy_hdr_VAE_encode/decode -> Koolook/VAE
2. Easy_hdr_VAE_encode / Easy_hdr_VAE_decode display names: previously
the display equalled the node ID (`Easy_hdr_VAE_encode`) so the user
couldn't find them by typing `koolook` in the node-add search filter
— they were registered correctly but invisible to the filter. Now:
"Easy_hdr_VAE_encode" -> "Easy HDR VAE Encode (Koolook)"
"Easy_hdr_VAE_decode" -> "Easy HDR VAE Decode (Koolook)"
Matches the title-case " (Koolook)"-suffixed convention used by the
rest of the pack since v0.1.7.
3. README node tables + categories list updated to match.
Class IDs unchanged everywhere -- saved workflows that reference any of
these IDs continue to load and run unchanged. CATEGORY is purely a UI
hint and only affects the node-add menu hierarchy.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
ee8ccefdf8 |
docs(reorg) + chore: roll [Unreleased] forward + bump 0.1.7 (#37)
Bundles two changes for v0.1.7: 1. PR #36 (already on main, just rolling [Unreleased] -> [0.1.7]): suffix all node display names with " (Koolook)" so they group together in ComfyUI's node-add search. Class IDs unchanged. 2. Docs reorg: - git mv Glossary.md -> docs/reference/glossary.md - git mv RELEASING.md -> docs/maintainers/releasing.md - Three audience buckets under docs/: user_guide/ (end users), reference/ (lookups), maintainers/ (project ops). Each gets a short index README.md. - Top-level docs/README.md as the navigation entry point. - New: docs/maintainers/registry-api.md captures the Comfy Registry undocumented version-management API (deprecate/yank/status enum, auto-deprecation behavior, curl recipes, optional GH Actions workflow YAML for codified mgmt without exposing the token). - New: docs/maintainers/node-versioning.md codifies the rules from the v0.1.5 PR-review discussion for changing INPUT_TYPES / RETURN_TYPES / class names without breaking saved workflows. - Imported the existing untracked docs/ images that the maintainer started locally (node inventory screenshot, Easy Image Batch helper image). - Cross-references updated in README.md, CLAUDE.md, the issue template, and .claude/skills/add-external-fork/SKILL.md. Root drops from 6 -> 4 markdown files. The four that stay (README, LICENSE, CHANGELOG, CLAUDE.md) are the ones that *must* live there for GitHub / Comfy Registry / tooling / Claude Code reasons. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
f57ea38b71 |
feat(EasyResize): rename to EasyResize_Koolook + proper KJ Nodes attribution (#35)
Resolves the genuine `EasyResize` node-ID collision with `ComfyUI-EasyFilePaths` (orange-triangle warning #8 in ComfyUI-Manager). Both packs were registering the bare class name `EasyResize` — load order determined which one a user actually got. Changes: - k_easy_resize.py: - Add SPDX-License-Identifier: GPL-3.0-or-later header. - Add proper attribution block crediting kijai/ComfyUI-KJNodes (Resize Image V2, GPL-3.0) as the original interface inspiration, with a per-feature list of how the Koolook implementation materially extended the surface (aspect-ratio parser, keep_proportion modes, mask + composed outputs, device selection, target/original W/H reporting, color-panel passthrough). - Register both `EasyResize_Koolook` (canonical, new) AND `EasyResize` (deprecated alias) IDs pointing at the same class. Saved workflows using the legacy `EasyResize` ID continue to load and run; new workflows pick `EasyResize_Koolook` from the search and avoid the conflict. - Display name on legacy alias reads `Easy Resize (deprecated, use 'Easy Resize (Koolook)')` to nudge users to switch. - forks/THIRD_PARTY.md: full attribution entry for KJ Nodes (was missing — manifest had it as `license: "unknown"` / `sync_state: "needs-upstream-reference"`). License verified as GPL-3.0 against upstream `LICENSE` file (same as our pack — no relicense pressure). - forks/forks_manifest.yaml: KJ Nodes entry upgraded from `license: "unknown"` to `license: "GPL-3.0"` + `license_verified_at`, `status: "inspired_reimplementation"` (more accurate than `modified`), and the two exposed_node_ids are now recorded explicitly. - README.md: node table, category list, "Easy Resize" section, and Wan-related descriptions all updated to use the new canonical `EasyResize_Koolook` ID and display name. - pyproject.toml: 0.1.5 -> 0.1.6. - CHANGELOG.md: [0.1.6] entry covering the rename + attribution work. Follows the node-versioning rules from the `node-versioning` discussion in PR review: rename via alias, never break saved workflows, document the deprecation in CHANGELOG. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
60e90ec6c8 |
Release v0.1.5: registry hygiene + drop unused v1_0_1 Radiance fork (44 nodes → 8) (#34)
* chore: remove maintainer dev workspaces from git tracking + bump 0.1.4 ComfyUI-Manager was reporting "44 nodes / 13 conflicts" for this pack because the Comfy Registry's static scanner walks every `*.py` in the published archive and reads `NODE_CLASS_MAPPINGS` dicts — including the ones inside vendored third-party clones sitting in two folders that were never meant to be part of the package: upscaler_FIX/github_repos/ComfyUI-SuperUltimateVaceTools/ upscaler_FIX/github_repos/ComfyUI-multigpu/ nuke_CAM_exporter/wrapper_FIX/wrapper_fun_camera_*-code/ nuke_CAM_exporter/_Utils-CAM-track/Github-Repos/... These folders were the maintainer's local debugging / camera-tracking scratchpads, accidentally committed in Dec 2025 commits |
||
|
|
5881e8a808 |
Release v0.1.3: GPL-3.0 relicense, Easy_hdr_VAE_encode/decode (v2.3 fork), license/fork skills (#33)
* chore: relicense to GPL-3.0 and backfill upstream attribution ComfyUI-Koolook v0.1.0 and v0.1.1 shipped under a claimed MIT license while already incorporating GPL-3.0-derived code from fxtdstudios/radiance under forks/radiance_koolook/. GPL-3.0 §5(c) requires the entire combined work to be GPL-3.0 — relicensing the whole package is the simplest honest fix and aligns with the dominant license posture of the ComfyUI custom-node ecosystem. Changes: - Add full GPL-3.0 LICENSE text at repo root (pyproject.toml already referenced LICENSE but the file did not exist). - Update README.md "## License" section to declare GPL-3.0 and explain the §5(c) implication for downstream users. - Backfill forks/THIRD_PARTY.md with full attribution entries for the Radiance v1.0.1 baseline (already in the repo) and the upcoming Radiance v2.3.3 VAE subset (scaffolded in v0.1.2). - Update forks/forks_manifest.yaml: set the existing entry's license field to "GPL-3.0" (was "to_verify_at_source_ref"), add a license_verified_at field, and pre-register the v2.3.3 VAE entry with verified license metadata. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(skills): add license-pre-check + port add-external-fork to .claude/skills/ Two skills to prevent the v0.1.0/v0.1.1 license-incompatibility class of bug from happening again on future forks: - license-pre-check: blocking compatibility audit run BEFORE any code is copied or written based on upstream. Reads the upstream LICENSE, compares against ours via an SPDX matrix, refuses to proceed on incompatible combinations, and records the result for the manifest. - add-external-fork: ports the existing .cursor/skills/add-external-fork procedure to .claude/skills/ so Claude Code can run it directly. The port adds a mandatory Phase 0 ("run license-pre-check first") and expands Phase 3c to require GPL §5(a) modification headers on derived files. The .cursor/ copy is left in place for Cursor users; the two should be kept in sync manually until a unifying mechanism is adopted. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(forks): port v2.3 VAE Encode/Decode as slim video-friendly wrapper Adds forks/radiance_koolook/versions/v2_3_3/ — a slim re-implementation of RadianceVAEEncode / RadianceVAEDecode under the namespace suffix __koolook_v2_3_3. Mirrors the input surface of upstream's RadianceVAE4KEncode / RadianceVAE4KDecode (fxtdstudios/radiance v2.3.3, GPL-3.0) but deliberately skips the 4K cosine-blend tile engine. Why skip the tile engine: that pipeline errors with `size of tensor a (192) must match the size of tensor b (132) at non-singleton dimension 4` when chained into Wan 2.2 video workflows, because the tiler's spatial layout disagrees with the video VAE's internal temporal-aware encoding. Modern video VAEs (Wan 2.2, Hunyuan, CogVideoX, LTX) handle their own internal stitching, so the tiler is redundant and actively breaks the chain. What ships: - forks/radiance_koolook/versions/v2_3_3/nodes_vae.py — RadianceVAEEncode and RadianceVAEDecode classes (~330 lines), rank-agnostic, with a STRING `debug_info` second return that mirrors the pattern from v1's RadianceOCIOColorTransformV2. - forks/radiance_koolook/versions/v2_3_3/__init__.py — applies __koolook_v2_3_3 namespace suffix and " (Koolook v2.3.3)" display suffix, mirroring the v1_0_1 wrapper layout. - forks/radiance_koolook/versions/v2_3_3/UPSTREAM_PIN.yaml — pinned upstream commit f262f47 + license metadata. - forks/radiance_koolook/__init__.py — merges v1_0_1 + v2_3_3 mappings. - pyproject.toml — version bumped to 0.1.2. - CHANGELOG.md — full [0.1.2] entry covering the license relicense, the new v2_3_3 fork, the two new skills, and the carry-over RELEASING.md docs that landed on main since 0.1.1. Existing __koolook_v1_0_1 nodes are unchanged and remain available for backward compatibility with saved workflows. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(forks/v2_3_3): rename VAE nodes to Easy_hdr_VAE_encode/decode + bump 0.1.3 Per user direction, the new video-friendly VAE wrappers are now exposed under clean Koolook-style IDs and display names — no more `__koolook_v2_3_3` suffix in the ComfyUI node-add UI: - `Easy_hdr_VAE_encode` (was `RadianceVAEEncode__koolook_v2_3_3`) - `Easy_hdr_VAE_decode` (was `RadianceVAEDecode__koolook_v2_3_3`) Why: when both upstream Radiance v2.3.3 and our pack are installed, both expose `Radiance VAE Encode` in the search — the Koolook-suffixed display name still buried "Koolook" at the end of a long line, so users were likely to mis-pick. The new names lead with the Koolook brand and the Easy_* family convention used elsewhere in this pack (Easy_Version, EasyAIPipeline, EasyResize, etc.). Mechanism: new `SKIP_VERSION_SUFFIX = {...}` set in `versions/v2_3_3/__init__.py` opts specific IDs out of the auto-suffix. The version is still tracked structurally (file path) and textually (file header, UPSTREAM_PIN.yaml, forks/THIRD_PARTY.md). Future Koolook-original nodes in this fork can opt out the same way; ports of upstream-named classes still get the suffix by default for collision safety. 0.1.2 was test-published to the registry with the previous IDs but never tagged or formally released; bumping to 0.1.3 publishes the renamed version cleanly. Old 0.1.2 entry retained in CHANGELOG with a "superseded" note for transparency. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
e8a61e5270 |
Release v0.1.1: re-tag at current main HEAD (#31)
The v0.1.0 tag points to an orphaned merge commit (PR #26 was re-merged at |
||
|
|
7121094780 |
ci: add lint + bandit security gate, wire main protections (#29) (#30)
* ci: add lint, syntax-smoke, and bandit security gate
Two gating jobs for PRs to main: ruff + python -m compileall ("Lint & Smoke")
and bandit static analysis ("Security Gate"). Bandit excludes vendored sub-trees
(forks/, upscaler_FIX/, nuke_CAM_exporter/).
Pattern lifted from malkuthro/ForgeFlow's CI. Status checks will be wired into
the Protect-main ruleset after first green run on main.
Refs #29
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* ci: exclude vendored sub-trees from lint and smoke
Ruff was flagging 93 errors in upscaler_FIX/github_repos/ComfyUI-multigpu/
(vendored upstream copies). Match bandit's exclude list so lint only checks
this repo's own code.
De-vendoring is tracked separately in #28.
Refs #29
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: remove unused imports, add ruff config
- Drop unused os/json from k_easy_version.py and torch from k_easy_wan22_prompt.py.
- Add [tool.ruff] in pyproject.toml: extend-exclude vendored sub-trees,
per-file-ignores F841 for k_easy_resize.py (intentional symmetry vars
pad_b/pad_r/original_B/original_C are kept for clarity).
- CI workflow simplifies to plain `ruff check .` since config is now centralized.
Refs #29
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
c529a1f947 |
Prepare v0.1.0 release metadata and node naming.
Set project version to 0.1.0, align changelog entries for the new fork/versioning baseline, and include the compact OCIO node ID override for the v1_0_1 Radiance set. Made-with: Cursor |
||
|
|
0e9bc6e73f |
chore: bump version to 1.0.1 to trigger registry publish
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
7d856cf67f | Update PublisherId in pyproject.toml | ||
|
|
15f7846ff5 | chore(pyproject): Add pyproject.toml for Custom Node Registry |