Repository navigation
Releases
comfyui-mcp 0.1.10
Added
-
Installing, which is the middle of the extension concept and was built last on
purpose. Three tools -install_extension,update_extension,
repair_extension- in a group of their own,extensions_install.What installing a pack does to a machine is almost never its own files. Those
are a folder thatset_extension_enabledhides again in one rename. It is the
packages its requirements move, and that is the half with no undo - so the order
is the whole design: the plan is read before anything is fetched and an unsafe one
is refused outright, a checkpoint is written whether or not anybody asked for one,
and the difference is measured afterwards rather than reported from the plan a
second time.The fetching is ComfyUI-Manager's and the packages are this server's, and the
seam is one flag.skip_post_installmakes Manager resolve the registry id,
download the archive, extract it and write its.trackingfile - and then drop
its post-install step, which nothing in its HTTP path ever calls. That step does
two things worth not having happen unwatched: it pip-installsrequirements.txt
one line at a time, which is exactly the mechanism that leaves a real ComfyUI
environment unresolvable as a whole, and it runs the pack's owninstall.py,
which is arbitrary code. Reimplementing the fetch was never the alternative: a
second set of conventions for the same folders is how a pack ends up installed
twice under two names.Manager's own security setting still decides what may be installed, and nothing
here works around it. Installing needs itssecurity_levelat middle-or-below;
anything it rates high - every git URL it does not already know, and every
nightly- needsweak. That setting is the administrator's answer to whether
this ComfyUI may install code at all, and a tool that routed around it would be
the thing this whole concept exists to prevent. No route exposes the level, so
Manager'sconfig.iniis read from disk to word the refusal and Manager's own
HTTP status is always the authority.A pack lands whole; its packages are a separate decision and sometimes a later
one. A plan that only adds packages is applied straight away - nothing already
imported is replaced. A plan that moves an installed version is not, because
replacing a file a running process holds open fails outright on Windows and leaves
the package half written. The pack is placed, the packages wait, and the reply
says to stop ComfyUI and callrepair_extension- which needs neither
ComfyUI-Manager nor a running ComfyUI, and is therefore also the tool for a pack
whose import fails on a missing module.The requirements are read twice, from two sources, deliberately. The
registry's copy answers before anything is fetched, which is what makes a refusal
possible at all; therequirements.txtthat arrives with the pack is what the
version actually on disk asks for. They are allowed to disagree - a pack published
a year ago and installed today is the ordinary case - so both are planned, and a
pack that lands with an unsafe one is left inert rather than half-installed. Inert
is visible: its import fails,describe_extensionreports zero registered types,
and no package has moved.A pack's
install.pyis never run, and the reply says so when there is one.
Arbitrary code from the pack, at install time, with no sandbox: there is no safe
way to run it on somebody's behalf.There is no uninstall tool, and that is the ordering rather than an omission.
set_extension_enabledis the reversible undo and costs one rename; deleting a
folder incustom_nodesis not reversible, and those folders are the user's -
the same reason a checkpoint never copies them.restore_checkpointis what puts
packages back. -
stage_extensionis the answer to "this pack is on GitHub and not published", and
it stops one step short of installing on purpose. It clones the repository to a
staging directory that is notcustom_nodes- the only place ComfyUI looks - so
nothing is imported, noinstall.pyruns and no package moves. What comes back is
what a decision needs: what the pack says it is, what it would install, what that
would move in this environment, whether the same pack is already here under another
folder name, and how ComfyUI-Manager would rate it.The rating is Manager's own rule, reproduced.
get_risky_levelcompares the URL
against every repository incustom-node-list.json- 4783 of them on this machine -
and the requirements against the pip packages listed beside them: an unknown
repository ishighand needssecurity_levelweak, and a known repository asking
for a package the catalogue has never seen isblock, which no level permits. Only
the local catalogue is read while Manager merges a fresher remote copy when it is
actually asked, so this is stricter than Manager and never looser - the safe
direction for a sentence somebody decides on.When Manager would refuse, the refusal stands, and there is no tool here that
proceeds anyway. No git-URL installer was built, and that is the decision rather
than an unfinished piece:security_levelis the administrator's answer to whether
this ComfyUI may install unvetted code, and routing around it is the exact shape of
the incident this whole concept exists to prevent. Going further is a command for a
person - and the staged clone is what that decision should be made on.Only
https://URLs are cloned. The value is an argument togit clone, so one
beginning-is an option rather than a repository (--upload-pack=...is the known
shape), andfile://, a bare path andgit@host:pathall reach something that is not
a fetch over the network. -
update_extensiongoes back as readily as forward. Manager's install route
sends an already-enabled pack at a different version tocnr_switch_version, so
one mechanism covers install, update and downgrade. "The update broke it" is
therefore a fixable sentence: name the version that worked.
Fixed
-
The network diagnosis shipped in 0.1.9 was right about the shape and wrong in two
details, and a day later half of it had disappeared.pypi.org's 10.2 s is in the
TCP connect, not the TLS handshake -time_connect10.15 s against
time_appconnect10.21 s, so TLS itself costs 60 ms - which points at one of its four
A records rather than at anything cryptographic. And it is intermittent: measured
the next day, curl answered in 0.46 s, a uv resolve failed outright after 47.6 s, and
another resolved in 24.4 s minutes later. The interception of
files.pythonhosted.orgwas simply gone - a genuine GlobalSign certificate where AO
Kaspersky Lab's had been, so that half was somebody's antivirus setting rather than a
property of the network. Both are corrected in CLAUDE.md with both measurements kept,
because a check that concluded "PyPI is unreachable" from one failure would have been
wrong on both days in opposite directions.COMFYUI_PACKAGE_INDEXstays empty by
default for exactly that reason. -
A plan that changes nothing was being treated as a plan that changes
everything.plan_is_additiveanswers False for a no-op plan - correctly, since
no listed package is new - and asking it the other question made an install
defer packages it was never going to move, and a repair refuse while ComfyUI ran
over a plan whose every requirement was already satisfied. Measured on
ComfyUI-KJNodes, whose five requirements are all present here.
disturbs_installedis the predicate that was actually wanted: does this plan
touch something already installed. -
A
#in the middle of a requirement is not a comment, and this is the second
place that mattered.parse_requirementsfollows pip's rule - a comment starts
at the beginning of a line or after whitespace - because a#with nothing before
it is routinely part of a direct reference (pkg @ https://host/x.whl#sha256=...).
ComfyUI-Manager's own installer doessplit('#')[0], which truncates that to a
URL with no fragment and installs different bytes without saying so. Lines
beginning-are instructions to pip rather than requirements and are reported
rather than dropped:--index-urlchanges where every package comes from.
Добавлено
-
Установка - середина концепции расширений, и построена она последней намеренно.
Три инструмента -install_extension,update_extension,repair_extension- в
отдельной группеextensions_install.То, что установка пака делает с машиной, - это почти никогда не его собственные
файлы. Файлы - это папка, которуюset_extension_enabledснова спрячет одним
переименованием. Дело в пакетах, которые двигают его требования, а вот у этой
половины отмены нет. Отсюда весь порядок: план читается до того, как что-то
скачано, и небезопасный отклоняется сразу; чекпойнт пишется независимо от того,
просили о нём или нет; а разница измеряется после, а не пересказывается по плану
второй раз.Скачивает ComfyUI-Manager, о пакетах решает этот сервер, и шов между ними - один
флаг.skip_post_installзаставляет Manager разрешить id реестра, скачать архив,
распаковать его и записать свой.tracking- и уронить свой post-install шаг,
который в его HTTP-пути всё равно никто не вызывает. Этот шаг делает две вещи,
которым лучше не происходить без присмотра: ставитrequirements.txtпо одной
строке за раз - ровно тот механизм, из-за которого настоящее окружение ComfyUI
перестаёт быть разрешимым целиком, - и запускает собственныйinstall.pyпака, то
есть произвольный код. Переписать скачивание своими руками альтернативой никогда не
было: второй набор соглашений для тех же папок - это как раз то, отчего пак
оказывается устано...
comfyui-mcp 0.1.9
Added
-
Extensions: three tools that start at both ends of an install and leave the
middle for later. That ordering is the design rather than an accident - an
install tool with no undo is the thing this whole concept exists to prevent,
so what ships first is the half that answers before anything is downloaded
and the half that takes it back.search_extensionsreads the Comfy Registry, which answers before a single
file is on disk. Each hit carries the pack's own dependency list, so
plan_packagescan be asked what installing it would move while the pack is
still a row in somebody else's database - cloning a repository to find out
what it wants is precisely what this avoids. No key and no account. It is
also a third host: measured while pypi.org was unreachable from this
machine, the registry answered normally.A hit says whether that same pack is already here, compared by registry id
and git remote rather than by folder name. A disabled copy is reported
separately and is deliberately not a conflict:ComfyUI-WanVideoWrapperis
installed twice on this machine - enabled, and a disabled@nightlycheckout- under one registry id, and a duplicate check counting the second would
refuse to touch the copy actually in use over a folder somebody switched off.
describe_extensionfinally joins the three sources instead of counting
them against each other./object_infocarriespython_moduleon every
entry, and on this install it is exactlycustom_nodes.<folder name>- 208
distinct modules across 2823 node types. So the question a person actually
has is now answerable: does this pack's nodes load. A pack that is installed,
enabled and registers zero types is one whose import died, and ComfyUI
catches that and carries on - nothing on the canvas says so, and every
workflow using it simply has holes where its nodes were.get_comfy_logis
where the traceback is.Naming a pack is harder than it looks. The registry calls it
comfyui-kjnodes, itspyproject.tomlagrees, the folder here is
comfyui-kjnodes, on the next machineComfyUI-KJNodes, and the person says
"kjnodes". An exact match on either machine-readable identity wins outright;
a substring returns everything it matched rather than picking, because
acting on the wrong pack looks exactly like acting on the right one.
describe_extension("comfyui")matches 26 packs here and is refused.set_extension_enabledis the safest tool in the concept and the one to
reach for first. One rename insidecustom_nodes: nothing is downloaded,
no package moves, and the same call with the other value puts it back. When a
pack breaks ComfyUI's startup this is the fix - and it is the fix that still
works then, needing neither a running ComfyUI nor a network, which is exactly
what ComfyUI-Manager's own HTTP route cannot say.Manager's two spellings are both honoured, and the asymmetry between them is
the rule: it writescustom_nodes/.disabled/<name>and reads that plus
the older<name>.disabled, so a pack switched off years ago comes back.
A state rather than a toggle, so asking for the state a pack is already in
changes nothing.renameis used rather thanshutil.movebecause it
refuses to overwrite: both copies on disk at once is a state Manager can
produce, and the folders incustom_nodesare the user's.Disabling moves the folder and nothing else - the pack's Python packages stay
installed, since they are shared and uninstalling one pack's requirements
routinely takes another pack's with them. - under one registry id, and a duplicate check counting the second would
Fixed
-
The server told every client it was 0.1.0, and had done through eight
releases.__version__insrc/comfyui_mcp/__init__.pyis what goes out in
the MCP handshake, and it is a third copy of the version: the release workflow
compares the git tag againstpyproject.toml, and neither of those two ever
reads it. Now in step, and pinned by a test - the same guard the tool count
has, because a fact written down twice drifts silently either way. -
A pack's declared dependencies can contain things that are not
dependencies, and both sources repeat them.ComfyUI-MMAudioships
"# for Image utils"and"# for T5XXL tokenizer (SD3/FLUX)"as entries in
itspyproject.tomldependency list - arequirements.txtconverted to TOML
by something that kept the comment lines - and the registry mirrors the
pack's own metadata, so it arrives that way from the disk and from the
network both. Handing that to uv fails a whole plan over a line that was
never a requirement. Only a leading#is dropped: a#further along is
routinely part of a direct reference (pkg @ https://host/x.whl#sha256=...),
and truncating there produces a requirement that installs different bytes. -
The explanation written into the "could not move it" message was wrong, and
measuring it said the opposite. Renaming a directory that holds a loaded
.pydsucceeds on Windows 11 - the module loader opens it with
FILE_SHARE_DELETE, so the mapping does not pin the path. What refuses is an
ordinary open handle, which is whatopen()produces by default: a pack's
log, cache or database. So a running ComfyUI is usually not in the way, the
move is attempted rather than pre-refused, and the message now names the real
cause. Verified live with ComfyUI running: a disabled pack made a full round
trip and came back where it started.
Settings
-
COMFYUI_PACKAGE_INDEX- empty means PyPI, uv's own default. Set it when
PyPI cannot be reached. Whoever sets it is trusted with what gets
installed, since a mirror serves the bytes, soplan_packagesreports the
index it used on every call. -
COMFYUI_REGISTRY_URL- empty meanshttps://api.comfy.org, which needs no
account. For a mirror or a proxy; it is not an off switch,COMFYUI_TOOLSis. -
COMFYUI_REGISTRY_TIMEOUT.
Добавлено
-
Расширения: три инструмента, которые заходят с обоих концов установки и
оставляют середину на потом. Такой порядок - это замысел, а не случайность:
инструмент установки без отмены и есть то самое, ради предотвращения чего вся
концепция затевалась. Поэтому первой выходит половина, отвечающая до того, как
что-то скачано, и половина, которая берёт сделанное назад.search_extensionsчитает Comfy Registry, а тот отвечает, когда на диске
ещё ни одного файла. В каждой находке лежит собственный список зависимостей
пака, так чтоplan_packagesможно спросить, что сдвинет установка, пока пак
остаётся строкой в чужой базе, - клонировать репозиторий ради выяснения его
аппетитов как раз и не нужно. Ни ключа, ни аккаунта. И это третий хост:
замерено при недоступном с этой машины pypi.org - реестр отвечал как обычно.Находка сообщает, стоит ли этот же пак уже здесь, - сравнение по id реестра и
git-origin, а не по имени папки. Выключенная копия отмечается отдельно и
конфликтом намеренно не считается:ComfyUI-WanVideoWrapperна этой машине
установлен дважды - включённый и выключенный@nightly- под одним id
реестра, и проверка дубликатов, считающая вторую копию, отказалась бы трогать
ту, которой пользуются, из-за папки, которую сами же и выключили.describe_extensionнаконец сшивает три источника, а не считает их друг
против друга. В/object_infoу каждой записи естьpython_module, и на
этой установке это ровноcustom_nodes.<имя папки>: 208 разных модулей на
2823 типа нод. Значит, стал отвечаемым тот вопрос, который у человека и есть:
грузятся ли ноды этого пака. Пак, который установлен, включён и не
зарегистрировал ни одного типа, - это пак с умершим импортом; ComfyUI ловит
это и идёт дальше, так что на холсте об этом не сказано ничем, а в каждом
workflow, где он нужен, просто дыры. Трассировка - вget_comfy_log.Назвать пак сложнее, чем кажется. Реестр зовёт его
comfyui-kjnodes, его
pyproject.tomlсогласен, папка здесь -comfyui-kjnodes, на соседней машине
ComfyUI-KJNodes, а человек говорит «kjnodes». Точное совпадение по любому из
машиночитаемых имён выигрывает сразу; подстрока возвращает всё, что
совпало, а не выбирает, потому что действие над не тем паком выглядит ровно
так же, как над тем.describe_extension("comfyui")попадает здесь в 26 паков
и получает отказ.set_extension_enabled- самый безопасный инструмент всей концепции и тот,
за которым стоит тянуться первым. Одно переименование внутриcustom_nodes:
ничего не качается, ни один пакет не двигается, а тот же вызов с другим
значением возвращает всё назад. Когда пак ломает запуск ComfyUI - это лечение,
и именно оно тогда работает: ему не нужны ни запущенная ComfyUI, ни сеть, чего
как раз не может сказать о себе HTTP-маршрут самого ComfyUI-Manager.Оба написания Manager'а учтены, и асимметрия между ними и есть правило: он
пишетcustom_nodes/.disabled/<имя>, а читает и это, и старое
<имя>.disabled, так что пак, выключенный годы назад, возвращается. Это
состояние, а не переключатель: просьба о том состоянии, в котором пак уже
находится, не меняет ничего. Используетсяrename, а неshutil.move,
именно потому что он отказывается затирать: обе копии на диске разом - это
состояние, которое Manager умеет создать, а папки вcustom_nodes
принадлежат пользователю.Выключение двигает папку и больше ничего: python-пакеты пака остаются
установленными, потому что они общие, и удаление требований одного пака
регулярно уносит требования другого.
Исправлено
- Сервер сообщал каждому клиенту, что он 0.1.0, и делал это восемь релизов
подряд.__version__вsrc/comfyui_mcp/__init__.py- это то, что уходит в
MCP-рукопожатии, и это третья копия версии: релизный workflow сверяет git-тег с
pyproject.toml, и ни один из этих двух её не читает. Теперь синхронно и
прибито тестом - той же защ...
comfyui-mcp 0.1.8
Added
-
The Python environment: seven tools that make installing something a decision
rather than an accident. The first half of a concept that continues in
extensions; what is here is reading, planning, auditing and undoing.The reason it exists is that a caller with no tool for a job does not stop
wanting the job done - it reaches for the shell, where nothing knows what a
ComfyUI install is. This isdownload_model's argument one layer down: the
narrowest point of the chain running from a node pack'srequirements.txtto
pip installon somebody's machine.plan_packagessays what an install would change, and refuses when the answer
is torch. Measured on the install this was built against: asking for
torchvision==0.25.0- an ordinary pin out of a pack's requirements - plans to
replacetorch 2.11.0+cu130withtorch 2.10.0, the build with no CUDA. Nothing
in the request mentions torch, nothing in the output is an error, and the install
would succeed. Refused rather than warned about, because the cost of being wrong
is a multi-gigabyte download and a machine that has stopped using its GPU.create_checkpointandrestore_checkpointare the undo. A checkpoint is
text files - everyname==version, what is in custom_nodes and at which commit,
which PyTorch index this install came from - so it costs kilobytes and needs no
disk-space check. Models, inputs, outputs and the contents of custom_nodes are
never copied; it records what was installed, not the files.The list is captured as
uv pip list --format=freeze, neveruv pip freeze: the
latter reports a wheel install asname @ file:///D:/a/ComfyUI/..., the path on
the runner that built the portable archive - 80 of 286 packages here - and a
checkpoint written that way cannot be restored on any machine.Restoring writes a checkpoint of the current state first and names it in the
reply, which isload_workspace's guard: the tool cannot ask whether it is about
to destroy something, so it makes the answer not matter. It is refused while
ComfyUI is running, because that process holds the DLLs being replaced. Two
strengths - the default puts the recorded versions back and leaves everything else
alone,exact=Truealso uninstalls what is not on the list and names by name what
it cannot put back before it starts.sageattentionandsageattn3on this
machine were compiled locally and are on no index at all.Two things about restoring were learned from the first one run against a real
install rather than a test fixture. It installs with--no-deps, because a real
ComfyUI environment is routinely not resolvable as a whole - packs install their
requirements one at a time and pip lets a later one break an earlier one's
constraint in silence, so uv refuses the recorded set as unsatisfiable and the
restore does nothing. And the exact form carries the packaging tools explicitly:
omitting pip and setuptools tellsinstallto leave them alone but tellssyncto
delete them, which it duly planned to do.The interpreter is confirmed against the running ComfyUI, not merely found. A
portable root routinely holds more than one python, and a package installed into
the wrong one is a silent no-op: uv reports success, the import still fails, and
the wrong conclusion is easy.sys.versionfrom/system_statsis compared
whole. A restore cannot use that, ComfyUI having to be stopped for one, so it
checks the checkpoint's own recorded version instead.audit_packagesruns pip-audit throughuv tool run, so nothing is added to
this project's dependencies - the same principle as the vendoreduv.exe.
Measured: 118 advisories across 19 of 286 packages, which makes a non-empty answer
the normal state of a ComfyUI environment rather than an emergency. There is no
--fixand there will not be one.It reads the installed distributions by path rather than resolving a requirements
file, because a ComfyUI environment routinely holds packages that are on no index -
cstr, installed from a git URL by was-node-suite, failed an entire audit by
itself. The advisories come from OSV rather than PyPI's own service: broader
coverage, and a different host, so the audit still answers when PyPI is what is
down.describe_environmentreads three sources because no one of them is the
answer. The disk says what is installed in custom_nodes,/object_infosays
what registered, andget_comfy_logsays why a pack did neither - a disabled pack
and a pack whose import died are both simply absent from ComfyUI's own view.
Measured here: 38 packs, two of them disabled and therefore invisible. Both of
ComfyUI-Manager's disabled spellings are recognised, because a pack this server
calls installed while Manager's UI calls it disabled is a disagreement the user
experiences as a node that is present and does not work.Two new tool groups in
configure.bat, both on by default. Switching the
checkpoints off makes the rest less safe rather than more, which is what the
warning beside it says.
Configuration
COMFYUI_PYTHONandCOMFYUI_UV- the escape hatch for a layout the search does
not know. Both empty by default and both found automatically.COMFYUI_CHECKPOINT_DIR- empty means<COMFYUI_ROOT>/mcp_checkpoints, beside
the install it describes rather than inside this checkout.COMFYUI_CHECKPOINT_KEEP,COMFYUI_PACKAGE_TIMEOUT,COMFYUI_AUDIT_TIMEOUT.
Добавлено
-
Python-окружение: семь инструментов, после которых установка становится
решением, а не случайностью. Первая половина концепции, продолжение - в
расширениях; здесь чтение, планирование, аудит и отмена.Существует всё это потому, что тот, у кого нет инструмента под задачу, не
перестаёт хотеть её решить - он идёт в шелл, где никто не знает, что такое
установка ComfyUI. Это доводdownload_model, применённый на слой ниже: самое
узкое место цепочки, идущей отrequirements.txtчужого пака доpip install
на чьей-то машине.plan_packagesговорит, что изменит установка, и отказывает, когда ответ -
torch. Замерено на той установке, против которой всё писалось: запрос
torchvision==0.25.0- обычный пин из требований пака - планирует заменить
torch 2.11.0+cu130наtorch 2.10.0, сборку без CUDA. В запросе torch не
упомянут, в выводе нет ни одной ошибки, и установка бы прошла. Именно отказ, а
не предупреждение: цена ошибки - многогигабайтная закачка и машина, переставшая
использовать свой GPU.create_checkpointиrestore_checkpoint- это отмена. Чекпойнт - текстовые
файлы: каждыйname==version, что лежит в custom_nodes и на каком коммите, с
какого индекса PyTorch пришла эта установка. Поэтому он весит килобайты и не
требует проверки места на диске. Модели, входы, выходы и содержимое custom_nodes
не копируются никогда: записано то, что было установлено, а не сами файлы.Список снимается как
uv pip list --format=freeze, а неuv pip freeze: второй
печатает установку из колеса какname @ file:///D:/a/ComfyUI/..., путь на
раннере, который собирал портативный архив, - здесь так записаны 80 пакетов из
286, и восстановить такой чекпойнт нельзя ни на одной машине.Откат сначала пишет чекпойнт текущего состояния и называет его в ответе - это
приёмload_workspace: инструмент не может спросить, не уничтожает ли он
что-нибудь, поэтому делает так, чтобы ответ не имел значения. При работающей
ComfyUI откат запрещён: её процесс держит те самые DLL, которые заменяются. Две
силы - по умолчанию возвращаются записанные версии, а всё лишнее остаётся;
exact=Trueещё и удаляет то, чего нет в списке, и до начала называет поимённо
то, что вернуть будет нечем.sageattentionиsageattn3на этой машине
собраны локально и не лежат ни в одном индексе.Две вещи про откат выяснились на первом же запуске против настоящей установки, а
не тестовой заготовки. Установка идёт с--no-deps, потому что реальное окружение
ComfyUI как целое, как правило, неразрешимо: паки ставят свои требования по
очереди, и pip молча позволяет позднему сломать ограничение раннего, - uv признаёт
записанный набор невыполнимым, и откат не делает ничего. А полная форма явно несёт
с собой инструменты упаковки: отсутствие pip и setuptools в списке дляinstall
означает «не трогать», а дляsync- «удалить», что он и собирался сделать.Интерпретатор не просто находится, а сверяется с работающей ComfyUI. В
портативном корне регулярно оказывается больше одного python, и пакет,
поставленный не в тот, - это тихий холостой ход: uv отчитывается об успехе,
импорт по-прежнему падает, и напрашивается неверный вывод.sys.versionиз
/system_statsсравнивается целиком. Откату это недоступно - для него ComfyUI
должна быть остановлена, - поэтому он сверяется с версией, записанной в самом
чекпойнте.audit_packagesзапускает pip-audit черезuv tool run, так что в
зависимости проекта ничего не добавляется - тот же принцип, что и у вендоренного
uv.exe. Замерено: 118 уязвимостей в 19 пакетах из 286, - то есть непустой ответ
здесь нормальное состояние окружения ComfyUI, а не ЧП. Флага--fixнет и не будет.Читаются установленные дистрибутивы по пути, а не резолвится файл требований:
в окружении ComfyUI регулярно есть пакеты, которых нет ни в одном индексе, -cstr,
поставленный из git-ссылки пакетом was-node-suite, в одиночку роняет весь аудит.
Уязвимости берутся из OSV, а не из сервиса самого PyPI: шире покрытие и другой хост,
так что аудит отвечает даже когда лежит именно PyPI.describe_environmentчитает три источника, потому что ни один из них не
является ответом. Диск говорит, что установлено в custom_nodes,/object_info- что зарегистрировалось,
get_comfy_log- почему пак не сделал ни того, ни
другого: ...
- что зарегистрировалось,
comfyui-mcp 0.1.7
Added
-
ask_workspaceandconfirm_workspace: put a question on the ComfyUI screen
and wait for the person to answer it. The first tools here whose answer comes
from a human rather than from the graph, for the step that turns on something
only they know - which of two results they preferred, whether a value looks
right - instead of guessing and building on the guess. They open the frontend's
own dialogs throughapp.extensionManager.dialog, so nothing is drawn by hand.The question goes below the canvas, not over it. A dialog is modal and closes
on a click beside it, so on the question most worth asking - "look at this and
tell me" - looking is what cancels it. The default surface is a bottom panel tab,
which covers nothing and dismisses on nothing, so the graph can be panned and
inspected while the question waits.modal=Trueasks in a dialog instead, for
something that should interrupt rather than wait to be noticed. The panel draws
its own buttons, so yes, no and dismiss are all reachable there whateverkind
says.choicesmakes it a pick rather than a typing exercise. One click instead of
a sentence, and the answer arrives as one of the strings that were offered rather
than as something to parse - withchoice_indexbeside it, since two options can
read alike once phrased and an index cannot. Six or fewer are buttons, more become
a dropdown, andallow_otheradds a free-text box answering with-1. Only the
panel can offer them: ComfyUI's dialog service haspromptandconfirmand
nothing that picks from a list, andconfirm'sitemListrenders a plain<ul>
that cannot be clicked - somodal=Truewith choices is refused rather than
quietly ignored.One question at a time, refused rather than queued.
promptandconfirm
both open throughdialogStore.showDialogwith the same key, and showDialog
handed a key already in the stack re-shows the dialog that is there and drops
the options it was given - including the callbacks the promise is waiting on. A
second question would therefore neither appear nor ever be answered, so it is
refused, naming the one in the way. ComfyUI's own prompts share that key too.A deadline that leaves nothing dangling. The wait is bounded and reports
timed_outwithstill_on_screen, because a question we stopped waiting for is
still in front of the user and still holds the dialog. The ordering that matters
is dialog < bridge < httpx < the client's own per-call cap.Dismissal is an answer, and not the one you want. Clicking beside the box
closes it, sonullarrives easily; it is neither yes nor no and is reported as
dismissedrather than folded into either. Forconfirm_workspacea plain
falseneeds a deny button, which ComfyUI draws forkind="dirtyClose"alone -
every other kind offers Cancel and Confirm, so Cancel lands onnull. Passing
deny_labelwith any other kind is refused instead of doing nothing quietly.The
askgroup is switched on by default and can be turned off like any other;
a tab that predates it answersunknown method, which already means "reload".
Fixed
-
arrange_workspaceno longer makes a workflow harder to read than it was.
Measured against the layouts their authors had made by hand, it was producing
three to nine times the link crossings and up to five times the total link
length - on a 133-node workflow, 527 crossings against the author's 59. Three
things were wrong and each is now the other way round.A node's column was one past everything feeding it, which drags every source to
the far left however deep the thing that reads it sits: 71 of those 133 nodes
landed in column 0, and one link was stretched across seventeen columns. A node
now goes as far right as its consumers allow, so a loader sits beside the
sampler that reads it.Row order was decided by looking only at what fed a node, so a node's own
consumers had no say and the last sweep won whether or not it helped. It now
sweeps both ways and keeps the round with the fewest crossings, and a link
spanning several columns gets a placeholder in each one it passes through -
without which crossing minimisation cannot see it at all.Heights were not chosen: each column was packed from its own top and the columns
centred against one another, which honours the order and ignores the links. Every
node is now placed as near as it can get to the middle of what it connects to,
which is what the vertical travel measures - down from 114k to 11k on that same
workflow.Nodes with no links at all get a column of their own rather than a row in a flow
they are not part of: a note that sat far below the graph used to keep its height
and strand the canvas reaching for it.Groups are laid out as groups. A group box has no membership - what is in it
is whatever falls inside - so arranging a grouped canvas flat scatters each
group's nodes across the columns and its box stretches to follow them. On a real
workflow that turned a 670x670 group into 6130x900 and left eight of them as
overlapping sheets covering the canvas: the links were measurably tidier and the
result was unreadable, which is the whole lesson about optimising a number. The
layout now runs inside each group first and then over the blocks they form, with
ungrouped nodes travelling together as one more block.onlystill ignores them,
since a caller naming the nodes has already said what they mean.The same four workflows now come out at 7, 4, 32 and 65 crossings against 21, 7,
59 and 95 by hand. Worth saying plainly: fewer crossings is not the same as
prettier, and a layered layout still does not group related nodes the way a
person does.
Добавлено
-
ask_workspaceиconfirm_workspace: показать вопрос в окне ComfyUI и
дождаться ответа человека. Первые здесь инструменты, чей ответ приходит от
человека, а не из графа, - для шага, который зависит от того, что знает только
он: какой из двух результатов ему больше понравился, верно ли выглядит
значение. Вместо догадки, на которой потом строится всё остальное. Открываются
собственные диалоги фронтенда черезapp.extensionManager.dialog, ничего не
рисуется руками.Вопрос показывается под холстом, а не поверх него. Диалог модальный и
закрывается от клика мимо, так что на самом ценном вопросе - «посмотри и скажи» -
именно разглядывание его и отменяет. Поверхность по умолчанию - вкладка нижней
панели: она ничего не перекрывает и ни от чего не закрывается, поэтому холст можно
двигать и изучать, пока вопрос ждёт.modal=Trueспрашивает диалогом - для того,
что должно перебить, а не ждать, пока заметят. Панель рисует свои кнопки, так что
«да», «нет» и «закрыть» доступны там при любомkind.choicesпревращает вопрос в выбор, а не в сочинение. Один клик вместо
фразы, и ответ приходит одной из предложенных строк, а не текстом, который надо
разбирать, - вместе сchoice_index, потому что два варианта после
формулирования могут читаться одинаково, а индекс нет. Шесть и меньше рисуются
кнопками, дальше - выпадающим списком;allow_otherдобавляет поле для своего
ответа и отвечает индексом-1. Предложить их может только панель: у диалогов
ComfyUI естьpromptиconfirmи ничего для выбора из списка, аitemListу
confirmрисует обычный<ul>, по которому нельзя кликнуть, - поэтому
modal=Trueвместе с choices отклоняется, а не игнорируется молча.Вопрос строго один за раз, и второй отклоняется, а не встаёт в очередь.
promptиconfirmоткрываются черезdialogStore.showDialogс одним и тем
же ключом, а showDialog, получив ключ, который уже в стеке, показывает
имеющийся диалог и отбрасывает переданные опции - вместе с колбэками,
которых ждёт промис. Второй вопрос не появился бы и не получил бы ответа
никогда, поэтому он отклоняется с указанием, что именно мешает. Тот же ключ
используют и собственные диалоги ComfyUI.Дедлайн, не оставляющий висящих концов. Ожидание ограничено и сообщает
timed_outвместе сstill_on_screen: вопрос, которого мы перестали ждать,
по-прежнему перед человеком и по-прежнему занимает диалог. Важен порядок:
диалог < мост < httpx < потолок одного вызова у клиента.Закрыть окно - это тоже ответ, только не тот. Клик мимо окна закрывает его,
так чтоnullприходит легко; это не «да» и не «нет», и сообщается отдельным
dismissed, а не подменяет одно из них. Дляconfirm_workspaceобычныйfalse
требует кнопки отказа, а её ComfyUI рисует только приkind="dirtyClose"- у
остальных типов есть лишь Cancel и Confirm, и Cancel даётnull. Передача
deny_labelс другим типом отклоняется, а не игнорируется молча.Группа
askвключена по умолчанию и выключается как любая другая; вкладка
старее её отвечаетunknown method, что уже означает «перезагрузите».
Исправлено
-
arrange_workspaceбольше не делает воркфлоу хуже, чем он был. В сравнении
с раскладками, которые авторы сделали руками, он давал в три-девять раз больше
пересечений связей и до пяти раз большую их суммарную длину: на воркфлоу из 133
нод - 527 пересечений против 59 у автора. Неверными были три вещи, и каждая
теперь работает наоборот.Колонка ноды считалась как «на одну правее всего, что её кормит», а это утаскивает
каждый источник в крайний левый столбец независимо от того, где его читают: 71
нода из тех 133 оказывалась в нулевой колонке, а одна связь растягивалась на
семнадцать столбцов. Теперь нода уезжает настолько вправо, насколько позволяют её
потребители, и загрузчик стоит рядом с сэмплером, который его читает.Порядок строк определялся только по входам, так что собственные потребители ноды
не имели голоса, а побеждал последний проход - независимо от того, помог он или
навредил. Теперь проходы идут в ...
comfyui-mcp 0.1.6
Fixed
-
A ComfyUI orphaned by a hard kill of this server can be stopped again.
Stopping on a graceful exit covers the tidy case; a killed server still leaves
ComfyUI running, and until now that orphan was also unreachable, because
ownership lived only in memory -comfy_stoprefused the very process it had
started, while the port stayed taken and the model stayed resident. The pid is
now recorded when ComfyUI is launched, and a starting server takes it back.It adopts; it does not reap. Killing whatever the record names at startup is
the obvious shortcut and is wrong twice: a generation outlives an editor crash
and can still be running - twenty minutes and more is ordinary here - and two MCP
clients open at once would each shoot down the other's ComfyUI on launch. So
ownership is restored and the decision is left to whoever is sitting there.A pid alone is a number, so the start time is recorded with it. Pids are
reused, readily on Windows and on every platform after a reboot, so a record that
only named a pid could later name somebody's editor. Adoption requires the start
time to match too, and a record that fails to verify is deleted rather than kept.
Where that stamp cannot be read the record is not written at all, since acting on
it later could not be made safe. The check also has to mean running rather than
findable: a process object outlives the process while any handle to it survives,
so reading a start time succeeds for one that has already exited - which showed up
live ascomfy_statusreporting a ComfyUI of ours that was long gone. Note thattempfile's auto-deleting kinds are
unusable for this: such a file is removed when the process is killed, which is
precisely the case it would exist to survive. -
A ComfyUI this server started is no longer left running when the server
exits, and stopping it now reaches ComfyUI itself. Two faults met here. The
process held is thecmd.exeor shell the launch script runs in, and python is
its child, so the POSIX path - a bareterminate()on that shim - killed the
wrapper and left python holding the port and the GPU; only the Windows path
walked the tree. And nothing stopped anything on the way out:main'sfinally
closed the HTTP client and never touched the process, so even a clean shutdown
orphaned it.stopnow signals the whole tree on both platforms - a process
group on POSIX,taskkill /Ton Windows - and a graceful exit stops it.Asked before it is forced. The old path went straight to a hard kill, which
costs ComfyUI the chance to finish what it had open. It escalates immediately
rather than waiting out the grace period when the ask cannot be delivered, which
on Windows is the ordinary case: taskkill's polite form talks to windows, and a
ComfyUI started withCREATE_NO_WINDOWhas none to talk to.A ComfyUI you started yourself is still never touched. That gate is
ownership, not whether something answers the port - a hand-started instance
answers it too. For the same reason the pid is deliberately not written to
disk to survive a server restart: pids are reused, and adopting one by number
risks signalling a process that is not ours. A hard kill of the server therefore
still leaves ComfyUI running, which is the safe direction to fail in - tying its
lifetime to this process would take a running generation down with an IDE
restart.
Исправлено
-
ComfyUI, осиротевший после жёсткого убийства сервера, снова можно
остановить. Гашение при штатном выходе закрывает аккуратный случай; убитый
сервер по-прежнему оставляет ComfyUI работать, и до сих пор такая сирота была
ещё и недостижима: владение жило только в памяти, поэтомуcomfy_stopотказывал
тому самому процессу, который сам же и запустил, - а порт оставался занят и
модель висела в видеопамяти. Теперь pid записывается при запуске, и стартующий
сервер забирает процесс обратно.Усыновляет, а не добивает. Убивать при старте всё, что названо в записи, -
очевидное сокращение и неверное дважды: генерация переживает падение редактора и
может всё ещё идти (двадцать минут и больше здесь обычное дело), а два
одновременно открытых MCP-клиента при запуске расстреливали бы ComfyUI друг
друга. Поэтому владение восстанавливается, а решение остаётся за человеком.Один pid - это просто число, поэтому рядом пишется время старта. Pid'ы
переиспользуются: на Windows охотно, а после перезагрузки - на любой системе, так
что запись с одним лишь номером со временем могла бы указывать на чужой редактор.
Для усыновления время старта тоже должно совпасть, а не прошедшая проверку запись
удаляется, а не хранится дальше. Там, где эту метку прочитать нельзя, запись не
делается вовсе: действовать по ней потом было бы небезопасно. Проверка при этом
должна означать работает, а не находится: объект процесса переживает сам
процесс, пока жив хоть один хендл на него, поэтому время старта успешно читается и
у уже завершившегося - вживую это проявилось какcomfy_status, сообщавший о нашем
ComfyUI, которого давно нет. Отдельно:
самоудаляющиеся файлы изtempfileдля этого непригодны - такой файл исчезает при
убийстве процесса, то есть ровно в том случае, ради которого он и заводился. -
ComfyUI, запущенный этим сервером, больше не остаётся жить после его выхода,
а остановка теперь достаёт до самого ComfyUI. Здесь сошлись две ошибки.
Процесс, за который мы держимся, - этоcmd.exeили шелл, в котором крутится
скрипт запуска, а python ему дочерний, поэтому POSIX-ветка с голым
terminate()убивала обёртку и оставляла python с занятым портом и
видеопамятью; дерево обходила только ветка Windows. При этом на выходе не
останавливалось ничего:finallyвmainзакрывал HTTP-клиент и не трогал
процесс, так что сирота оставалась даже после штатного завершения. Теперьstop
сигналит всему дереву на обеих платформах - группе процессов на POSIX,
taskkill /Tна Windows, - а штатный выход его гасит.Сначала просит, потом заставляет. Раньше сразу шло жёсткое убийство, лишавшее
ComfyUI возможности дописать начатое. Если просьбу доставить нельзя, эскалация
происходит немедленно, а не после полного grace-периода, - и на Windows это
обычный случай: вежливая форма taskkill разговаривает с окнами, а у ComfyUI,
запущенного сCREATE_NO_WINDOW, окна нет.ComfyUI, который вы запустили сами, по-прежнему не трогается никогда. Признак
тут - владение, а не то, отвечает ли что-то на порту: запущенный руками отвечает
ровно так же. По той же причине pid намеренно не пишется на диск, чтобы
пережить перезапуск сервера: pid'ы переиспользуются, и усыновление по номеру
рискует отправить сигнал чужому процессу. Поэтому жёсткое убийство сервера
по-прежнему оставляет ComfyUI работать - и это безопасная сторона отказа:
привязка его жизни к нашему процессу уронила бы идущую генерацию при перезапуске
IDE.
comfyui-mcp 0.1.5
Fixed
- A name containing a dot no longer loses everything after it. Saving
bench_0.8mp_15swrotebench_0.json.Path.with_suffixreads.8mp_15sas
the extension and replaces it instead of appending, and a name carrying a
resolution or a version number is written with dots all the time. Nothing
reported it: the call succeeded and answered with the truncated path, so the
file was merely somewhere other than the name asked for - and a second save
under a neighbouring name would have silently overwritten it. The three places
that turn a name into a.jsonfile -resolve_path,_write_jsonbehind
bothsave_workflowandsave_export, andresolve_graph_file- now share
one helper that appends. A name already ending in.jsonis untouched, as
before, and the sidecar guide still resolves beside it.
Исправлено
- Имя с точкой больше не теряет всё, что после неё. Сохранение
bench_0.8mp_15sзаписывалоbench_0.json.Path.with_suffixсчитает
.8mp_15sрасширением и заменяет его вместо того, чтобы дописать, - а имя,
несущее разрешение или номер версии, пишут через точку постоянно. Никто об
этом не сообщал: вызов отрабатывал успешно и возвращал уже обрезанный путь,
так что файл просто оказывался не там, где просили, - а следующее сохранение
под соседним именем молча его затёрло бы. Три места, превращающие имя в
.json-файл -resolve_path,_write_jsonподsave_workflowи
save_export, иresolve_graph_file, - теперь пользуются одним хелпером,
который дописывает. Имя, уже оканчивающееся на.json, не трогается, как и
раньше, а сайдкар-гайд по-прежнему находится рядом.
comfyui-mcp 0.1.4
Added
-
COMFYUI_USERandCOMFYUI_PASSWORD: Basic Auth for a ComfyUI reached
through a reverse proxy. The bridge token guards the bridge's own routes; a
proxy asks for credentials in front of the whole API, so the two compose rather
than overlap. The pair is sent on the HTTP calls and on the WebSocket from one
source - two sources drift, and that failure reads as a broken WebSocket rather
than a credential problem. The password goes in.envrather than into the URL,
where it would surface in process lists, logs and error text.Empty changes nothing. Both halves or neither: with none set the client is
built exactly as it always was, which is the case everybody on localhost is in.
Half a pair authenticates with nothing, so that is named outright instead of
being left to a 401 that says nothing about the setting somebody was part way
through filling in.
Changed
-
comfy_statussays why it could not reach ComfyUI. A proxy answering 401
looked exactly like a ComfyUI that was not running, which sends somebody off to
start one that is already up, or to open a firewall that was never shut. Three
cases are now told apart: nothing configured and something is asking,
credentials sent and rejected, and any other non-200. It also reports
auth: "basic"when it is on - never the credentials themselves. -
websocketsnow needs 14.0 or newer. The header parameter was renamed
additional_headersin 14, and the oldextra_headersis gone rather than
deprecated: passing it raisesTypeError, which would have been swallowed into
a silent fall back to polling. Nothing else about the dependency changed.
Добавлено
-
COMFYUI_USERиCOMFYUI_PASSWORD: Basic Auth для ComfyUI за обратным
прокси. Токен моста закрывает только его собственные маршруты, а прокси
спрашивает пароль перед всем API, так что эти два механизма дополняют друг
друга, а не пересекаются. Пара отправляется и в HTTP-запросах, и в WebSocket
из одного места: два источника разъезжаются, и выглядит это как сломанный
WebSocket, а не как проблема с паролем. Сам пароль лежит в.env, а не в URL,
где он попал бы в список процессов, логи и тексты ошибок.Пусто - значит ничего не меняется. Либо обе половины, либо ни одной: без
них клиент собирается ровно так же, как собирался, и в этом случае находятся
все, у кого ComfyUI на localhost. Одна половина без другой не аутентифицирует
ничего, поэтому о ней говорится прямо, а не оставляется на голый 401, который
ни слова не скажет про недописанную настройку.
Изменено
-
comfy_statusобъясняет, почему не достучался. Прокси, отвечающая 401,
выглядела точно так же, как незапущенный ComfyUI, - и человек шёл запускать
уже работающий или открывать firewall, который никто не закрывал. Теперь
различаются три случая: ничего не настроено и пароль спрашивают, данные
отправлены и отвергнуты, и любой иной не-200. Плюс сообщаетсяauth: "basic",
когда авторизация включена, - но никогда сами учётные данные. -
websocketsтеперь нужен версии 14.0 или новее. В 14-й параметр
переименовали вadditional_headers, а старыйextra_headersне устарел, а
исчез: его передача роняетTypeError, который был бы проглочен и превращён в
тихий откат на опрос вместо потоковых событий. Больше в зависимостях ничего не
поменялось.
comfyui-mcp 0.1.3
Added
-
close_workspace_tab, andswitch_workspace_tab(to="new")for a blank
one. Tabs could be listed and switched between; now they can be made and
closed. "new" runs the command the + button runs. Closing deliberately does
not use ComfyUI's matching command, because that one raises the unsaved-changes
dialog and waits for a human - the check is made here instead, so a tab with
unsaved work is refused with a sentence saying what to do rather than a modal
nobody on this side can answer. The last remaining tab is refused too, and
closing the tab on screen moves to a neighbour first so the canvas never shows
a workflow that is no longer open. -
promote_workspace_inputs: exposing an inner node's inputs on the face of
the subgraph that holds it. A subgraph with nothing promoted is a sealed box- its values are reachable only from inside, and nothing outside can be wired
to it. A widget row and a plain socket take the same path, and the id names
both ends:98:12is node 12 inside subgraph node 98, so there is no "which
subgraph" argument and no navigating first. An input already wired inside is
refused rather than quietly rewired.
- its values are reachable only from inside, and nothing outside can be wired
-
pack_workspace_subgraph: folding nodes and groups into a subgraph, and
dissolving one back. A group can be named instead of listing its nodes.
Unpacking runs before packing, so one call can take a subgraph apart and
rebuild it differently. Ids do not survive either direction - packing replaces
the nodes with one, unpacking hands the contents fresh ones - so read the graph
again afterwards. -
A
.ps1may be the launch script, along with.cmdand.sh. Offering
only.batwas never a boundary: the setting already runs a script of the
user's choosing, and a.batcan do everything a.ps1can, starting with
calling PowerShell. What it could not do was carry the multi-GPU flags people
keep in a.ps1. A.ps1is run through PowerShell with-ExecutionPolicy Bypass, which applies to that one process and changes nothing on the machine;
the alternative would be asking somebody to loosen a machine-wide policy to
start ComfyUI.
Changed
load_workspacesays which tab it landed in. It never replaced the
workflow on screen - ComfyUI opens the file in a tab of its own - but nothing
reported that, so tabs accumulated silently and afterwards made a reload
refuse. The reply now names the tabs and whether one was added.
Fixed
-
A load could take over a workflow tab that had nothing to do with it.
ComfyUI resolves the name against its own saved workflows rather than the
directory the file came from, so a file whose name matched one of the user's
workflows filled that workflow's tab instead of opening one - leaving it
marked modified, one Ctrl+S from overwriting real work. That is refused now,
withforcefor when replacing it is the actual intent. -
Two loads in quick succession opened
X (2).jsoninstead of reusing the
tab. The active workflow is set behind an await thatloadGraphDatadoes not
wait for, so the second load arrived before the first had landed and missed the
reuse. The same gap made a switch report the tab it had come from. -
The launch script could point outside the ComfyUI folder.
root / name
returnsnameunchanged when it is absolute - pathlib's rule, and a silent one- so
COMFYUI_LAUNCH_SCRIPT=C:\anything.batnamed a program outside the
install and nothing said so. Both the settings window andcomfy_startrefuse
that now. It was the only real risk in this setting, and it had nothing to do
with the extension.
- so
Добавлено
-
close_workspace_tabиswitch_workspace_tab(to="new")для пустой
вкладки. Вкладки можно было перечислять и переключать; теперь их можно
создавать и закрывать. За "new" стоит та же команда, что и за кнопкой "+".
Закрытие намеренно не использует парную команду ComfyUI: та поднимает
модальное окно про несохранённые правки и ждёт человека. Проверка сделана на
нашей стороне, поэтому вкладка с несохранённой работой получает отказ с
внятным текстом, а не диалог, на который отсюда некому ответить. Последняя
вкладка тоже не закрывается, а перед закрытием активной сначала происходит
уход на соседнюю - иначе холст показывал бы воркфлоу, которого в панели уже
нет. -
promote_workspace_inputs: проброс входов внутренней ноды на лицо
сабграфа. Сабграф без пробросов - запечатанный ящик: его значения доступны
только изнутри, и снаружи к нему ничего не подключить. Виджет и обычный сокет
идут одним путём, а составной id называет обе стороны сразу:98:12- это
нода 12 внутри ноды-сабграфа 98, поэтому не нужен ни отдельный аргумент "в
какой сабграф", ни предварительная навигация. Вход, уже подключённый внутри,
отклоняется, а не перевязывается молча. -
pack_workspace_subgraph: упаковка нод и групп в сабграф и обратная
распаковка. Вместо перечисления нод можно назвать группу. Распаковка идёт до
упаковки, так что один вызов способен разобрать сабграф и собрать его иначе.
Id при этом не сохраняются ни в одну сторону - упаковка заменяет ноды одной,
распаковка выдаёт содержимому новые, - поэтому после операции граф нужно
перечитать. -
Скриптом запуска может быть
.ps1, а также.cmdи.sh. Список из
одних.batникогда не был границей безопасности: настройка и так запускает
скрипт по выбору пользователя, а.batумеет всё то же, что.ps1, начиная с
того, что может позвать PowerShell сам. Чего он не умел - нести multi-GPU
флаги, которые люди держат в.ps1. Такой скрипт запускается через PowerShell
с-ExecutionPolicy Bypass, а он действует на один процесс и на машине не
меняет ничего; иначе пришлось бы просить человека ослабить политику глобально
ради запуска ComfyUI.
Изменено
load_workspaceсообщает, в какую вкладку он попал. Он никогда не заменял
воркфлоу на экране - ComfyUI открывает файл своей вкладкой, - но об этом ничто
не сообщало, поэтому вкладки копились молча и потом мешали перезагрузке. Теперь
в ответе перечислены вкладки и сказано, добавилась ли новая.
Исправлено
-
Загрузка могла захватить чужую вкладку. ComfyUI ищет имя в своём
хранилище воркфлоу, а не в том каталоге, откуда пришёл файл, поэтому файл с
совпадающим именем попадал во вкладку пользовательского воркфлоу вместо своей -
и оставлял её помеченной изменённой, в одном Ctrl+S от затирания настоящей
работы. Теперь это отклоняется, аforceостаётся для случая, когда заменить
тот воркфлоу и есть намерение. -
Две загрузки подряд открывали
X (2).jsonвместо переиспользования
вкладки. Активный воркфлоу выставляется заawait, которогоloadGraphData
не дожидается, так что вторая загрузка приходила раньше, чем оседала первая, и
не находила своей вкладки. Та же щель заставляла переключение отчитываться о
вкладке, с которой оно ушло. -
Скрипт запуска мог указывать за пределы папки ComfyUI.
root / name
возвращает самname, когда тот абсолютный - правило pathlib, и молчаливое, -
поэтомуCOMFYUI_LAUNCH_SCRIPT=C:\что_угодно.batназывал программу вне
установки, и ничто об этом не говорило. Теперь это отклоняют и окно настроек, и
comfy_start. Это и был единственный настоящий риск в данной настройке, и к
расширению файла он отношения не имел.
comfyui-mcp 0.1.2
Added
-
switch_workspace_tab: the workflow tabs along the top of the ComfyUI
window, reported and switched between. These are open workflows rather than
browser tabs - one browser tab holds all of them - and every other workspace
tool acts on whichever one is on screen, so this is what points them at a
different workflow. Called with no target it reports and moves nothing, which
is how to find out what is open before naming one; a name matching two open
tabs is refused rather than guessed.nextandpreviousstep along the bar,
whilerecentis ComfyUI's own activation history, and a switch made here is
written into that history exactly as a click is.Switching does what clicking the tab does and no more: the canvas is reloaded
from that workflow's own stored state, through the same call the frontend
makes. Unsaved edits in the tab being left behind are not lost - they belong to
that workflow, which is why the bar can show one tab as modified while another
is on screen. The store this reads was assumed to be out of reach, since the
frontend keeps it behind a minified module; it turns out to be public after all
throughapp.extensionManager, which is the same surface ComfyUI offers every
extension.
Добавлено
-
switch_workspace_tab: вкладки воркфлоу вдоль верхнего края окна ComfyUI -
показать список и переключиться. Это открытые воркфлоу, а не вкладки
браузера: одна вкладка браузера держит их все. Все прочие инструменты холста
работают с тем воркфлоу, что сейчас на экране, поэтому именно этот инструмент
наводит их на другой. Вызванный без цели, он только показывает и ничего не
двигает - так и выясняют, что открыто, прежде чем что-то называть; имя,
подходящее сразу двум вкладкам, отклоняется, а не угадывается.nextи
previousшагают по панели, аrecent- это собственная история активаций
ComfyUI, и переключение отсюда записывается в неё ровно так же, как клик мышью.Переключение делает то же, что клик по вкладке, и не больше: холст
перезагружается из сохранённого состояния этого воркфлоу, тем же вызовом,
которым это делает сам фронтенд. Несохранённые правки в покидаемой вкладке не
теряются - они принадлежат своему воркфлоу, поэтому панель и может показывать
одну вкладку изменённой, пока на экране другая. Стор, из которого всё это
читается, считался недоступным - фронтенд держит его в минифицированном модуле,- но он оказался публичным через
app.extensionManager, ту самую поверхность,
которую ComfyUI предлагает любому расширению.
- но он оказался публичным через