Compare commits
551 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bba55d4d5a | ||
|
|
87111bd889 | ||
|
|
3661ffd3ab | ||
|
|
d8f111a5e3 | ||
|
|
ae5565ce68 | ||
|
|
e4c370a7d9 | ||
|
|
891005bcd3 | ||
|
|
d3a4a7a0fa | ||
|
|
10211d1a93 | ||
|
|
7f019a932b | ||
|
|
fae909de2f | ||
|
|
d8455ef6e5 | ||
|
|
934c994783 | ||
|
|
d0961d596d | ||
|
|
382df24764 | ||
|
|
bfcfa42125 | ||
|
|
2333886c34 | ||
|
|
0cdad3c886 | ||
|
|
eee23c543b | ||
|
|
f0a8812f5e | ||
|
|
a8d603f753 | ||
|
|
22acaa1d2c | ||
|
|
fe791ccee9 | ||
|
|
414557eee0 | ||
|
|
97d2741360 | ||
|
|
b95e5f1eae | ||
|
|
43b200dc91 | ||
|
|
29014699bb | ||
|
|
5576672957 | ||
|
|
5002606861 | ||
|
|
ba0fb343ff | ||
|
|
17e5ae6bc2 | ||
|
|
7a0186efc8 | ||
|
|
de64af4a68 | ||
|
|
4a852ac8a8 | ||
|
|
6784bfb98c | ||
|
|
c8f246d344 | ||
|
|
8b3d31a936 | ||
|
|
5e88d6445b | ||
|
|
fd7dff88df | ||
|
|
8cfee1f483 | ||
|
|
cf4d8e6125 | ||
|
|
c0e8a41d2a | ||
|
|
a02c27b1af | ||
|
|
712e1bac0d | ||
|
|
513ea46cbe | ||
|
|
b1919b6f95 | ||
|
|
43561d209b | ||
|
|
16dcbc5412 | ||
|
|
c8dd2d5cad | ||
|
|
4b37777066 | ||
|
|
95ecd85a12 | ||
|
|
5c475e3c15 | ||
|
|
f705ee6863 | ||
|
|
1f67c18989 | ||
|
|
de6d451c5b | ||
|
|
580296d6f3 | ||
|
|
a9e28fbce3 | ||
|
|
311779cb20 | ||
|
|
d2f8a89e87 | ||
|
|
84c95bf322 | ||
|
|
f75c801955 | ||
|
|
faa2f54371 | ||
|
|
4249ac193a | ||
|
|
c709274a28 | ||
|
|
c8f05e79db | ||
|
|
4d2887e99f | ||
|
|
29256a5154 | ||
|
|
82d42e4094 | ||
|
|
53850fb627 | ||
|
|
34b4c8ce46 | ||
|
|
e944841054 | ||
|
|
f6a5ff5552 | ||
|
|
01763b59d4 | ||
|
|
044173b2a1 | ||
|
|
99e7a88dbd | ||
|
|
01cd9fbb0e | ||
|
|
aaed1dc3d5 | ||
|
|
c8dce94c03 | ||
|
|
06496d07b3 | ||
|
|
a97f98c9cc | ||
|
|
8d0406f74f | ||
|
|
c64d14701d | ||
|
|
00332ae444 | ||
|
|
e8deb3d8fe | ||
|
|
8b234c99cf | ||
|
|
1f986d9c45 | ||
|
|
bacb8fb3cd | ||
|
|
e4a90089ab | ||
|
|
674b9f3705 | ||
|
|
4941fb8aa0 | ||
|
|
183af0dfa5 | ||
|
|
45ac5429f8 | ||
|
|
c771977a95 | ||
|
|
668d7bbb2c | ||
|
|
926cfabb58 | ||
|
|
a9a8d05115 | ||
|
|
e368f4366a | ||
|
|
dc5bddbc17 | ||
|
|
358a480408 | ||
|
|
c96fdb3c7a | ||
|
|
c090abcc02 | ||
|
|
1ff02be35f | ||
|
|
10fbfb88f7 | ||
|
|
9753df72ed | ||
|
|
095cc3f792 | ||
|
|
656171037b | ||
|
|
7ac10f9442 | ||
|
|
3925ba27b4 | ||
|
|
44ba79aa31 | ||
|
|
14d0e31268 | ||
|
|
033acffad1 | ||
|
|
d29ff808a5 | ||
|
|
dc9b6d655b | ||
|
|
d340c85013 | ||
|
|
e328353664 | ||
|
|
02785af8fd | ||
|
|
736ae5d63e | ||
|
|
e1eeb617d2 | ||
|
|
23b6c7f0de | ||
|
|
997f97e1fc | ||
|
|
ff335ff1a0 | ||
|
|
cb3036ef81 | ||
|
|
f762906188 | ||
|
|
dde7920f8c | ||
|
|
1a0d24110a | ||
|
|
e79f6c4471 | ||
|
|
a8a7024a84 | ||
|
|
d93d002da0 | ||
|
|
baaa0479e8 | ||
|
|
cc3bd7a056 | ||
|
|
4ecefb3b71 | ||
|
|
f24b5aa251 | ||
|
|
de547da4cd | ||
|
|
0f884166a6 | ||
|
|
63379f759d | ||
|
|
8fdff20243 | ||
|
|
5dfa07ca03 | ||
|
|
343645be6a | ||
|
|
91bf21d7a8 | ||
|
|
be6516cfd3 | ||
|
|
61f1e516a3 | ||
|
|
73b2278b45 | ||
|
|
aa625e30b6 | ||
|
|
29a46fe4ce | ||
|
|
5b3ee49530 | ||
|
|
a9158a101f | ||
|
|
feed8abb34 | ||
|
|
70decc740f | ||
|
|
5b5c83f8c5 | ||
|
|
773c06f40d | ||
|
|
737e6ad5ed | ||
|
|
81bca9c94e | ||
|
|
eef0654de2 | ||
|
|
997a00b8a2 | ||
|
|
4d25232c5f | ||
|
|
135befa101 | ||
|
|
44cac3fc43 | ||
|
|
449fa3510e | ||
|
|
d958af8aad | ||
|
|
09f8d5cb2d | ||
|
|
aedc99cefd | ||
|
|
b32cab6e9a | ||
|
|
a95186965e | ||
|
|
7067de1bb2 | ||
|
|
f45d878d21 | ||
|
|
a0532b938d | ||
|
|
6ad12b7652 | ||
|
|
02887c6c9b | ||
|
|
1b645e1cc3 | ||
|
|
0a4b2a0488 | ||
|
|
d4ce6ddc52 | ||
|
|
5a5b989533 | ||
|
|
b57cffb0fa | ||
|
|
72aa95cacf | ||
|
|
14ef448937 | ||
|
|
1c10607c06 | ||
|
|
41e53a1f2a | ||
|
|
cde83e9a38 | ||
|
|
6c206b1c72 | ||
|
|
a474219e7b | ||
|
|
3b8d25eb31 | ||
|
|
0faa0aa668 | ||
|
|
0fcdcc93a2 | ||
|
|
461d5e72fe | ||
|
|
3f030a2121 | ||
|
|
7fb8e8662f | ||
|
|
dd3ab9cff2 | ||
|
|
97b518de12 | ||
|
|
d2a795c866 | ||
|
|
8a3d65be20 | ||
|
|
b2126d8ba5 | ||
|
|
6386411d21 | ||
|
|
4250244136 | ||
|
|
77c4f9993d | ||
|
|
c7c8417577 | ||
|
|
9d0985ded8 | ||
|
|
3663e10e33 | ||
|
|
5f37a82c3c | ||
|
|
026bf1dfd7 | ||
|
|
643a6e5080 | ||
|
|
5267502896 | ||
|
|
c3c152122d | ||
|
|
afeac097e5 | ||
|
|
e5cea64132 | ||
|
|
26da78cf15 | ||
|
|
179a1e1ca0 | ||
|
|
b379d275d1 | ||
|
|
133cdfb203 | ||
|
|
2b79edd9be | ||
|
|
3862a92e04 | ||
|
|
f4e3817fcc | ||
|
|
61f0f5d67c | ||
|
|
87f57551ea | ||
|
|
ee51efed69 | ||
|
|
5dab865681 | ||
|
|
8c0581eebc | ||
|
|
a72f9f422c | ||
|
|
1354a8c970 | ||
|
|
00a5115267 | ||
|
|
00282eab7b | ||
|
|
bec128de58 | ||
|
|
9edfa7b4fa | ||
|
|
a9af70e5f0 | ||
|
|
910caf593f | ||
|
|
02dc072dc7 | ||
|
|
78fb354452 | ||
|
|
66f5eca7fa | ||
|
|
be95396a57 | ||
|
|
59cbed429f | ||
|
|
d49df7aebb | ||
|
|
0aa9faad2e | ||
|
|
1337def888 | ||
|
|
4b100c558b | ||
|
|
1425a71ece | ||
|
|
a8524508fe | ||
|
|
a5ff973d53 | ||
|
|
337c9aa2c7 | ||
|
|
f1448403ac | ||
|
|
d0b5f77ec6 | ||
|
|
9cb22ffb60 | ||
|
|
f556962d82 | ||
|
|
d28448d519 | ||
|
|
c590a88ffd | ||
|
|
a1fc6c817b | ||
|
|
5554e52799 | ||
|
|
ca749eb4d2 | ||
|
|
41ceee3d24 | ||
|
|
5acfd52986 | ||
|
|
ec4c7b2f6a | ||
|
|
22a3d8f95f | ||
|
|
06b89ca277 | ||
|
|
9e5ffbd00a | ||
|
|
39e92ed778 | ||
|
|
68a3ec567a | ||
|
|
28231e81b3 | ||
|
|
b2ee0feeaa | ||
|
|
5541b6b366 | ||
|
|
408a5fe27e | ||
|
|
bffc73f976 | ||
|
|
bd6edfc9dd | ||
|
|
2cb24e8a94 | ||
|
|
a49779c4d2 | ||
|
|
15a5a5f5df | ||
|
|
b5e0558d6e | ||
|
|
4d683b23fc | ||
|
|
c13da606b2 | ||
|
|
c792f9277c | ||
|
|
b430f42622 | ||
|
|
fee822f5ae | ||
|
|
192659ecbd | ||
|
|
810431b9e2 | ||
|
|
02d845adf3 | ||
|
|
89c7b960fb | ||
|
|
ed1e399a56 | ||
|
|
8a3ce1ae57 | ||
|
|
d89ff649f8 | ||
|
|
24a73b5d1c | ||
|
|
4d0c40ff8a | ||
|
|
b5a2bed539 | ||
|
|
0efb79f571 | ||
|
|
df944b9a0f | ||
|
|
2c11846430 | ||
|
|
0035c01186 | ||
|
|
34be3384fe | ||
|
|
ebbc7b3335 | ||
|
|
4ccc8c3086 | ||
|
|
af9ebc9568 | ||
|
|
ca4b61c5f0 | ||
|
|
393839b3ab | ||
|
|
dadfc96e00 | ||
|
|
a0a33aef03 | ||
|
|
99ed81e0f5 | ||
|
|
5b697db219 | ||
|
|
8e5bf46e14 | ||
|
|
9f649b0900 | ||
|
|
abb15e06d3 | ||
|
|
11a317493e | ||
|
|
e8cece0c1b | ||
|
|
1ab882f81d | ||
|
|
b9338186e3 | ||
|
|
7c3cbff425 | ||
|
|
1ff0afc633 | ||
|
|
bfe7ee8fba | ||
|
|
49c73ed10e | ||
|
|
f571baacf9 | ||
|
|
6f02e1114c | ||
|
|
e230f43565 | ||
|
|
0d9593e71b | ||
|
|
20778ecfb0 | ||
|
|
2ea991d960 | ||
|
|
119c107834 | ||
|
|
800a0d0449 | ||
|
|
95c43f0189 | ||
|
|
9c77176c7f | ||
|
|
ddb6a55cd6 | ||
|
|
56a4f6fdd7 | ||
|
|
8a30f788b5 | ||
|
|
380a1c2c8c | ||
|
|
cd8e8335cf | ||
|
|
6e1beb54a4 | ||
|
|
9217c965dd | ||
|
|
a4d71ef487 | ||
|
|
518f332047 | ||
|
|
9257d497b8 | ||
|
|
07cf5de4f7 | ||
|
|
43ad69e48d | ||
|
|
c62e236cc6 | ||
|
|
15a2fbb293 | ||
|
|
16800c3fa0 | ||
|
|
ce09f41aa3 | ||
|
|
47dc2f036a | ||
|
|
f27a154bfd | ||
|
|
79757366e8 | ||
|
|
2cd9a417d6 | ||
|
|
deb05c6cc3 | ||
|
|
b6f171de51 | ||
|
|
a58d5f6999 | ||
|
|
e0b3f3eb45 | ||
|
|
4bbc8594a7 | ||
|
|
3a377300e1 | ||
|
|
33a07e3a86 | ||
|
|
212cafc1d7 | ||
|
|
2643b3cbcc | ||
|
|
d445229b6d | ||
|
|
dab5c451b0 | ||
|
|
7bdf06131a | ||
|
|
854648d5af | ||
|
|
c5f7b97359 | ||
|
|
dd8a727ad6 | ||
|
|
6c627fe422 | ||
|
|
ee980e1caf | ||
|
|
22bfaf6527 | ||
|
|
48ab48cc30 | ||
|
|
a0b14d4127 | ||
|
|
03f9fe1a70 | ||
|
|
8915b8d796 | ||
|
|
c77ffeeec0 | ||
|
|
4acf5660b2 | ||
|
|
2d9f0a668c | ||
|
|
9e6cb246cc | ||
|
|
14544ca63d | ||
|
|
26b347c04c | ||
|
|
36f75d1811 | ||
|
|
27fc787294 | ||
|
|
d23286d390 | ||
|
|
7c3ccc76c3 | ||
|
|
892dc5d4f3 | ||
|
|
e278692749 | ||
|
|
8d77dd2246 | ||
|
|
14ede2a585 | ||
|
|
5b525622f1 | ||
|
|
a24b11905c | ||
|
|
5d70858341 | ||
|
|
3daa006741 | ||
|
|
0bcc0c2101 | ||
|
|
b8850c808c | ||
|
|
f4f2c01ac1 | ||
|
|
7072e82dff | ||
|
|
53dc36c4cf | ||
|
|
5aadc3af00 | ||
|
|
8c28a698ed | ||
|
|
5ed6d8b202 | ||
|
|
b73dc7bf5e | ||
|
|
71d0f4ab63 | ||
|
|
d479dcde81 | ||
|
|
ae536017d5 | ||
|
|
67ddfce279 | ||
|
|
b1f39b34d7 | ||
|
|
6cf958ccce | ||
|
|
eaed3677d3 | ||
|
|
b9c88da54d | ||
|
|
104ae77f7a | ||
|
|
bfcb2ce61b | ||
|
|
63ba5fed09 | ||
|
|
98a8464933 | ||
|
|
7e3e6726e0 | ||
|
|
09567b2bb2 | ||
|
|
f3bd116184 | ||
|
|
7509737563 | ||
|
|
cfb815d879 | ||
|
|
44241fb967 | ||
|
|
c4b45129bd | ||
|
|
70741008ca | ||
|
|
6c2d2cae2a | ||
|
|
28f13d3311 | ||
|
|
4e31aaa8fb | ||
|
|
ba99f0c2cc | ||
|
|
e0a96b4937 | ||
|
|
82c055f527 | ||
|
|
f94008192c | ||
|
|
3895d5279e | ||
|
|
3d85ecc525 | ||
|
|
7da00796e5 | ||
|
|
6086419cb6 | ||
|
|
5bc1f2f2c0 | ||
|
|
32a83b211e | ||
|
|
bead7b3a7f | ||
|
|
815d6d6572 | ||
|
|
95ce812992 | ||
|
|
9a36f4748c | ||
|
|
50b7849a35 | ||
|
|
6f1245b27c | ||
|
|
cc87ed3899 | ||
|
|
1d9037fefe | ||
|
|
03016e2d16 | ||
|
|
3d41617f4e | ||
|
|
35151ffdd1 | ||
|
|
4527d41a7a | ||
|
|
553cba12f3 | ||
|
|
116e068ac3 | ||
|
|
1010dd2d28 | ||
|
|
7354242906 | ||
|
|
e7d0b158e9 | ||
|
|
330c4657b1 | ||
|
|
72a109f109 | ||
|
|
cf45c51dfb | ||
|
|
0b013adb34 | ||
|
|
7457d91f64 | ||
|
|
7fe1159426 | ||
|
|
c2665e3677 | ||
|
|
d63de803a4 | ||
|
|
11aca3513c | ||
|
|
561c9f40e5 | ||
|
|
54ed13aadf | ||
|
|
109cc21337 | ||
|
|
7e46b30fa5 | ||
|
|
0ba112c2c7 | ||
|
|
fc15d94170 | ||
|
|
dcb37d9c55 | ||
|
|
755b9d6342 | ||
|
|
3d6151c94f | ||
|
|
590bd8c4b9 | ||
|
|
e99aafd876 | ||
|
|
1f0adf8bcf | ||
|
|
dbd5d5fb43 | ||
|
|
a8b0e3641b | ||
|
|
9efb350be9 | ||
|
|
8d9820b3fb | ||
|
|
103f89551a | ||
|
|
6030d961ad | ||
|
|
ee08c9e17f | ||
|
|
48dd9a3240 | ||
|
|
e122e206a6 | ||
|
|
398b905758 | ||
|
|
dc2ec08fe3 | ||
|
|
3bf5edf5c9 | ||
|
|
134bca526c | ||
|
|
3393e58b06 | ||
|
|
eab6cdeee4 | ||
|
|
e8ec1ce8e3 | ||
|
|
b3581564ed | ||
|
|
29e1bd95fd | ||
|
|
8bff401c14 | ||
|
|
41798e9255 | ||
|
|
9e4f0228d1 | ||
|
|
76ee93c98c | ||
|
|
fb1a89efb7 | ||
|
|
aface43554 | ||
|
|
a35f0157b2 | ||
|
|
9b32162906 | ||
|
|
21bba62572 | ||
|
|
302327d6b3 | ||
|
|
5667e8bcbb | ||
|
|
ae66bd0e31 | ||
|
|
48dfadc02d | ||
|
|
3df6272bb6 | ||
|
|
e7f9bcda01 | ||
|
|
205044ca66 | ||
|
|
d497eb1f00 | ||
|
|
4e6f970ee9 | ||
|
|
0b6cdda6f5 | ||
|
|
a896ded763 | ||
|
|
fb5dd9ebc2 | ||
|
|
c8b7db6c38 | ||
|
|
44a3191be3 | ||
|
|
b4f7cdc9e7 | ||
|
|
8da07018d5 | ||
|
|
0c19a27065 | ||
|
|
3296b0ecdf | ||
|
|
0a07261124 | ||
|
|
33106d0ecf | ||
|
|
5bb887206a | ||
|
|
b30b0e27cb | ||
|
|
363736489c | ||
|
|
8dbf5e87a0 | ||
|
|
0b30f2cb50 | ||
|
|
ba5265dac4 | ||
|
|
ecb9c65917 | ||
|
|
8a98474600 | ||
|
|
b072216e67 | ||
|
|
cfb3181716 | ||
|
|
ab684cdc99 | ||
|
|
facadc3a44 | ||
|
|
281319d2da | ||
|
|
5cb203685c | ||
|
|
01fa37900b | ||
|
|
edbe744e17 | ||
|
|
2a32a1a4a8 | ||
|
|
404bdb21e6 | ||
|
|
b260c9a512 | ||
|
|
4b941adb6a | ||
|
|
bd752550a8 | ||
|
|
b8b71bb961 | ||
|
|
5aaf7a4092 | ||
|
|
030e02ffb8 | ||
|
|
d962aa03f4 | ||
|
|
9e4a2aae43 | ||
|
|
ee6eb685e7 | ||
|
|
09a38a32ce | ||
|
|
d13b19d43d | ||
|
|
e730dca1ad | ||
|
|
8da30640bb | ||
|
|
6f4eb88e07 | ||
|
|
d9592b9dab | ||
|
|
b87ada72aa | ||
|
|
83363ba1f0 | ||
|
|
23ebe7f718 | ||
|
|
e04264cfa3 | ||
|
|
8d29e5037f | ||
|
|
6926ed45b0 | ||
|
|
736b85b8bb | ||
|
|
9e3361bc31 | ||
|
|
6e10381020 | ||
|
|
a1d37d379c | ||
|
|
07d87db7a2 | ||
|
|
4e556673d2 | ||
|
|
f421304fc1 | ||
|
|
c9271b1686 | ||
|
|
12eb6863da | ||
|
|
4834874091 |
58
.github/workflows/publish-to-pypi.yml
vendored
Normal file
58
.github/workflows/publish-to-pypi.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
name: Publish to PyPI
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- draft-v4
|
||||||
|
paths:
|
||||||
|
- "pyproject.toml"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.repository_owner == 'ltdrdata' || github.repository_owner == 'Comfy-Org' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.9'
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install build twine
|
||||||
|
|
||||||
|
- name: Get current version
|
||||||
|
id: current_version
|
||||||
|
run: |
|
||||||
|
CURRENT_VERSION=$(grep -oP 'version = "\K[^"]+' pyproject.toml)
|
||||||
|
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "Current version: $CURRENT_VERSION"
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
run: python -m build
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
id: create_release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
files: dist/*
|
||||||
|
tag_name: v${{ steps.current_version.outputs.version }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
generate_release_notes: true
|
||||||
|
|
||||||
|
- name: Publish to PyPI
|
||||||
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
|
with:
|
||||||
|
password: ${{ secrets.PYPI_TOKEN }}
|
||||||
|
skip-existing: true
|
||||||
|
verbose: true
|
||||||
60
README.md
60
README.md
@@ -5,6 +5,7 @@
|
|||||||

|

|
||||||
|
|
||||||
## NOTICE
|
## NOTICE
|
||||||
|
* V3.38: **Security patch** - Manager data migrated to protected path. See [Migration Guide](docs/en/v3.38-userdata-security-migration.md).
|
||||||
* V3.16: Support for `uv` has been added. Set `use_uv` in `config.ini`.
|
* V3.16: Support for `uv` has been added. Set `use_uv` in `config.ini`.
|
||||||
* V3.10: `double-click feature` is removed
|
* V3.10: `double-click feature` is removed
|
||||||
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
|
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
|
||||||
@@ -17,7 +18,7 @@
|
|||||||
|
|
||||||
To install ComfyUI-Manager in addition to an existing installation of ComfyUI, you can follow the following steps:
|
To install ComfyUI-Manager in addition to an existing installation of ComfyUI, you can follow the following steps:
|
||||||
|
|
||||||
1. goto `ComfyUI/custom_nodes` dir in terminal(cmd)
|
1. Go to `ComfyUI/custom_nodes` dir in terminal (cmd)
|
||||||
2. `git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager`
|
2. `git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager`
|
||||||
3. Restart ComfyUI
|
3. Restart ComfyUI
|
||||||
|
|
||||||
@@ -28,8 +29,8 @@ To install ComfyUI-Manager in addition to an existing installation of ComfyUI, y
|
|||||||
- standalone version
|
- standalone version
|
||||||
- select option: use windows default console window
|
- select option: use windows default console window
|
||||||
2. Download [scripts/install-manager-for-portable-version.bat](https://github.com/ltdrdata/ComfyUI-Manager/raw/main/scripts/install-manager-for-portable-version.bat) into installed `"ComfyUI_windows_portable"` directory
|
2. Download [scripts/install-manager-for-portable-version.bat](https://github.com/ltdrdata/ComfyUI-Manager/raw/main/scripts/install-manager-for-portable-version.bat) into installed `"ComfyUI_windows_portable"` directory
|
||||||
- Don't click. Right click the link and use save as...
|
- Don't click. Right-click the link and choose 'Save As...'
|
||||||
3. double click `install-manager-for-portable-version.bat` batch file
|
3. Double-click `install-manager-for-portable-version.bat` batch file
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ pip install comfy-cli
|
|||||||
comfy install
|
comfy install
|
||||||
```
|
```
|
||||||
|
|
||||||
Linux/OSX:
|
Linux/macOS:
|
||||||
```commandline
|
```commandline
|
||||||
python -m venv venv
|
python -m venv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
@@ -57,13 +58,13 @@ comfy install
|
|||||||
* See also: https://github.com/Comfy-Org/comfy-cli
|
* See also: https://github.com/Comfy-Org/comfy-cli
|
||||||
|
|
||||||
|
|
||||||
### Installation[method4] (Installation for linux+venv: ComfyUI + ComfyUI-Manager)
|
### Installation[method4] (Installation for Linux+venv: ComfyUI + ComfyUI-Manager)
|
||||||
|
|
||||||
To install ComfyUI with ComfyUI-Manager on Linux using a venv environment, you can follow these steps:
|
To install ComfyUI with ComfyUI-Manager on Linux using a venv environment, you can follow these steps:
|
||||||
* **prerequisite: python-is-python3, python3-venv, git**
|
* **prerequisite: python-is-python3, python3-venv, git**
|
||||||
|
|
||||||
1. Download [scripts/install-comfyui-venv-linux.sh](https://github.com/ltdrdata/ComfyUI-Manager/raw/main/scripts/install-comfyui-venv-linux.sh) into empty install directory
|
1. Download [scripts/install-comfyui-venv-linux.sh](https://github.com/ltdrdata/ComfyUI-Manager/raw/main/scripts/install-comfyui-venv-linux.sh) into empty install directory
|
||||||
- Don't click. Right click the link and use save as...
|
- Don't click. Right-click the link and choose 'Save As...'
|
||||||
- ComfyUI will be installed in the subdirectory of the specified directory, and the directory will contain the generated executable script.
|
- ComfyUI will be installed in the subdirectory of the specified directory, and the directory will contain the generated executable script.
|
||||||
2. `chmod +x install-comfyui-venv-linux.sh`
|
2. `chmod +x install-comfyui-venv-linux.sh`
|
||||||
3. `./install-comfyui-venv-linux.sh`
|
3. `./install-comfyui-venv-linux.sh`
|
||||||
@@ -140,20 +141,27 @@ This repository provides Colab notebooks that allow you to install and use Comfy
|
|||||||
|
|
||||||
|
|
||||||
## Paths
|
## Paths
|
||||||
In `ComfyUI-Manager` V3.0 and later, configuration files and dynamically generated files are located under `<USER_DIRECTORY>/default/ComfyUI-Manager/`.
|
Starting from V3.38, Manager uses a protected system path for enhanced security.
|
||||||
|
|
||||||
* <USER_DIRECTORY>
|
* <USER_DIRECTORY>
|
||||||
* If executed without any options, the path defaults to ComfyUI/user.
|
* If executed without any options, the path defaults to ComfyUI/user.
|
||||||
* It can be set using --user-directory <USER_DIRECTORY>.
|
* It can be set using --user-directory <USER_DIRECTORY>.
|
||||||
|
|
||||||
* Basic config files: `<USER_DIRECTORY>/default/ComfyUI-Manager/config.ini`
|
| ComfyUI Version | Manager Path |
|
||||||
* Configurable channel lists: `<USER_DIRECTORY>/default/ComfyUI-Manager/channels.ini`
|
|-----------------|--------------|
|
||||||
* Configurable pip overrides: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_overrides.json`
|
| v0.3.76+ (with System User API) | `<USER_DIRECTORY>/__manager/` |
|
||||||
* Configurable pip blacklist: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_blacklist.list`
|
| Older versions | `<USER_DIRECTORY>/default/ComfyUI-Manager/` |
|
||||||
* Configurable pip auto fix: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_auto_fix.list`
|
|
||||||
* Saved snapshot files: `<USER_DIRECTORY>/default/ComfyUI-Manager/snapshots`
|
* Basic config files: `config.ini`
|
||||||
* Startup script files: `<USER_DIRECTORY>/default/ComfyUI-Manager/startup-scripts`
|
* Configurable channel lists: `channels.list`
|
||||||
* Component files: `<USER_DIRECTORY>/default/ComfyUI-Manager/components`
|
* Configurable pip overrides: `pip_overrides.json`
|
||||||
|
* Configurable pip blacklist: `pip_blacklist.list`
|
||||||
|
* Configurable pip auto fix: `pip_auto_fix.list`
|
||||||
|
* Saved snapshot files: `snapshots/`
|
||||||
|
* Startup script files: `startup-scripts/`
|
||||||
|
* Component files: `components/`
|
||||||
|
|
||||||
|
> **Note**: See [Migration Guide](docs/en/v3.38-userdata-security-migration.md) for upgrade details.
|
||||||
|
|
||||||
|
|
||||||
## `extra_model_paths.yaml` Configuration
|
## `extra_model_paths.yaml` Configuration
|
||||||
@@ -176,7 +184,7 @@ The following settings are applied based on the section marked as `is_default`.
|
|||||||

|

|
||||||
|
|
||||||
|
|
||||||
## cm-cli: command line tools for power user
|
## cm-cli: command line tools for power users
|
||||||
* A tool is provided that allows you to use the features of ComfyUI-Manager without running ComfyUI.
|
* A tool is provided that allows you to use the features of ComfyUI-Manager without running ComfyUI.
|
||||||
* For more details, please refer to the [cm-cli documentation](docs/en/cm-cli.md).
|
* For more details, please refer to the [cm-cli documentation](docs/en/cm-cli.md).
|
||||||
|
|
||||||
@@ -222,7 +230,7 @@ The following settings are applied based on the section marked as `is_default`.
|
|||||||
* `<current timestamp>` Ensure that the timestamp is always unique.
|
* `<current timestamp>` Ensure that the timestamp is always unique.
|
||||||
* "components" should have the same structure as the content of the file stored in `<USER_DIRECTORY>/default/ComfyUI-Manager/components`.
|
* "components" should have the same structure as the content of the file stored in `<USER_DIRECTORY>/default/ComfyUI-Manager/components`.
|
||||||
* `<component name>`: The name should be in the format `<prefix>::<node name>`.
|
* `<component name>`: The name should be in the format `<prefix>::<node name>`.
|
||||||
* `<compnent nodeata>`: In the nodedata of the group node.
|
* `<component node data>`: In the node data of the group node.
|
||||||
* `<version>`: Only two formats are allowed: `major.minor.patch` or `major.minor`. (e.g. `1.0`, `2.2.1`)
|
* `<version>`: Only two formats are allowed: `major.minor.patch` or `major.minor`. (e.g. `1.0`, `2.2.1`)
|
||||||
* `<datetime>`: Saved time
|
* `<datetime>`: Saved time
|
||||||
* `<packname>`: If the packname is not empty, the category becomes packname/workflow, and it is saved in the <packname>.pack file in `<USER_DIRECTORY>/default/ComfyUI-Manager/components`.
|
* `<packname>`: If the packname is not empty, the category becomes packname/workflow, and it is saved in the <packname>.pack file in `<USER_DIRECTORY>/default/ComfyUI-Manager/components`.
|
||||||
@@ -240,7 +248,7 @@ The following settings are applied based on the section marked as `is_default`.
|
|||||||
* Dragging and dropping or pasting a single component will add a node. However, when adding multiple components, nodes will not be added.
|
* Dragging and dropping or pasting a single component will add a node. However, when adding multiple components, nodes will not be added.
|
||||||
|
|
||||||
|
|
||||||
## Support of missing nodes installation
|
## Support for installing missing nodes
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -279,10 +287,10 @@ The following settings are applied based on the section marked as `is_default`.
|
|||||||
* Logging to file feature
|
* Logging to file feature
|
||||||
* This feature is enabled by default and can be disabled by setting `file_logging = False` in the `config.ini`.
|
* This feature is enabled by default and can be disabled by setting `file_logging = False` in the `config.ini`.
|
||||||
|
|
||||||
* Fix node(recreate): When right-clicking on a node and selecting `Fix node (recreate)`, you can recreate the node. The widget's values are reset, while the connections maintain those with the same names.
|
* Fix node (recreate): When right-clicking on a node and selecting `Fix node (recreate)`, you can recreate the node. The widget's values are reset, while the connections maintain those with the same names.
|
||||||
* It is used to correct errors in nodes of old workflows created before, which are incompatible with the version changes of custom nodes.
|
* It is used to correct errors in nodes of old workflows created before, which are incompatible with the version changes of custom nodes.
|
||||||
|
|
||||||
* Double-Click Node Title: You can set the double click behavior of nodes in the ComfyUI-Manager menu.
|
* Double-Click Node Title: You can set the double-click behavior of nodes in the ComfyUI-Manager menu.
|
||||||
* `Copy All Connections`, `Copy Input Connections`: Double-clicking a node copies the connections of the nearest node.
|
* `Copy All Connections`, `Copy Input Connections`: Double-clicking a node copies the connections of the nearest node.
|
||||||
* This action targets the nearest node within a straight-line distance of 1000 pixels from the center of the node.
|
* This action targets the nearest node within a straight-line distance of 1000 pixels from the center of the node.
|
||||||
* In the case of `Copy All Connections`, it duplicates existing outputs, but since it does not allow duplicate connections, the existing output connections of the original node are disconnected.
|
* In the case of `Copy All Connections`, it duplicates existing outputs, but since it does not allow duplicate connections, the existing output connections of the original node are disconnected.
|
||||||
@@ -348,7 +356,7 @@ When you run the `scan.sh` script:
|
|||||||
|
|
||||||
* It updates the `github-stats.json`.
|
* It updates the `github-stats.json`.
|
||||||
* This uses the GitHub API, so set your token with `export GITHUB_TOKEN=your_token_here` to avoid quickly reaching the rate limit and malfunctioning.
|
* This uses the GitHub API, so set your token with `export GITHUB_TOKEN=your_token_here` to avoid quickly reaching the rate limit and malfunctioning.
|
||||||
* To skip this step, add the `--skip-update-stat` option.
|
* To skip this step, add the `--skip-stat-update` option.
|
||||||
|
|
||||||
* The `--skip-all` option applies both `--skip-update` and `--skip-stat-update`.
|
* The `--skip-all` option applies both `--skip-update` and `--skip-stat-update`.
|
||||||
|
|
||||||
@@ -356,9 +364,9 @@ When you run the `scan.sh` script:
|
|||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
* If your `git.exe` is installed in a specific location other than system git, please install ComfyUI-Manager and run ComfyUI. Then, specify the path including the file name in `git_exe = ` in the `<USER_DIRECTORY>/default/ComfyUI-Manager/config.ini` file that is generated.
|
* If your `git.exe` is installed in a specific location other than system git, please install ComfyUI-Manager and run ComfyUI. Then, specify the path including the file name in `git_exe = ` in the `<USER_DIRECTORY>/default/ComfyUI-Manager/config.ini` file that is generated.
|
||||||
* If updating ComfyUI-Manager itself fails, please go to the **ComfyUI-Manager** directory and execute the command `git update-ref refs/remotes/origin/main a361cc1 && git fetch --all && git pull`.
|
* If updating ComfyUI-Manager itself fails, please go to the **ComfyUI-Manager** directory and execute the command `git update-ref refs/remotes/origin/main a361cc1 && git fetch --all && git pull`.
|
||||||
* If you encounter the error message `Overlapped Object has pending operation at deallocation on Comfyui Manager load` under Windows
|
* If you encounter the error message `Overlapped Object has pending operation at deallocation on ComfyUI Manager load` under Windows
|
||||||
* Edit `config.ini` file: add `windows_selector_event_loop_policy = True`
|
* Edit `config.ini` file: add `windows_selector_event_loop_policy = True`
|
||||||
* if `SSL: CERTIFICATE_VERIFY_FAILED` error is occured.
|
* If the `SSL: CERTIFICATE_VERIFY_FAILED` error occurs.
|
||||||
* Edit `config.ini` file: add `bypass_ssl = True`
|
* Edit `config.ini` file: add `bypass_ssl = True`
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
4
check.sh
4
check.sh
@@ -37,7 +37,7 @@ find ~/.tmp/default -name "*.py" -print0 | xargs -0 grep -E "crypto|^_A="
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo CHECK3
|
echo CHECK3
|
||||||
find ~/.tmp/default -name "requirements.txt" | xargs grep "^\s*https\\?:"
|
find ~/.tmp/default -name "requirements.txt" | xargs grep "^\s*[^#]*https\?:"
|
||||||
find ~/.tmp/default -name "requirements.txt" | xargs grep "\.whl"
|
find ~/.tmp/default -name "requirements.txt" | xargs grep "^\s*[^#].*\.whl"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
9828
custom-node-list.json
Executable file → Normal file
9828
custom-node-list.json
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
@@ -139,9 +139,9 @@ You can set whether to use ComfyUI-Manager solely via CLI.
|
|||||||
`restore-dependencies`
|
`restore-dependencies`
|
||||||
|
|
||||||
* This command can be used if custom nodes are installed under the `ComfyUI/custom_nodes` path but their dependencies are not installed.
|
* This command can be used if custom nodes are installed under the `ComfyUI/custom_nodes` path but their dependencies are not installed.
|
||||||
* It is useful when starting a new cloud instance, like colab, where dependencies need to be reinstalled and installation scripts re-executed.
|
* It is useful when starting a new cloud instance, like Colab, where dependencies need to be reinstalled and installation scripts re-executed.
|
||||||
* It can also be utilized if ComfyUI is reinstalled and only the custom_nodes path has been backed up and restored.
|
* It can also be utilized if ComfyUI is reinstalled and only the custom_nodes path has been backed up and restored.
|
||||||
|
|
||||||
### 7. Clear
|
### 7. Clear
|
||||||
|
|
||||||
In the GUI, installations, updates, or snapshot restorations are scheduled to execute the next time ComfyUI is launched. The `clear` command clears this scheduled state, ensuring no pre-execution actions are applied.
|
In the GUI, installations, updates, or snapshot restorations are scheduled to execute the next time ComfyUI is launched. The `clear` command clears this scheduled state, ensuring no pre-execution actions are applied.
|
||||||
|
|||||||
230
docs/en/v3.38-userdata-security-migration.md
Normal file
230
docs/en/v3.38-userdata-security-migration.md
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
# ComfyUI-Manager V3.38: Userdata Security Migration Guide
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
ComfyUI-Manager V3.38 introduces a **security patch** that migrates Manager's configuration and data to a protected system path. This change leverages ComfyUI's new System User Protection API (PR #10966) to provide enhanced security isolation.
|
||||||
|
|
||||||
|
This guide explains what happens during the migration and how to handle various situations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Changed
|
||||||
|
|
||||||
|
### Finding Your Paths
|
||||||
|
|
||||||
|
When ComfyUI starts, it displays the full paths in the terminal:
|
||||||
|
|
||||||
|
```
|
||||||
|
** User directory: /path/to/ComfyUI/user
|
||||||
|
** ComfyUI-Manager config path: /path/to/ComfyUI/user/__manager/config.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
Look for these lines in your startup log to find the exact location on your system. In this guide, paths are shown relative to the `user` directory.
|
||||||
|
|
||||||
|
### Path Migration
|
||||||
|
|
||||||
|
| Data | Legacy Path | New Path |
|
||||||
|
|------|-------------|----------|
|
||||||
|
| Configuration | `user/default/ComfyUI-Manager/` | `user/__manager/` |
|
||||||
|
| Snapshots | `user/default/ComfyUI-Manager/snapshots/` | `user/__manager/snapshots/` |
|
||||||
|
|
||||||
|
### Why This Change
|
||||||
|
|
||||||
|
In older ComfyUI versions, the `default/` directory was **unprotected** and accessible via web APIs. If you ran ComfyUI with `--listen 0.0.0.0` or similar options to allow external connections, this data **may have been tampered with** by malicious actors.
|
||||||
|
|
||||||
|
**Note:** If you only used ComfyUI locally (without `--listen` or with `--listen 127.0.0.1`), your data was not exposed to this vulnerability.
|
||||||
|
|
||||||
|
The new `__manager` path uses ComfyUI's protected system directory, which:
|
||||||
|
- **Cannot be accessed** from outside (protected by ComfyUI)
|
||||||
|
- Isolates system settings from user data
|
||||||
|
- Enables stricter security for remote access
|
||||||
|
|
||||||
|
**This is why only `config.ini` is automatically migrated** - other files (snapshots) may have been compromised and should be manually verified before copying.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Automatic Migration
|
||||||
|
|
||||||
|
When you start ComfyUI with the new System User Protection API, Manager automatically handles the migration:
|
||||||
|
|
||||||
|
### Step 1: Configuration Migration
|
||||||
|
|
||||||
|
Only `config.ini` is migrated automatically.
|
||||||
|
|
||||||
|
**Important**: Snapshots are **NOT** automatically migrated. You must copy them manually if needed.
|
||||||
|
|
||||||
|
### Step 2: Security Level Check
|
||||||
|
|
||||||
|
During migration, if your security level is below `normal` (i.e., `weak` or `normal-`), it will be automatically raised to `normal`. This is a safety measure because the security level setting itself may have been tampered with in the old version.
|
||||||
|
|
||||||
|
```
|
||||||
|
======================================================================
|
||||||
|
[ComfyUI-Manager] WARNING: Security level adjusted
|
||||||
|
- Previous: 'weak' → New: 'normal'
|
||||||
|
- Raised to prevent unauthorized remote access.
|
||||||
|
======================================================================
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need a lower security level, you can manually edit the config after migration.
|
||||||
|
|
||||||
|
### Step 3: Legacy Backup
|
||||||
|
|
||||||
|
Your entire legacy directory is moved to a backup location:
|
||||||
|
```
|
||||||
|
user/__manager/.legacy-manager-backup/
|
||||||
|
```
|
||||||
|
|
||||||
|
This backup is preserved until you manually delete it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Persistent Backup Notification
|
||||||
|
|
||||||
|
As long as the backup exists, Manager will remind you on **every startup**:
|
||||||
|
|
||||||
|
```
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
[ComfyUI-Manager] NOTICE: Legacy backup exists
|
||||||
|
- Your old Manager data was backed up to:
|
||||||
|
/path/to/ComfyUI/user/__manager/.legacy-manager-backup
|
||||||
|
- Please verify and remove it when no longer needed.
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
```
|
||||||
|
|
||||||
|
**To stop this notification**: Delete the `.legacy-manager-backup` folder inside `user/__manager/` after confirming you don't need any data from it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recovering Old Data
|
||||||
|
|
||||||
|
### Snapshots
|
||||||
|
|
||||||
|
If you need your old snapshots, copy the contents of `.legacy-manager-backup/snapshots/` to `user/__manager/snapshots/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Outdated ComfyUI Warning
|
||||||
|
|
||||||
|
If you're running an older version of ComfyUI without the System User Protection API, Manager will:
|
||||||
|
|
||||||
|
1. **Force security level to `strong`** - All installations are blocked
|
||||||
|
2. **Display warning message**:
|
||||||
|
|
||||||
|
```
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
[ComfyUI-Manager] ERROR: ComfyUI version is outdated!
|
||||||
|
- Most operations are blocked for security.
|
||||||
|
- ComfyUI update is still allowed.
|
||||||
|
- Please update ComfyUI to use Manager normally.
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**: Update ComfyUI to v0.3.76 or later.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Levels
|
||||||
|
|
||||||
|
| Level | What's Allowed |
|
||||||
|
|-------|----------------|
|
||||||
|
| `strong` | ComfyUI update only. All other installations blocked. |
|
||||||
|
| `normal` | Install/update/remove registered custom nodes and models. |
|
||||||
|
| `normal-` | Above + Install via Git URL or pip (localhost only). |
|
||||||
|
| `weak` | All operations allowed, including from remote connections. |
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
- `strong` is forced on outdated ComfyUI versions.
|
||||||
|
- `normal` is the default and recommended for most users.
|
||||||
|
- `normal-` is for developers who need to install unregistered nodes locally.
|
||||||
|
- `weak` should only be used in isolated development environments.
|
||||||
|
|
||||||
|
### Changing Security Level
|
||||||
|
|
||||||
|
Edit `user/__manager/config.ini`:
|
||||||
|
```ini
|
||||||
|
[default]
|
||||||
|
security_level = normal
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Messages
|
||||||
|
|
||||||
|
### "comfyui_outdated" (HTTP 403)
|
||||||
|
|
||||||
|
This error appears when:
|
||||||
|
- Your ComfyUI doesn't have the System User Protection API
|
||||||
|
- All installations are blocked until you update ComfyUI
|
||||||
|
|
||||||
|
**Solution**: Update ComfyUI to the latest version.
|
||||||
|
|
||||||
|
### "security_level" (HTTP 403)
|
||||||
|
|
||||||
|
This error appears when:
|
||||||
|
- Your security level blocks the requested operation
|
||||||
|
- For example, `strong` level blocks all installations
|
||||||
|
|
||||||
|
**Solution**: Lower your security level in config.ini if appropriate for your use case.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Warning: Suspicious Path
|
||||||
|
|
||||||
|
If you see this error on an **older** ComfyUI:
|
||||||
|
|
||||||
|
```
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
[ComfyUI-Manager] ERROR: Suspicious path detected!
|
||||||
|
- '__manager' exists with low security level: 'weak'
|
||||||
|
- Please verify manually:
|
||||||
|
/path/to/ComfyUI/user/__manager/config.ini
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
```
|
||||||
|
|
||||||
|
On older ComfyUI versions, the `__manager` directory is not normally created. If this directory exists, it may have been created externally. For safety, manually verify the contents of this directory before updating ComfyUI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### All my installations are blocked
|
||||||
|
|
||||||
|
**Check 1**: Is your ComfyUI updated?
|
||||||
|
- Old ComfyUI forces `security_level = strong`
|
||||||
|
- Update ComfyUI to resolve
|
||||||
|
|
||||||
|
**Check 2**: What's your security level?
|
||||||
|
- Check `user/__manager/config.ini`
|
||||||
|
- `security_level = strong` blocks all installations
|
||||||
|
|
||||||
|
### My snapshots are missing
|
||||||
|
|
||||||
|
Snapshots are not automatically migrated. You need to manually copy the `snapshots` folder from inside `.legacy-manager-backup` to the `user/__manager/` directory.
|
||||||
|
|
||||||
|
### I keep seeing the backup notification
|
||||||
|
|
||||||
|
Delete the `.legacy-manager-backup` folder inside `user/__manager/` after confirming you don't need any data from it.
|
||||||
|
|
||||||
|
### Snapshot restore is blocked
|
||||||
|
|
||||||
|
On old ComfyUI (without System User API), snapshot restore is blocked because security is forced to `strong`. Update ComfyUI to enable snapshot restore.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Structure Reference
|
||||||
|
|
||||||
|
```
|
||||||
|
user/
|
||||||
|
└── __manager/
|
||||||
|
├── config.ini # Manager configuration
|
||||||
|
├── channels.list # Custom node channels
|
||||||
|
├── snapshots/ # Environment snapshots
|
||||||
|
└── .legacy-manager-backup/ # Backup of old Manager data (temporary)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- **ComfyUI**: v0.3.76 or later (with System User Protection API)
|
||||||
|
- **ComfyUI-Manager**: V3.38 or later
|
||||||
@@ -23,13 +23,13 @@ OPTIONS:
|
|||||||
## How To Use?
|
## How To Use?
|
||||||
* `python cm-cli.py` 를 통해서 실행 시킬 수 있습니다.
|
* `python cm-cli.py` 를 통해서 실행 시킬 수 있습니다.
|
||||||
* 예를 들어 custom node를 모두 업데이트 하고 싶다면
|
* 예를 들어 custom node를 모두 업데이트 하고 싶다면
|
||||||
* ComfyUI-Manager경로 에서 `python cm-cli.py update all` 를 command를 실행할 수 있습니다.
|
* ComfyUI-Manager 경로에서 `python cm-cli.py update all` 명령을 실행할 수 있습니다.
|
||||||
* ComfyUI 경로에서 실행한다면, `python custom_nodes/ComfyUI-Manager/cm-cli.py update all` 와 같이 cm-cli.py 의 경로를 지정할 수도 있습니다.
|
* ComfyUI 경로에서 실행한다면, `python custom_nodes/ComfyUI-Manager/cm-cli.py update all` 와 같이 cm-cli.py 의 경로를 지정할 수도 있습니다.
|
||||||
|
|
||||||
## Prerequisite
|
## Prerequisite
|
||||||
* ComfyUI 를 실행하는 python과 동일한 python 환경에서 실행해야 합니다.
|
* ComfyUI 를 실행하는 python과 동일한 python 환경에서 실행해야 합니다.
|
||||||
* venv를 사용할 경우 해당 venv를 activate 한 상태에서 실행해야 합니다.
|
* venv를 사용할 경우 해당 venv를 activate 한 상태에서 실행해야 합니다.
|
||||||
* portable 버전을 사용할 경우 run_nvidia_gpu.bat 파일이 있는 경로인 경우, 다음과 같은 방식으로 코맨드를 실행해야 합니다.
|
* portable 버전을 사용할 경우 run_nvidia_gpu.bat 파일이 있는 경로인 경우, 다음과 같은 방식으로 명령을 실행해야 합니다.
|
||||||
`.\python_embeded\python.exe ComfyUI\custom_nodes\ComfyUI-Manager\cm-cli.py update all`
|
`.\python_embeded\python.exe ComfyUI\custom_nodes\ComfyUI-Manager\cm-cli.py update all`
|
||||||
* ComfyUI 의 경로는 COMFYUI_PATH 환경 변수로 설정할 수 있습니다. 만약 생략할 경우 다음과 같은 경고 메시지가 나타나며, ComfyUI-Manager가 설치된 경로를 기준으로 상대 경로로 설정됩니다.
|
* ComfyUI 의 경로는 COMFYUI_PATH 환경 변수로 설정할 수 있습니다. 만약 생략할 경우 다음과 같은 경고 메시지가 나타나며, ComfyUI-Manager가 설치된 경로를 기준으로 상대 경로로 설정됩니다.
|
||||||
```
|
```
|
||||||
@@ -40,8 +40,8 @@ OPTIONS:
|
|||||||
|
|
||||||
### 1. --channel, --mode
|
### 1. --channel, --mode
|
||||||
* 정보 보기 기능과 커스텀 노드 관리 기능의 경우는 --channel과 --mode를 통해 정보 DB를 설정할 수 있습니다.
|
* 정보 보기 기능과 커스텀 노드 관리 기능의 경우는 --channel과 --mode를 통해 정보 DB를 설정할 수 있습니다.
|
||||||
* 예들 들어 `python cm-cli.py update all --channel recent --mode remote`와 같은 command를 실행할 경우, 현재 ComfyUI-Manager repo에 내장된 로컬의 정보가 아닌 remote의 최신 정보를 기준으로 동작하며, recent channel에 있는 목록을 대상으로만 동작합니다.
|
* 예를 들어 `python cm-cli.py update all --channel recent --mode remote`와 같은 명령을 실행할 경우, 현재 ComfyUI-Manager repo에 내장된 로컬의 정보가 아닌 remote의 최신 정보를 기준으로 동작하며, recent channel에 있는 목록을 대상으로만 동작합니다.
|
||||||
* --channel, --mode 는 `simple-show, show, install, uninstall, update, disable, enable, fix` command에서만 사용 가능합니다.
|
* --channel, --mode 는 `simple-show, show, install, uninstall, update, disable, enable, fix` 명령에서만 사용 가능합니다.
|
||||||
|
|
||||||
### 2. 관리 정보 보기
|
### 2. 관리 정보 보기
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ OPTIONS:
|
|||||||
* `[show|simple-show]` - `show`는 상세하게 정보를 보여주며, `simple-show`는 간단하게 정보를 보여줍니다.
|
* `[show|simple-show]` - `show`는 상세하게 정보를 보여주며, `simple-show`는 간단하게 정보를 보여줍니다.
|
||||||
|
|
||||||
|
|
||||||
`python cm-cli.py show installed` 와 같은 코맨드를 실행하면 설치된 커스텀 노드의 정보를 상세하게 보여줍니다.
|
`python cm-cli.py show installed` 와 같은 명령을 실행하면 설치된 커스텀 노드의 정보를 상세하게 보여줍니다.
|
||||||
```
|
```
|
||||||
-= ComfyUI-Manager CLI (V2.24) =-
|
-= ComfyUI-Manager CLI (V2.24) =-
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ FETCH DATA from: https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main
|
|||||||
[ DISABLED ] ComfyUI-Loopchain (author: Fannovel16)
|
[ DISABLED ] ComfyUI-Loopchain (author: Fannovel16)
|
||||||
```
|
```
|
||||||
|
|
||||||
`python cm-cli.py simple-show installed` 와 같은 코맨드를 이용해서 설치된 커스텀 노드의 정보를 간단하게 보여줍니다.
|
`python cm-cli.py simple-show installed` 와 같은 명령을 이용해서 설치된 커스텀 노드의 정보를 간단하게 보여줍니다.
|
||||||
|
|
||||||
```
|
```
|
||||||
-= ComfyUI-Manager CLI (V2.24) =-
|
-= ComfyUI-Manager CLI (V2.24) =-
|
||||||
@@ -89,7 +89,7 @@ ComfyUI-Loopchain
|
|||||||
* `installed`: enable, disable 여부와 상관없이 설치된 모든 노드를 보여줍니다
|
* `installed`: enable, disable 여부와 상관없이 설치된 모든 노드를 보여줍니다
|
||||||
* `not-installed`: 설치되지 않은 커스텀 노드의 목록을 보여줍니다.
|
* `not-installed`: 설치되지 않은 커스텀 노드의 목록을 보여줍니다.
|
||||||
* `all`: 모든 커스텀 노드의 목록을 보여줍니다.
|
* `all`: 모든 커스텀 노드의 목록을 보여줍니다.
|
||||||
* `snapshot`: 현재 설치된 커스텀 노드의 snapshot 정보를 보여줍니다. `show`롤 통해서 볼 경우는 json 출력 형태로 보여주며, `simple-show`를 통해서 볼 경우는 간단하게, 커밋 해시와 함께 보여줍니다.
|
* `snapshot`: 현재 설치된 커스텀 노드의 snapshot 정보를 보여줍니다. `show`를 통해서 볼 경우는 json 출력 형태로 보여주며, `simple-show`를 통해서 볼 경우는 간단하게, 커밋 해시와 함께 보여줍니다.
|
||||||
* `snapshot-list`: ComfyUI-Manager/snapshots 에 저장된 snapshot 파일의 목록을 보여줍니다.
|
* `snapshot-list`: ComfyUI-Manager/snapshots 에 저장된 snapshot 파일의 목록을 보여줍니다.
|
||||||
|
|
||||||
### 3. 커스텀 노드 관리 하기
|
### 3. 커스텀 노드 관리 하기
|
||||||
@@ -98,7 +98,7 @@ ComfyUI-Loopchain
|
|||||||
|
|
||||||
* `python cm-cli.py install ComfyUI-Impact-Pack ComfyUI-Inspire-Pack ComfyUI_experiments` 와 같이 커스텀 노드의 이름을 나열해서 관리 기능을 적용할 수 있습니다.
|
* `python cm-cli.py install ComfyUI-Impact-Pack ComfyUI-Inspire-Pack ComfyUI_experiments` 와 같이 커스텀 노드의 이름을 나열해서 관리 기능을 적용할 수 있습니다.
|
||||||
* 커스텀 노드의 이름은 `show`를 했을 때 보여주는 이름이며, git repository의 이름입니다.
|
* 커스텀 노드의 이름은 `show`를 했을 때 보여주는 이름이며, git repository의 이름입니다.
|
||||||
(추후 nickname 을 사용가능하돌고 업데이트 할 예정입니다.)
|
(추후 nickname을 사용 가능하도록 업데이트할 예정입니다.)
|
||||||
|
|
||||||
`[update|disable|enable|fix] all ?[--channel <channel name>] ?[--mode [remote|local|cache]]`
|
`[update|disable|enable|fix] all ?[--channel <channel name>] ?[--mode [remote|local|cache]]`
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ ComfyUI-Loopchain
|
|||||||
* `--pip-non-local-url`: web URL에 등록된 pip 패키지들에 대해서 복구를 수행
|
* `--pip-non-local-url`: web URL에 등록된 pip 패키지들에 대해서 복구를 수행
|
||||||
* `--pip-local-url`: local 경로를 지정하고 있는 pip 패키지들에 대해서 복구를 수행
|
* `--pip-local-url`: local 경로를 지정하고 있는 pip 패키지들에 대해서 복구를 수행
|
||||||
* `--user-directory`: 사용자 디렉토리 설정
|
* `--user-directory`: 사용자 디렉토리 설정
|
||||||
* `--restore-to`: 복구될 커스텀 노드가 설치될 경로. (이 옵션을 적용할 경우 오직 대상 경로에 설치된 custom nodes 만 설치된 것으로 인식함.)
|
* `--restore-to`: 복구될 커스텀 노드가 설치될 경로. (이 옵션을 적용할 경우 오직 대상 경로에 설치된 custom nodes만 설치된 것으로 인식함.)
|
||||||
|
|
||||||
### 5. CLI only mode
|
### 5. CLI only mode
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ ComfyUI-Manager를 CLI로만 사용할 것인지를 설정할 수 있습니다.
|
|||||||
`cli-only-mode [enable|disable]`
|
`cli-only-mode [enable|disable]`
|
||||||
|
|
||||||
* security 혹은 policy 의 이유로 GUI 를 통한 ComfyUI-Manager 사용을 제한하고 싶은 경우 이 모드를 사용할 수 있습니다.
|
* security 혹은 policy 의 이유로 GUI 를 통한 ComfyUI-Manager 사용을 제한하고 싶은 경우 이 모드를 사용할 수 있습니다.
|
||||||
* CLI only mode를 적용할 경우 ComfyUI-Manager 가 매우 제한된 상태로 로드되어, 내부적으로 제공하는 web API가 비활성화 되며, 메인 메뉴에서도 Manager 버튼이 표시되지 않습니다.
|
* CLI only mode를 적용할 경우 ComfyUI-Manager 가 매우 제한된 상태로 로드되어, 내부적으로 제공하는 web API가 비활성화되며, 메인 메뉴에서도 Manager 버튼이 표시되지 않습니다.
|
||||||
|
|
||||||
|
|
||||||
### 6. 의존성 설치
|
### 6. 의존성 설치
|
||||||
@@ -141,10 +141,10 @@ ComfyUI-Manager를 CLI로만 사용할 것인지를 설정할 수 있습니다.
|
|||||||
`restore-dependencies`
|
`restore-dependencies`
|
||||||
|
|
||||||
* `ComfyUI/custom_nodes` 하위 경로에 커스텀 노드들이 설치되어 있긴 하지만, 의존성이 설치되지 않은 경우 사용할 수 있습니다.
|
* `ComfyUI/custom_nodes` 하위 경로에 커스텀 노드들이 설치되어 있긴 하지만, 의존성이 설치되지 않은 경우 사용할 수 있습니다.
|
||||||
* colab 과 같이 cloud instance를 새로 시작하는 경우 의존성 재설치 및 설치 스크립트가 재실행 되어야 하는 경우 사용합니다.
|
* Colab과 같이 cloud instance를 새로 시작하는 경우 의존성 재설치 및 설치 스크립트가 재실행되어야 하는 경우 사용합니다.
|
||||||
* ComfyUI을 재설치할 경우, custom_nodes 경로만 백업했다가 재설치 할 경우 활용 가능합니다.
|
* ComfyUI를 재설치할 경우, custom_nodes 경로만 백업했다가 재설치할 경우 활용 가능합니다.
|
||||||
|
|
||||||
|
|
||||||
### 7. clear
|
### 7. clear
|
||||||
|
|
||||||
GUI에서 install, update를 하거나 snapshot 을 restore하는 경우 예약을 통해서 다음번 ComfyUI를 실행할 경우 실행되는 구조입니다. `clear` 는 이런 예약 상태를 clear해서, 아무런 사전 실행이 적용되지 않도록 합니다.
|
GUI에서 install, update를 하거나 snapshot을 restore하는 경우 예약을 통해서 다음번 ComfyUI를 실행할 경우 실행되는 구조입니다. `clear` 는 이런 예약 상태를 clear해서, 아무런 사전 실행이 적용되지 않도록 합니다.
|
||||||
|
|||||||
16254
extension-node-map.json
16254
extension-node-map.json
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
|
import time
|
||||||
|
|
||||||
import git
|
import git
|
||||||
import json
|
import json
|
||||||
@@ -219,7 +220,14 @@ def gitpull(path):
|
|||||||
repo.close()
|
repo.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
remote.pull()
|
try:
|
||||||
|
repo.git.pull('--ff-only')
|
||||||
|
except git.GitCommandError:
|
||||||
|
backup_name = f'backup_{time.strftime("%Y%m%d_%H%M%S")}'
|
||||||
|
repo.create_head(backup_name)
|
||||||
|
print(f"[ComfyUI-Manager] Cannot fast-forward. Backup created: {backup_name}")
|
||||||
|
repo.git.reset('--hard', f'{remote_name}/{branch_name}')
|
||||||
|
print(f"[ComfyUI-Manager] Reset to {remote_name}/{branch_name}")
|
||||||
|
|
||||||
repo.git.submodule('update', '--init', '--recursive')
|
repo.git.submodule('update', '--init', '--recursive')
|
||||||
new_commit_hash = repo.head.commit.hexsha
|
new_commit_hash = repo.head.commit.hexsha
|
||||||
|
|||||||
22226
github-stats-cache.json
Normal file
22226
github-stats-cache.json
Normal file
File diff suppressed because it is too large
Load Diff
15014
github-stats.json
15014
github-stats.json
File diff suppressed because it is too large
Load Diff
@@ -40,10 +40,11 @@ import cnr_utils
|
|||||||
import manager_util
|
import manager_util
|
||||||
import git_utils
|
import git_utils
|
||||||
import manager_downloader
|
import manager_downloader
|
||||||
|
import manager_migration
|
||||||
from node_package import InstalledNodePackage
|
from node_package import InstalledNodePackage
|
||||||
|
|
||||||
|
|
||||||
version_code = [3, 35]
|
version_code = [3, 38, 3]
|
||||||
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||||
|
|
||||||
|
|
||||||
@@ -214,9 +215,10 @@ def update_user_directory(user_dir):
|
|||||||
global manager_pip_blacklist_path
|
global manager_pip_blacklist_path
|
||||||
global manager_components_path
|
global manager_components_path
|
||||||
|
|
||||||
manager_files_path = os.path.abspath(os.path.join(user_dir, 'default', 'ComfyUI-Manager'))
|
manager_files_path = manager_migration.get_manager_path(user_dir)
|
||||||
if not os.path.exists(manager_files_path):
|
if not os.path.exists(manager_files_path):
|
||||||
os.makedirs(manager_files_path)
|
os.makedirs(manager_files_path)
|
||||||
|
manager_migration.run_migration_checks(user_dir, manager_files_path)
|
||||||
|
|
||||||
manager_snapshot_path = os.path.join(manager_files_path, "snapshots")
|
manager_snapshot_path = os.path.join(manager_files_path, "snapshots")
|
||||||
if not os.path.exists(manager_snapshot_path):
|
if not os.path.exists(manager_snapshot_path):
|
||||||
@@ -1484,6 +1486,7 @@ class UnifiedManager:
|
|||||||
return ManagedResult('skip')
|
return ManagedResult('skip')
|
||||||
elif self.is_disabled(node_id):
|
elif self.is_disabled(node_id):
|
||||||
return self.unified_enable(node_id)
|
return self.unified_enable(node_id)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
version_spec = self.resolve_unspecified_version(node_id)
|
version_spec = self.resolve_unspecified_version(node_id)
|
||||||
|
|
||||||
@@ -1718,7 +1721,7 @@ def read_config():
|
|||||||
manager_util.use_uv = default_conf['use_uv'].lower() == 'true' if 'use_uv' in default_conf else False
|
manager_util.use_uv = default_conf['use_uv'].lower() == 'true' if 'use_uv' in default_conf else False
|
||||||
manager_util.bypass_ssl = get_bool('bypass_ssl', False)
|
manager_util.bypass_ssl = get_bool('bypass_ssl', False)
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
'http_channel_enabled': get_bool('http_channel_enabled', False),
|
'http_channel_enabled': get_bool('http_channel_enabled', False),
|
||||||
'preview_method': default_conf.get('preview_method', manager_funcs.get_current_preview_method()).lower(),
|
'preview_method': default_conf.get('preview_method', manager_funcs.get_current_preview_method()).lower(),
|
||||||
'git_exe': default_conf.get('git_exe', ''),
|
'git_exe': default_conf.get('git_exe', ''),
|
||||||
@@ -1738,6 +1741,8 @@ def read_config():
|
|||||||
'security_level': default_conf.get('security_level', 'normal').lower(),
|
'security_level': default_conf.get('security_level', 'normal').lower(),
|
||||||
'db_mode': default_conf.get('db_mode', 'cache').lower(),
|
'db_mode': default_conf.get('db_mode', 'cache').lower(),
|
||||||
}
|
}
|
||||||
|
manager_migration.force_security_level_if_needed(result)
|
||||||
|
return result
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
import importlib.util
|
import importlib.util
|
||||||
@@ -1745,7 +1750,7 @@ def read_config():
|
|||||||
manager_util.use_uv = importlib.util.find_spec("uv") is not None and platform.system() != "Windows"
|
manager_util.use_uv = importlib.util.find_spec("uv") is not None and platform.system() != "Windows"
|
||||||
manager_util.bypass_ssl = False
|
manager_util.bypass_ssl = False
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
'http_channel_enabled': False,
|
'http_channel_enabled': False,
|
||||||
'preview_method': manager_funcs.get_current_preview_method(),
|
'preview_method': manager_funcs.get_current_preview_method(),
|
||||||
'git_exe': '',
|
'git_exe': '',
|
||||||
@@ -1765,6 +1770,8 @@ def read_config():
|
|||||||
'security_level': 'normal', # strong | normal | normal- | weak
|
'security_level': 'normal', # strong | normal | normal- | weak
|
||||||
'db_mode': 'cache', # local | cache | remote
|
'db_mode': 'cache', # local | cache | remote
|
||||||
}
|
}
|
||||||
|
manager_migration.force_security_level_if_needed(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_config():
|
def get_config():
|
||||||
@@ -2246,9 +2253,17 @@ def git_pull(path):
|
|||||||
|
|
||||||
current_branch = repo.active_branch
|
current_branch = repo.active_branch
|
||||||
remote_name = current_branch.tracking_branch().remote_name
|
remote_name = current_branch.tracking_branch().remote_name
|
||||||
remote = repo.remote(name=remote_name)
|
|
||||||
|
|
||||||
remote.pull()
|
try:
|
||||||
|
repo.git.pull('--ff-only')
|
||||||
|
except git.GitCommandError:
|
||||||
|
branch_name = current_branch.name
|
||||||
|
backup_name = f'backup_{time.strftime("%Y%m%d_%H%M%S")}'
|
||||||
|
repo.create_head(backup_name)
|
||||||
|
logging.info(f"[ComfyUI-Manager] Cannot fast-forward. Backup created: {backup_name}")
|
||||||
|
repo.git.reset('--hard', f'{remote_name}/{branch_name}')
|
||||||
|
logging.info(f"[ComfyUI-Manager] Reset to {remote_name}/{branch_name}")
|
||||||
|
|
||||||
repo.git.submodule('update', '--init', '--recursive')
|
repo.git.submodule('update', '--init', '--recursive')
|
||||||
|
|
||||||
repo.close()
|
repo.close()
|
||||||
@@ -2516,22 +2531,23 @@ def update_to_stable_comfyui(repo_path):
|
|||||||
logging.error('\t'+branch.name)
|
logging.error('\t'+branch.name)
|
||||||
return "fail", None
|
return "fail", None
|
||||||
|
|
||||||
versions, current_tag, _ = get_comfyui_versions(repo)
|
versions, current_tag, latest_tag = get_comfyui_versions(repo)
|
||||||
|
|
||||||
if len(versions) == 0 or (len(versions) == 1 and versions[0] == 'nightly'):
|
if latest_tag is None:
|
||||||
logging.info("[ComfyUI-Manager] Unable to update to the stable ComfyUI version.")
|
logging.info("[ComfyUI-Manager] Unable to update to the stable ComfyUI version.")
|
||||||
return "fail", None
|
return "fail", None
|
||||||
|
|
||||||
if versions[0] == 'nightly':
|
|
||||||
latest_tag = versions[1]
|
|
||||||
else:
|
|
||||||
latest_tag = versions[0]
|
|
||||||
|
|
||||||
if current_tag == latest_tag:
|
tag_ref = next((t for t in repo.tags if t.name == latest_tag), None)
|
||||||
|
if tag_ref is None:
|
||||||
|
logging.info(f"[ComfyUI-Manager] Unable to locate tag '{latest_tag}' in repository.")
|
||||||
|
return "fail", None
|
||||||
|
|
||||||
|
if repo.head.commit == tag_ref.commit:
|
||||||
return "skip", None
|
return "skip", None
|
||||||
else:
|
else:
|
||||||
logging.info(f"[ComfyUI-Manager] Updating ComfyUI: {current_tag} -> {latest_tag}")
|
logging.info(f"[ComfyUI-Manager] Updating ComfyUI: {current_tag} -> {latest_tag}")
|
||||||
repo.git.checkout(latest_tag)
|
repo.git.checkout(tag_ref.name)
|
||||||
|
execute_install_script("ComfyUI", repo_path, instant_execution=False, no_deps=False)
|
||||||
return 'updated', latest_tag
|
return 'updated', latest_tag
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@@ -2663,9 +2679,13 @@ def check_state_of_git_node_pack_single(item, do_fetch=False, do_update_check=Tr
|
|||||||
|
|
||||||
|
|
||||||
def get_installed_pip_packages():
|
def get_installed_pip_packages():
|
||||||
# extract pip package infos
|
try:
|
||||||
cmd = manager_util.make_pip_cmd(['freeze'])
|
# extract pip package infos
|
||||||
pips = subprocess.check_output(cmd, text=True).split('\n')
|
cmd = manager_util.make_pip_cmd(['freeze'])
|
||||||
|
pips = subprocess.check_output(cmd, text=True).split('\n')
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning("[ComfyUI-Manager] Could not enumerate pip packages for snapshot: %s", e)
|
||||||
|
return {}
|
||||||
|
|
||||||
res = {}
|
res = {}
|
||||||
for x in pips:
|
for x in pips:
|
||||||
@@ -3350,36 +3370,80 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
|
|
||||||
|
|
||||||
def get_comfyui_versions(repo=None):
|
def get_comfyui_versions(repo=None):
|
||||||
if repo is None:
|
repo = repo or git.Repo(comfy_path)
|
||||||
repo = git.Repo(comfy_path)
|
|
||||||
|
|
||||||
|
remote_name = None
|
||||||
try:
|
try:
|
||||||
remote = get_remote_name(repo)
|
remote_name = get_remote_name(repo)
|
||||||
repo.remotes[remote].fetch()
|
repo.remotes[remote_name].fetch()
|
||||||
except:
|
except:
|
||||||
logging.error("[ComfyUI-Manager] Failed to fetch ComfyUI")
|
logging.error("[ComfyUI-Manager] Failed to fetch ComfyUI")
|
||||||
|
|
||||||
versions = [x.name for x in repo.tags if x.name.startswith('v')]
|
def parse_semver(tag_name):
|
||||||
|
match = re.match(r'^v(\d+)\.(\d+)\.(\d+)$', tag_name)
|
||||||
|
return tuple(int(x) for x in match.groups()) if match else None
|
||||||
|
|
||||||
# nearest tag
|
def normalize_describe(tag_name):
|
||||||
versions = sorted(versions, key=lambda v: repo.git.log('-1', '--format=%ct', v), reverse=True)
|
if not tag_name:
|
||||||
versions = versions[:4]
|
return None
|
||||||
|
base = tag_name.split('-', 1)[0]
|
||||||
|
return base if parse_semver(base) else None
|
||||||
|
|
||||||
current_tag = repo.git.describe('--tags')
|
# Collect semver tags and sort descending (highest first)
|
||||||
|
semver_tags = []
|
||||||
|
for tag in repo.tags:
|
||||||
|
semver = parse_semver(tag.name)
|
||||||
|
if semver:
|
||||||
|
semver_tags.append((semver, tag.name))
|
||||||
|
semver_tags.sort(key=lambda x: x[0], reverse=True)
|
||||||
|
semver_tags = [name for _, name in semver_tags]
|
||||||
|
|
||||||
if current_tag not in versions:
|
latest_tag = semver_tags[0] if semver_tags else None
|
||||||
versions = sorted(versions + [current_tag], key=lambda v: repo.git.log('-1', '--format=%ct', v), reverse=True)
|
|
||||||
versions = versions[:4]
|
|
||||||
|
|
||||||
main_branch = repo.heads.master
|
try:
|
||||||
latest_commit = main_branch.commit
|
described = repo.git.describe('--tags')
|
||||||
latest_tag = repo.git.describe('--tags', latest_commit.hexsha)
|
except Exception:
|
||||||
|
described = ''
|
||||||
|
|
||||||
if latest_tag != versions[0]:
|
try:
|
||||||
versions.insert(0, 'nightly')
|
exact_tag = repo.git.describe('--tags', '--exact-match')
|
||||||
else:
|
except Exception:
|
||||||
versions[0] = 'nightly'
|
exact_tag = ''
|
||||||
|
|
||||||
|
head_is_default = False
|
||||||
|
if remote_name:
|
||||||
|
try:
|
||||||
|
default_head_ref = repo.refs[f'{remote_name}/HEAD']
|
||||||
|
default_commit = default_head_ref.reference.commit
|
||||||
|
head_is_default = repo.head.commit == default_commit
|
||||||
|
except Exception:
|
||||||
|
head_is_default = False
|
||||||
|
|
||||||
|
nearest_semver = normalize_describe(described)
|
||||||
|
exact_semver = exact_tag if parse_semver(exact_tag) else None
|
||||||
|
|
||||||
|
if head_is_default and not exact_tag:
|
||||||
current_tag = 'nightly'
|
current_tag = 'nightly'
|
||||||
|
else:
|
||||||
|
current_tag = exact_tag or described or 'nightly'
|
||||||
|
|
||||||
|
# Prepare semver list for display: top 4 plus the current/nearest semver if missing
|
||||||
|
display_semver_tags = semver_tags[:4]
|
||||||
|
if exact_semver and exact_semver not in display_semver_tags:
|
||||||
|
display_semver_tags.append(exact_semver)
|
||||||
|
elif nearest_semver and nearest_semver not in display_semver_tags:
|
||||||
|
display_semver_tags.append(nearest_semver)
|
||||||
|
|
||||||
|
versions = ['nightly']
|
||||||
|
|
||||||
|
if current_tag and not exact_semver and current_tag not in versions and current_tag not in display_semver_tags:
|
||||||
|
versions.append(current_tag)
|
||||||
|
|
||||||
|
for tag in display_semver_tags:
|
||||||
|
if tag not in versions:
|
||||||
|
versions.append(tag)
|
||||||
|
|
||||||
|
versions = versions[:6]
|
||||||
|
|
||||||
return versions, current_tag, latest_tag
|
return versions, current_tag, latest_tag
|
||||||
|
|
||||||
|
|||||||
356
glob/manager_migration.py
Normal file
356
glob/manager_migration.py
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
"""
|
||||||
|
ComfyUI-Manager migration module.
|
||||||
|
Handles migration from legacy paths to new __manager path structure.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
# Startup notices for notice board
|
||||||
|
startup_notices = [] # List of (message, level) tuples
|
||||||
|
|
||||||
|
|
||||||
|
def add_startup_notice(message, level='warning'):
|
||||||
|
"""Add a notice to be displayed on Manager notice board.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message: HTML-formatted message string
|
||||||
|
level: 'warning', 'error', 'info'
|
||||||
|
"""
|
||||||
|
global startup_notices
|
||||||
|
startup_notices.append((message, level))
|
||||||
|
|
||||||
|
|
||||||
|
# Cache for API check (computed once per session)
|
||||||
|
_cached_has_system_user_api = None
|
||||||
|
|
||||||
|
|
||||||
|
def has_system_user_api():
|
||||||
|
"""Check if ComfyUI has the System User Protection API (PR #10966).
|
||||||
|
|
||||||
|
Result is cached for performance.
|
||||||
|
"""
|
||||||
|
global _cached_has_system_user_api
|
||||||
|
if _cached_has_system_user_api is None:
|
||||||
|
try:
|
||||||
|
import folder_paths
|
||||||
|
_cached_has_system_user_api = hasattr(folder_paths, 'get_system_user_directory')
|
||||||
|
except Exception:
|
||||||
|
_cached_has_system_user_api = False
|
||||||
|
return _cached_has_system_user_api
|
||||||
|
|
||||||
|
|
||||||
|
def get_manager_path(user_dir):
|
||||||
|
"""Get the appropriate manager files path based on ComfyUI version.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: manager_files_path
|
||||||
|
"""
|
||||||
|
if has_system_user_api():
|
||||||
|
return os.path.abspath(os.path.join(user_dir, '__manager'))
|
||||||
|
else:
|
||||||
|
return os.path.abspath(os.path.join(user_dir, 'default', 'ComfyUI-Manager'))
|
||||||
|
|
||||||
|
|
||||||
|
def run_migration_checks(user_dir, manager_files_path):
|
||||||
|
"""Run all migration and security checks.
|
||||||
|
|
||||||
|
Call this after get_manager_path() to handle:
|
||||||
|
- Legacy config migration (new ComfyUI)
|
||||||
|
- Legacy backup notification (every startup)
|
||||||
|
- Suspicious directory detection (old ComfyUI)
|
||||||
|
- Outdated ComfyUI warning (old ComfyUI)
|
||||||
|
"""
|
||||||
|
if has_system_user_api():
|
||||||
|
migrated = migrate_legacy_config(user_dir, manager_files_path)
|
||||||
|
# Only check for legacy backup if migration didn't just happen
|
||||||
|
# (migration already shows backup location in its message)
|
||||||
|
if not migrated:
|
||||||
|
check_legacy_backup(manager_files_path)
|
||||||
|
else:
|
||||||
|
check_suspicious_manager(user_dir)
|
||||||
|
warn_outdated_comfyui()
|
||||||
|
|
||||||
|
|
||||||
|
def check_legacy_backup(manager_files_path):
|
||||||
|
"""Check for legacy backup and notify user to verify and remove it.
|
||||||
|
|
||||||
|
This runs on every startup to remind users about pending legacy backup.
|
||||||
|
"""
|
||||||
|
backup_dir = os.path.join(manager_files_path, '.legacy-manager-backup')
|
||||||
|
if not os.path.exists(backup_dir):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Terminal output
|
||||||
|
print("\n" + "-"*70)
|
||||||
|
print("[ComfyUI-Manager] NOTICE: Legacy backup exists")
|
||||||
|
print(" - Your old Manager data was backed up to:")
|
||||||
|
print(f" {backup_dir}")
|
||||||
|
print(" - Please verify and remove it when no longer needed.")
|
||||||
|
print("-"*70 + "\n")
|
||||||
|
|
||||||
|
# Notice board output
|
||||||
|
add_startup_notice(
|
||||||
|
"Legacy ComfyUI-Manager data backup exists. Please verify and remove when no longer needed. See terminal for details.",
|
||||||
|
level='info'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def check_suspicious_manager(user_dir):
|
||||||
|
"""Check for suspicious __manager directory on old ComfyUI.
|
||||||
|
|
||||||
|
On old ComfyUI without System User API, if __manager exists with low security,
|
||||||
|
warn the user to verify manually.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if suspicious setup detected
|
||||||
|
"""
|
||||||
|
if has_system_user_api():
|
||||||
|
return False # Not suspicious on new ComfyUI
|
||||||
|
|
||||||
|
suspicious_path = os.path.abspath(os.path.join(user_dir, '__manager'))
|
||||||
|
if not os.path.exists(suspicious_path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
config_path = os.path.join(suspicious_path, 'config.ini')
|
||||||
|
if not os.path.exists(config_path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(config_path)
|
||||||
|
sec_level = config.get('default', 'security_level', fallback='normal').lower()
|
||||||
|
|
||||||
|
if sec_level in ['weak', 'normal-']:
|
||||||
|
# Terminal output
|
||||||
|
print("\n" + "!"*70)
|
||||||
|
print("[ComfyUI-Manager] ERROR: Suspicious path detected!")
|
||||||
|
print(f" - '__manager' exists with low security level: '{sec_level}'")
|
||||||
|
print(" - Please verify manually:")
|
||||||
|
print(f" {config_path}")
|
||||||
|
print("!"*70 + "\n")
|
||||||
|
|
||||||
|
# Notice board output
|
||||||
|
add_startup_notice(
|
||||||
|
"[Security Alert] Suspicious path detected. See terminal log for details.",
|
||||||
|
level='error'
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def warn_outdated_comfyui():
|
||||||
|
"""Warn user about outdated ComfyUI without System User API."""
|
||||||
|
if has_system_user_api():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Terminal output
|
||||||
|
print("\n" + "!"*70)
|
||||||
|
print("[ComfyUI-Manager] ERROR: ComfyUI version is outdated!")
|
||||||
|
print(" - Most operations are blocked for security.")
|
||||||
|
print(" - ComfyUI update is still allowed.")
|
||||||
|
print(" - Please update ComfyUI to use Manager normally.")
|
||||||
|
print("!"*70 + "\n")
|
||||||
|
|
||||||
|
# Notice board output
|
||||||
|
add_startup_notice(
|
||||||
|
"[Security Alert] ComfyUI outdated. Installations blocked (update allowed).<BR>"
|
||||||
|
"Update ComfyUI for normal operation.",
|
||||||
|
level='error'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_legacy_config(user_dir, manager_files_path):
|
||||||
|
"""Migrate ONLY config.ini to new __manager path if needed.
|
||||||
|
|
||||||
|
IMPORTANT: Only config.ini is migrated. Other files (snapshots, cache, etc.)
|
||||||
|
are NOT migrated - users must recreate them.
|
||||||
|
|
||||||
|
Scenarios:
|
||||||
|
1. Legacy exists, New doesn't exist → Migrate config.ini
|
||||||
|
2. Legacy exists, New exists → First update after upgrade
|
||||||
|
- Run ComfyUI dependency installation
|
||||||
|
- Rename legacy to .backup
|
||||||
|
3. Legacy doesn't exist → No migration needed
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if migration was performed
|
||||||
|
"""
|
||||||
|
if not has_system_user_api():
|
||||||
|
return False
|
||||||
|
|
||||||
|
legacy_dir = os.path.join(user_dir, 'default', 'ComfyUI-Manager')
|
||||||
|
legacy_config = os.path.join(legacy_dir, 'config.ini')
|
||||||
|
new_config = os.path.join(manager_files_path, 'config.ini')
|
||||||
|
|
||||||
|
if not os.path.exists(legacy_dir):
|
||||||
|
return False # No legacy directory, nothing to migrate
|
||||||
|
|
||||||
|
# IMPORTANT: Check for config.ini existence, not just directory
|
||||||
|
# (because makedirs() creates __manager before this function is called)
|
||||||
|
|
||||||
|
# Case: Both configs exist (first update after ComfyUI upgrade)
|
||||||
|
# This means user ran new ComfyUI at least once, creating __manager/config.ini
|
||||||
|
if os.path.exists(legacy_config) and os.path.exists(new_config):
|
||||||
|
_handle_first_update_migration(user_dir, legacy_dir, manager_files_path)
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Case: Legacy config exists but new config doesn't (normal migration)
|
||||||
|
# This is the first run after ComfyUI upgrade
|
||||||
|
if os.path.exists(legacy_config) and not os.path.exists(new_config):
|
||||||
|
pass # Continue with normal migration below
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Terminal output
|
||||||
|
print("\n" + "-"*70)
|
||||||
|
print("[ComfyUI-Manager] NOTICE: Legacy config.ini detected")
|
||||||
|
print(f" - Old: {legacy_config}")
|
||||||
|
print(f" - New: {new_config}")
|
||||||
|
print(" - Migrating config.ini only (other files are NOT migrated).")
|
||||||
|
print(" - Security level below 'normal' will be raised.")
|
||||||
|
print("-"*70 + "\n")
|
||||||
|
|
||||||
|
_migrate_config_with_security_check(legacy_config, new_config)
|
||||||
|
|
||||||
|
# Move legacy directory to backup
|
||||||
|
_move_legacy_to_backup(legacy_dir, manager_files_path)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_first_update_migration(user_dir, legacy_dir, manager_files_path):
|
||||||
|
"""Handle first ComfyUI update when both legacy and new directories exist.
|
||||||
|
|
||||||
|
This scenario happens when:
|
||||||
|
- User was on old ComfyUI (using default/ComfyUI-Manager)
|
||||||
|
- ComfyUI was updated (now has System User API)
|
||||||
|
- Manager already created __manager on first new run
|
||||||
|
- But legacy directory still exists
|
||||||
|
|
||||||
|
Actions:
|
||||||
|
1. Run ComfyUI dependency installation
|
||||||
|
2. Move legacy to __manager/.legacy-manager-backup
|
||||||
|
"""
|
||||||
|
# Terminal output
|
||||||
|
print("\n" + "-"*70)
|
||||||
|
print("[ComfyUI-Manager] NOTICE: First update after ComfyUI upgrade detected")
|
||||||
|
print(" - Both legacy and new directories exist.")
|
||||||
|
print(" - Running ComfyUI dependency installation...")
|
||||||
|
print("-"*70 + "\n")
|
||||||
|
|
||||||
|
# Run ComfyUI dependency installation
|
||||||
|
# Path: glob/manager_migration.py → glob → comfyui-manager → custom_nodes → ComfyUI
|
||||||
|
try:
|
||||||
|
comfyui_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
||||||
|
requirements_path = os.path.join(comfyui_path, 'requirements.txt')
|
||||||
|
if os.path.exists(requirements_path):
|
||||||
|
subprocess.run([sys.executable, '-m', 'pip', 'install', '-r', requirements_path],
|
||||||
|
capture_output=True, check=False)
|
||||||
|
print("[ComfyUI-Manager] ComfyUI dependencies installation completed.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ComfyUI-Manager] WARNING: Failed to install ComfyUI dependencies: {e}")
|
||||||
|
|
||||||
|
# Move legacy to backup inside __manager
|
||||||
|
_move_legacy_to_backup(legacy_dir, manager_files_path)
|
||||||
|
|
||||||
|
|
||||||
|
def _move_legacy_to_backup(legacy_dir, manager_files_path):
|
||||||
|
"""Move legacy directory to backup inside __manager.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Path to backup directory if successful, None if failed
|
||||||
|
"""
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
backup_dir = os.path.join(manager_files_path, '.legacy-manager-backup')
|
||||||
|
|
||||||
|
try:
|
||||||
|
if os.path.exists(backup_dir):
|
||||||
|
shutil.rmtree(backup_dir) # Remove old backup if exists
|
||||||
|
shutil.move(legacy_dir, backup_dir)
|
||||||
|
|
||||||
|
# Terminal output (full paths shown here only)
|
||||||
|
print("\n" + "-"*70)
|
||||||
|
print("[ComfyUI-Manager] NOTICE: Legacy settings migrated")
|
||||||
|
print(f" - Old location: {legacy_dir}")
|
||||||
|
print(f" - Backed up to: {backup_dir}")
|
||||||
|
print(" - Please verify and remove the backup when no longer needed.")
|
||||||
|
print("-"*70 + "\n")
|
||||||
|
|
||||||
|
# Notice board output (no full paths for security)
|
||||||
|
add_startup_notice(
|
||||||
|
"Legacy ComfyUI-Manager data migrated. See terminal for details.",
|
||||||
|
level='info'
|
||||||
|
)
|
||||||
|
return backup_dir
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ComfyUI-Manager] WARNING: Failed to backup legacy directory: {e}")
|
||||||
|
add_startup_notice(
|
||||||
|
f"[MIGRATION] Failed to backup legacy directory: {e}",
|
||||||
|
level='warning'
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _migrate_config_with_security_check(legacy_path, new_path):
|
||||||
|
"""Migrate legacy config, raising security level only if below default."""
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
try:
|
||||||
|
config.read(legacy_path)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ComfyUI-Manager] WARNING: Failed to parse config.ini: {e}")
|
||||||
|
print(" - Creating fresh config with default settings.")
|
||||||
|
add_startup_notice(
|
||||||
|
"[MIGRATION] Failed to parse legacy config. Using defaults.",
|
||||||
|
level='warning'
|
||||||
|
)
|
||||||
|
return # Skip migration, let Manager create fresh config
|
||||||
|
|
||||||
|
# Security level hierarchy: strong > normal > normal- > weak
|
||||||
|
# Default is 'normal', only raise if below default
|
||||||
|
if 'default' in config:
|
||||||
|
current_level = config['default'].get('security_level', 'normal').lower()
|
||||||
|
below_default_levels = ['weak', 'normal-']
|
||||||
|
|
||||||
|
if current_level in below_default_levels:
|
||||||
|
config['default']['security_level'] = 'normal'
|
||||||
|
|
||||||
|
# Terminal output
|
||||||
|
print("\n" + "="*70)
|
||||||
|
print("[ComfyUI-Manager] WARNING: Security level adjusted")
|
||||||
|
print(f" - Previous: '{current_level}' → New: 'normal'")
|
||||||
|
print(" - Raised to prevent unauthorized remote access.")
|
||||||
|
print("="*70 + "\n")
|
||||||
|
|
||||||
|
# Notice board output
|
||||||
|
add_startup_notice(
|
||||||
|
f"[MIGRATION] Security level raised: '{current_level}' → 'normal'.<BR>"
|
||||||
|
"To prevent unauthorized remote access.",
|
||||||
|
level='warning'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f" - Security level: '{current_level}' (no change needed)")
|
||||||
|
|
||||||
|
# Ensure directory exists
|
||||||
|
os.makedirs(os.path.dirname(new_path), exist_ok=True)
|
||||||
|
|
||||||
|
with open(new_path, 'w') as f:
|
||||||
|
config.write(f)
|
||||||
|
|
||||||
|
|
||||||
|
def force_security_level_if_needed(config_dict):
|
||||||
|
"""Force security level to 'strong' if on old ComfyUI.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_dict: Configuration dictionary to modify in-place
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if security level was forced
|
||||||
|
"""
|
||||||
|
if not has_system_user_api():
|
||||||
|
config_dict['security_level'] = 'strong'
|
||||||
|
return True
|
||||||
|
return False
|
||||||
@@ -22,6 +22,7 @@ import asyncio
|
|||||||
import queue
|
import queue
|
||||||
|
|
||||||
import manager_downloader
|
import manager_downloader
|
||||||
|
import manager_migration
|
||||||
|
|
||||||
|
|
||||||
logging.info(f"### Loading: ComfyUI-Manager ({core.version_str})")
|
logging.info(f"### Loading: ComfyUI-Manager ({core.version_str})")
|
||||||
@@ -276,6 +277,13 @@ import zipfile
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
|
|
||||||
|
def security_403_response():
|
||||||
|
"""Return appropriate 403 response based on ComfyUI version."""
|
||||||
|
if not manager_migration.has_system_user_api():
|
||||||
|
return web.json_response({"error": "comfyui_outdated"}, status=403)
|
||||||
|
return web.json_response({"error": "security_level"}, status=403)
|
||||||
|
|
||||||
|
|
||||||
def get_model_dir(data, show_log=False):
|
def get_model_dir(data, show_log=False):
|
||||||
if 'download_model_base' in folder_paths.folder_names_and_paths:
|
if 'download_model_base' in folder_paths.folder_names_and_paths:
|
||||||
models_base = folder_paths.folder_names_and_paths['download_model_base'][0][0]
|
models_base = folder_paths.folder_names_and_paths['download_model_base'][0][0]
|
||||||
@@ -732,7 +740,7 @@ async def fetch_updates(request):
|
|||||||
async def update_all(request):
|
async def update_all(request):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403)
|
return security_403_response()
|
||||||
|
|
||||||
with task_worker_lock:
|
with task_worker_lock:
|
||||||
is_processing = task_worker_thread is not None and task_worker_thread.is_alive()
|
is_processing = task_worker_thread is not None and task_worker_thread.is_alive()
|
||||||
@@ -965,7 +973,7 @@ async def get_snapshot_list(request):
|
|||||||
async def remove_snapshot(request):
|
async def remove_snapshot(request):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403)
|
return security_403_response()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target = request.rel_url.query["target"]
|
target = request.rel_url.query["target"]
|
||||||
@@ -983,7 +991,7 @@ async def remove_snapshot(request):
|
|||||||
async def restore_snapshot(request):
|
async def restore_snapshot(request):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403)
|
return security_403_response()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target = request.rel_url.query["target"]
|
target = request.rel_url.query["target"]
|
||||||
@@ -1302,7 +1310,7 @@ async def fix_custom_node(request):
|
|||||||
async def install_custom_node_git_url(request):
|
async def install_custom_node_git_url(request):
|
||||||
if not is_allowed_security_level('high'):
|
if not is_allowed_security_level('high'):
|
||||||
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
|
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
|
||||||
return web.Response(status=403)
|
return security_403_response()
|
||||||
|
|
||||||
url = await request.text()
|
url = await request.text()
|
||||||
res = await core.gitclone_install(url)
|
res = await core.gitclone_install(url)
|
||||||
@@ -1322,7 +1330,7 @@ async def install_custom_node_git_url(request):
|
|||||||
async def install_custom_node_pip(request):
|
async def install_custom_node_pip(request):
|
||||||
if not is_allowed_security_level('high'):
|
if not is_allowed_security_level('high'):
|
||||||
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
|
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
|
||||||
return web.Response(status=403)
|
return security_403_response()
|
||||||
|
|
||||||
packages = await request.text()
|
packages = await request.text()
|
||||||
core.pip_install(packages.split(' '))
|
core.pip_install(packages.split(' '))
|
||||||
@@ -1594,6 +1602,16 @@ async def get_notice(request):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Prepend startup notices from manager_migration
|
||||||
|
for message, level in reversed(manager_migration.startup_notices):
|
||||||
|
if level == 'error':
|
||||||
|
style = 'color:red; background-color:white; font-weight:bold'
|
||||||
|
elif level == 'warning':
|
||||||
|
style = 'color:orange; background-color:white; font-weight:bold'
|
||||||
|
else:
|
||||||
|
style = 'color:blue; background-color:white'
|
||||||
|
markdown_content = f'<P style="{style}">{message}</P>' + markdown_content
|
||||||
|
|
||||||
return web.Response(text=markdown_content, status=200)
|
return web.Response(text=markdown_content, status=200)
|
||||||
else:
|
else:
|
||||||
return web.Response(text="Unable to retrieve Notice", status=200)
|
return web.Response(text="Unable to retrieve Notice", status=200)
|
||||||
@@ -1601,11 +1619,35 @@ async def get_notice(request):
|
|||||||
return web.Response(text="Unable to retrieve Notice", status=200)
|
return web.Response(text="Unable to retrieve Notice", status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@routes.get("/manager/startup_alerts")
|
||||||
|
async def get_startup_alerts(request):
|
||||||
|
"""Return startup alerts for customAlert display on page load.
|
||||||
|
|
||||||
|
Returns JSON array of alerts that should be shown to user immediately.
|
||||||
|
All startup notices (error, warning, info) are returned.
|
||||||
|
"""
|
||||||
|
alerts = []
|
||||||
|
|
||||||
|
# Return all startup notices for alert display
|
||||||
|
for message, level in manager_migration.startup_notices:
|
||||||
|
# Convert HTML BR to newlines for customAlert
|
||||||
|
text = message.replace('<BR>', '\n').replace('<br>', '\n')
|
||||||
|
# Add [ComfyUI-Manager] prefix for customAlert (notice board shows in Manager UI anyway)
|
||||||
|
text = text.replace('[Security Alert]', '[ComfyUI-Manager] Security Alert:')
|
||||||
|
text = text.replace('[MIGRATION]', '[ComfyUI-Manager] Migration:')
|
||||||
|
alerts.append({
|
||||||
|
'message': text,
|
||||||
|
'level': level
|
||||||
|
})
|
||||||
|
|
||||||
|
return web.json_response(alerts)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/reboot")
|
@routes.get("/manager/reboot")
|
||||||
def restart(self):
|
def restart(self):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403)
|
return security_403_response()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sys.stdout.close_log()
|
sys.stdout.close_log()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import re
|
|||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
import shlex
|
import shlex
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
|
||||||
cache_lock = threading.Lock()
|
cache_lock = threading.Lock()
|
||||||
@@ -34,18 +35,64 @@ def add_python_path_to_env():
|
|||||||
os.environ['PATH'] = os.path.dirname(sys.executable)+sep+os.environ['PATH']
|
os.environ['PATH'] = os.path.dirname(sys.executable)+sep+os.environ['PATH']
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=2)
|
||||||
|
def get_pip_cmd(force_uv=False):
|
||||||
|
"""
|
||||||
|
Get the base pip command, with automatic fallback to uv if pip is unavailable.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
force_uv (bool): If True, use uv directly without trying pip
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: Base command for pip operations
|
||||||
|
"""
|
||||||
|
embedded = 'python_embeded' in sys.executable
|
||||||
|
|
||||||
|
# Try pip first (unless forcing uv)
|
||||||
|
if not force_uv:
|
||||||
|
try:
|
||||||
|
test_cmd = [sys.executable] + (['-s'] if embedded else []) + ['-m', 'pip', '--version']
|
||||||
|
subprocess.check_output(test_cmd, stderr=subprocess.DEVNULL, timeout=5)
|
||||||
|
return [sys.executable] + (['-s'] if embedded else []) + ['-m', 'pip']
|
||||||
|
except Exception:
|
||||||
|
logging.warning("[ComfyUI-Manager] `python -m pip` not available. Falling back to `uv`.")
|
||||||
|
|
||||||
|
# Try uv (either forced or pip failed)
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
# Try uv as Python module
|
||||||
|
try:
|
||||||
|
test_cmd = [sys.executable] + (['-s'] if embedded else []) + ['-m', 'uv', '--version']
|
||||||
|
subprocess.check_output(test_cmd, stderr=subprocess.DEVNULL, timeout=5)
|
||||||
|
logging.info("[ComfyUI-Manager] Using `uv` as Python module for pip operations.")
|
||||||
|
return [sys.executable] + (['-s'] if embedded else []) + ['-m', 'uv', 'pip']
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Try standalone uv
|
||||||
|
if shutil.which('uv'):
|
||||||
|
logging.info("[ComfyUI-Manager] Using standalone `uv` for pip operations.")
|
||||||
|
return ['uv', 'pip']
|
||||||
|
|
||||||
|
# Nothing worked
|
||||||
|
logging.error("[ComfyUI-Manager] Neither `python -m pip` nor `uv` are available. Cannot proceed with package operations.")
|
||||||
|
raise Exception("Neither `pip` nor `uv` are available for package management")
|
||||||
|
|
||||||
|
|
||||||
def make_pip_cmd(cmd):
|
def make_pip_cmd(cmd):
|
||||||
if 'python_embeded' in sys.executable:
|
"""
|
||||||
if use_uv:
|
Create a pip command by combining the cached base pip command with the given arguments.
|
||||||
return [sys.executable, '-s', '-m', 'uv', 'pip'] + cmd
|
|
||||||
else:
|
Args:
|
||||||
return [sys.executable, '-s', '-m', 'pip'] + cmd
|
cmd (list): List of pip command arguments (e.g., ['install', 'package'])
|
||||||
else:
|
|
||||||
# FIXED: https://github.com/ltdrdata/ComfyUI-Manager/issues/1667
|
Returns:
|
||||||
if use_uv:
|
list: Complete command list ready for subprocess execution
|
||||||
return [sys.executable, '-m', 'uv', 'pip'] + cmd
|
"""
|
||||||
else:
|
global use_uv
|
||||||
return [sys.executable, '-m', 'pip'] + cmd
|
base_cmd = get_pip_cmd(force_uv=use_uv)
|
||||||
|
return base_cmd + cmd
|
||||||
|
|
||||||
|
|
||||||
# DON'T USE StrictVersion - cannot handle pre_release version
|
# DON'T USE StrictVersion - cannot handle pre_release version
|
||||||
# try:
|
# try:
|
||||||
|
|||||||
@@ -335,8 +335,7 @@ async def share_art(request):
|
|||||||
content_type = assetFileType
|
content_type = assetFileType
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from nio import AsyncClient, LoginResponse, RoomSendResponse, UploadResponse, RoomMessageText, RoomMessageMedia
|
from nio import AsyncClient, LoginResponse, UploadResponse
|
||||||
import asyncio
|
|
||||||
|
|
||||||
homeserver = 'matrix.org'
|
homeserver = 'matrix.org'
|
||||||
if matrix_auth:
|
if matrix_auth:
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ This directory contains the JavaScript frontend implementation for ComfyUI-Manag
|
|||||||
## Sharing Components
|
## Sharing Components
|
||||||
|
|
||||||
- **comfyui-share-common.js**: Base functionality for workflow sharing features.
|
- **comfyui-share-common.js**: Base functionality for workflow sharing features.
|
||||||
- **comfyui-share-copus.js**: Integration with the ComfyUI Opus sharing platform.
|
- **comfyui-share-copus.js**: Integration with the ComfyUI Copus sharing platform.
|
||||||
- **comfyui-share-openart.js**: Integration with the OpenArt sharing platform.
|
- **comfyui-share-openart.js**: Integration with the OpenArt sharing platform.
|
||||||
- **comfyui-share-youml.js**: Integration with the YouML sharing platform.
|
- **comfyui-share-youml.js**: Integration with the YouML sharing platform.
|
||||||
|
|
||||||
@@ -47,4 +47,4 @@ CSS files are included for specific components:
|
|||||||
- **custom-nodes-manager.css**: Styling for the node management UI
|
- **custom-nodes-manager.css**: Styling for the node management UI
|
||||||
- **model-manager.css**: Styling for the model management UI
|
- **model-manager.css**: Styling for the model management UI
|
||||||
|
|
||||||
This frontend implementation provides a comprehensive yet user-friendly interface for managing the ComfyUI ecosystem.
|
This frontend implementation provides a comprehensive yet user-friendly interface for managing the ComfyUI ecosystem.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { api } from "../../scripts/api.js";
|
import { api } from "../../scripts/api.js";
|
||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { sleep, customConfirm, customAlert } from "./common.js";
|
import { sleep, customConfirm, customAlert, handle403Response, show_message } from "./common.js";
|
||||||
|
|
||||||
async function tryInstallCustomNode(event) {
|
async function tryInstallCustomNode(event) {
|
||||||
let msg = '-= [ComfyUI Manager] extension installation request =-\n\n';
|
let msg = '-= [ComfyUI Manager] extension installation request =-\n\n';
|
||||||
@@ -42,7 +42,7 @@ async function tryInstallCustomNode(event) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(response.status == 403) {
|
if(response.status == 403) {
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
await handle403Response(response);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if(response.status == 400) {
|
else if(response.status == 400) {
|
||||||
@@ -54,7 +54,7 @@ async function tryInstallCustomNode(event) {
|
|||||||
|
|
||||||
let response = await api.fetchApi("/manager/reboot");
|
let response = await api.fetchApi("/manager/reboot");
|
||||||
if(response.status == 403) {
|
if(response.status == 403) {
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
await handle403Response(response);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
|||||||
import {
|
import {
|
||||||
free_models, install_pip, install_via_git_url, manager_instance,
|
free_models, install_pip, install_via_git_url, manager_instance,
|
||||||
rebootAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
rebootAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
||||||
infoToast, showTerminal, setNeedRestart
|
infoToast, showTerminal, setNeedRestart, handle403Response
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
|
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
|
||||||
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
||||||
@@ -753,9 +753,9 @@ async function onQueueStatus(event) {
|
|||||||
|
|
||||||
const rebootButton = document.getElementById('cm-reboot-button5');
|
const rebootButton = document.getElementById('cm-reboot-button5');
|
||||||
rebootButton?.addEventListener("click",
|
rebootButton?.addEventListener("click",
|
||||||
function() {
|
async function() {
|
||||||
if(rebootAPI()) {
|
if(await rebootAPI()) {
|
||||||
manager_dialog.close();
|
manager_instance.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -780,8 +780,13 @@ async function updateAll(update_comfyui) {
|
|||||||
|
|
||||||
const response = await api.fetchApi(`/manager/queue/update_all?mode=${mode}`);
|
const response = await api.fetchApi(`/manager/queue/update_all?mode=${mode}`);
|
||||||
|
|
||||||
if (response.status == 401) {
|
if (response.status == 403) {
|
||||||
|
await handle403Response(response);
|
||||||
|
reset_action_buttons();
|
||||||
|
}
|
||||||
|
else if (response.status == 401) {
|
||||||
customAlert('Another task is already in progress. Please stop the ongoing task first.');
|
customAlert('Another task is already in progress. Please stop the ongoing task first.');
|
||||||
|
reset_action_buttons();
|
||||||
}
|
}
|
||||||
else if(response.status == 200) {
|
else if(response.status == 200) {
|
||||||
is_updating = true;
|
is_updating = true;
|
||||||
@@ -1453,6 +1458,31 @@ app.registerExtension({
|
|||||||
|
|
||||||
load_components();
|
load_components();
|
||||||
|
|
||||||
|
// Fetch and show startup alerts (critical errors like outdated ComfyUI)
|
||||||
|
// Poll until extensionManager.toast is ready (set in Vue onMounted)
|
||||||
|
const showStartupAlerts = async () => {
|
||||||
|
let toastWaitCount = 0;
|
||||||
|
const waitForToast = () => {
|
||||||
|
if (window['app']?.extensionManager?.toast) {
|
||||||
|
fetch('/manager/startup_alerts')
|
||||||
|
.then(response => response.ok ? response.json() : [])
|
||||||
|
.then(alerts => {
|
||||||
|
for (const alert of alerts) {
|
||||||
|
customAlert(alert.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(e => console.warn('[ComfyUI-Manager] Failed to fetch startup alerts:', e));
|
||||||
|
} else if (toastWaitCount < 300) { // Max 30 seconds (300 * 100ms)
|
||||||
|
toastWaitCount++;
|
||||||
|
setTimeout(waitForToast, 100);
|
||||||
|
} else {
|
||||||
|
console.warn('[ComfyUI-Manager] Timeout waiting for toast. Startup alerts skipped.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
waitForToast();
|
||||||
|
};
|
||||||
|
showStartupAlerts();
|
||||||
|
|
||||||
const menu = document.querySelector(".comfy-menu");
|
const menu = document.querySelector(".comfy-menu");
|
||||||
const separator = document.createElement("hr");
|
const separator = document.createElement("hr");
|
||||||
|
|
||||||
|
|||||||
@@ -201,13 +201,15 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
});
|
});
|
||||||
this.LockInput = $el("input", {
|
this.LockInput = $el("input", {
|
||||||
type: "text",
|
type: "text",
|
||||||
placeholder: "",
|
placeholder: "0",
|
||||||
style: {
|
style: {
|
||||||
width: "100px",
|
width: "100px",
|
||||||
padding: "7px",
|
padding: "7px",
|
||||||
|
paddingLeft: "30px",
|
||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
border: "1px solid #ddd",
|
border: "1px solid #ddd",
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
|
position: "relative",
|
||||||
},
|
},
|
||||||
oninput: (event) => {
|
oninput: (event) => {
|
||||||
let input = event.target.value;
|
let input = event.target.value;
|
||||||
@@ -375,7 +377,7 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const blockChainSection_lock = $el("div", { style: sectionStyle }, [
|
const blockChainSection_lock = $el("div", { style: sectionStyle }, [
|
||||||
$el("label", { style: labelStyle }, ["6️⃣ Pay to download"]),
|
$el("label", { style: labelStyle }, ["6️⃣ Download threshold"]),
|
||||||
$el(
|
$el(
|
||||||
"label",
|
"label",
|
||||||
{
|
{
|
||||||
@@ -395,6 +397,7 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
marginLeft: "5px",
|
marginLeft: "5px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
position: "relative",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@@ -408,8 +411,18 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
color: "#fff",
|
color: "#fff",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
["Price US$"]
|
["Unlock with"]
|
||||||
),
|
),
|
||||||
|
$el("img", {
|
||||||
|
style: {
|
||||||
|
width: "16px",
|
||||||
|
height: "16px",
|
||||||
|
position: "absolute",
|
||||||
|
right: "75px",
|
||||||
|
zIndex: "100",
|
||||||
|
},
|
||||||
|
src: "https://static.copus.io/images/admin/202507/prod/e2919a1d8f3c2d99d3b8fe27ff94b841.png",
|
||||||
|
}),
|
||||||
this.LockInput,
|
this.LockInput,
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
@@ -420,7 +433,7 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
{ style: { display: "flex", alignItems: "center", cursor: "pointer" } },
|
{ style: { display: "flex", alignItems: "center", cursor: "pointer" } },
|
||||||
[
|
[
|
||||||
this.radioButtonsCheckOff_lock,
|
this.radioButtonsCheckOff_lock,
|
||||||
$el(
|
$el(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
@@ -429,9 +442,7 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[
|
[$el("span", { style: { marginLeft: "5px" } }, ["OFF"])]
|
||||||
$el("span", { style: { marginLeft: "5px" } }, ["OFF"]),
|
|
||||||
]
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
@@ -440,7 +451,6 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
"p",
|
"p",
|
||||||
{ style: { fontSize: "16px", color: "#fff", margin: "10px 0 0 0" } },
|
{ style: { fontSize: "16px", color: "#fff", margin: "10px 0 0 0" } },
|
||||||
[
|
[
|
||||||
"Get paid from your workflow. You can change the price and withdraw your earnings on Copus.",
|
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|||||||
40
js/common.js
40
js/common.js
@@ -100,6 +100,19 @@ export function show_message(msg) {
|
|||||||
app.ui.dialog.element.style.zIndex = 1100;
|
app.ui.dialog.element.style.zIndex = 1100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function handle403Response(res, defaultMessage) {
|
||||||
|
try {
|
||||||
|
const data = await res.json();
|
||||||
|
if(data.error === 'comfyui_outdated') {
|
||||||
|
show_message('ComfyUI version is outdated.<BR>Please update ComfyUI to use Manager normally.');
|
||||||
|
} else {
|
||||||
|
show_message(defaultMessage || 'This action is not allowed with this security level configuration.');
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
show_message(defaultMessage || 'This action is not allowed with this security level configuration.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function sleep(ms) {
|
export async function sleep(ms) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
@@ -163,20 +176,23 @@ export async function customPrompt(title, message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function rebootAPI() {
|
export async function rebootAPI() {
|
||||||
if ('electronAPI' in window) {
|
if ('electronAPI' in window) {
|
||||||
window.electronAPI.restartApp();
|
window.electronAPI.restartApp();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
customConfirm("Are you sure you'd like to reboot the server?").then((isConfirmed) => {
|
const isConfirmed = await customConfirm("Are you sure you'd like to reboot the server?");
|
||||||
if (isConfirmed) {
|
if (isConfirmed) {
|
||||||
try {
|
try {
|
||||||
api.fetchApi("/manager/reboot");
|
const response = await api.fetchApi("/manager/reboot");
|
||||||
|
if (response.status == 403) {
|
||||||
|
await handle403Response(response);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
catch(exception) {}
|
|
||||||
}
|
}
|
||||||
});
|
catch(exception) {}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -216,7 +232,7 @@ export async function install_pip(packages) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(res.status == 403) {
|
if(res.status == 403) {
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
await handle403Response(res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +267,7 @@ export async function install_via_git_url(url, manager_dialog) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(res.status == 403) {
|
if(res.status == 403) {
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
await handle403Response(res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,9 +278,9 @@ export async function install_via_git_url(url, manager_dialog) {
|
|||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
rebootButton.addEventListener("click",
|
rebootButton.addEventListener("click",
|
||||||
function() {
|
async function() {
|
||||||
if(rebootAPI()) {
|
if(await rebootAPI()) {
|
||||||
manager_dialog.close();
|
manager_instance.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.cn-manager {
|
.cn-manager {
|
||||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segue UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
z-index: 1099;
|
z-index: 1099;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
height: 80%;
|
height: 80%;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
|
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
|
||||||
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
|
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
|
||||||
storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss,
|
storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss,
|
||||||
showPopover, hidePopover
|
showPopover, hidePopover, handle403Response
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
|
|
||||||
// https://cenfun.github.io/turbogrid/api.html
|
// https://cenfun.github.io/turbogrid/api.html
|
||||||
@@ -1528,7 +1528,16 @@ export class CustomNodesManager {
|
|||||||
errorMsg = `'${item.title}': `;
|
errorMsg = `'${item.title}': `;
|
||||||
|
|
||||||
if(res.status == 403) {
|
if(res.status == 403) {
|
||||||
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
try {
|
||||||
|
const data = await res.json();
|
||||||
|
if(data.error === 'comfyui_outdated') {
|
||||||
|
errorMsg += `ComfyUI version is outdated. Please update ComfyUI to use Manager normally.\n`;
|
||||||
|
} else {
|
||||||
|
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
||||||
|
}
|
||||||
} else if(res.status == 404) {
|
} else if(res.status == 404) {
|
||||||
errorMsg += `With the current security level configuration, only custom nodes from the <B>"default channel"</B> can be installed.\n`;
|
errorMsg += `With the current security level configuration, only custom nodes from the <B>"default channel"</B> can be installed.\n`;
|
||||||
} else {
|
} else {
|
||||||
@@ -1625,17 +1634,35 @@ export class CustomNodesManager {
|
|||||||
getNodesInWorkflow() {
|
getNodesInWorkflow() {
|
||||||
let usedGroupNodes = new Set();
|
let usedGroupNodes = new Set();
|
||||||
let allUsedNodes = {};
|
let allUsedNodes = {};
|
||||||
|
const visitedGraphs = new Set();
|
||||||
|
|
||||||
for(let k in app.graph._nodes) {
|
const visitGraph = (graph) => {
|
||||||
let node = app.graph._nodes[k];
|
if (!graph || visitedGraphs.has(graph)) return;
|
||||||
|
visitedGraphs.add(graph);
|
||||||
|
|
||||||
if(node.type.startsWith('workflow>')) {
|
const nodes = graph._nodes || graph.nodes || [];
|
||||||
usedGroupNodes.add(node.type.slice(9));
|
for(let k in nodes) {
|
||||||
continue;
|
let node = nodes[k];
|
||||||
|
if (!node) continue;
|
||||||
|
|
||||||
|
// If it's a SubgraphNode, recurse into its graph and continue searching
|
||||||
|
if (node.isSubgraphNode?.() && node.subgraph) {
|
||||||
|
visitGraph(node.subgraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.type) continue;
|
||||||
|
|
||||||
|
// Group nodes / components
|
||||||
|
if(typeof node.type === 'string' && node.type.startsWith('workflow>')) {
|
||||||
|
usedGroupNodes.add(node.type.slice(9));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
allUsedNodes[node.type] = node;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
allUsedNodes[node.type] = node;
|
visitGraph(app.graph);
|
||||||
}
|
|
||||||
|
|
||||||
for(let k of usedGroupNodes) {
|
for(let k of usedGroupNodes) {
|
||||||
let subnodes = app.graph.extra.groupNodes[k]?.nodes;
|
let subnodes = app.graph.extra.groupNodes[k]?.nodes;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { $el } from "../../scripts/ui.js";
|
import { $el } from "../../scripts/ui.js";
|
||||||
import {
|
import {
|
||||||
manager_instance, rebootAPI,
|
manager_instance, rebootAPI,
|
||||||
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
|
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
|
||||||
storeColumnWidth, restoreColumnWidth, loadCss
|
storeColumnWidth, restoreColumnWidth, loadCss, handle403Response
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
import { api } from "../../scripts/api.js";
|
import { api } from "../../scripts/api.js";
|
||||||
|
|
||||||
@@ -477,7 +477,16 @@ export class ModelManager {
|
|||||||
errorMsg = `'${item.name}': `;
|
errorMsg = `'${item.name}': `;
|
||||||
|
|
||||||
if(res.status == 403) {
|
if(res.status == 403) {
|
||||||
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
try {
|
||||||
|
const data = await res.json();
|
||||||
|
if(data.error === 'comfyui_outdated') {
|
||||||
|
errorMsg += `ComfyUI version is outdated. Please update ComfyUI to use Manager normally.\n`;
|
||||||
|
} else {
|
||||||
|
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errorMsg += await res.text() + '\n';
|
errorMsg += await res.text() + '\n';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { api } from "../../scripts/api.js"
|
import { api } from "../../scripts/api.js"
|
||||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||||
import { manager_instance, rebootAPI, show_message } from "./common.js";
|
import { manager_instance, rebootAPI, show_message, handle403Response } from "./common.js";
|
||||||
|
|
||||||
|
|
||||||
async function restore_snapshot(target) {
|
async function restore_snapshot(target) {
|
||||||
@@ -10,7 +10,7 @@ async function restore_snapshot(target) {
|
|||||||
const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" });
|
const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" });
|
||||||
|
|
||||||
if(response.status == 403) {
|
if(response.status == 403) {
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
await handle403Response(response);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ async function remove_snapshot(target) {
|
|||||||
const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" });
|
const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" });
|
||||||
|
|
||||||
if(response.status == 403) {
|
if(response.status == 403) {
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
await handle403Response(response);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,8 +145,8 @@ export class SnapshotManager extends ComfyDialog {
|
|||||||
if(btn_id) {
|
if(btn_id) {
|
||||||
const rebootButton = document.getElementById(btn_id);
|
const rebootButton = document.getElementById(btn_id);
|
||||||
const self = this;
|
const self = this;
|
||||||
rebootButton.onclick = function() {
|
rebootButton.onclick = async function() {
|
||||||
if(rebootAPI()) {
|
if(await rebootAPI()) {
|
||||||
self.close();
|
self.close();
|
||||||
self.manager_dialog.close();
|
self.manager_dialog.close();
|
||||||
}
|
}
|
||||||
|
|||||||
273
json-checker.py
273
json-checker.py
@@ -1,25 +1,264 @@
|
|||||||
import json
|
#!/usr/bin/env python3
|
||||||
import argparse
|
"""JSON Entry Validator
|
||||||
|
|
||||||
def check_json_syntax(file_path):
|
Validates JSON entries based on content structure.
|
||||||
|
|
||||||
|
Validation rules based on JSON content:
|
||||||
|
- {"custom_nodes": [...]}: Validates required fields (author, title, reference, files, install_type, description)
|
||||||
|
- {"models": [...]}: Validates JSON syntax only (no required fields)
|
||||||
|
- Other JSON structures: Validates JSON syntax only
|
||||||
|
|
||||||
|
Git repository URL validation (for custom_nodes):
|
||||||
|
1. URLs must NOT end with .git
|
||||||
|
2. URLs must follow format: https://github.com/{author}/{reponame}
|
||||||
|
3. .py and .js files are exempt from this check
|
||||||
|
|
||||||
|
Supported formats:
|
||||||
|
- Array format: [{...}, {...}]
|
||||||
|
- Object format: {"custom_nodes": [...]} or {"models": [...]}
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
# Required fields for each entry type
|
||||||
|
REQUIRED_FIELDS_CUSTOM_NODE = ['author', 'title', 'reference', 'files', 'install_type', 'description']
|
||||||
|
REQUIRED_FIELDS_MODEL = [] # model-list.json doesn't require field validation
|
||||||
|
|
||||||
|
# Pattern for valid GitHub repository URL (without .git suffix)
|
||||||
|
GITHUB_REPO_PATTERN = re.compile(r'^https://github\.com/[^/]+/[^/]+$')
|
||||||
|
|
||||||
|
|
||||||
|
def get_entry_context(entry: Dict) -> str:
|
||||||
|
"""Get identifying information from entry for error messages
|
||||||
|
|
||||||
|
Args:
|
||||||
|
entry: JSON entry
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
String with author and reference info
|
||||||
|
"""
|
||||||
|
parts = []
|
||||||
|
if 'author' in entry:
|
||||||
|
parts.append(f"author={entry['author']}")
|
||||||
|
if 'reference' in entry:
|
||||||
|
parts.append(f"ref={entry['reference']}")
|
||||||
|
if 'title' in entry:
|
||||||
|
parts.append(f"title={entry['title']}")
|
||||||
|
|
||||||
|
if parts:
|
||||||
|
return " | ".join(parts)
|
||||||
|
else:
|
||||||
|
# No identifying info - show actual entry content (truncated)
|
||||||
|
import json
|
||||||
|
entry_str = json.dumps(entry, ensure_ascii=False)
|
||||||
|
if len(entry_str) > 100:
|
||||||
|
entry_str = entry_str[:100] + "..."
|
||||||
|
return f"content={entry_str}"
|
||||||
|
|
||||||
|
|
||||||
|
def validate_required_fields(entry: Dict, entry_index: int, required_fields: List[str]) -> List[str]:
|
||||||
|
"""Validate that all required fields are present
|
||||||
|
|
||||||
|
Args:
|
||||||
|
entry: JSON entry to validate
|
||||||
|
entry_index: Index of entry in array (for error reporting)
|
||||||
|
required_fields: List of required field names
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of error descriptions (without entry prefix/context)
|
||||||
|
"""
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
for field in required_fields:
|
||||||
|
if field not in entry:
|
||||||
|
errors.append(f"Missing required field '{field}'")
|
||||||
|
elif entry[field] is None:
|
||||||
|
errors.append(f"Field '{field}' is null")
|
||||||
|
elif isinstance(entry[field], str) and not entry[field].strip():
|
||||||
|
errors.append(f"Field '{field}' is empty")
|
||||||
|
elif field == 'files' and not entry[field]: # Empty array
|
||||||
|
errors.append("Field 'files' is empty array")
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
def validate_git_repo_urls(entry: Dict, entry_index: int) -> List[str]:
|
||||||
|
"""Validate git repository URLs in 'files' array
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- Git repo URLs must NOT end with .git
|
||||||
|
- Must follow format: https://github.com/{author}/{reponame}
|
||||||
|
- .py and .js files are exempt
|
||||||
|
|
||||||
|
Args:
|
||||||
|
entry: JSON entry to validate
|
||||||
|
entry_index: Index of entry in array (for error reporting)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of error descriptions (without entry prefix/context)
|
||||||
|
"""
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
if 'files' not in entry or not isinstance(entry['files'], list):
|
||||||
|
return errors
|
||||||
|
|
||||||
|
for file_url in entry['files']:
|
||||||
|
if not isinstance(file_url, str):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip .py and .js files - they're exempt from git repo validation
|
||||||
|
if file_url.endswith('.py') or file_url.endswith('.js'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if it's a GitHub URL (likely a git repo)
|
||||||
|
if 'github.com' in file_url:
|
||||||
|
# Error if URL ends with .git
|
||||||
|
if file_url.endswith('.git'):
|
||||||
|
errors.append(f"Git repo URL must NOT end with .git: {file_url}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Validate format: https://github.com/{author}/{reponame}
|
||||||
|
if not GITHUB_REPO_PATTERN.match(file_url):
|
||||||
|
errors.append(f"Invalid git repo URL format (expected https://github.com/author/reponame): {file_url}")
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
def validate_entry(entry: Dict, entry_index: int, required_fields: List[str]) -> List[str]:
|
||||||
|
"""Validate a single JSON entry
|
||||||
|
|
||||||
|
Args:
|
||||||
|
entry: JSON entry to validate
|
||||||
|
entry_index: Index of entry in array (for error reporting)
|
||||||
|
required_fields: List of required field names
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of error messages (empty if valid)
|
||||||
|
"""
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
# Check required fields
|
||||||
|
errors.extend(validate_required_fields(entry, entry_index, required_fields))
|
||||||
|
|
||||||
|
# Check git repository URLs
|
||||||
|
errors.extend(validate_git_repo_urls(entry, entry_index))
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
def validate_json_file(file_path: str) -> Tuple[bool, List[str]]:
|
||||||
|
"""Validate JSON file containing entries
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: Path to JSON file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (is_valid, error_messages)
|
||||||
|
"""
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
# Check file exists
|
||||||
|
path = Path(file_path)
|
||||||
|
if not path.exists():
|
||||||
|
return False, [f"File not found: {file_path}"]
|
||||||
|
|
||||||
|
# Load JSON
|
||||||
try:
|
try:
|
||||||
with open(file_path, 'r', encoding='utf-8') as file:
|
with open(path, 'r', encoding='utf-8') as f:
|
||||||
json_str = file.read()
|
data = json.load(f)
|
||||||
json.loads(json_str)
|
|
||||||
print(f"[ OK ] {file_path}")
|
|
||||||
except UnicodeDecodeError as e:
|
|
||||||
print(f"Unicode decode error: {e}")
|
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
print(f"[FAIL] {file_path}\n\n {e}\n")
|
return False, [f"Invalid JSON: {e}"]
|
||||||
except FileNotFoundError:
|
except Exception as e:
|
||||||
print(f"[FAIL] {file_path}\n\n File not found\n")
|
return False, [f"Error reading file: {e}"]
|
||||||
|
|
||||||
|
# Determine required fields based on JSON content
|
||||||
|
required_fields = []
|
||||||
|
|
||||||
|
# Validate structure - support both array and object formats
|
||||||
|
entries_to_validate = []
|
||||||
|
|
||||||
|
if isinstance(data, list):
|
||||||
|
# Direct array format: [{...}, {...}]
|
||||||
|
entries_to_validate = data
|
||||||
|
elif isinstance(data, dict):
|
||||||
|
# Object format: {"custom_nodes": [...]} or {"models": [...]}
|
||||||
|
# Determine validation based on keys
|
||||||
|
if 'custom_nodes' in data and isinstance(data['custom_nodes'], list):
|
||||||
|
required_fields = REQUIRED_FIELDS_CUSTOM_NODE
|
||||||
|
entries_to_validate = data['custom_nodes']
|
||||||
|
elif 'models' in data and isinstance(data['models'], list):
|
||||||
|
required_fields = REQUIRED_FIELDS_MODEL
|
||||||
|
entries_to_validate = data['models']
|
||||||
|
else:
|
||||||
|
# Other JSON structures (extension-node-map.json, etc.) - just validate JSON syntax
|
||||||
|
return True, []
|
||||||
|
else:
|
||||||
|
return False, ["JSON root must be either an array or an object containing arrays"]
|
||||||
|
|
||||||
|
# Validate each entry
|
||||||
|
for idx, entry in enumerate(entries_to_validate, start=1):
|
||||||
|
if not isinstance(entry, dict):
|
||||||
|
# Show actual value for type errors
|
||||||
|
entry_str = json.dumps(entry, ensure_ascii=False) if not isinstance(entry, str) else repr(entry)
|
||||||
|
if len(entry_str) > 150:
|
||||||
|
entry_str = entry_str[:150] + "..."
|
||||||
|
errors.append(f"\n❌ Entry #{idx}: Must be an object, got {type(entry).__name__}")
|
||||||
|
errors.append(f" Actual value: {entry_str}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
entry_errors = validate_entry(entry, idx, required_fields)
|
||||||
|
if entry_errors:
|
||||||
|
# Group errors by entry with context
|
||||||
|
context = get_entry_context(entry)
|
||||||
|
errors.append(f"\n❌ Entry #{idx} ({context}):")
|
||||||
|
for error in entry_errors:
|
||||||
|
errors.append(f" - {error}")
|
||||||
|
|
||||||
|
is_valid = len(errors) == 0
|
||||||
|
return is_valid, errors
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="JSON File Syntax Checker")
|
"""Main entry point"""
|
||||||
parser.add_argument("file_path", type=str, help="Path to the JSON file for syntax checking")
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: python json-checker.py <json-file>")
|
||||||
|
print("\nValidates JSON entries based on content:")
|
||||||
|
print(" - {\"custom_nodes\": [...]}: Validates required fields (author, title, reference, files, install_type, description)")
|
||||||
|
print(" - {\"models\": [...]}: Validates JSON syntax only (no required fields)")
|
||||||
|
print(" - Other JSON structures: Validates JSON syntax only")
|
||||||
|
print("\nGit repo URL validation (for custom_nodes):")
|
||||||
|
print(" - URLs must NOT end with .git")
|
||||||
|
print(" - URLs must follow: https://github.com/{author}/{reponame}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
args = parser.parse_args()
|
file_path = sys.argv[1]
|
||||||
check_json_syntax(args.file_path)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
is_valid, errors = validate_json_file(file_path)
|
||||||
|
|
||||||
|
if is_valid:
|
||||||
|
print(f"✅ {file_path}: Validation passed")
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
print(f"Validating: {file_path}")
|
||||||
|
print("=" * 60)
|
||||||
|
print("❌ Validation failed!\n")
|
||||||
|
print("Errors:")
|
||||||
|
# Count actual errors (lines starting with " -")
|
||||||
|
error_count = sum(1 for e in errors if e.strip().startswith('-'))
|
||||||
|
for error in errors:
|
||||||
|
# Don't add ❌ prefix to grouped entries (they already have it)
|
||||||
|
if error.strip().startswith('❌'):
|
||||||
|
print(error)
|
||||||
|
else:
|
||||||
|
print(error)
|
||||||
|
print(f"\nTotal errors: {error_count}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
412
model-list.json
412
model-list.json
@@ -5045,6 +5045,105 @@
|
|||||||
"size": "1.26GB"
|
"size": "1.26GB"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 i2v high noise 14B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for i2v high noise 14B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_i2v_high_noise_14B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_i2v_high_noise_14B_fp16.safetensors",
|
||||||
|
"size": "28.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 i2v high noise 14B (fp8_scaled)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for i2v high noise 14B (fp8_scaled)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_i2v_high_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_i2v_high_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"size": "14.3GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 i2v low noise 14B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for i2v low noise 14B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_i2v_low_noise_14B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_i2v_low_noise_14B_fp16.safetensors",
|
||||||
|
"size": "28.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 i2v low noise 14B (fp8_scaled)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for i2v low noise 14B (fp8_scaled)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_i2v_low_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_i2v_low_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"size": "14.3GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 t2v high noise 14B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for t2v high noise 14B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_t2v_high_noise_14B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_t2v_high_noise_14B_fp16.safetensors",
|
||||||
|
"size": "28.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 t2v high noise 14B (fp8_scaled)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for t2v high noise 14B (fp8_scaled)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_t2v_high_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_t2v_high_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"size": "14.3GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 t2v low noise 14B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for t2v low noise 14B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_t2v_low_noise_14B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_t2v_low_noise_14B_fp16.safetensors",
|
||||||
|
"size": "28.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 t2v low noise 14B (fp8_scaled)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for t2v low noise 14B (fp8_scaled)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_t2v_low_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_t2v_low_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"size": "14.3GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 ti2v 5B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for ti2v 5B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_ti2v_5B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_ti2v_5B_fp16.safetensors",
|
||||||
|
"size": "10.0GB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Comfy-Org/umt5_xxl_fp16.safetensors",
|
"name": "Comfy-Org/umt5_xxl_fp16.safetensors",
|
||||||
@@ -5256,6 +5355,317 @@
|
|||||||
"filename": "LBM_relighting.safetensors",
|
"filename": "LBM_relighting.safetensors",
|
||||||
"url": "https://huggingface.co/jasperai/LBM_relighting/resolve/main/model.safetensors",
|
"url": "https://huggingface.co/jasperai/LBM_relighting/resolve/main/model.safetensors",
|
||||||
"size": "5.02GB"
|
"size": "5.02GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "vae/qwen-image",
|
||||||
|
"description": "VAE model for Qwen-Image",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI",
|
||||||
|
"filename": "qwen_image_vae.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/vae/qwen_image_vae.safetensors",
|
||||||
|
"size": "335MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen 2.5 VL 7B Text Encoder (fp8_scaled)",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "Qwen-2.5-VL",
|
||||||
|
"save_path": "text_encoders/qwen",
|
||||||
|
"description": "Qwen 2.5 VL 7B text encoder model (fp8_scaled)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI",
|
||||||
|
"filename": "qwen_2.5_vl_7b_fp8_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors",
|
||||||
|
"size": "3.75GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen 2.5 VL 7B Text Encoder",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "Qwen-2.5-VL",
|
||||||
|
"save_path": "text_encoders/qwen",
|
||||||
|
"description": "Qwen 2.5 VL 7B text encoder model",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI",
|
||||||
|
"filename": "qwen_2.5_vl_7b.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/text_encoders/qwen_2.5_vl_7b.safetensors",
|
||||||
|
"size": "7.51GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image Diffusion Model (fp8_e4m3fn)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "diffusion_models/qwen-image",
|
||||||
|
"description": "Qwen-Image diffusion model (fp8_e4m3fn)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI",
|
||||||
|
"filename": "qwen_image_fp8_e4m3fn.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_fp8_e4m3fn.safetensors",
|
||||||
|
"size": "4.89GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image Diffusion Model (bf16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "diffusion_models/qwen-image",
|
||||||
|
"description": "Qwen-Image diffusion model (bf16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI",
|
||||||
|
"filename": "qwen_image_bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_bf16.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit 2509 Diffusion Model (fp8_e4m3fn)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "diffusion_models/qwen-image-edit",
|
||||||
|
"description": "Qwen-Image-Edit 2509 diffusion model (fp8_e4m3fn)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI",
|
||||||
|
"filename": "qwen_image_edit_2509_fp8_e4m3fn.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_edit_2509_fp8_e4m3fn.safetensors",
|
||||||
|
"size": "4.89GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit 2509 Diffusion Model (bf16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "diffusion_models/qwen-image-edit",
|
||||||
|
"description": "Qwen-Image-Edit 2509 diffusion model (bf16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI",
|
||||||
|
"filename": "qwen_image_edit_2509_bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_edit_2509_bf16.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit Diffusion Model (fp8_e4m3fn)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "diffusion_models/qwen-image-edit",
|
||||||
|
"description": "Qwen-Image-Edit diffusion model (fp8_e4m3fn)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI",
|
||||||
|
"filename": "qwen_image_edit_fp8_e4m3fn.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_edit_fp8_e4m3fn.safetensors",
|
||||||
|
"size": "4.89GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit Diffusion Model (bf16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "diffusion_models/qwen-image-edit",
|
||||||
|
"description": "Qwen-Image-Edit diffusion model (bf16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI",
|
||||||
|
"filename": "qwen_image_edit_bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_edit_bf16.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Lightning 8steps V1.0",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "loras/qwen-image-lightning",
|
||||||
|
"description": "Qwen-Image-Lightning 8-step LoRA model V1.0",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Lightning-8steps-V1.0.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-8steps-V1.0.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Lightning 4steps V1.0",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "loras/qwen-image-lightning",
|
||||||
|
"description": "Qwen-Image-Lightning 4-step LoRA model V1.0",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Lightning-4steps-V1.0.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-4steps-V1.0.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Lightning 4steps V1.0 (bf16)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "loras/qwen-image-lightning",
|
||||||
|
"description": "Qwen-Image-Lightning 4-step LoRA model V1.0 (bf16)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Lightning-4steps-V1.0-bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-4steps-V1.0-bf16.safetensors",
|
||||||
|
"size": "19.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Lightning 4steps V2.0",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "loras/qwen-image-lightning",
|
||||||
|
"description": "Qwen-Image-Lightning 4-step LoRA model V2.0",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Lightning-4steps-V2.0.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-4steps-V2.0.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Lightning 4steps V2.0 (bf16)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "loras/qwen-image-lightning",
|
||||||
|
"description": "Qwen-Image-Lightning 4-step LoRA model V2.0 (bf16)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Lightning-4steps-V2.0-bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-4steps-V2.0-bf16.safetensors",
|
||||||
|
"size": "19.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Lightning 8steps V1.1",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "loras/qwen-image-lightning",
|
||||||
|
"description": "Qwen-Image-Lightning 8-step LoRA model V1.1",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Lightning-8steps-V1.1.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-8steps-V1.1.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Lightning 8steps V1.1 (bf16)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "loras/qwen-image-lightning",
|
||||||
|
"description": "Qwen-Image-Lightning 8-step LoRA model V1.1 (bf16)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Lightning-8steps-V1.1-bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-8steps-V1.1-bf16.safetensors",
|
||||||
|
"size": "19.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Lightning 8steps V2.0",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "loras/qwen-image-lightning",
|
||||||
|
"description": "Qwen-Image-Lightning 8-step LoRA model V2.0",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Lightning-8steps-V2.0.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-8steps-V2.0.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Lightning 8steps V2.0 (bf16)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "loras/qwen-image-lightning",
|
||||||
|
"description": "Qwen-Image-Lightning 8-step LoRA model V2.0 (bf16)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Lightning-8steps-V2.0-bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-8steps-V2.0-bf16.safetensors",
|
||||||
|
"size": "19.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit-Lightning 4steps V1.0",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "loras/qwen-image-edit-lightning",
|
||||||
|
"description": "Qwen-Image-Edit-Lightning 4-step LoRA model V1.0",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Edit-Lightning-4steps-V1.0.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Edit-Lightning-4steps-V1.0.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit-Lightning 4steps V1.0 (bf16)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "loras/qwen-image-edit-lightning",
|
||||||
|
"description": "Qwen-Image-Edit-Lightning 4-step LoRA model V1.0 (bf16)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Edit-Lightning-4steps-V1.0-bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Edit-Lightning-4steps-V1.0-bf16.safetensors",
|
||||||
|
"size": "19.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit-Lightning 8steps V1.0",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "loras/qwen-image-edit-lightning",
|
||||||
|
"description": "Qwen-Image-Edit-Lightning 8-step LoRA model V1.0",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Edit-Lightning-8steps-V1.0.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Edit-Lightning-8steps-V1.0.safetensors",
|
||||||
|
"size": "9.78GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit-Lightning 8steps V1.0 (bf16)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "loras/qwen-image-edit-lightning",
|
||||||
|
"description": "Qwen-Image-Edit-Lightning 8-step LoRA model V1.0 (bf16)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Edit-Lightning-8steps-V1.0-bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Edit-Lightning-8steps-V1.0-bf16.safetensors",
|
||||||
|
"size": "19.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit-2509-Lightning 4steps V1.0 (bf16)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "loras/qwen-image-edit-lightning",
|
||||||
|
"description": "Qwen-Image-Edit-2509-Lightning 4-step LoRA model V1.0 (bf16)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Edit-2509-Lightning-4steps-V1.0-bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Edit-2509/Qwen-Image-Edit-2509-Lightning-4steps-V1.0-bf16.safetensors",
|
||||||
|
"size": "19.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit-2509-Lightning 4steps V1.0 (fp32)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "loras/qwen-image-edit-lightning",
|
||||||
|
"description": "Qwen-Image-Edit-2509-Lightning 4-step LoRA model V1.0 (fp32)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Edit-2509-Lightning-4steps-V1.0-fp32.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Edit-2509/Qwen-Image-Edit-2509-Lightning-4steps-V1.0-fp32.safetensors",
|
||||||
|
"size": "39.1GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit-2509-Lightning 8steps V1.0 (bf16)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "loras/qwen-image-edit-lightning",
|
||||||
|
"description": "Qwen-Image-Edit-2509-Lightning 8-step LoRA model V1.0 (bf16)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Edit-2509-Lightning-8steps-V1.0-bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Edit-2509/Qwen-Image-Edit-2509-Lightning-8steps-V1.0-bf16.safetensors",
|
||||||
|
"size": "19.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image-Edit-2509-Lightning 8steps V1.0 (fp32)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "Qwen-Image-Edit",
|
||||||
|
"save_path": "loras/qwen-image-edit-lightning",
|
||||||
|
"description": "Qwen-Image-Edit-2509-Lightning 8-step LoRA model V1.0 (fp32)",
|
||||||
|
"reference": "https://huggingface.co/lightx2v/Qwen-Image-Lightning",
|
||||||
|
"filename": "Qwen-Image-Edit-2509-Lightning-8steps-V1.0-fp32.safetensors",
|
||||||
|
"url": "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Edit-2509/Qwen-Image-Edit-2509-Lightning-8steps-V1.0-fp32.safetensors",
|
||||||
|
"size": "39.1GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image InstantX ControlNet Union",
|
||||||
|
"type": "controlnet",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "controlnet/qwen-image/instantx",
|
||||||
|
"description": "Qwen-Image InstantX ControlNet Union model",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image-InstantX-ControlNets",
|
||||||
|
"filename": "Qwen-Image-InstantX-ControlNet-Union.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image-InstantX-ControlNets/resolve/main/split_files/controlnet/Qwen-Image-InstantX-ControlNet-Union.safetensors",
|
||||||
|
"size": "2.54GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Qwen-Image InstantX ControlNet Inpainting",
|
||||||
|
"type": "controlnet",
|
||||||
|
"base": "Qwen-Image",
|
||||||
|
"save_path": "controlnet/qwen-image/instantx",
|
||||||
|
"description": "Qwen-Image InstantX ControlNet Inpainting model",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Qwen-Image-InstantX-ControlNets",
|
||||||
|
"filename": "Qwen-Image-InstantX-ControlNet-Inpainting.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Qwen-Image-InstantX-ControlNets/resolve/main/split_files/controlnet/Qwen-Image-InstantX-ControlNet-Inpainting.safetensors",
|
||||||
|
"size": "2.54GB"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
rm ~/.tmp/dev/*.py > /dev/null 2>&1
|
rm ~/.tmp/dev/*.py > /dev/null 2>&1
|
||||||
python ../../scanner.py ~/.tmp/dev
|
python ../../scanner.py ~/.tmp/dev $*
|
||||||
@@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"custom_nodes": [
|
"custom_nodes": [
|
||||||
|
{
|
||||||
|
"author": "synchronicity-labs",
|
||||||
|
"title": "ComfyUI Sync Lipsync Node",
|
||||||
|
"reference": "https://github.com/synchronicity-labs/sync-comfyui",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/synchronicity-labs/sync-comfyui"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This custom node allows you to perform audio-video lip synchronization inside ComfyUI using a simple interface."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"author": "joaomede",
|
"author": "joaomede",
|
||||||
"title": "ComfyUI-Unload-Model-Fork",
|
"title": "ComfyUI-Unload-Model-Fork",
|
||||||
@@ -159,6 +169,16 @@
|
|||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "A fork of KJNodes for ComfyUI.\nVarious quality of life -nodes for ComfyUI, mostly just visual stuff to improve usability"
|
"description": "A fork of KJNodes for ComfyUI.\nVarious quality of life -nodes for ComfyUI, mostly just visual stuff to improve usability"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "huixingyun",
|
||||||
|
"title": "ComfyUI-SoundFlow",
|
||||||
|
"reference": "https://github.com/huixingyun/ComfyUI-SoundFlow",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/huixingyun/ComfyUI-SoundFlow"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "forked from https://github.com/fredconex/ComfyUI-SoundFlow (removed)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,106 @@
|
|||||||
{
|
{
|
||||||
"models": [
|
"models": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 i2v high noise 14B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for i2v high noise 14B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_i2v_high_noise_14B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_i2v_high_noise_14B_fp16.safetensors",
|
||||||
|
"size": "28.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 i2v high noise 14B (fp8_scaled)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for i2v high noise 14B (fp8_scaled)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_i2v_high_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_i2v_high_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"size": "14.3GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 i2v low noise 14B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for i2v low noise 14B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_i2v_low_noise_14B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_i2v_low_noise_14B_fp16.safetensors",
|
||||||
|
"size": "28.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 i2v low noise 14B (fp8_scaled)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for i2v low noise 14B (fp8_scaled)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_i2v_low_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_i2v_low_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"size": "14.3GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 t2v high noise 14B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for t2v high noise 14B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_t2v_high_noise_14B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_t2v_high_noise_14B_fp16.safetensors",
|
||||||
|
"size": "28.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 t2v high noise 14B (fp8_scaled)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for t2v high noise 14B (fp8_scaled)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_t2v_high_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_t2v_high_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"size": "14.3GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 t2v low noise 14B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for t2v low noise 14B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_t2v_low_noise_14B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_t2v_low_noise_14B_fp16.safetensors",
|
||||||
|
"size": "28.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 t2v low noise 14B (fp8_scaled)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for t2v low noise 14B (fp8_scaled)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_t2v_low_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_t2v_low_noise_14B_fp8_scaled.safetensors",
|
||||||
|
"size": "14.3GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/Wan2.2 ti2v 5B (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Wan2.2",
|
||||||
|
"save_path": "diffusion_models/Wan2.2",
|
||||||
|
"description": "Wan2.2 diffusion model for ti2v 5B (fp16)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged",
|
||||||
|
"filename": "wan2.2_ti2v_5B_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/resolve/main/split_files/diffusion_models/wan2.2_ti2v_5B_fp16.safetensors",
|
||||||
|
"size": "10.0GB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "sam2.1_hiera_tiny.pt",
|
"name": "sam2.1_hiera_tiny.pt",
|
||||||
"type": "sam2.1",
|
"type": "sam2.1",
|
||||||
@@ -586,109 +687,6 @@
|
|||||||
"filename": "llava_llama3_fp16.safetensors",
|
"filename": "llava_llama3_fp16.safetensors",
|
||||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/text_encoders/llava_llama3_fp16.safetensors",
|
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/text_encoders/llava_llama3_fp16.safetensors",
|
||||||
"size": "16.1GB"
|
"size": "16.1GB"
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "PixArt-Sigma-XL-2-512-MS.safetensors (diffusion)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "pixart-sigma",
|
|
||||||
"save_path": "diffusion_models/PixArt-Sigma",
|
|
||||||
"description": "PixArt-Sigma Diffusion model",
|
|
||||||
"reference": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-512-MS",
|
|
||||||
"filename": "PixArt-Sigma-XL-2-512-MS.safetensors",
|
|
||||||
"url": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-512-MS/resolve/main/transformer/diffusion_pytorch_model.safetensors",
|
|
||||||
"size": "2.44GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "PixArt-Sigma-XL-2-1024-MS.safetensors (diffusion)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "pixart-sigma",
|
|
||||||
"save_path": "diffusion_models/PixArt-Sigma",
|
|
||||||
"description": "PixArt-Sigma Diffusion model",
|
|
||||||
"reference": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-1024-MS",
|
|
||||||
"filename": "PixArt-Sigma-XL-2-1024-MS.safetensors",
|
|
||||||
"url": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-1024-MS/resolve/main/transformer/diffusion_pytorch_model.safetensors",
|
|
||||||
"size": "2.44GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "PixArt-XL-2-1024-MS.safetensors (diffusion)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "pixart-alpha",
|
|
||||||
"save_path": "diffusion_models/PixArt-Alpha",
|
|
||||||
"description": "PixArt-Alpha Diffusion model",
|
|
||||||
"reference": "https://huggingface.co/PixArt-alpha/PixArt-XL-2-1024-MS",
|
|
||||||
"filename": "PixArt-XL-2-1024-MS.safetensors",
|
|
||||||
"url": "https://huggingface.co/PixArt-alpha/PixArt-XL-2-1024-MS/resolve/main/transformer/diffusion_pytorch_model.safetensors",
|
|
||||||
"size": "2.45GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/hunyuan_video_t2v_720p_bf16.safetensors",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Hunyuan Video",
|
|
||||||
"save_path": "diffusion_models/hunyuan_video",
|
|
||||||
"description": "Huyuan Video diffusion model. repackaged version.",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
|
||||||
"filename": "hunyuan_video_t2v_720p_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/diffusion_models/hunyuan_video_t2v_720p_bf16.safetensors",
|
|
||||||
"size": "25.6GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/hunyuan_video_vae_bf16.safetensors",
|
|
||||||
"type": "VAE",
|
|
||||||
"base": "Hunyuan Video",
|
|
||||||
"save_path": "VAE",
|
|
||||||
"description": "Huyuan Video VAE model. repackaged version.",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
|
||||||
"filename": "hunyuan_video_vae_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/vae/hunyuan_video_vae_bf16.safetensors",
|
|
||||||
"size": "493MB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 2B v0.9.1 Checkpoint",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "LTX-Video is the first DiT-based video generation model capable of generating high-quality videos in real-time. It produces 24 FPS videos at a 768x512 resolution faster than they can be watched. Trained on a large-scale dataset of diverse videos, the model generates high-resolution videos with realistic and varied content.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltx-video-2b-v0.9.1.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.1.safetensors",
|
|
||||||
"size": "5.72GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "XLabs-AI/flux-canny-controlnet-v3.safetensors",
|
|
||||||
"type": "controlnet",
|
|
||||||
"base": "FLUX.1",
|
|
||||||
"save_path": "xlabs/controlnets",
|
|
||||||
"description": "ControlNet checkpoints for FLUX.1-dev model by Black Forest Labs.",
|
|
||||||
"reference": "https://huggingface.co/XLabs-AI/flux-controlnet-collections",
|
|
||||||
"filename": "flux-canny-controlnet-v3.safetensors",
|
|
||||||
"url": "https://huggingface.co/XLabs-AI/flux-controlnet-collections/resolve/main/flux-canny-controlnet-v3.safetensors",
|
|
||||||
"size": "1.49GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "XLabs-AI/flux-depth-controlnet-v3.safetensors",
|
|
||||||
"type": "controlnet",
|
|
||||||
"base": "FLUX.1",
|
|
||||||
"save_path": "xlabs/controlnets",
|
|
||||||
"description": "ControlNet checkpoints for FLUX.1-dev model by Black Forest Labs.",
|
|
||||||
"reference": "https://huggingface.co/XLabs-AI/flux-controlnet-collections",
|
|
||||||
"filename": "flux-depth-controlnet-v3.safetensors",
|
|
||||||
"url": "https://huggingface.co/XLabs-AI/flux-controlnet-collections/resolve/main/flux-depth-controlnet-v3.safetensors",
|
|
||||||
"size": "1.49GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "XLabs-AI/flux-hed-controlnet-v3.safetensors",
|
|
||||||
"type": "controlnet",
|
|
||||||
"base": "FLUX.1",
|
|
||||||
"save_path": "xlabs/controlnets",
|
|
||||||
"description": "ControlNet checkpoints for FLUX.1-dev model by Black Forest Labs.",
|
|
||||||
"reference": "https://huggingface.co/XLabs-AI/flux-controlnet-collections",
|
|
||||||
"filename": "flux-hed-controlnet-v3.safetensors",
|
|
||||||
"url": "https://huggingface.co/XLabs-AI/flux-controlnet-collections/resolve/main/flux-hed-controlnet-v3.safetensors",
|
|
||||||
"size": "1.49GB"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,16 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "A minimal template for creating React/TypeScript frontend extensions for ComfyUI, with complete boilerplate setup including internationalization and unit testing."
|
"description": "A minimal template for creating React/TypeScript frontend extensions for ComfyUI, with complete boilerplate setup including internationalization and unit testing."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"author": "comfyui-wiki",
|
||||||
|
"title": "ComfyUI-i18n-demo",
|
||||||
|
"reference": "https://github.com/comfyui-wiki/ComfyUI-i18n-demo",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/comfyui-wiki/ComfyUI-i18n-demo"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI custom node develop i18n support demo "
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"author": "Suzie1",
|
"author": "Suzie1",
|
||||||
"title": "Guide To Making Custom Nodes in ComfyUI",
|
"title": "Guide To Making Custom Nodes in ComfyUI",
|
||||||
@@ -341,6 +351,26 @@
|
|||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "A minimal test suite demonstrating how remote COMBO inputs behave in ComfyUI, with and without force_input"
|
"description": "A minimal test suite demonstrating how remote COMBO inputs behave in ComfyUI, with and without force_input"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "J1mB091",
|
||||||
|
"title": "ComfyUI-J1mB091 Custom Nodes",
|
||||||
|
"reference": "https://github.com/J1mB091/ComfyUI-J1mB091",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/J1mB091/ComfyUI-J1mB091"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Vibe Coded ComfyUI Custom Nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "aiforhumans",
|
||||||
|
"title": "XDev Nodes - Complete Toolkit",
|
||||||
|
"reference": "https://github.com/aiforhumans/comfyui-xdev-nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/aiforhumans/comfyui-xdev-nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Complete ComfyUI development toolkit with 8 professional nodes including VAE tools, universal type testing, and comprehensive debugging infrastructure."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
57
openapi.yaml
57
openapi.yaml
@@ -104,6 +104,38 @@ components:
|
|||||||
type: boolean
|
type: boolean
|
||||||
description: Whether the queue is currently processing
|
description: Whether the queue is currently processing
|
||||||
|
|
||||||
|
ImportFailInfoBulkRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
cnr_ids:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: A list of CNR IDs to check.
|
||||||
|
urls:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: A list of repository URLs to check.
|
||||||
|
|
||||||
|
ImportFailInfoBulkResponse:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
$ref: '#/components/schemas/ImportFailInfoItem'
|
||||||
|
description: >-
|
||||||
|
A dictionary where each key is a cnr_id or url from the request,
|
||||||
|
and the value is the corresponding error info.
|
||||||
|
|
||||||
|
ImportFailInfoItem:
|
||||||
|
oneOf:
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
traceback:
|
||||||
|
type: string
|
||||||
|
- type: "null"
|
||||||
|
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
securityLevel:
|
securityLevel:
|
||||||
type: apiKey
|
type: apiKey
|
||||||
@@ -306,6 +338,31 @@ paths:
|
|||||||
'400':
|
'400':
|
||||||
description: No information available
|
description: No information available
|
||||||
|
|
||||||
|
/v2/customnode/import_fail_info_bulk:
|
||||||
|
post:
|
||||||
|
summary: Get import failure info for multiple nodes
|
||||||
|
description: Retrieves recorded import failure information for a list of custom nodes.
|
||||||
|
tags:
|
||||||
|
- customnode
|
||||||
|
requestBody:
|
||||||
|
description: A list of CNR IDs or repository URLs to check.
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ImportFailInfoBulkRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A dictionary containing the import failure information.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ImportFailInfoBulkResponse'
|
||||||
|
'400':
|
||||||
|
description: Bad Request. The request body is invalid.
|
||||||
|
'500':
|
||||||
|
description: Internal Server Error.
|
||||||
|
|
||||||
/customnode/install/git_url:
|
/customnode/install/git_url:
|
||||||
post:
|
post:
|
||||||
summary: Install custom node via Git URL
|
summary: Install custom node via Git URL
|
||||||
|
|||||||
@@ -85,7 +85,15 @@ cm_global.register_api('cm.is_import_failed_extension', is_import_failed_extensi
|
|||||||
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
custom_nodes_base_path = folder_paths.get_folder_paths('custom_nodes')[0]
|
custom_nodes_base_path = folder_paths.get_folder_paths('custom_nodes')[0]
|
||||||
manager_files_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), 'default', 'ComfyUI-Manager'))
|
|
||||||
|
# Check for System User API availability (PR #10966)
|
||||||
|
_has_system_user_api = hasattr(folder_paths, 'get_system_user_directory')
|
||||||
|
|
||||||
|
if _has_system_user_api:
|
||||||
|
manager_files_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), '__manager'))
|
||||||
|
else:
|
||||||
|
manager_files_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), 'default', 'ComfyUI-Manager'))
|
||||||
|
|
||||||
manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json")
|
manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json")
|
||||||
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
||||||
restore_snapshot_path = os.path.join(manager_files_path, "startup-scripts", "restore-snapshot.json")
|
restore_snapshot_path = os.path.join(manager_files_path, "startup-scripts", "restore-snapshot.json")
|
||||||
@@ -516,7 +524,8 @@ check_bypass_ssl()
|
|||||||
|
|
||||||
# Perform install
|
# Perform install
|
||||||
processed_install = set()
|
processed_install = set()
|
||||||
script_list_path = os.path.join(folder_paths.user_directory, "default", "ComfyUI-Manager", "startup-scripts", "install-scripts.txt")
|
# Use manager_files_path for consistency (fixes path inconsistency bug)
|
||||||
|
script_list_path = os.path.join(manager_files_path, "startup-scripts", "install-scripts.txt")
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
|
||||||
|
|
||||||
|
|
||||||
@@ -793,7 +802,11 @@ def execute_startup_script():
|
|||||||
|
|
||||||
|
|
||||||
# Check if script_list_path exists
|
# Check if script_list_path exists
|
||||||
if os.path.exists(script_list_path):
|
# Block startup-scripts on old ComfyUI (security measure)
|
||||||
|
if not _has_system_user_api:
|
||||||
|
if os.path.exists(script_list_path):
|
||||||
|
print("[ComfyUI-Manager] Startup scripts blocked on old ComfyUI version.")
|
||||||
|
elif os.path.exists(script_list_path):
|
||||||
execute_startup_script()
|
execute_startup_script()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-manager"
|
name = "comfyui-manager"
|
||||||
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
||||||
version = "3.35"
|
version = "3.38.3"
|
||||||
license = { file = "LICENSE.txt" }
|
license = { file = "LICENSE.txt" }
|
||||||
dependencies = ["GitPython", "PyGithub", "matrix-nio", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
|
dependencies = ["GitPython", "PyGithub", "matrix-nio", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ GitPython
|
|||||||
PyGithub
|
PyGithub
|
||||||
matrix-nio
|
matrix-nio
|
||||||
transformers
|
transformers
|
||||||
huggingface-hub>0.20
|
huggingface-hub
|
||||||
typer
|
typer
|
||||||
rich
|
rich
|
||||||
typing-extensions
|
typing-extensions
|
||||||
|
|||||||
1026
scanner.py
1026
scanner.py
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user