ci(deps): bump actions/upload-artifact from 4 to 7#3
Closed
dependabot[bot] wants to merge 558 commits intomainfrom
Closed
ci(deps): bump actions/upload-artifact from 4 to 7#3dependabot[bot] wants to merge 558 commits intomainfrom
dependabot[bot] wants to merge 558 commits intomainfrom
Conversation
One-click Windows script that: - Builds installer, manual-update, and Wizard Guild executables - Commits to SFW repo and pushes - Mirrors source + binaries to NSFW sibling repo and pushes - Supports --no-push, --installer-only, --guild-only, --push-only Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous version tried to git-commit 300MB .exe files which exceeds GitHub's 100MB limit. Now: - Adds .gitignore (dist/, build/, __pycache__, etc.) - Builds all executables into dist/ - Pushes SOURCE only to both SFW and NSFW repos - Uploads binaries to GitHub Release via gh CLI or Python fallback - Uses selective file copy for NSFW mirror (preserves NSFW-only content) - Supports --tag vX.Y to set release version Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ntax Inline Python in batch files breaks cmd.exe parser. Moved release upload to standalone release_upload.py. Also fixed NSFW mirror to use selective copy preserving NSFW-only content. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root now shows only: Install.bat — run the installer Settings.bat — configure guild settings Wizard Guild.bat — launch the wizard guild Developer scripts moved to dev/ (rebuild.bat, release_upload.py, debug_guild.bat, update.bat). Rebuild.bat paths updated for dev/ location. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Canonical spellcaster_core/ package with architectures, composites, node_factory, model_detect, and prompt_enhance modules. GIMP plugin and Wizard Guild now import from this single source via thin shims. 4 ComfyUI nodes: SpellcasterLoader (auto-arch), SpellcasterPromptEnhance (LLM), SpellcasterSampler (auto-config), SpellcasterOutput (privacy). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds _install_spellcaster_nodes() to copy comfyui-spellcaster/ into custom_nodes/ComfyUI-Spellcaster during installation. Skips local nodes in the git clone loop. Manifest updated with node pack entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ComfyUI-Spellcaster now lives at github.com/laboratoiresonore/ComfyUI-Spellcaster - Installer manifest updated: git clone (auto-update) instead of local copy - Removed _install_spellcaster_nodes() local copy logic from installer - Synced all rewritten node files with correct ComfyUI APIs - Added web extension, workflow templates, README, LICENSE Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added comfyui-spellcaster/ with all 4 base nodes + 2 NSFW additions: SpellcasterNSFWLoRA (preset/manual/stack) and SpellcasterNSFWLoRAModelOnly - NSFW LoRA presets for Flux Dev, Klein, SDXL, Illustrious, WAN I2V - Updated manifest.json to git clone from ComfyUI-Spellcaster-NSFW repo - Updated rebuild.bat to sync only base files (preserve NSFW additions) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aming - Fix missing architectures (ltx, wan, seedvr, chroma, pony) in GIMP, Darktable, and guild_common ARCH_LORA_PREFIXES — video LoRAs no longer leak into SDXL/SD1.5 dropdowns - Change server.py LoRA fallback from ["sd15"] to ["unknown"] to prevent unclassified LoRAs from polluting SD1.5 menus - Add POST /api/scaffold_edit endpoint with persistent scaffold_overrides.json — scaffold editor changes now survive restarts - Wire frontend ScaffoldEditor to debounce-save edits to server - Load server-side scaffolds into editor on mount (auto-detected wizards now appear in the scaffold list) - Add background LLM-powered scaffold enhancement: auto-names "Unnamed Wizard" entries using local LLM when available - Installer: add LLM URL prompt, ComfyUI server probing, shared settings - Add "Wan-2.2-I2V\\" to wan LoRA prefixes in canonical model_detect.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When installed via ComfyUI Manager (git clone), users only get the nodes. On first load, Spellcaster now: - Drops Install_Spellcaster_Suite.bat into custom_nodes/ — one click to clone the full repo and run the installer - Prints a console banner with instructions - Shows a dismissable toast in the ComfyUI web UI pointing to the bat All signals are suppressed once the full suite is detected (spellcaster_settings.json or .suite_installed marker). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nager flow - Main README: add "Already Using ComfyUI Manager?" section explaining the auto-dropped Install_Spellcaster_Suite.bat - ComfyUI node README: add Full Spellcaster Suite section, note about install toast, add Pony to supported architectures table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
During installation, features whose custom nodes and required models are already present on the ComfyUI server are now shown as locked (✓) and cannot be toggled off — reinstalling them would be a no-op. - Add feature_already_installed() that cross-references manifest against server_info from probing (nodes via provides, models via filename match) - step_select_features now accepts server_info, shows locked features as cyan [✓] with "Already on server" reason - step_install_nodes skips node packs whose provides are already in available_nodes - Hardware profile summary shows installed count separately Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract KLEIN_MODELS, FLUX2_VAE to module-level constants in _workflows_v2.py; delete 8 duplicated inline dicts (~70 lines) - Import KLEIN_MODELS in comfyui-connector.py instead of redefining - Add STUDIO_FACE/BODY/SCENE dimension constants for canvas-aware compositing across the Magic Studio 5-act pipeline - build_photobooth: fixed square output (STUDIO_FACE_W×STUDIO_FACE_H) instead of deriving from reference aspect; add transparent= flag for rembg background removal with alpha matting - build_rembg: expose alpha_matting and model params - build_klein_blend: default scale from STUDIO_BODY_IN_SCENE_SCALE so body PNGs fill ~85% of scene height automatically Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
For users who run ComfyUI on a separate machine in their local network. Probes the remote server, auto-detects local apps (GIMP, Darktable), installs plugins + Wizard Guild + shortcuts — zero interaction needed. - install_remote.py: standalone autonomous installer - Network scan (--scan) to auto-discover ComfyUI on /24 subnet - Server probing via /system_stats + /object_info endpoints - Feature detection by cross-referencing server nodes vs manifest - Auto-detect GIMP, Darktable paths (cross-platform) - Plugin deployment with remote server URL pre-configured - Wizard Guild + scaffold installation with launcher scripts - Desktop/Start Menu shortcuts pointing at remote ComfyUI - --dry-run, --interactive, --skip-* flags for flexibility - build_installer.py: --remote / --remote-only flags for PyInstaller build Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The remote installer now probes the ComfyUI server's LoRA and model lists for known NSFW patterns (nicegirls, aidmansfw, phr00t, etc.) to detect whether the SFW or NSFW edition is running. Based on detection: - Writes nsfw_mode flag to spellcaster_settings.json - Sets up nsfw/.github_token for Wizard Guild NSFW auto-updates - Shows edition in summary banner New flags: --nsfw-token <PAT>, --force-sfw Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents the new install_remote.py with usage examples, feature list, full flag reference, and download badges. Updates the existing installer walkthrough to link to the dedicated remote installer section. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e instructions No release binary exists yet — point users at the Python script instead, with a note on how to build the standalone .exe themselves. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a clearly labeled button on page 1 of the GUI installer for users whose ComfyUI backend runs on a separate LAN machine. The button launches install_remote.py --interactive in a new terminal window. Descriptive text makes it clear this only installs local plugins and shortcuts (GIMP, Darktable, Wizard Guild) — it does NOT install ComfyUI or models on the local machine. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The main GUI installer now has the Antenna Installer button built into the Welcome page, so the verbose clone-and-run-from-source instructions, build-your-own-exe section, collapsible walkthrough, and CLI flag table are no longer needed. Replaced with a single sentence pointing users to the button. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When user enters a remote ComfyUI server URL in the Welcome page prereqs panel and the connection test succeeds, the Antenna Installer button now appears right there — offering to install only local plugins and shortcuts without going through the full 8-step wizard. Also scrubs remaining example IPs from install.py and patch_sillytavern.py doc strings, replacing with <SERVER-IP> placeholders. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rebuild.bat now passes --remote to build_installer.py so the spellcaster-remote-installer.exe is built alongside the main installer and uploaded to GitHub Releases. _launch_antenna_installer now correctly handles frozen .exe mode by looking for spellcaster-remote-installer.exe next to the main installer, instead of trying to run a .py with no Python interpreter. Also passes the server URL if the user already entered one. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace supplementary-plane emoji (U+1F4E1) with BMP arrow (U+2192) in antenna button labels — Tcl/Tk on Windows can choke on codepoints above U+FFFF in certain widget contexts. Replace <SERVER-IP> placeholder in entry field with plain text "your-server-ip" to avoid potential Tcl angle-bracket interpretation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of warning and skipping, rebuild.bat now clones spellcaster_NSFW automatically next to the SFW repo when it can't find it. Falls back gracefully if the clone fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Windows 'start' command interprets the first quoted argument as a window title. When the exe path has spaces, start treats it as the title and launches nothing (or crashes). Fix: pass empty "" as the title so the exe path is correctly interpreted as the program to run. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The NSFW repo is private. The previous clone URL had no auth and would hang prompting for credentials or fail with 403. Now extracts the full remote URL from the SFW repo (which includes the PAT) and swaps the repo name, so the clone authenticates automatically. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Step 7 crash: The Python one-liner for version auto-detection had nested single quotes that broke cmd.exe parsing (`.read( was unexpected`). Fixed by writing to a temp .py file first. Steps 5/6 push failures: Added git pull --rebase before push for both SFW and NSFW repos so remote changes don't cause rejections. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parentheses in .read() and .group(1) were parsed by cmd.exe as batch block delimiters when inside an if() block. Moved the detection outside the if block using goto, and pipe output to a temp file with set /p instead of for /f. Uses \x22 and \x27 escapes for quote chars to avoid all quoting conflicts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The installer now asks, per registered service (comfyui, kobold, ollama,
sillytavern, gimp, darktable, resolve, ...), where it runs:
[1] Local — on THIS machine
[2] Remote — on another machine on my network
[3] Skip — I'm not using this
Remote selections are grouped by IP so one antenna.bat is generated per
machine, not per service. A single box hosting both ComfyUI + Ollama
gets a single .bat that advertises both.
Generated files per remote machine (written to <install>/antennas/):
- antenna_for_<ip>.bat Windows launcher
- antenna_for_<ip>.sh Linux/macOS launcher
- README.txt One-line instructions per file
Each launcher:
- Embeds a UNIQUE per-machine token (secrets.token_urlsafe(32))
so leaking one bat doesn't compromise other remotes
- Embeds the hub URL (auto-detected via LAN socket trick)
- Embeds the services array the remote antenna should advertise
- On first launch: downloads the antenna bundle from
<hub>/api/antenna/bundle.zip (endpoint coming next commit),
writes config + token to ~/.spellcaster/, launches the agent
- On subsequent launches: just starts the agent
- Friendly error if hub is unreachable
step_ask_remote_services(args) returns a dict suitable for passing
straight into generate_antenna_files(remotes, output_dir, hub_url).
install.py will wire these into main() in the next commit.
Live-tested:
- 2 remote machines declared (ComfyUI+Ollama, SillyTavern)
- Correct per-machine service isolation (sillytavern box doesn't
advertise comfyui in its 'services' array)
- Tokens are unique per machine (verified via regex extraction)
- README lists each remote with its services
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…esence
Introduces the "no dead functions" architecture. Every frontend (GIMP,
Darktable, Resolve, Guild, SillyTavern, Signal) can now publish events,
upload assets, and heartbeat its presence to the Wizard Guild. The UI
renders interface-specific chips (Send to Resolve / Open in GIMP / Edit
in Darktable) only when the target is actually installed + enabled +
online — never dead buttons pointing at nonexistent plugins.
Core modules (spellcaster_core/, synced across 4 repos):
- event_bus.py — thread-safe pub/sub with ring-buffer replay
- interface_registry.py — installed+enabled+online gate for every UI
- asset_gallery.py — hash-indexed shared asset store with SHA-256
dedup and sharded blob layout
- cross_interface.py — thin HTTP client every frontend imports
- model_registry.py — unified /object_info cache shared across tools
- signal_notifier.py — outbound notifications on render completion
Guild server (tavern/server.py):
- Added 8 endpoints: /api/interfaces, /api/interfaces/heartbeat,
/api/events, /api/events/stream, /api/events/emit, /api/assets (GET/POST),
/api/assets/<hash>, /api/models
- HTTPServer → ThreadingHTTPServer so SSE subscriptions don't block
the rest of the API
Guild UI (tavern/static/):
- Sidebar "Connected apps" strip polls /api/interfaces every 10s,
renders only active interfaces. Empty = hidden entirely.
- Image action chips filter by interface availability. New cross-
interface chips: 🎬 Send to Resolve, 🖼️ Open in GIMP, 📷 Edit in
Darktable. Each first persists the image to the shared gallery,
then emits the bus event with a stable hash URL.
DaVinci Resolve plugin suite (plugins/resolve/):
- Spellcaster Bridge workflow integration (always-on): SSE client,
media pool sync, auto-heartbeat, bus subscription to resolve.* events
- Generate from Playhead: keyboard-shortcut script that grabs the
current frame and creates a new shot on the Guild
- Smart Fill Gap: detects gaps between clips and queues FLF renders
- Shared HTTP client + resolve_helpers, README with install paths
GIMP plugin:
- Lazy-start cross-interface client, auto-heartbeat on every menu
invocation, _publish_event helper ready for _run_* methods
Darktable plugin:
- guild_heartbeat() + guild_emit_event() helpers (curl-based to match
existing HTTP pattern), fires a heartbeat on plugin load
Dynamic presence guarantee: all "Send to X" chips, overflow entries,
sidebar items, and event handlers gate on the registry. No UI element
exists for a plugin that isn't there — verified end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User hit RuntimeError on first launch: 'openssl is required' even though Git for Windows was installed (bundles openssl, just not on PATH). Now: 1. _find_openssl() searches PATH first, then Git-for-Windows common locations (Program Files\Git\usr\bin, mingw64\bin), Chocolatey bin, and C:\OpenSSL-Win64. Anyone who cloned via Git has openssl. 2. _generate_cert_powershell() added as Windows-without-openssl fallback using New-SelfSignedCertificate. The PFX-to-PEM extraction is a dangling TODO — noted with a clear RuntimeError that directs the user to install Git or OpenSSL instead. Easiest win given the PKCS12-decode complexity without the cryptography package. 3. ANTENNA_NO_TLS=1 escape hatch in config.tls_enabled() + agent.serve(): - Skips cert generation entirely on bootstrap - Serves plain HTTP on the configured port instead of wrapping TLS - Startup banner shows scheme + reminds user it's plain HTTP - Bearer token auth still required — LAN + token alone is 95% safe - Clear "install openssl to re-enable TLS" hint in banner 4. Flushed the 'Ready' banner so buffered stdout in subprocess launches displays the full startup header immediately instead of waiting for the first request (saw this in earlier testing on Windows). The user can now run ANTENNA_NO_TLS=1 python -m antenna.agent on the ComfyUI box and the agent starts immediately. Upgrade to TLS later by installing openssl + re-running without the env var. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enables zero-touch remote iteration: dev pushes to main, curls
/self-update with the bearer token, the remote agent pulls the new
code and restarts within ~2 seconds. The operator never has to touch
the remote terminal.
Endpoint flow:
1. git fetch + reset --hard origin/main (if source is a git repo)
2. Compile-check every antenna/**/*.py (py_compile each file)
3. On syntax error:
- git reset back to the pre-update SHA
- return 500 with the error — agent stays alive on old code
4. On success:
- record pre-update SHA to ~/.spellcaster/antenna_last_sha
- return 202 with {updated_from, updated_to}
- schedule a detached successor process (python -m antenna.agent)
after a 500ms delay so the HTTP response flushes first
- os._exit(0) after the successor has had 800ms to rebind :7334
Rollback mode ({"rollback": true} in body):
- Reads antenna_last_sha
- git reset --hard to it
- Re-validates + restarts
- Returns 202 {rolled_back_to: <sha>}
- 409 if no last_sha exists (nothing to roll back to)
Safety layers:
- Bearer-token gated (audit-logged)
- Syntax validation BEFORE restart — bad push never bricks remote
- Never touches ~/.spellcaster/ (token, cert, audit log, last_sha
all survive the git reset)
- Timeouts on every subprocess call (no hung git fetches)
- Detached successor on Windows (CREATE_NEW_PROCESS_GROUP +
DETACHED_PROCESS) so parent exit doesn't kill child
From here on, every installer/antenna fix can reach the remote box
via one curl to /self-update.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_build_routes() was swallowing all ImportErrors silently, making it
impossible to debug why /self-update wasn't appearing. Now:
- ImportError → WARN line to stderr with the error
- Any other exception → also logged (catches SyntaxError in
dynamically-imported modules which isn't an ImportError)
- Success → 'registered: POST /self-update'
- At end of _build_routes: prints full route table
Safe for production — no behaviour change, just better observability
on startup. Operators can now tell from the first 20 lines of stdout
whether an endpoint module loaded or failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
First real auto-deploy round-trip confirmed: push a923fa4 → curl /self-update → successor process started → old agent exited → new agent (uptime 3s) serving requests on the same port. No more manual restarts needed on the remote box. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dge)
Remote antennas now advertise each declared service as its own
interface chip in the Guild sidebar. A beefy GPU box running both
ComfyUI and Ollama emits two heartbeats per cycle:
interface=antenna.comfyui, meta={machine, ip, agent_url, vram_*}
interface=antenna.ollama, meta={machine, ip, agent_url, reachable}
The antenna.* namespace keeps remote services distinct from local
ones of the same name (local GIMP and remote GIMP coexist without
collision). Meta includes machine hostname + LAN IP + agent_url so
cross-app events (davinci.asset.send, gimp.asset.open) can route to
the right box.
antenna/heartbeat.py
- _HeartbeatThread: daemon thread, 10s interval, wake-on-stop event
- _build_payloads: one payload per service, adds service-specific
vitals (ComfyUI VRAM probe)
- _send_one: POST with 5s timeout, silent on transport errors
- Consecutive-failure tracking: log first failure + every 30th
(every 5 min at 10s interval) instead of spamming stderr
- start()/stop() idempotent singleton
antenna/agent.py
- Import heartbeat module
- serve() calls heartbeat.start(cfg) after server banner
- No-op when cfg["hub_url"] is empty (local-only agent)
The protocol contract (POST {hub}/api/interfaces/heartbeat, body
{interface, meta}) matches the Mega Bridge Round 1 spec. If that
evolves, only _build_payloads() needs updating.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…to main()
Closes the installer loop for multi-machine setups. After the regular
install finishes, the installer asks per service (ComfyUI, Kobold,
Ollama, SillyTavern, GIMP, Darktable, Resolve) whether it's local,
remote, or skipped. Each remote IP collected gets one tailored
.bat + .sh with:
- Embedded unique bearer token (secrets.token_urlsafe 256-bit)
- Embedded hub_url pointing at THIS machine (installer LAN IP:7777)
- Embedded services array the remote antenna should advertise
- git clone bootstrap on first launch (replaces the old
/api/antenna/bundle.zip approach — simpler, no Guild endpoint
dependency, works today since all boxes cloning via git already
have git)
- git pull on subsequent launches so the antenna picks up updates
even when /self-update isn't reachable
- Prominent bootstrap banner on first launch printing token +
fingerprint so the operator can paste back if needed
Install.py integration:
- Only runs in interactive mode (skipped on --yes / --dry-run)
- Any exception in the remote step logs a warning but never crashes
the install (user's local setup is done before we ask about remotes)
- Output dir: <install>/../antennas/ — sits next to ComfyUI so the
user can see it in the same directory they set during install
- Prints generated filenames + README.txt pointer
The generated scripts no longer depend on /api/antenna/bundle.zip —
one less hub endpoint to build. Each remote box pulls directly from
GitHub via git. The hub URL is still used for heartbeats.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…estarts
When the operator edits ~/.spellcaster/antenna_config.json manually
(e.g. setting hub_url after bootstrap), they need a way to restart the
agent to pick up the new config without pushing a code change. Now
POST /self-update with {"force": true} does exactly that — no code
pull needed, just schedules a restart.
No-force behavior unchanged: already-up-to-date returns 200 with a
helpful hint about the force flag.
Operators couldn't tell from outside whether the antenna was actually
trying to heartbeat or silently skipping. Now /status includes:
'heartbeat': {
'enabled': true,
'hub_url': 'http://...',
'consecutive_failures': 3,
'last_cycle_ok': false,
'interval_seconds': 10
}
So a client curling /status can distinguish:
- enabled=false → antenna wasn't given a hub_url
- enabled=true, last_cycle_ok=true → heartbeats are flowing
- enabled=true, last_cycle_ok=false → antenna is trying but hub
is unreachable (firewall, down, wrong URL, etc.)
Useful right now: the user's Guild hit a startup error and isn't
accepting connections. With this, we can confirm the antenna side
is healthy while debugging the hub side independently.
load_config() drops any key not in DEFAULT_CONFIG as a typo guard.
hub_url wasn't listed, so every time the agent restarted, it silently
stripped the user's hub_url out of their config file.
Symptom: heartbeat thread always showed {enabled: false, hub_url: null}
on /status despite the user having manually added hub_url to their
antenna_config.json.
Now hub_url is a first-class config key with empty-string default
(= heartbeats disabled, agent still serves /status locally).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HANDOVER_*.md files at repo root contain real LAN IPs + local paths (intended for Claude-to-Claude orchestration between sessions, never for the public repo). Per CLAUDE.md rule 11. Also deleted 7 throwaway Playwright screenshots that were cluttering the repo root from prior automated testing runs.
Rounds 37-43 (all fully shipped, 384 tests pass):
- R37: prompt character count + limit warning
- R38: auto-scroll to rendering shot, data-shot-id attr
- R39: queue ETA from historical render-time avg
- R40: shot diff indicator — prompt/preset/overrides vs last render
- R41: revert shot to last-rendered settings (per-shot + audit)
- R42: side-by-side comparison view for changed fields
- R43: negative prompt in diff/revert, batch revert for selected shots
Round 44 (PARTIAL — backend + server only, no frontend, no tests yet):
- Shotboard.batch_prompt_edit(shot_ids, prefix, suffix, mode)
Idempotent add/remove of common prefix/suffix across selected
prompts. Skips locked shots. Returns {modified, skipped}.
- POST /api/video/batch-prompt-edit with 400-validation on missing
prefix+suffix, invalid mode, empty shot_ids.
R44 frontend (batch UI + keyboard nav) and tests land in the next
commit per the 2-features-per-round-commit contract — this partial
is safe because the endpoint is auth-gated and the method is
idempotent with no cross-shot side effects.
Verified backward-compat (audit 2026-04-18):
- GIMP plugin standalone works without Guild
- Guild works without antennas or bridge wiring
- test_video_layer.py exercises only shotboard + /api/video/*
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Every successful ComfyUI download now also uploads to the Wizard Guild's shared asset gallery via CrossInterfaceClient.upload_asset(), firing a gimp.asset.uploaded bus event. That lights the image up in the Guild's Recent-across-apps sidebar strip, so generations made in GIMP are visible to users in the Guild chat UI without re-upload. Implementation: - _maybe_publish_to_guild_gallery(data, filename) — best-effort helper with silent-fail semantics. Hooked into _download_image at both the cache-hit and cache-miss branches. - Size gate: skip <100 bytes (sentinel error images) and >32 MB (probably video frames — gallery is image-only). - Extension gate: only .png/.jpg/.jpeg/.webp uploaded; skip video and anim formats to avoid flooding. - Zero breakage when Guild is offline — lazy CrossInterfaceClient returns None when unreachable; try/except wraps the whole path. Audit-confirmed backward-compat: GIMP plugin standalone operation (no Guild running) unaffected. All errors swallowed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Thread-safe bounded deque (100 msgs, 5-min TTL) exposing:
- Mailbox class with deliver() / peek(consume=..., since_id=...) / ack_ids()
- get_mailbox(iface_key) singleton registry
- fanout_from_event(event) — origin-aware router that drops
<iface>.<rest> events into the matching mailbox, skipping echo
Synced to canonical (comfyui-spellcaster/) and dev copy
(plugins/gimp/comfyui-connector/). Sibling repos
(ComfyUI-Spellcaster[-NSFW]) get this in their own commits.
Not yet wired into tavern/server.py — the route table / handler
methods the Mega Bridge handover describes were never saved to disk
by the originating session. This primitive is ready for whoever
picks up that work. Zero runtime impact until wired: audit confirmed
no file imports mailbox outside its own docstring example.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- spellcaster_api.py: queue_shot() added with render_shot back-compat alias; list_presets() normalises 4 different server response shapes - media_pool_sync.py: accept both new 'shot-update' SSE shape AND legacy 'shot.ready' / 'shot.updated' events - scripts/generate_from_playhead.py + smart_fill_gap.py: updated against the current /api/video/* contract Pure drift fixes. Independent of the mailbox / cross-iface work. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5f — ship antenna/endpoints/comfyui.py as Phase-2 stubs
Previously every antenna startup logged
[antenna] comfyui service declared but endpoints not yet built
because agent.py unconditionally imports this module. Stubs expose
install_node() and install_model() returning 501 not_yet_implemented
with a clear hint pointing operators at the CLI installer for now.
Future request/response shapes documented in docstrings so client
code written today pins the right contract.
5g — README endpoint table split Phase 1 (live) vs Phase 2 (stub)
Was misleading operators into curl-ing /install-node and seeing it
"work" (it was previously a 404 when the agent had the ImportError
silently swallowed). Now the table explicitly flags Phase 1 vs Phase
2 status, cross-links to the endpoint stubs.
Both fixes from the backward-compat audit 2026-04-18. Zero behavior
change for existing Phase 1 endpoints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GitHub's dependency graph only tracks PyPI packages, but Spellcaster's real AI-side dependencies are ComfyUI custom-node packs (24 of them, tracked in installer/manifest.json). This adds: - DEPENDENCIES.md — human-readable table grouped into required (20) vs optional (4) packs, with repo links, the features each unlocks, and notes. - scripts/generate_dependencies_md.py — regenerator that keeps DEPENDENCIES.md in sync with installer/manifest.json as the single source of truth. - README: extra status badges (stars, issues, last-commit, downloads, dep count) and a Dependencies link in the nav row. Part of the GitHub discoverability pass across the Spellcaster ecosystem. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-surfaces which Spellcaster-compatible apps are present on the machine where the antenna runs, without needing to manually edit antenna_config.json's services array. Callers get an inventory keyed by service_key with installed/evidence/signals details. Three detection signals, combined (ANY hit = installed=True): 1. Filesystem — walks drive letters (Windows) / $HOME, /Applications, /opt, /usr (POSIX) looking for declared detect_paths. Hits return the full resolved path as evidence. 2. Process — tasklist/ps scan for detect_process substring match. 3. Network — HTTP GET on http://127.0.0.1:<default_port><probe_path> with 1.5s timeout (only for services with default_port > 0). Evidence priority: network > process > filesystem (stronger signals win). Parallelized via threads with a 3-second batch cap so /status stays snappy. 30-second result cache so consecutive /status calls don't re-probe. Surface: - `antenna/detect.py:detect_installed_services(services)` — pure function, returns {key: evidence}. No side effects. - `antenna/detect.py:invalidate_cache()` — force re-probe (called by self-update after code refresh). - `antenna/endpoints/status.py` — /status response now includes `services_detected` alongside `services_declared`. Operators can see "hey, Darktable's installed here but I didn't enable it on the antenna — maybe add it". Stdlib-only. Silent on every error path — detection must never 500 the /status endpoint. All exceptions caught and surfaced as "evidence: detect failed: <msg>". Live-tested on this machine: 7 services inventoried correctly — GIMP/Darktable via filesystem, Ollama via network+process, others correctly reported absent. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](actions/upload-artifact@v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Author
LabelsThe following labels could not be found: Please fix the above issues or remove invalid values from |
laboratoiresonore
pushed a commit
that referenced
this pull request
May 1, 2026
Closes the loop on the eval doc's "highest-leverage product improvement pending" (`_dev_docs/EVAL_LANGGRAPH_COMFYSCRIPT.md` §6 Implement #2). Two distinct wins shipped together: 1. **Kill the /history poll race.** Tight workflows (<2s on warm cache) routinely complete between the poll loop's 500 ms ticks; the client either misses the result or sees a stale empty entry. With ws, ComfyUI pushes an `executing` message with `node==None` the instant the prompt graph finishes. 2. **Eliminate filesystem round-trip on output.** Pre-Phase-9 output went `SaveImage -> output/foo.png -> /view?filename=foo.png`. With `SaveImageWebsocket` (ComfyUI core) / `ETN_SendImageWebSocket` (Acly's pack), image bytes arrive as binary ws frames on the same socket as the status messages. No file ever lands in `output/`. Privacy improvement + ~50-200 ms saved per image. Pair with `ETN_LoadImageBase64` (Acly's pack) for the input side — embeds input images as base64 inside the prompt JSON, eliminating `POST /upload/image` + `GET /view` for input-side round-trip too. Wire format (binary frames) -- per ComfyUI server.py: header = struct.pack(">II", event_type, image_format) frame = header + image_bytes event_type=1 is preview/output image; image_format is 1=jpg, 2=png, 3=jpeg legacy, 4=webp. What landed ----------- `comfyui-spellcaster/spellcaster_core/comfy_ws.py` (NEW, 478 LOC) * `WSImageFrame` dataclass for binary frames (event/format/bytes) * `WSDispatchResult` for the full collection from one prompt * `WSError` hierarchy: `WSUnreachable`, `WSTimeout`, `WSExecutionError`, `WSDependencyMissing` * `_build_ws_url`, `_decode_binary_frame`, `_collect_outputs_from_executed`, `_format_execution_error` helpers * `submit_and_listen()` — single entry point. Connects `/ws?clientId=<uuid>` BEFORE posting `/prompt` (race-free), then listens until the canonical done signal. Filters messages by `prompt_id` so other clients' broadcasts don't leak into the result. * Lazy import of `websockets.sync.client` so the module loads even if the package is missing (only fails at submit time with a clear message). `comfyui-spellcaster/spellcaster_core/dispatch.py` (modified) * `DispatchResult` gains `binary_outputs` (list of (format_name, bytes) tuples) and `transport` ("poll" | "websocket"). Backward-compat: defaults are empty list and "poll" so existing callers work unchanged. * `dispatch_workflow()` gains `use_websocket: bool = False` (opt-in for Phase 9) and `ws_fallback_to_poll: bool = True` (graceful degradation on ws failure: imports missing, connection refused, mid-listen drop). * Branch path replaces the submit + poll block with `submit_and_listen()` when ws is enabled. Same DispatchResult shape on the way out. * Privacy cleanup pass still runs against any FILE outputs produced in the same workflow (mixed-mode supported). * Same `extract_execution_error` / `has_usable_outputs` spirit: if execution_error fires AND outputs exist, warn + return partial; if execution_error fires AND no outputs, raise. `comfyui-spellcaster/spellcaster_core/node_factory.py` (modified) Three new methods on NodeFactory, mirroring the existing load_image / save_image pattern: * `etn_load_image_base64(image_b64)` — input via base64 (Acly's ETN_LoadImageBase64 class, GPL-3 sibling pack) * `etn_send_image_websocket(images_ref, format="PNG")` — output via ws binary frame (Acly's ETN_SendImageWebSocket) * `save_image_websocket(images_ref)` — output via ws binary frame (ComfyUI core SaveImageWebsocket; no sibling pack needed, always PNG). Documented as the lic-clean alternative when the GPL-3 sibling dep is undesirable. `tests/test_phase9_ws.py` (NEW, 28 tests) Mocks `websockets.sync.client.connect` so no real ComfyUI server is needed. Coverage: * URL building (http->ws, https->wss, trailing slash, no scheme) * Binary frame decoding (png, jpg, too-short guard, unknown-event guard) * `_collect_outputs_from_executed` (images + gifs, empty) * `submit_and_listen` happy path (text + binary mixed) * `submit_and_listen` execution_error * `submit_and_listen` execution_interrupted * `submit_and_listen` filters other clients' prompt_ids * `submit_and_listen` passes client_id in /prompt body AND matching ws URL * `submit_and_listen` post unreachable / http error paths * `submit_and_listen` progress callback fires for each stage * `dispatch_workflow(use_websocket=True)` happy + execution_error + partial-success + interrupted * `dispatch_workflow(use_websocket=True)` ws-failure fallback to poll path * `dispatch_workflow(use_websocket=True, ws_fallback_to_poll=False)` ws-failure raises hard * `dispatch_workflow()` poll path UNCHANGED — same result shape minus the new fields' defaults * NodeFactory ETN methods emit correct class_types `.gitignore` (modified) Add `tests/test_phase9_ws.py` to the carve-out whitelist (matches the convention for canonical shared harnesses; tests/* is gitignored by default). Verification ------------ `python tests/test_phase9_ws.py` -> 28/28 passed. Sibling test sweep -> all unchanged from baseline. The only failures (test_quality_boost: 3/54, test_video_layer: ImportError) reproduce on the pre-Phase-9 tree, verified via `git stash` + retest. Not caused by this change. Adoption -------- Opt-in per call: dispatch_workflow(server, workflow, use_websocket=True, # turn on ws path ws_fallback_to_poll=True) # graceful degrade For full inline-transport (no filesystem): nf = NodeFactory() img_id = nf.etn_load_image_base64(b64_input) # input-side # ... pipeline nodes ... nf.save_image_websocket([decode_id, 0]) # output-side workflow = nf.build() result = dispatch_workflow(server, workflow, use_websocket=True) # result.binary_outputs == [("png", <image_bytes>)] # result.outputs == [] (no file landed) Default behavior is unchanged (use_websocket=False); existing build_* / dispatch callers keep the historical poll path until they opt in. Per the eval doc's "ship the lower-risk transport upgrade first" guidance, this lands without any caller changes. Refs: _dev_docs/EVAL_LANGGRAPH_COMFYSCRIPT.md §6 Implement #2 + §7 _dev_docs/ARCHITECTURAL_STUDY_2026-04-30.md sprint-1 #3 + research-doc PARTIAL items Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
laboratoiresonore
added a commit
that referenced
this pull request
May 2, 2026
Bug: The Photobooth did a ReActor self-swap (same image as input AND source), so all 3 "variants" were identical except for face restore intensity. No actual visual variation. Fix: Now uses txt2img to generate 3 genuinely different headshot bases with different lighting and angles, THEN swaps the user's face onto each. Same approach as Body Factory. 3 headshot prompts for variety: #1: Soft studio lighting from left, neutral grey background #2: Bright even lighting, white background, slight smile #3: Dramatic Rembrandt lighting, dark background, confident Each gets quality-boosted prompts (skin realism tokens) and a unique random seed. After txt2img, ReActor High preset (ReSwapper 256 + GPEN-2048) swaps the reference face onto each base. Added model selector dropdown to the dialog (SDXL/Flux/etc.). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
|
OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting If you change your mind, just re-open this PR and I'll resolve any conflicts on it. |
laboratoiresonore
added a commit
that referenced
this pull request
May 2, 2026
Replaced the txt2img + ReActor face swap approach with Klein Flux 2
iterative img2img. This preserves the ACTUAL face throughout — no
face swap means no identity loss or artifacts.
Klein pipeline (denoise 0.35):
1. Load user's photo as Flux2ReferenceLatent
2. Klein img2img with portrait prompt → replaces background,
enhances lighting, cleans up while preserving face identity
3. 3 variants with different backgrounds:
#1: Neutral grey studio, soft even lighting
#2: White studio, bright soft lighting
#3: Dark studio, dramatic portrait lighting
Why Klein beats txt2img + face swap:
- Face is REAL throughout — no swap = no uncanny valley
- Klein's low denoise preserves identity perfectly at 0.35
- Background replacement is Klein's strongest use case
- One ComfyUI call per variant instead of two
- No dependency on ReActor for the core pipeline
Removed the generation model selector (no longer needed — Klein
uses its own Flux 2 model directly).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
laboratoiresonore
added a commit
that referenced
this pull request
May 2, 2026
Closes the loop on the eval doc's "highest-leverage product improvement pending" (`_dev_docs/EVAL_LANGGRAPH_COMFYSCRIPT.md` §6 Implement #2). Two distinct wins shipped together: 1. **Kill the /history poll race.** Tight workflows (<2s on warm cache) routinely complete between the poll loop's 500 ms ticks; the client either misses the result or sees a stale empty entry. With ws, ComfyUI pushes an `executing` message with `node==None` the instant the prompt graph finishes. 2. **Eliminate filesystem round-trip on output.** Pre-Phase-9 output went `SaveImage -> output/foo.png -> /view?filename=foo.png`. With `SaveImageWebsocket` (ComfyUI core) / `ETN_SendImageWebSocket` (Acly's pack), image bytes arrive as binary ws frames on the same socket as the status messages. No file ever lands in `output/`. Privacy improvement + ~50-200 ms saved per image. Pair with `ETN_LoadImageBase64` (Acly's pack) for the input side — embeds input images as base64 inside the prompt JSON, eliminating `POST /upload/image` + `GET /view` for input-side round-trip too. Wire format (binary frames) -- per ComfyUI server.py: header = struct.pack(">II", event_type, image_format) frame = header + image_bytes event_type=1 is preview/output image; image_format is 1=jpg, 2=png, 3=jpeg legacy, 4=webp. What landed ----------- `comfyui-spellcaster/spellcaster_core/comfy_ws.py` (NEW, 478 LOC) * `WSImageFrame` dataclass for binary frames (event/format/bytes) * `WSDispatchResult` for the full collection from one prompt * `WSError` hierarchy: `WSUnreachable`, `WSTimeout`, `WSExecutionError`, `WSDependencyMissing` * `_build_ws_url`, `_decode_binary_frame`, `_collect_outputs_from_executed`, `_format_execution_error` helpers * `submit_and_listen()` — single entry point. Connects `/ws?clientId=<uuid>` BEFORE posting `/prompt` (race-free), then listens until the canonical done signal. Filters messages by `prompt_id` so other clients' broadcasts don't leak into the result. * Lazy import of `websockets.sync.client` so the module loads even if the package is missing (only fails at submit time with a clear message). `comfyui-spellcaster/spellcaster_core/dispatch.py` (modified) * `DispatchResult` gains `binary_outputs` (list of (format_name, bytes) tuples) and `transport` ("poll" | "websocket"). Backward-compat: defaults are empty list and "poll" so existing callers work unchanged. * `dispatch_workflow()` gains `use_websocket: bool = False` (opt-in for Phase 9) and `ws_fallback_to_poll: bool = True` (graceful degradation on ws failure: imports missing, connection refused, mid-listen drop). * Branch path replaces the submit + poll block with `submit_and_listen()` when ws is enabled. Same DispatchResult shape on the way out. * Privacy cleanup pass still runs against any FILE outputs produced in the same workflow (mixed-mode supported). * Same `extract_execution_error` / `has_usable_outputs` spirit: if execution_error fires AND outputs exist, warn + return partial; if execution_error fires AND no outputs, raise. `comfyui-spellcaster/spellcaster_core/node_factory.py` (modified) Three new methods on NodeFactory, mirroring the existing load_image / save_image pattern: * `etn_load_image_base64(image_b64)` — input via base64 (Acly's ETN_LoadImageBase64 class, GPL-3 sibling pack) * `etn_send_image_websocket(images_ref, format="PNG")` — output via ws binary frame (Acly's ETN_SendImageWebSocket) * `save_image_websocket(images_ref)` — output via ws binary frame (ComfyUI core SaveImageWebsocket; no sibling pack needed, always PNG). Documented as the lic-clean alternative when the GPL-3 sibling dep is undesirable. `tests/test_phase9_ws.py` (NEW, 28 tests) Mocks `websockets.sync.client.connect` so no real ComfyUI server is needed. Coverage: * URL building (http->ws, https->wss, trailing slash, no scheme) * Binary frame decoding (png, jpg, too-short guard, unknown-event guard) * `_collect_outputs_from_executed` (images + gifs, empty) * `submit_and_listen` happy path (text + binary mixed) * `submit_and_listen` execution_error * `submit_and_listen` execution_interrupted * `submit_and_listen` filters other clients' prompt_ids * `submit_and_listen` passes client_id in /prompt body AND matching ws URL * `submit_and_listen` post unreachable / http error paths * `submit_and_listen` progress callback fires for each stage * `dispatch_workflow(use_websocket=True)` happy + execution_error + partial-success + interrupted * `dispatch_workflow(use_websocket=True)` ws-failure fallback to poll path * `dispatch_workflow(use_websocket=True, ws_fallback_to_poll=False)` ws-failure raises hard * `dispatch_workflow()` poll path UNCHANGED — same result shape minus the new fields' defaults * NodeFactory ETN methods emit correct class_types `.gitignore` (modified) Add `tests/test_phase9_ws.py` to the carve-out whitelist (matches the convention for canonical shared harnesses; tests/* is gitignored by default). Verification ------------ `python tests/test_phase9_ws.py` -> 28/28 passed. Sibling test sweep -> all unchanged from baseline. The only failures (test_quality_boost: 3/54, test_video_layer: ImportError) reproduce on the pre-Phase-9 tree, verified via `git stash` + retest. Not caused by this change. Adoption -------- Opt-in per call: dispatch_workflow(server, workflow, use_websocket=True, # turn on ws path ws_fallback_to_poll=True) # graceful degrade For full inline-transport (no filesystem): nf = NodeFactory() img_id = nf.etn_load_image_base64(b64_input) # input-side # ... pipeline nodes ... nf.save_image_websocket([decode_id, 0]) # output-side workflow = nf.build() result = dispatch_workflow(server, workflow, use_websocket=True) # result.binary_outputs == [("png", <image_bytes>)] # result.outputs == [] (no file landed) Default behavior is unchanged (use_websocket=False); existing build_* / dispatch callers keep the historical poll path until they opt in. Per the eval doc's "ship the lower-risk transport upgrade first" guidance, this lands without any caller changes. Refs: _dev_docs/EVAL_LANGGRAPH_COMFYSCRIPT.md §6 Implement #2 + §7 _dev_docs/ARCHITECTURAL_STUDY_2026-04-30.md sprint-1 #3 + research-doc PARTIAL items Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps actions/upload-artifact from 4 to 7.
Release notes
Sourced from actions/upload-artifact's releases.
... (truncated)
Commits
043fb46Merge pull request #797 from actions/yacaovsnc/update-dependency634250cInclude changes in typespec/ts-http-runtime 0.3.5e454baaReadme: bump all the example versions to v7 (#796)74fad66Update the readme with direct upload details (#795)bbbca2dSupport direct file uploads (#764)589182cUpgrade the module to ESM and bump dependencies (#762)47309c9Merge pull request #754 from actions/Link-/add-proxy-integration-tests02a8460Add proxy integration testb7c566aMerge pull request #745 from actions/upload-artifact-v6-releasee516bc8docs: correct description of Node.js 24 support in READMEDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)