ci(deps): bump actions/setup-python from 5 to 6#4
Closed
dependabot[bot] wants to merge 558 commits intomainfrom
Closed
ci(deps): bump actions/setup-python from 5 to 6#4dependabot[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>
…ing installer) Rule 13 warns that Guild and GIMP plugin restarts auto-download from main and OVERWRITE uncommitted local edits. Details: - Which updater fires when, what's protected, what gets clobbered - Pre-restart git-status check that Claude must run - Three safe-restart options (commit, DEVNOUPDATE launcher, stash) - Explicit rule: never say a restart is "safe" without checking first Rule 14 documents the new self-updating installer bootstrap pattern so future Claude sessions know the .exe doesn't need rebuilding for most fixes. Just push to main and existing .exe picks it up. Also added cross-reference from Rule 5 (local GIMP deploy) to Rule 13, and rule #6 in spellcaster-expert.md subagent prompt teaching the same. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spellcaster's "antenna" was a probe-only client installer — it could
detect what was on a remote ComfyUI but couldn't install anything. The
user's vision: one agent per server machine on the LAN, each advertising
what it hosts (LLM / ComfyUI / Resolve), clients routing requests to
whichever agent they need.
This commit is Phase 1 foundation:
antenna/README.md Full architecture spec + threat model + multi-machine
diagram (Machine A = LLM, B = ComfyUI, C = Resolve
with one client coordinating).
antenna/__init__.py Version.
antenna/config.py First-launch bootstrap:
- ~/.spellcaster/antenna_config.json (port, bind,
services list, service-specific paths)
- ~/.spellcaster/antenna_token (secrets.token_urlsafe
256-bit bearer, 0600 on POSIX)
- ~/.spellcaster/antenna.crt + .key (openssl-generated
self-signed RSA2048, 10yr, SANs for hostname +
localhost + LAN IP so clients connecting by any
address pass hostname verification)
- cert_fingerprint() helper for client pinning
- rotate_token() for "token leaked, nuke it" scenarios
Zero non-stdlib deps. openssl CLI used for cert generation (every
realistic ComfyUI host has it; stdlib fallback path raises with an
install-openssl message).
Services layer (Phase 1 = "comfyui", Phase 2 = "llm", Phase 4 =
"resolve") declared in config["services"] so adding a new service type
is a new file in antenna/services/, not a rewrite.
Live-tested on this box: bootstrap() generates config + token + cert,
cert SANs include hostname/localhost/LAN IP, fingerprint is 64-char
SHA-256 pair-formatted for easy client comparison.
Next: auth.py (token compare + rate limit), agent.py (HTTPS server),
first endpoints (/, /status).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 1 incremental — standing up the TLS server with full auth pipeline
and the two endpoints clients need to discover a box on the LAN.
antenna/auth.py
- load_token() re-reads from disk per request so rotate-token takes
effect without restarting the agent
- verify_token() uses hmac.compare_digest (constant-time)
- extract_bearer() strict parser — case-insensitive scheme, rejects
non-Bearer, rejects empty values
- RateLimiter: sliding-window per IP, thread-safe deque, default
30 req/min configurable via antenna_config.json
- authenticate_request() returns (ok, error_msg) so the agent can
echo a short, non-sensitive reason in the JSON 401
antenna/agent.py
- ThreadingHTTPServer wrapped in TLS 1.2+ SSLContext from the
self-signed cert built by config.py
- 6-step request pipeline: rate-limit → route-match → auth →
body-parse → dispatch → audit-log
- Lazy endpoint imports so antennas with services=["llm"] don't need
comfyui endpoint modules installed
- CORS preflight + Access-Control-Allow headers so the Wizard Guild
(browser JS) can call us cross-origin
- 1 MB request body cap
- Exception handler catches unhandled endpoint errors, logs the
traceback to stderr + audit log, returns generic 500 to client
(no internal leakage)
- Graceful SIGINT/SIGTERM via threaded server.shutdown()
- log_message override silences default stderr access-log noise —
we have our own structured audit log
antenna/endpoints/__init__.py — handler contract docstring
antenna/endpoints/status.py
- GET / — unauthenticated liveness, deliberately minimal
(service name + version + protocol num only; no hostname, no paths)
- GET /status — authenticated, rich: version, uptime_seconds,
hostname (already in cert SAN), services_declared, services_detail
with per-service reachability probe. Paths returned as basenames
only so attackers can't use the response to guess the user's home
directory layout.
- _probe_comfyui() hits /system_stats, extracts gpu_name + VRAM
total/free, swallows exceptions into an error string
- _probe_llm() probes Kobold (/api/v1/model) or Ollama (/api/tags)
based on config.llm_engine
Live-tested end-to-end:
GET / → 200 {"service":"spellcaster-antenna",...}
GET /status (no auth) → 401 missing Authorization...
GET /status (wrong token) → 401 invalid token
GET /status (valid token) → 200 full JSON w/ ComfyUI probe result
GET /nonexistent → 404
31 rapid hits → 30 pass, then 429 with Retry-After
Audit log writes every request line with status code. TLS handshake
verified (curl -k needed for self-signed; clients will pin the
fingerprint instead).
Next: integration with Mega Bridge's /api/interfaces heartbeat, then
POST /install-node and POST /install-model endpoints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…artup
Adding a new Spellcaster-compatible service (e.g. SillyTavern, a future
video tool, etc.) previously needed a .py edit + rebuild. Now it's a
JSON file that both the installer and the antenna fetch from main at
runtime, with the baked copy as offline fallback.
New files:
installer/remote_services.json registry: 7 services (comfyui, kobold,
ollama, sillytavern, gimp, darktable,
resolve). Each entry has key, label,
description, default_port, detect_paths,
detect_process, probe_path, managed_ops.
installer/remote_services.py dynamic loader:
1. Fetch GitHub JSON (5s timeout)
2. Validate required keys
3. Fall back to baked copy
Module-level cache + refresh=True for
long-running antennas. Convenience
accessors: by_key(), all_keys(),
desktop_apps(), network_services().
Pattern matches installer/bootstrap.py — fetch-latest-from-GitHub with
offline fallback. Pushing a new service to main reaches every running
installer/antenna on next startup, no redistribution needed.
Live-tested: `python3 -m installer.remote_services` with no network
access → falls back to baked copy, lists all 7 services correctly.
After push, the fetch path will activate.
Next: installer step_ask_remote_services() that loops over this list
asking "is X on another machine?" per service, then generates a
per-machine antenna.bat.
Co-Authored-By: Claude Opus 4.6 (1M context) <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/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](actions/setup-python@v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' 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 |
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. |
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/setup-python from 5 to 6.
Release notes
Sourced from actions/setup-python's releases.
... (truncated)
Commits
a309ff8Bump urllib3 from 2.6.0 to 2.6.3 in /tests/data (#1264)bfe8cc5Upgrade@actionsdependencies to Node 24 compatible versions (#1259)4f41a90Bump urllib3 from 2.5.0 to 2.6.0 in /tests/data (#1253)83679a8Bump@types/nodefrom 24.1.0 to 24.9.1 and update macos-13 to macos-15-intel ...bfc4944Bump prettier from 3.5.3 to 3.6.2 (#1234)97aeb3eBump requests from 2.32.2 to 2.32.4 in /tests/data (#1130)443da59Bump actions/publish-action from 0.3.0 to 0.4.0 & Documentation update for pi...cfd55cagraalpy: add graalpy early-access and windows builds (#880)bba65e5Bump typescript from 5.4.2 to 5.9.3 and update docs/advanced-usage.md (#1094)18566f8Improve wording and "fix example" (remove 3.13) on testing against pre-releas...Dependabot 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)