Changelog
September 1, 2026 · View on GitHub
All notable changes to comfy-cli are documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
This file starts at v1.11.0, the first release with curated release notes. Entries below are condensed from the published GitHub releases; each links to the full notes. For v1.10.5 and earlier, the GitHub releases carry only auto-generated "What's Changed" pull-request lists rather than curated notes, so they are not transcribed here — see the releases page for that history.
Unreleased
Fixed
comfy install --fast-deps --nvidiano longer installs a torch that its torchvision was not built against, which made ComfyUI fail to import withRuntimeError: operator torchvision::nms does not exist. The GPU override namedtorchoutright, and a uv--overridereplaces every requirement for the package it names, so torchvision'storch==<x.y.z>pin was discarded and a same-day torch release resolved ahead of its matching torchvision.comfy … | headexits 0 again, and--jsonkeeps stderr clean, on typer= 0.24. typer now runs on a vendored copy of click, so the broken-pipe guard no longer recognized the stdout wrapper click installs on EPIPE and reported a genuine failure instead. The wrapper is now matched by class name and owning package, which also stops the deprecated
click.utils.PacifyFlushWrapperimport from printing a warning onto stderr.comfy --help-jsonlistschoicesfor enum options again, for the same reason: the vendored param types are not instances of the installed click'sChoice, so the choice list was silently dropped.- Promoted subgraph widgets are edited where the frontend reads them. The
frontend (ADR 0009) keeps a promoted widget's value on the HOST instance
(
widgets_valuespositional over the widget-backed subgraph inputs) and runs that value over the interior default;set-widget 57.widthused to follow the legacyproxyWidgetsroute into the interior node, so the edit neither ran nor showed on the canvas.set-widget,set-slotandvarynow write the host value for<instance>.<input>, redirect an interior address that backs a promotion (57/13.width) to the same host value (redirected_fromon the op), follow an outside link feeding the promoted input to its primitive (PrimitiveInt/PrimitiveString*/legacyPrimitiveNode, throughReroutes) and refuse — naming the driver — when a non-primitive node computes the value. Unpromoted interior widgets still write the definition. The op carriespromoted.value_indexand the materializedhost_widgets_values. comfy workflow connectwires an outside node to a promoted widget (PrimitiveInt.INT → 57.width): the definition declares the input, so it is materialized on the instance (with the frontend'swidgetmarker) and linked, type-checked against the declared input type.comfy workflow slotsadvertises promoted widgets at the instance address with the value the frontend runs (host value, else interior default), flags widgets fed by a link (linked_from), keeps unpromoted interior widgets reachable at<instance>/<inner>.<widget>(nested instances included), and no longer advertises the interior address behind a promotion.convert_ui_to_api(comfy run,validate) applies host-owned promoted values onto the expanded interior nodes — a post-migration template whose host prompt differs from the interior default (audio_minimax_music_3: interior caption'') was submitted with the interior value. Precedence matches the frontend: outside link, then host value, then interior.comfy workflow connectcan wire a Load Image / Load Video / Load Audio into an auto-grow group nested under a dynamic combo (GeminiNanoBanana2V2model.images,MinimaxHailuo03ReferenceNode/ByteDance2ReferenceNodeV2model.reference_images/reference_videos/reference_audios, and the other 27 partner nodes shaped that way). Slots are resolved from the node's schema for its current selection — not from a pre-existing input — so an agent-built node growsmodel.images.image_1,image_2, … by name or by addressing the group base, a UI-built node reuses its free pre-created slot and then keeps growing, a UI-built top-level group (BatchImagesNode) can be base-addressed and grown past its free slot, a wrong element type is refused (type mismatch), and a group never grows past the schema'snameslength /max.comfy nodes shownow lists every dynamic-combo option's sub-inputs and names each auto-grow group's element type, slot vocabulary and first keys to wire.- The widget order no longer counts a dynamic combo's link-only sub-inputs
(auto-grow groups,
GEMINI_INPUT_FILES, …) aswidgets_valuesslots.add-nodewrote them as phantomnullvalues and the published widget catalog named them, so every widget after the groups was one or more slots off: an agent-built Nano Banana 2 converted withseedinresponse_modalities, and through the CRDT doc host a UI-built MiniMax H3 node'sseedposition mapped ontomodel.reference_images.add-node, the catalog andset-widgetnow share one walk, so a fresh node's layout is exactly whatset-widgetindexes and what the frontend serializes (41 classes in the cloud catalog change width). - The widget order (
comfy nodes widget-catalog,set-widgetindexing, the UI→API converter) now names every slot the frontend serializes: theuploadbutton frontend extensions inject on media loaders (LoadImage,LoadImageMask,LoadVideo,LoadAudio, ...), theaudioUIplayer on the audio family, thePREVIEW_3DimageonSaveGLB/Preview3D, DOM widgets declared under an uppercase custom type (Load3D.image), and inputs whosewidgetTypeoverrides a link-shaped socket type (LTXVEmptyLatentAudio.frame_rate, the "Basic data handling" math nodes). Before, a workflow with any of these nodes carried morewidgets_valuesthan the catalog could name, so the cloud doc host refused to mint it (createNodeMap(LoadImage): widgets_values has 2 entries but widget_order names only 1) andset-widget/conversion read the values after such a slot one position off. comfy generate <model>,comfy generate resumeand sync-mode creates now emit theenvelope/1contract in--output json/ndjsonmodes instead of a bare partner blob: the partner payload is wrapped asdata.result(verbatim) withdata.savedlisting--downloadartifacts, and the payload schema is registered ascomfy generate→generate_result.jsonsocomfy discoveradvertises it. Pretty mode with a tail--jsonkeeps the legacy raw blob.
Added
-
comfy workflow add-nodeand anadd_nodeop incomfy workflow applyrefuse a class the catalog marks deprecated (node_deprecated), naming the live class with the same display name when there is one. Pass--allow-deprecated(or"allow_deprecated": trueon the op) to add it anyway. -
comfy nodes searchandcomfy nodes lshide deprecated classes by default;--include-deprecatedshows them. -
comfy knowledge pickattaches each pick's modelfitsblock (VRAM per variant, credit rate, max refs) when the bundle carries one, so a size or price constraint can be checked against a number rather than the caveat text. -
comfy-build, the skill for building a custom ComfyUI environment on the developer platform, is now bundled with the CLI.comfy skills show comfy-buildworks, and an argument-freecomfy skills installwrites it on a machine with no network. -
comfy build init --from-workflow <workflow.json>andcomfy build update --from-workflow <workflow.json>read a ComfyUI workflow, in the editing format or the API export, into the local spec. -
comfy build init --base-image <id>andcomfy build update --base-image <id>choose the curated base image a build is built on — the CUDA, Python and torch runtime — instead of leaving it to the catalog default.comfy build refs base-imageslists the ids. -
A workflow import prints its full report: the node classes nothing provides, the closest pack the registry named for each one, every model the graph loads (a workflow import carries none of them), the classes served by a partner API, and whether a ComfyUI version still has to be pinned.
-
comfy build pullnames what it would change before changing it: the same definition diffcomfy build updateprints, echoed in the confirmation and carried in the--jsonpayload assummaryanddiff. A fetched Build that omitsmodelsorcustomNodesdrops the local entries, and the diff is where that is now visible.comfy build pull --dry-runprints the diff and writes nothing — with--yesthe payload only arrives after the write, so this is how a non-interactive caller reads the diff before deciding. -
CONTRIBUTING.md(renamed fromDEV_README.md) and this changelog. -
comfy deploy— run a Build release as a serverless endpoint:up,status,ls,show,logs,events,scale,stop,start,delete,run, andrefs compute.
Changed
comfy skills installno longer fetches any skill over the network.comfy-buildwas the only one it fetched, and it now ships in the wheel and is versioned with the CLI release, so the skill and the commands it describes can no longer drift apart.- The builder client module is now
comfy_cli.builder_api(wascomfy_cli.distribution_api), and its methods say build and release (create_build,create_release,list_releases, ...), matching the builder's public API. Thedistribution-definition/0schema id is unchanged. - Breaking:
comfy build --jsonpayloads saybuildId,releaseId,buildsandreleases, and nothing else: the retireddistributionId,versionId,distributionsandversionskeys are gone from every payload, every errordetails, and every shipped schema rather than being carried alongside. Read the builder's own spelling. - Breaking:
import comfy_cli.distribution_apino longer works. The deprecation shim is removed together with the surface it shimmed; importcomfy_cli.builder_api. - Breaking:
comfy buildis restructured around a localcomfy-build.yamlspec —init,push,pull,status,ls,show,validate,update,delete, plusrelease,refs, andblobsubgroups. Thecomfy distributionalias and thescan/create/version/artifact download/from-snapshot/from-workflowcommands it fronted are removed.from-workflowreturns as thecomfy build init --from-workflowandcomfy build update --from-workflowoptions described under Added. - Breaking: the read verbs are renamed, with no aliases left behind:
comfy build list→comfy build ls,comfy build get→comfy build show, andcomfy build blob list→comfy build blob ls. The reference lookups (resolve,base-images,build-targets,model-dirs) move undercomfy build refs, andcomfy build blob uploadis removed —comfy build pushuploads local models and nodes from the spec. - Breaking: the
build_upload_unavailableerror code is retired. It was only ever raised by the removedcreatepath, so nothing emits it and it no longer appears incomfy discover. This is the one exception to the append-only rule incomfy_cli/schemas/error_codes.md: the code is retired, never reused. - Breaking: a
--jsonrun ofcomfy buildorcomfy deployis never prompted, even from a terminal. A confirmation or missing required option now returns the matching refusal envelope —*_needs_confirm,*_missing_input, orbuild_id_unknownwhere a Build id could not be resolved — and exits 1, where it previously opened a TUI prompt on the same stream the envelope is written to. Pass--yesor the option itself to proceed non-interactively. Other command families still prompt under--json; they do not route throughcomfy_cli.interaction, and--skip-promptremains the way to suppress them. - Breaking: the global
--skip-promptnow applies tocomfy build delete, which previously ignored it. Combined with a non-agentic caller it accepts the delete confirmation, matchingbuild pullandbuild update. - Breaking: packaging a local custom node is all-or-nothing. Anything under
custom_nodes/<node>/that cannot be read now failsinit/update/status/push/pullwith onebuild_spec_invalidenvelope naming the node directory. Previously the two failure modes diverged and neither was usable: an unreadable directory was silently dropped from the archive whose digest becomes the node's committedlocalDigest, while an unreadable file escaped as an uncaughtPermissionError— a traceback with no envelope at all, even under--json. - Symlinks inside a custom node are still excluded from its archive, but are no
longer excluded in silence:
init,update,pushandpullname them on stderr and carry them in askipped_symlinkspayload key, including on a--dry-runthat writes nothing.statusrescans but publishes no definition to point into, so for it the stderr warning is the whole report.
Fixed
comfy build lsandcomfy build release lsshow every row. The builder pages both reads, and the client took only the first page, so a workspace or a build past one page lost its tail — silently, with no error and nothing in the output to say rows were missing.- The
--from-snapshotpath is no longer sent to analytics. It is a local filesystem path naming the user's home directory and their install layout, and it was shipped verbatim: the URL scrubber only strips credentials out of URLs and returns a bare path untouched, so the key has to be named in the redaction set, and the rename from--fromhad left it out. - A misconfigured
COMFY_DEPLOY_URLis reported as adeploy_insecure_urlerror instead of a traceback. The https guard signalled refusal with a bareValueError, which no deploy command listed in itsexcepttuple, so the failure escaped the command layer and--jsonprinted no envelope at all. The message now names the setting actually in play rather thanCOMFY_CLOUD_BASE_URL. comfy deploy lscannot hang on a defective pagination cursor. The loop walked whatevernextCursorcame back until it was falsy, so a repeated cursor spun forever on an ever-growing list. A repeated cursor and a run past the page ceiling are both reported asdeploy_server_errornow.comfy deploy runvalidates a job output'snode_id,typeandidbefore it downloads the file rather than after. A malformed response used to leave files inoutputs/that the result envelope then never accounted for, followed by exit 1 and no manifest.comfy deploy show,statusandstoprefuse a blank release id instead of matching on it. A lax local copy of the shared field validator accepted the empty string, so a release with a blankidadopted every deployment whosereleaseIdwas also blank as belonging to that Build — which could point a lifecycle mutation at the wrong deployment.- An asset upload's
Content-Lengthalways describes the bytes that follow it. The length came from astat()at request-build time while the body was opened and read later, when urllib got round to consuming it; both now come from a single open handle, and the body is bounded to exactly the declared size.
1.16.0 - 2026-08-10
Full notes · 16 commits since v1.15.0. No breaking changes.
Fixed
- Failed cloud jobs report their cause again:
jobs status/jobs watchwere reading field names/api/jobs/<id>does not serve, so every failure surfaced as an empty error. (#683) jobs watchstreams live progress — it now attaches as the submitting client and understands the per-stepprogress_statemessage. (#693)- Local
models search --textmatches token-wise and separator-insensitively, so--text "sdxl base"findssd_xl_base_1.0.safetensors. (#684) nodes pathconstrains hops by source type and no longer claims exactness unconditionally. (#695)comfy outdatedprefers the highest stable semver tag known to the local checkout over a mis-set GitHubreleases/latestflag. (#694)nvidia-smiis resolved by absolute path in the CUDA probe, blocking Windows CWD planting. (#641)- Registry failures are typed and carry a machine-readable error code. (#528)
1.15.0 - 2026-08-05
Full notes · 12 commits since v1.14.0. No breaking changes.
Added
comfy stop --port <p>— verified stop of a local ComfyUI this CLI did not launch. (#675)comfy update --exit-on-fail— a failed pack update exits non-zero. (#676)error_codeonjobs lsrows. (#677)text_outputsin localjobs status. (#550)partner_nodes_detectedtelemetry, andcaller_kindstamped on every event. (#647)
Fixed
- Empty COMBO enums no longer skip validation — a loader with an empty option
list (
UNETLoader,CLIPLoaderon an install with no models) was treated as unconstrained, so its missing model was never reported. (#680) - An explicit
--port 0is no longer swallowed by a--host host:portvalue. (#679) validateenforces promoted hard checks only on output-reachable nodes. (#565)- Apple Silicon is detected under Rosetta 2 in the GPU probe. (#568)
1.14.0 - 2026-08-05
Full notes · 89 commits since v1.13.0. No breaking changes.
Added
comfy system-stats/comfy free— ComfyUI/system_statsand/freepassthrough. (#626)comfy workflow notes <path>— readNote/MarkdownNotetext out of a workflow, offline. (#611)comfy node deps— per-pack Python dependency report;--registry <node-id>pre-checks a not-yet-installed pack. (#610, #625)comfy model download --background, plusdownload-status,downloads, anddownload-cancel. (#607)comfy update comfy --version <X>— headless ComfyUI version switch/rollback with tag validation. (#606)comfy templates check— per-template runnable / missing / api-required verdict. (#557)comfy upload --host/--port— local target routing for uploads. (#648)envelope/1fromcomfy launch/comfy stop(#588) andgenerate list/generate schema(#621); envelope errors on everymodel downloadandgeneratefailure path (#581, #601).comfy run --allow-spendgate on paid partner nodes;validatereportspartner_nodes/spends_credits. (#591, #590)
Fixed
- A dead local server is attributable to the job that killed it:
jobs statusfalls back to the on-disk state file when the server is down (#602),run --waitnames theprompt_idon disconnect (#605), the watcher records a terminalserver_died(#604), andjobs statusconsults that record even when the server is back up (#674). models search --textwalks every model folder, not justcheckpoints. (#603)comfy envdetects legacy ComfyUI-Manager clones and reconciles a stalemanager_gui_mode. (#609)comfy logsresolves the right file, with auser/comfyui.logfallback and staleness metadata. (#608)- Model downloads are atomic — streamed to a
.partsibling and renamed on completion. (#666) validate/nodesread the same serverrunsubmits to instead of always consulting127.0.0.1:8188. (#667)nodes searchis tokenized and order-independent. (#646)jobs lsstate-file rows are scoped to the resolved--wheretarget, with--allto opt out (#582);jobs cancelemitsprompt_not_foundfor an unknown local id (#580).
Security
- Server-supplied text is stripped of ANSI/control sequences and escaped before reaching Rich markup sinks. (#614, #627, #655)
- Absolute-path resolution for probe binaries blocks Windows CWD planting (#567); shared read cap on unbounded HTTP body reads (#654); authed urllib routed through a no-redirect opener (#530).
1.13.0 - 2026-07-28
Full notes · 41 commits since v1.12.0.
Added
comfy cloud loginis agent-drivable —--jsonmode emits a machine-readablelogin_urlevent as soon as the authorize URL exists.comfy run-template— fetch a template, fill its params, spend-gate, and run to completion in one verb.comfy outdated— read-only version check for ComfyUI core and installed node packs.comfy run --prompt/--set— local text2img from the bundled default workflow.comfy generatespend gate: explicit consent before spending credits.COMFY_LOCAL_URLis honored for the local ComfyUI address; the:8188hardcode is gone.
Changed
comfy generatederives partner model enums from the active OpenAPI spec rather than a pinned list.comfy validateauto-converts UI-format workflows, matchingcomfy run.get_job_statusmigrated off the deprecated/api/job/<id>/statusendpoint.- Telemetry providers are built lazily so
comfy installcan upgradepydantic_core;mixpanel<5pinned socomfy installcannot wedgepydantic-coreon Windows.
Fixed
- Downloads verify
Content-Lengthand land atomically via.partrename; the extension taken from an untrusted?filename=param is sanitized. _http_requestdetects oversize responses instead of silently truncating.- Widget-aware dynamic-combo expansion and name-aligned control-marker filtering
in
workflow_to_api. validatepresence-checks required inputs, adds a no-outputs check, and hard-errors range violations.- The OAuth session is refreshed during local partner-credential injection; the
run WebSocket is closed on every exit path of local
--wait. - Telemetry network I/O is bounded so it cannot outlive the run envelope.
- Square brackets are escaped in Typer help strings so choice lists render.
1.12.0 - 2026-07-07
Added
comfy logs— background ComfyUI logs are persisted to<workspace>/user/comfyui_<port>.logand readable viacomfy logs --tail N. (#491)- Local saved-workflow support: the saved-workflow verbs work with
--where localvia ComfyUI's/userdata. (#486) - ROCm 7.2 support, set as the default. (#476)
Changed
comfy downloadcopies on-disk local outputs instead of refusing them. (#485)- Shared IPv6-aware
host:portresolver forcomfy run(#488); redirect-refusal and SSRF loopback guards consolidated intocomfy_cli/http.py(#487, #482); thewheredefault config read centralized viawhere.resolve_default()(#477).
Fixed
comfy launch --backgroundno longer crashes on Python 3.14's removed implicit event loop. (#481)
1.11.1 - 2026-06-22
The first PyPI-published cut of the agent-first line. v1.11.0 was tagged but
did not publish to PyPI due to a release-trigger mismatch; v1.11.1 is
identical code.
1.11.0 - 2026-06-22
The agent-first release: an agent or a human can build, validate, run, and review image/video/audio workflows on a local server or Comfy Cloud entirely from the terminal. Additive and backward-compatible for interactive use.
Added
- One machine contract — every command emits the same versioned JSON envelope,
and every error carries a registered code plus an actionable hint (
--json, automatic on non-TTY). - A compile model for workflows: typed fragments wired by YAML blueprints via
comfy workflow compose, withcomfy workflow decomposeas the inverse. - Pre-flight validation (CQL):
comfy validatechecks a graph against the live server'sobject_infobefore you spend. - Async-by-default execution with
comfy jobs, includingcomfy jobs wait <id…>to block on a whole batch. - Projects — a
project/1layout with content-addressedassets push, a run journal, and--whererouting. comfy preview— image to thumbnail, video to contact sheet, audio to waveform.- Bundled skills (
comfy skills) that teach agents to operate, build, debug, and present the CLI. comfy setup— a guided onboarding wizard, surfaced from the welcome screen and a first-run nudge.
Changed
- Behavior change: when output is piped or redirected (non-TTY), commands
with structured output default to JSON instead of text. Pass
--no-jsonor setCOMFY_OUTPUT=prettyfor the old piped text. Interactive terminal use is unchanged. comfy install/comfy updateno longer assume the workspace interpreter ships pip; a pip-less uv-managed venv is bootstrapped automatically.
Earlier releases
v1.10.5 and earlier are published on the
releases page with
auto-generated pull-request lists.