Compare commits
431 Commits
feat/add-t
...
draft-v4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7799964de | ||
|
|
5378f0a8e9 | ||
|
|
e13bf68775 | ||
|
|
d970fe68ea | ||
|
|
63ba5fed09 | ||
|
|
98a8464933 | ||
|
|
7e3e6726e0 | ||
|
|
09567b2bb2 | ||
|
|
f3bd116184 | ||
|
|
7509737563 | ||
|
|
cfb815d879 | ||
|
|
44241fb967 | ||
|
|
c4b45129bd | ||
|
|
70741008ca | ||
|
|
6c2d2cae2a | ||
|
|
28f13d3311 | ||
|
|
4e31aaa8fb | ||
|
|
ba99f0c2cc | ||
|
|
e0a96b4937 | ||
|
|
82c055f527 | ||
|
|
f94008192c | ||
|
|
3895d5279e | ||
|
|
41be94690f | ||
|
|
3d85ecc525 | ||
|
|
7da00796e5 | ||
|
|
6086419cb6 | ||
|
|
5bc1f2f2c0 | ||
|
|
32a83b211e | ||
|
|
bead7b3a7f | ||
|
|
815d6d6572 | ||
|
|
fbecbee4c3 | ||
|
|
b9a7d2a78c | ||
|
|
95ce812992 | ||
|
|
9a36f4748c | ||
|
|
50b7849a35 | ||
|
|
6f1245b27c | ||
|
|
cc87ed3899 | ||
|
|
1d9037fefe | ||
|
|
03016e2d16 | ||
|
|
bdfb70a58a | ||
|
|
3d41617f4e | ||
|
|
35151ffdd1 | ||
|
|
4527d41a7a | ||
|
|
553cba12f3 | ||
|
|
00fb9c88e1 | ||
|
|
116e068ac3 | ||
|
|
1010dd2d28 | ||
|
|
68bc8302fd | ||
|
|
596dad5cda | ||
|
|
a924c280fb | ||
|
|
7354242906 | ||
|
|
3d0bcf5979 | ||
|
|
e7d0b158e9 | ||
|
|
10ff90787c | ||
|
|
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 | ||
|
|
648d7e73c6 | ||
|
|
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 | ||
|
|
f599bc22d7 | ||
|
|
281319d2da | ||
|
|
5cb203685c | ||
|
|
300c6e7406 | ||
|
|
9c4d6a0773 | ||
|
|
01fa37900b | ||
|
|
edbe744e17 | ||
|
|
2a32a1a4a8 | ||
|
|
404bdb21e6 | ||
|
|
b260c9a512 | ||
|
|
4b941adb6a | ||
|
|
bd752550a8 | ||
|
|
b8b71bb961 | ||
|
|
5aaf7a4092 | ||
|
|
030e02ffb8 | ||
|
|
60746c6253 | ||
|
|
d962aa03f4 | ||
|
|
121a5a1888 | ||
|
|
9e4a2aae43 | ||
|
|
ee6eb685e7 | ||
|
|
09a38a32ce | ||
|
|
d13b19d43d | ||
|
|
5316ec1b4d | ||
|
|
e730dca1ad | ||
|
|
8da30640bb | ||
|
|
6f4eb88e07 | ||
|
|
d9592b9dab | ||
|
|
b87ada72aa | ||
|
|
83363ba1f0 | ||
|
|
a2a7349ce4 | ||
|
|
23ebe7f718 | ||
|
|
e04264cfa3 | ||
|
|
8d29e5037f | ||
|
|
6926ed45b0 | ||
|
|
736b85b8bb | ||
|
|
9e3361bc31 | ||
|
|
6e10381020 | ||
|
|
a1d37d379c | ||
|
|
07d87db7a2 | ||
|
|
4e556673d2 | ||
|
|
f421304fc1 | ||
|
|
6867616973 | ||
|
|
c9271b1686 | ||
|
|
12eb6863da | ||
|
|
4834874091 | ||
|
|
8759ebf200 | ||
|
|
d4715aebef | ||
|
|
0fe2ade7bb | ||
|
|
0c71565535 | ||
|
|
cf8029ecd4 | ||
|
|
6a637091a2 | ||
|
|
31eba60012 | ||
|
|
51e58e9078 | ||
|
|
4a1e76730a | ||
|
|
5599bb028b | ||
|
|
552c6da0cc | ||
|
|
cc6817a891 | ||
|
|
fb48d1b485 | ||
|
|
1c336dad6b | ||
|
|
a4940d46cd | ||
|
|
499b2f44c1 | ||
|
|
2b200c9281 | ||
|
|
36a900c98f | ||
|
|
5236b03f66 | ||
|
|
8be35e3621 | ||
|
|
509f00fe89 | ||
|
|
a98b87f148 | ||
|
|
ae9b2b3b72 | ||
|
|
02e1ec0ae3 | ||
|
|
daefb0f120 | ||
|
|
ff0604e3b6 | ||
|
|
20e41e22fa | ||
|
|
59264c1fd9 | ||
|
|
a0e3bdd594 | ||
|
|
6580aaf3ad | ||
|
|
0b46701b60 | ||
|
|
0bb4effede | ||
|
|
b07082a52d | ||
|
|
04f267f5a7 | ||
|
|
03ccce2804 | ||
|
|
e894bd9f24 | ||
|
|
10e6988273 | ||
|
|
905b61e5d8 | ||
|
|
ee69d393ae | ||
|
|
cab39973ae | ||
|
|
d93f5d07bb | ||
|
|
ba00ffe1ae | ||
|
|
6afaf5eaf5 | ||
|
|
d30459cc34 | ||
|
|
e92fbb7b1b | ||
|
|
42d464b532 | ||
|
|
c2e9e5c63a | ||
|
|
bc36726925 | ||
|
|
22725b0188 | ||
|
|
7abbff8c31 | ||
|
|
6236f4bcf4 | ||
|
|
3c3e80f77f | ||
|
|
4aae2fb289 | ||
|
|
66ff07752f | ||
|
|
5cf92f2742 | ||
|
|
6d3fddc474 | ||
|
|
66d4ad6174 | ||
|
|
2a366a1607 | ||
|
|
d87a0995b4 | ||
|
|
9a73a41e04 | ||
|
|
ba041b36bc | ||
|
|
f5f9de69b4 | ||
|
|
71e56c62e8 | ||
|
|
a0b0c2b963 | ||
|
|
0f496619fd | ||
|
|
5fdd6a441a | ||
|
|
00f287bb63 | ||
|
|
785268efa6 | ||
|
|
2c976d9394 | ||
|
|
1e32582642 | ||
|
|
6f8f6d07f5 | ||
|
|
3958111e76 | ||
|
|
86fcc4af74 | ||
|
|
2fd26756df | ||
|
|
478f4b74d8 | ||
|
|
73d0d2a1bb | ||
|
|
546db08ec4 | ||
|
|
0dd41a8670 | ||
|
|
82c0c89f46 | ||
|
|
f4ce0fd5f1 | ||
|
|
c3798bf4c2 | ||
|
|
ff80b6ccb0 | ||
|
|
e729217116 | ||
|
|
94c695daca | ||
|
|
9f189f0420 | ||
|
|
ad09e53f60 | ||
|
|
092a7a5f3f | ||
|
|
f45649bd25 | ||
|
|
2595cc5ed7 | ||
|
|
2f62190c6f | ||
|
|
577314984c | ||
|
|
f0346b955b | ||
|
|
70139ded4a | ||
|
|
bf379900e1 | ||
|
|
9bafc90f5e | ||
|
|
fce0d9e88e | ||
|
|
2b3b154989 | ||
|
|
948d2440a1 | ||
|
|
5adbe1ce7a | ||
|
|
8157d34ffa | ||
|
|
3ec8cb2204 | ||
|
|
0daa826543 | ||
|
|
a66028da58 | ||
|
|
807c9e6872 | ||
|
|
e71f3774ba | ||
|
|
dd7314bf10 | ||
|
|
f33bc127dc | ||
|
|
db92b87782 | ||
|
|
eba41c8693 | ||
|
|
c855308162 | ||
|
|
73d971bed8 | ||
|
|
bcfe0c2874 | ||
|
|
931ff666ae | ||
|
|
18b6d86cc4 | ||
|
|
086040f858 | ||
|
|
adbeb527d6 | ||
|
|
043176168d | ||
|
|
3c5efa0662 | ||
|
|
9b739bcbbf | ||
|
|
db89076e48 | ||
|
|
19b341ef18 | ||
|
|
be3713b1a3 | ||
|
|
99c4415cfb | ||
|
|
7b311f2ccf | ||
|
|
4aeabfe0a7 | ||
|
|
431ed02194 | ||
|
|
07f587ed83 | ||
|
|
0408341d82 | ||
|
|
5b3c9432f3 | ||
|
|
4a197e63f9 | ||
|
|
ad79a2ef45 | ||
|
|
0876a12fe9 | ||
|
|
c43c7ecc03 | ||
|
|
4a6dee3044 | ||
|
|
019acdd840 | ||
|
|
1c98512720 | ||
|
|
43041cebed | ||
|
|
23a09ad546 | ||
|
|
0836e8fe7c | ||
|
|
90196af8f8 | ||
|
|
002e549a86 | ||
|
|
1de6f859bf | ||
|
|
566fe05772 | ||
|
|
18772c6292 | ||
|
|
6278bddc9b | ||
|
|
f74bf71735 | ||
|
|
efe9ed68b2 | ||
|
|
7c1e75865d | ||
|
|
89530fc4e7 | ||
|
|
a0aee41f1a | ||
|
|
2049dd75f4 | ||
|
|
0864c35ba9 | ||
|
|
92c9f66671 | ||
|
|
223d6dad51 | ||
|
|
815784e809 | ||
|
|
2795d00d1e | ||
|
|
86dd0b4963 | ||
|
|
77a4f4819f | ||
|
|
b63d603482 | ||
|
|
e569b4e613 | ||
|
|
8a70997546 | ||
|
|
80d0a0f882 | ||
|
|
70b3997874 | ||
|
|
e8e4311068 | ||
|
|
cb0fa5829d | ||
|
|
a66f86d4af | ||
|
|
35d98dcea8 | ||
|
|
38fefde06d | ||
|
|
75ecb31f8c | ||
|
|
77133375ad | ||
|
|
c58b93ff51 | ||
|
|
7d8ebfe91b | ||
|
|
810381eab2 | ||
|
|
61dc6cf2de | ||
|
|
0205ebad2a | ||
|
|
09a94133ac | ||
|
|
1eb3c3b219 | ||
|
|
457845bb51 | ||
|
|
0c11b46585 | ||
|
|
c35100d9e9 | ||
|
|
847031cb04 | ||
|
|
d1ca6288a3 | ||
|
|
624ad4cfe6 | ||
|
|
f8d87bb452 | ||
|
|
f60b3505e0 | ||
|
|
addefbc511 | ||
|
|
c4314b25a3 | ||
|
|
921bb86127 | ||
|
|
d912fb0f8b | ||
|
|
e8fc053a32 | ||
|
|
ce3b2bab39 | ||
|
|
15e3699535 | ||
|
|
a4bf6bddbf | ||
|
|
f1b3c6b735 | ||
|
|
e923434d08 | ||
|
|
ddc9cd0fd5 | ||
|
|
d081db0c30 | ||
|
|
14298b0859 | ||
|
|
03ecda3cfe | ||
|
|
350cb767c3 | ||
|
|
f450dcbb57 | ||
|
|
32e003965a | ||
|
|
65f0764338 | ||
|
|
1bdb026079 | ||
|
|
b3a7fb9c3e | ||
|
|
c143c81a7e | ||
|
|
dd389ba0f8 | ||
|
|
46b1649ab8 | ||
|
|
89710412e4 | ||
|
|
931973b632 | ||
|
|
60aaa838e3 | ||
|
|
7e51286313 | ||
|
|
1246538bbb | ||
|
|
80518abf9d | ||
|
|
fc1ae2a18e | ||
|
|
3fd8d2049c | ||
|
|
35a6bcf20c | ||
|
|
0d75fc331e | ||
|
|
0a23e793e3 | ||
|
|
2c1c03e063 | ||
|
|
64059d2949 | ||
|
|
648aa7c4d3 | ||
|
|
274bb81a08 | ||
|
|
e2c90b4681 | ||
|
|
fa0a98ac6e | ||
|
|
e6e7b42415 | ||
|
|
0b7ef2e1d4 | ||
|
|
2fac67a9f9 | ||
|
|
8b9892de2e | ||
|
|
b3290dc909 | ||
|
|
3e3176eddb | ||
|
|
b1ef84894a | ||
|
|
c6cffc92c4 | ||
|
|
efb9fd2712 | ||
|
|
94b294ff93 | ||
|
|
99a9e33648 | ||
|
|
055d94a919 | ||
|
|
0978005240 | ||
|
|
1f796581ec | ||
|
|
f3a1716dad | ||
|
|
a1c3a0db1f | ||
|
|
9f80cc8a6b | ||
|
|
133786846e | ||
|
|
bdf297a5c6 | ||
|
|
6767254eb0 | ||
|
|
691cebd479 | ||
|
|
f3932cbf29 | ||
|
|
3f73a97037 | ||
|
|
226f1f5be4 | ||
|
|
7e45c07660 | ||
|
|
0c815036b9 |
30
.github/workflows/publish-to-pypi.yml
vendored
30
.github/workflows/publish-to-pypi.yml
vendored
@@ -4,7 +4,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- draft-v4
|
||||||
paths:
|
paths:
|
||||||
- "pyproject.toml"
|
- "pyproject.toml"
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.x'
|
||||||
|
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -31,27 +31,27 @@ jobs:
|
|||||||
- name: Get current version
|
- name: Get current version
|
||||||
id: current_version
|
id: current_version
|
||||||
run: |
|
run: |
|
||||||
CURRENT_VERSION=$(grep -oP 'version = "\K[^"]+' pyproject.toml)
|
CURRENT_VERSION=$(grep -oP '^version = "\K[^"]+' pyproject.toml)
|
||||||
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "Current version: $CURRENT_VERSION"
|
echo "Current version: $CURRENT_VERSION"
|
||||||
|
|
||||||
- name: Build package
|
- name: Build package
|
||||||
run: python -m build
|
run: python -m build
|
||||||
|
|
||||||
- name: Create GitHub Release
|
# - name: Create GitHub Release
|
||||||
id: create_release
|
# id: create_release
|
||||||
uses: softprops/action-gh-release@v2
|
# uses: softprops/action-gh-release@v2
|
||||||
env:
|
# env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
# GITHUB_TOKEN: ${{ github.token }}
|
||||||
with:
|
# with:
|
||||||
files: dist/*
|
# files: dist/*
|
||||||
tag_name: v${{ steps.current_version.outputs.version }}
|
# tag_name: v${{ steps.current_version.outputs.version }}
|
||||||
draft: false
|
# draft: false
|
||||||
prerelease: false
|
# prerelease: false
|
||||||
generate_release_notes: true
|
# generate_release_notes: true
|
||||||
|
|
||||||
- name: Publish to PyPI
|
- name: Publish to PyPI
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc
|
||||||
with:
|
with:
|
||||||
password: ${{ secrets.PYPI_TOKEN }}
|
password: ${{ secrets.PYPI_TOKEN }}
|
||||||
skip-existing: true
|
skip-existing: true
|
||||||
|
|||||||
25
.github/workflows/publish.yml
vendored
25
.github/workflows/publish.yml
vendored
@@ -1,25 +0,0 @@
|
|||||||
name: Publish to Comfy registry
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main-blocked
|
|
||||||
paths:
|
|
||||||
- "pyproject.toml"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish-node:
|
|
||||||
name: Publish Custom Node to registry
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.repository_owner == 'ltdrdata' || github.repository_owner == 'Comfy-Org' }}
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Publish Custom Node
|
|
||||||
uses: Comfy-Org/publish-node-action@v1
|
|
||||||
with:
|
|
||||||
## Add your own personal access token to your Github Repository secrets and reference it here.
|
|
||||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
|
||||||
49
README.md
49
README.md
@@ -215,13 +215,14 @@ The following settings are applied based on the section marked as `is_default`.
|
|||||||
downgrade_blacklist = <Set a list of packages to prevent downgrades. List them separated by commas.>
|
downgrade_blacklist = <Set a list of packages to prevent downgrades. List them separated by commas.>
|
||||||
security_level = <Set the security level => strong|normal|normal-|weak>
|
security_level = <Set the security level => strong|normal|normal-|weak>
|
||||||
always_lazy_install = <Whether to perform dependency installation on restart even in environments other than Windows.>
|
always_lazy_install = <Whether to perform dependency installation on restart even in environments other than Windows.>
|
||||||
network_mode = <Set the network mode => public|private|offline>
|
network_mode = <Set the network mode => public|private|offline|personal_cloud>
|
||||||
```
|
```
|
||||||
|
|
||||||
* network_mode:
|
* network_mode:
|
||||||
- public: An environment that uses a typical public network.
|
- public: An environment that uses a typical public network.
|
||||||
- private: An environment that uses a closed network, where a private node DB is configured via `channel_url`. (Uses cache if available)
|
- private: An environment that uses a closed network, where a private node DB is configured via `channel_url`. (Uses cache if available)
|
||||||
- offline: An environment that does not use any external connections when using an offline network. (Uses cache if available)
|
- offline: An environment that does not use any external connections when using an offline network. (Uses cache if available)
|
||||||
|
- personal_cloud: Applies relaxed security features in cloud environments such as Google Colab or Runpod, where strong security is not required.
|
||||||
|
|
||||||
|
|
||||||
## Additional Feature
|
## Additional Feature
|
||||||
@@ -312,31 +313,33 @@ When you run the `scan.sh` script:
|
|||||||
|
|
||||||
|
|
||||||
## Security policy
|
## Security policy
|
||||||
* Edit `config.ini` file: add `security_level = <LEVEL>`
|
|
||||||
* `strong`
|
|
||||||
* doesn't allow `high` and `middle` level risky feature
|
|
||||||
* `normal`
|
|
||||||
* doesn't allow `high` level risky feature
|
|
||||||
* `middle` level risky feature is available
|
|
||||||
* `normal-`
|
|
||||||
* doesn't allow `high` level risky feature if `--listen` is specified and not starts with `127.`
|
|
||||||
* `middle` level risky feature is available
|
|
||||||
* `weak`
|
|
||||||
* all feature is available
|
|
||||||
|
|
||||||
* `high` level risky features
|
The security settings are applied based on whether the ComfyUI server's listener is non-local and whether the network mode is set to `personal_cloud`.
|
||||||
* `Install via git url`, `pip install`
|
|
||||||
* Installation of custom nodes registered not in the `default channel`.
|
|
||||||
* Fix custom nodes
|
|
||||||
|
|
||||||
* `middle` level risky features
|
* **non-local**: When the server is launched with `--listen` and is bound to a network range other than the local `127.` range, allowing remote IP access.
|
||||||
* Uninstall/Update
|
* **personal\_cloud**: When the `network_mode` is set to `personal_cloud`.
|
||||||
* Installation of custom nodes registered in the `default channel`.
|
|
||||||
* Restore/Remove Snapshot
|
|
||||||
* Restart
|
### Risky Level Table
|
||||||
|
|
||||||
|
| Risky Level | features |
|
||||||
|
|-------------|---------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| high+ | * `Install via git url`, `pip install`<BR>* Installation of nodepack registered not in the `default channel`. |
|
||||||
|
| high | * Fix nodepack |
|
||||||
|
| middle+ | * Uninstall/Update<BR>* Installation of nodepack registered in the `default channel`.<BR>* Restore/Remove Snapshot<BR>* Install model |
|
||||||
|
| middle | * Restart |
|
||||||
|
| low | * Update ComfyUI |
|
||||||
|
|
||||||
|
|
||||||
|
### Security Level Table
|
||||||
|
|
||||||
|
| Security Level | local | non-local (personal_cloud) | non-local (not personal_cloud) |
|
||||||
|
|----------------|--------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
||||||
|
| strong | * Only `weak` level risky features are allowed | * Only `weak` level risky features are allowed | * Only `weak` level risky features are allowed |
|
||||||
|
| normal | * `high+` and `high` level risky features are not allowed<BR>* `middle+` and `middle` level risky features are available | * `high+` and `high` level risky features are not allowed<BR>* `middle+` and `middle` level risky features are available | * `high+`, `high` and `middle+` level risky features are not allowed<BR>* `middle` level risky features are available
|
||||||
|
| normal- | * All features are available | * `high+` and `high` level risky features are not allowed<BR>* `middle+` and `middle` level risky features are available | * `high+`, `high` and `middle+` level risky features are not allowed<BR>* `middle` level risky features are available
|
||||||
|
| weak | * All features are available | * All features are available | * `high+` and `middle+` level risky features are not allowed<BR>* `high`, `middle` and `low` level risky features are available
|
||||||
|
|
||||||
* `low` level risky features
|
|
||||||
* Update ComfyUI
|
|
||||||
|
|
||||||
|
|
||||||
# Disclaimer
|
# Disclaimer
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
from aiohttp import web
|
||||||
|
from .common.manager_security import HANDLER_POLICY
|
||||||
|
from .common import manager_security
|
||||||
|
from comfy.cli_args import args
|
||||||
|
|
||||||
|
|
||||||
def prestartup():
|
def prestartup():
|
||||||
from . import prestartup_script # noqa: F401
|
from . import prestartup_script # noqa: F401
|
||||||
@@ -7,8 +12,6 @@ def prestartup():
|
|||||||
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
from comfy.cli_args import args
|
|
||||||
|
|
||||||
logging.info('[START] ComfyUI-Manager')
|
logging.info('[START] ComfyUI-Manager')
|
||||||
from .common import cm_global # noqa: F401
|
from .common import cm_global # noqa: F401
|
||||||
|
|
||||||
@@ -17,15 +20,21 @@ def start():
|
|||||||
try:
|
try:
|
||||||
from .legacy import manager_server # noqa: F401
|
from .legacy import manager_server # noqa: F401
|
||||||
from .legacy import share_3rdparty # noqa: F401
|
from .legacy import share_3rdparty # noqa: F401
|
||||||
|
from .legacy import manager_core as core
|
||||||
import nodes
|
import nodes
|
||||||
|
|
||||||
logging.info("[ComfyUI-Manager] Legacy UI is enabled.")
|
logging.info("[ComfyUI-Manager] Legacy UI is enabled.")
|
||||||
nodes.EXTENSION_WEB_DIRS['comfyui-manager-legacy'] = os.path.join(os.path.dirname(__file__), 'js')
|
nodes.EXTENSION_WEB_DIRS['comfyui-manager-legacy'] = os.path.join(os.path.dirname(__file__), 'js')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error enabling legacy ComfyUI Manager frontend:", e)
|
print("Error enabling legacy ComfyUI Manager frontend:", e)
|
||||||
|
core = None
|
||||||
else:
|
else:
|
||||||
from .glob import manager_server # noqa: F401
|
from .glob import manager_server # noqa: F401
|
||||||
from .glob import share_3rdparty # noqa: F401
|
from .glob import share_3rdparty # noqa: F401
|
||||||
|
from .glob import manager_core as core
|
||||||
|
|
||||||
|
if core is not None:
|
||||||
|
manager_security.is_personal_cloud_mode = core.get_config()['network_mode'].lower() == 'personal_cloud'
|
||||||
|
|
||||||
|
|
||||||
def should_be_disabled(fullpath:str) -> bool:
|
def should_be_disabled(fullpath:str) -> bool:
|
||||||
@@ -33,8 +42,6 @@ def should_be_disabled(fullpath:str) -> bool:
|
|||||||
1. Disables the legacy ComfyUI-Manager.
|
1. Disables the legacy ComfyUI-Manager.
|
||||||
2. The blocklist can be expanded later based on policies.
|
2. The blocklist can be expanded later based on policies.
|
||||||
"""
|
"""
|
||||||
from comfy.cli_args import args
|
|
||||||
|
|
||||||
if not args.disable_manager:
|
if not args.disable_manager:
|
||||||
# In cases where installation is done via a zip archive, the directory name may not be comfyui-manager, and it may not contain a git repository.
|
# In cases where installation is done via a zip archive, the directory name may not be comfyui-manager, and it may not contain a git repository.
|
||||||
# It is assumed that any installed legacy ComfyUI-Manager will have at least 'comfyui-manager' in its directory name.
|
# It is assumed that any installed legacy ComfyUI-Manager will have at least 'comfyui-manager' in its directory name.
|
||||||
@@ -43,3 +50,55 @@ def should_be_disabled(fullpath:str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_client_ip(request):
|
||||||
|
peername = request.transport.get_extra_info("peername")
|
||||||
|
if peername is not None:
|
||||||
|
host, port = peername
|
||||||
|
return host
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
def create_middleware():
|
||||||
|
connected_clients = set()
|
||||||
|
is_local_mode = manager_security.is_loopback(args.listen)
|
||||||
|
|
||||||
|
@web.middleware
|
||||||
|
async def manager_middleware(request: web.Request, handler):
|
||||||
|
nonlocal connected_clients
|
||||||
|
|
||||||
|
# security policy for remote environments
|
||||||
|
prev_client_count = len(connected_clients)
|
||||||
|
client_ip = get_client_ip(request)
|
||||||
|
connected_clients.add(client_ip)
|
||||||
|
next_client_count = len(connected_clients)
|
||||||
|
|
||||||
|
if prev_client_count == 1 and next_client_count > 1:
|
||||||
|
manager_security.multiple_remote_alert()
|
||||||
|
|
||||||
|
policy = manager_security.get_handler_policy(handler)
|
||||||
|
is_banned = False
|
||||||
|
|
||||||
|
# policy check
|
||||||
|
if len(connected_clients) > 1:
|
||||||
|
if is_local_mode:
|
||||||
|
if HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NON_LOCAL in policy:
|
||||||
|
is_banned = True
|
||||||
|
if HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NOT_PERSONAL_CLOUD in policy:
|
||||||
|
is_banned = not manager_security.is_personal_cloud_mode
|
||||||
|
|
||||||
|
if HANDLER_POLICY.BANNED in policy:
|
||||||
|
is_banned = True
|
||||||
|
|
||||||
|
if is_banned:
|
||||||
|
logging.warning(f"[Manager] Banning request from {client_ip}: {request.path}")
|
||||||
|
response = web.Response(text="[Manager] This request is banned.", status=403)
|
||||||
|
else:
|
||||||
|
response: web.Response = await handler(request)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
return manager_middleware
|
||||||
|
|
||||||
@@ -46,10 +46,7 @@ comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
|||||||
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
||||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||||
|
|
||||||
if sys.version_info < (3, 13):
|
cm_global.pip_overrides = {}
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
|
||||||
else:
|
|
||||||
cm_global.pip_overrides = {}
|
|
||||||
|
|
||||||
if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json")):
|
if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json")):
|
||||||
with open(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json"), 'r', encoding="UTF-8", errors="ignore") as json_file:
|
with open(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json"), 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||||
@@ -152,9 +149,6 @@ class Ctx:
|
|||||||
with open(context.manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
with open(context.manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||||
cm_global.pip_overrides = json.load(json_file)
|
cm_global.pip_overrides = json.load(json_file)
|
||||||
|
|
||||||
if sys.version_info < (3, 13):
|
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
|
||||||
|
|
||||||
if os.path.exists(context.manager_pip_blacklist_path):
|
if os.path.exists(context.manager_pip_blacklist_path):
|
||||||
with open(context.manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
with open(context.manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||||
for x in f.readlines():
|
for x in f.readlines():
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ def install_node(node_id, version=None):
|
|||||||
else:
|
else:
|
||||||
url = f"{base_url}/nodes/{node_id}/install?version={version}"
|
url = f"{base_url}/nodes/{node_id}/install?version={version}"
|
||||||
|
|
||||||
response = requests.get(url)
|
response = requests.get(url, verify=not manager_util.bypass_ssl)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
# Convert the API response to a NodeVersion object
|
# Convert the API response to a NodeVersion object
|
||||||
return map_node_version(response.json())
|
return map_node_version(response.json())
|
||||||
@@ -191,7 +191,7 @@ def install_node(node_id, version=None):
|
|||||||
def all_versions_of_node(node_id):
|
def all_versions_of_node(node_id):
|
||||||
url = f"{base_url}/nodes/{node_id}/versions?statuses=NodeVersionStatusActive&statuses=NodeVersionStatusPending"
|
url = f"{base_url}/nodes/{node_id}/versions?statuses=NodeVersionStatusActive&statuses=NodeVersionStatusPending"
|
||||||
|
|
||||||
response = requests.get(url)
|
response = requests.get(url, verify=not manager_util.bypass_ssl)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
return response.json()
|
return response.json()
|
||||||
else:
|
else:
|
||||||
@@ -211,6 +211,7 @@ def read_cnr_info(fullpath):
|
|||||||
|
|
||||||
project = data.get('project', {})
|
project = data.get('project', {})
|
||||||
name = project.get('name').strip().lower()
|
name = project.get('name').strip().lower()
|
||||||
|
original_name = project.get('name')
|
||||||
|
|
||||||
# normalize version
|
# normalize version
|
||||||
# for example: 2.5 -> 2.5.0
|
# for example: 2.5 -> 2.5.0
|
||||||
@@ -222,6 +223,7 @@ def read_cnr_info(fullpath):
|
|||||||
if name and version: # repository is optional
|
if name and version: # repository is optional
|
||||||
return {
|
return {
|
||||||
"id": name,
|
"id": name,
|
||||||
|
"original_name": original_name,
|
||||||
"version": version,
|
"version": version,
|
||||||
"url": repository
|
"url": repository
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,4 +106,3 @@ def get_comfyui_tag():
|
|||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ class NetworkMode(enum.Enum):
|
|||||||
PUBLIC = "public"
|
PUBLIC = "public"
|
||||||
PRIVATE = "private"
|
PRIVATE = "private"
|
||||||
OFFLINE = "offline"
|
OFFLINE = "offline"
|
||||||
|
PERSONAL_CLOUD = "personal_cloud"
|
||||||
|
|
||||||
class SecurityLevel(enum.Enum):
|
class SecurityLevel(enum.Enum):
|
||||||
STRONG = "strong"
|
STRONG = "strong"
|
||||||
|
|||||||
@@ -55,7 +55,11 @@ def download_url(model_url: str, model_dir: str, filename: str):
|
|||||||
return aria2_download_url(model_url, model_dir, filename)
|
return aria2_download_url(model_url, model_dir, filename)
|
||||||
else:
|
else:
|
||||||
from torchvision.datasets.utils import download_url as torchvision_download_url
|
from torchvision.datasets.utils import download_url as torchvision_download_url
|
||||||
return torchvision_download_url(model_url, model_dir, filename)
|
try:
|
||||||
|
return torchvision_download_url(model_url, model_dir, filename)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"[ComfyUI-Manager] Failed to download: {model_url} / {repr(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def aria2_find_task(dir: str, filename: str):
|
def aria2_find_task(dir: str, filename: str):
|
||||||
|
|||||||
36
comfyui_manager/common/manager_security.py
Normal file
36
comfyui_manager/common/manager_security.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
is_personal_cloud_mode = False
|
||||||
|
handler_policy = {}
|
||||||
|
|
||||||
|
class HANDLER_POLICY(Enum):
|
||||||
|
MULTIPLE_REMOTE_BAN_NON_LOCAL = 1
|
||||||
|
MULTIPLE_REMOTE_BAN_NOT_PERSONAL_CLOUD = 2
|
||||||
|
BANNED = 3
|
||||||
|
|
||||||
|
|
||||||
|
def is_loopback(address):
|
||||||
|
import ipaddress
|
||||||
|
try:
|
||||||
|
return ipaddress.ip_address(address).is_loopback
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def do_nothing():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_handler_policy(x):
|
||||||
|
return handler_policy.get(x) or set()
|
||||||
|
|
||||||
|
def add_handler_policy(x, policy):
|
||||||
|
s = handler_policy.get(x)
|
||||||
|
if s is None:
|
||||||
|
s = set()
|
||||||
|
handler_policy[x] = s
|
||||||
|
|
||||||
|
s.add(policy)
|
||||||
|
|
||||||
|
|
||||||
|
multiple_remote_alert = do_nothing
|
||||||
@@ -15,7 +15,6 @@ import re
|
|||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
import shlex
|
import shlex
|
||||||
from . import cm_global
|
|
||||||
|
|
||||||
|
|
||||||
cache_lock = threading.Lock()
|
cache_lock = threading.Lock()
|
||||||
@@ -25,6 +24,7 @@ comfyui_manager_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '
|
|||||||
cache_dir = os.path.join(comfyui_manager_path, '.cache') # This path is also updated together in **manager_core.update_user_directory**.
|
cache_dir = os.path.join(comfyui_manager_path, '.cache') # This path is also updated together in **manager_core.update_user_directory**.
|
||||||
|
|
||||||
use_uv = False
|
use_uv = False
|
||||||
|
bypass_ssl = False
|
||||||
|
|
||||||
def is_manager_pip_package():
|
def is_manager_pip_package():
|
||||||
return not os.path.exists(os.path.join(comfyui_manager_path, '..', 'custom_nodes'))
|
return not os.path.exists(os.path.join(comfyui_manager_path, '..', 'custom_nodes'))
|
||||||
@@ -140,7 +140,7 @@ async def get_data(uri, silent=False):
|
|||||||
print(f"FETCH DATA from: {uri}", end="")
|
print(f"FETCH DATA from: {uri}", end="")
|
||||||
|
|
||||||
if uri.startswith("http"):
|
if uri.startswith("http"):
|
||||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=not bypass_ssl)) as session:
|
||||||
headers = {
|
headers = {
|
||||||
'Cache-Control': 'no-cache',
|
'Cache-Control': 'no-cache',
|
||||||
'Pragma': 'no-cache',
|
'Pragma': 'no-cache',
|
||||||
@@ -330,6 +330,32 @@ torch_torchvision_torchaudio_version_map = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def torch_rollback(prev):
|
||||||
|
spec = prev.split('+')
|
||||||
|
if len(spec) > 1:
|
||||||
|
platform = spec[1]
|
||||||
|
else:
|
||||||
|
cmd = make_pip_cmd(['install', '--force', 'torch', 'torchvision', 'torchaudio'])
|
||||||
|
subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
logging.error(cmd)
|
||||||
|
return
|
||||||
|
|
||||||
|
torch_ver = StrictVersion(spec[0])
|
||||||
|
torch_ver = f"{torch_ver.major}.{torch_ver.minor}.{torch_ver.patch}"
|
||||||
|
torch_torchvision_torchaudio_ver = torch_torchvision_torchaudio_version_map.get(torch_ver)
|
||||||
|
|
||||||
|
if torch_torchvision_torchaudio_ver is None:
|
||||||
|
cmd = make_pip_cmd(['install', '--pre', 'torch', 'torchvision', 'torchaudio',
|
||||||
|
'--index-url', f"https://download.pytorch.org/whl/nightly/{platform}"])
|
||||||
|
logging.info("[ComfyUI-Manager] restore PyTorch to nightly version")
|
||||||
|
else:
|
||||||
|
torchvision_ver, torchaudio_ver = torch_torchvision_torchaudio_ver
|
||||||
|
cmd = make_pip_cmd(['install', f'torch=={torch_ver}', f'torchvision=={torchvision_ver}', f"torchaudio=={torchaudio_ver}",
|
||||||
|
'--index-url', f"https://download.pytorch.org/whl/{platform}"])
|
||||||
|
logging.info(f"[ComfyUI-Manager] restore PyTorch to {torch_ver}+{platform}")
|
||||||
|
|
||||||
|
subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
|
||||||
|
|
||||||
class PIPFixer:
|
class PIPFixer:
|
||||||
def __init__(self, prev_pip_versions, comfyui_path, manager_files_path):
|
def __init__(self, prev_pip_versions, comfyui_path, manager_files_path):
|
||||||
@@ -337,32 +363,6 @@ class PIPFixer:
|
|||||||
self.comfyui_path = comfyui_path
|
self.comfyui_path = comfyui_path
|
||||||
self.manager_files_path = manager_files_path
|
self.manager_files_path = manager_files_path
|
||||||
|
|
||||||
def torch_rollback(self):
|
|
||||||
spec = self.prev_pip_versions['torch'].split('+')
|
|
||||||
if len(spec) > 0:
|
|
||||||
platform = spec[1]
|
|
||||||
else:
|
|
||||||
cmd = make_pip_cmd(['install', '--force', 'torch', 'torchvision', 'torchaudio'])
|
|
||||||
subprocess.check_output(cmd, universal_newlines=True)
|
|
||||||
logging.error(cmd)
|
|
||||||
return
|
|
||||||
|
|
||||||
torch_ver = StrictVersion(spec[0])
|
|
||||||
torch_ver = f"{torch_ver.major}.{torch_ver.minor}.{torch_ver.patch}"
|
|
||||||
torch_torchvision_torchaudio_ver = torch_torchvision_torchaudio_version_map.get(torch_ver)
|
|
||||||
|
|
||||||
if torch_torchvision_torchaudio_ver is None:
|
|
||||||
cmd = make_pip_cmd(['install', '--pre', 'torch', 'torchvision', 'torchaudio',
|
|
||||||
'--index-url', f"https://download.pytorch.org/whl/nightly/{platform}"])
|
|
||||||
logging.info("[ComfyUI-Manager] restore PyTorch to nightly version")
|
|
||||||
else:
|
|
||||||
torchvision_ver, torchaudio_ver = torch_torchvision_torchaudio_ver
|
|
||||||
cmd = make_pip_cmd(['install', f'torch=={torch_ver}', f'torchvision=={torchvision_ver}', f"torchaudio=={torchaudio_ver}",
|
|
||||||
'--index-url', f"https://download.pytorch.org/whl/{platform}"])
|
|
||||||
logging.info(f"[ComfyUI-Manager] restore PyTorch to {torch_ver}+{platform}")
|
|
||||||
|
|
||||||
subprocess.check_output(cmd, universal_newlines=True)
|
|
||||||
|
|
||||||
def fix_broken(self):
|
def fix_broken(self):
|
||||||
new_pip_versions = get_installed_packages(True)
|
new_pip_versions = get_installed_packages(True)
|
||||||
|
|
||||||
@@ -384,7 +384,7 @@ class PIPFixer:
|
|||||||
elif self.prev_pip_versions['torch'] != new_pip_versions['torch'] \
|
elif self.prev_pip_versions['torch'] != new_pip_versions['torch'] \
|
||||||
or self.prev_pip_versions['torchvision'] != new_pip_versions['torchvision'] \
|
or self.prev_pip_versions['torchvision'] != new_pip_versions['torchvision'] \
|
||||||
or self.prev_pip_versions['torchaudio'] != new_pip_versions['torchaudio']:
|
or self.prev_pip_versions['torchaudio'] != new_pip_versions['torchaudio']:
|
||||||
self.torch_rollback()
|
torch_rollback(self.prev_pip_versions['torch'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("[ComfyUI-Manager] Failed to restore PyTorch")
|
logging.error("[ComfyUI-Manager] Failed to restore PyTorch")
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
@@ -415,32 +415,14 @@ class PIPFixer:
|
|||||||
|
|
||||||
if len(targets) > 0:
|
if len(targets) > 0:
|
||||||
for x in targets:
|
for x in targets:
|
||||||
if sys.version_info < (3, 13):
|
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}"])
|
||||||
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}", "numpy<2"])
|
subprocess.check_output(cmd, universal_newlines=True)
|
||||||
subprocess.check_output(cmd, universal_newlines=True)
|
|
||||||
|
|
||||||
logging.info(f"[ComfyUI-Manager] 'opencv' dependencies were fixed: {targets}")
|
logging.info(f"[ComfyUI-Manager] 'opencv' dependencies were fixed: {targets}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("[ComfyUI-Manager] Failed to restore opencv")
|
logging.error("[ComfyUI-Manager] Failed to restore opencv")
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
|
|
||||||
# fix numpy
|
|
||||||
if sys.version_info >= (3, 13):
|
|
||||||
logging.info("[ComfyUI-Manager] In Python 3.13 and above, PIP Fixer does not downgrade `numpy` below version 2.0. If you need to force a downgrade of `numpy`, please use `pip_auto_fix.list`.")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
np = new_pip_versions.get('numpy')
|
|
||||||
if cm_global.pip_overrides.get('numpy') == 'numpy<2':
|
|
||||||
if np is not None:
|
|
||||||
if StrictVersion(np) >= StrictVersion('2'):
|
|
||||||
cmd = make_pip_cmd(['install', "numpy<2"])
|
|
||||||
subprocess.check_output(cmd , universal_newlines=True)
|
|
||||||
|
|
||||||
logging.info("[ComfyUI-Manager] 'numpy' dependency were fixed")
|
|
||||||
except Exception as e:
|
|
||||||
logging.error("[ComfyUI-Manager] Failed to restore numpy")
|
|
||||||
logging.error(e)
|
|
||||||
|
|
||||||
# fix missing frontend
|
# fix missing frontend
|
||||||
try:
|
try:
|
||||||
# NOTE: package name in requirements is 'comfyui-frontend-package'
|
# NOTE: package name in requirements is 'comfyui-frontend-package'
|
||||||
@@ -540,3 +522,69 @@ def robust_readlines(fullpath):
|
|||||||
|
|
||||||
print(f"[ComfyUI-Manager] Failed to recognize encoding for: {fullpath}")
|
print(f"[ComfyUI-Manager] Failed to recognize encoding for: {fullpath}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def restore_pip_snapshot(pips, options):
|
||||||
|
non_url = []
|
||||||
|
local_url = []
|
||||||
|
non_local_url = []
|
||||||
|
|
||||||
|
for k, v in pips.items():
|
||||||
|
# NOTE: skip torch related packages
|
||||||
|
if k.startswith("torch==") or k.startswith("torchvision==") or k.startswith("torchaudio==") or k.startswith("nvidia-"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if v == "":
|
||||||
|
non_url.append(k)
|
||||||
|
else:
|
||||||
|
if v.startswith('file:'):
|
||||||
|
local_url.append(v)
|
||||||
|
else:
|
||||||
|
non_local_url.append(v)
|
||||||
|
|
||||||
|
|
||||||
|
# restore other pips
|
||||||
|
failed = []
|
||||||
|
if '--pip-non-url' in options:
|
||||||
|
# try all at once
|
||||||
|
res = 1
|
||||||
|
try:
|
||||||
|
res = subprocess.check_output(make_pip_cmd(['install'] + non_url))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# fallback
|
||||||
|
if res != 0:
|
||||||
|
for x in non_url:
|
||||||
|
res = 1
|
||||||
|
try:
|
||||||
|
res = subprocess.check_output(make_pip_cmd(['install', '--no-deps', x]))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if res != 0:
|
||||||
|
failed.append(x)
|
||||||
|
|
||||||
|
if '--pip-non-local-url' in options:
|
||||||
|
for x in non_local_url:
|
||||||
|
res = 1
|
||||||
|
try:
|
||||||
|
res = subprocess.check_output(make_pip_cmd(['install', '--no-deps', x]))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if res != 0:
|
||||||
|
failed.append(x)
|
||||||
|
|
||||||
|
if '--pip-local-url' in options:
|
||||||
|
for x in local_url:
|
||||||
|
res = 1
|
||||||
|
try:
|
||||||
|
res = subprocess.check_output(make_pip_cmd(['install', '--no-deps', x]))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if res != 0:
|
||||||
|
failed.append(x)
|
||||||
|
|
||||||
|
print(f"Installation failed for pip packages: {failed}")
|
||||||
@@ -2,6 +2,8 @@ import sys
|
|||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from . import manager_util
|
||||||
|
|
||||||
|
|
||||||
def security_check():
|
def security_check():
|
||||||
print("[START] Security scan")
|
print("[START] Security scan")
|
||||||
@@ -66,18 +68,23 @@ https://blog.comfy.org/comfyui-statement-on-the-ultralytics-crypto-miner-situati
|
|||||||
"lolMiner": [os.path.join(comfyui_path, 'lolMiner')]
|
"lolMiner": [os.path.join(comfyui_path, 'lolMiner')]
|
||||||
}
|
}
|
||||||
|
|
||||||
installed_pips = subprocess.check_output([sys.executable, '-m', "pip", "freeze"], text=True)
|
installed_pips = subprocess.check_output(manager_util.make_pip_cmd(["freeze"]), text=True)
|
||||||
|
|
||||||
detected = set()
|
detected = set()
|
||||||
try:
|
try:
|
||||||
anthropic_info = subprocess.check_output([sys.executable, '-m', "pip", "show", "anthropic"], text=True, stderr=subprocess.DEVNULL)
|
anthropic_info = subprocess.check_output(manager_util.make_pip_cmd(["show", "anthropic"]), text=True, stderr=subprocess.DEVNULL)
|
||||||
anthropic_reqs = [x for x in anthropic_info.split('\n') if x.startswith("Requires")][0].split(': ')[1]
|
requires_lines = [x for x in anthropic_info.split('\n') if x.startswith("Requires")]
|
||||||
if "pycrypto" in anthropic_reqs:
|
if requires_lines:
|
||||||
location = [x for x in anthropic_info.split('\n') if x.startswith("Location")][0].split(': ')[1]
|
anthropic_reqs = requires_lines[0].split(": ", 1)[1]
|
||||||
for fi in os.listdir(location):
|
if "pycrypto" in anthropic_reqs:
|
||||||
if fi.startswith("anthropic"):
|
location_lines = [x for x in anthropic_info.split('\n') if x.startswith("Location")]
|
||||||
guide["ComfyUI_LLMVISION"] = f"\n0.Remove {os.path.join(location, fi)}" + guide["ComfyUI_LLMVISION"]
|
if location_lines:
|
||||||
detected.add("ComfyUI_LLMVISION")
|
location = location_lines[0].split(": ", 1)[1]
|
||||||
|
for fi in os.listdir(location):
|
||||||
|
if fi.startswith("anthropic"):
|
||||||
|
guide["ComfyUI_LLMVISION"] = (f"\n0.Remove {os.path.join(location, fi)}" + guide["ComfyUI_LLMVISION"])
|
||||||
|
detected.add("ComfyUI_LLMVISION")
|
||||||
|
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
6097
custom-node-list.json → comfyui_manager/custom-node-list.json
Executable file → Normal file
6097
custom-node-list.json → comfyui_manager/custom-node-list.json
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@ datamodel-codegen \
|
|||||||
--use-subclass-enum \
|
--use-subclass-enum \
|
||||||
--field-constraints \
|
--field-constraints \
|
||||||
--strict-types bytes \
|
--strict-types bytes \
|
||||||
|
--use-double-quotes \
|
||||||
--input openapi.yaml \
|
--input openapi.yaml \
|
||||||
--output comfyui_manager/data_models/generated_models.py \
|
--output comfyui_manager/data_models/generated_models.py \
|
||||||
--output-model-type pydantic_v2.BaseModel
|
--output-model-type pydantic_v2.BaseModel
|
||||||
|
|||||||
@@ -30,9 +30,15 @@ from .generated_models import (
|
|||||||
InstalledModelInfo,
|
InstalledModelInfo,
|
||||||
ComfyUIVersionInfo,
|
ComfyUIVersionInfo,
|
||||||
|
|
||||||
|
# Import Fail Info Models
|
||||||
|
ImportFailInfoBulkRequest,
|
||||||
|
ImportFailInfoBulkResponse,
|
||||||
|
ImportFailInfoItem,
|
||||||
|
ImportFailInfoItem1,
|
||||||
|
|
||||||
# Other models
|
# Other models
|
||||||
Kind,
|
OperationType,
|
||||||
StatusStr,
|
OperationResult,
|
||||||
ManagerPackInfo,
|
ManagerPackInfo,
|
||||||
ManagerPackInstalled,
|
ManagerPackInstalled,
|
||||||
SelectedVersion,
|
SelectedVersion,
|
||||||
@@ -49,6 +55,9 @@ from .generated_models import (
|
|||||||
UninstallPackParams,
|
UninstallPackParams,
|
||||||
DisablePackParams,
|
DisablePackParams,
|
||||||
EnablePackParams,
|
EnablePackParams,
|
||||||
|
UpdateAllQueryParams,
|
||||||
|
UpdateComfyUIQueryParams,
|
||||||
|
ComfyUISwitchVersionQueryParams,
|
||||||
QueueStatus,
|
QueueStatus,
|
||||||
ManagerMappings,
|
ManagerMappings,
|
||||||
ModelMetadata,
|
ModelMetadata,
|
||||||
@@ -59,8 +68,8 @@ from .generated_models import (
|
|||||||
HistoryResponse,
|
HistoryResponse,
|
||||||
HistoryListResponse,
|
HistoryListResponse,
|
||||||
InstallType,
|
InstallType,
|
||||||
OperationType,
|
SecurityLevel,
|
||||||
Result,
|
RiskLevel,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@@ -85,9 +94,15 @@ __all__ = [
|
|||||||
"InstalledModelInfo",
|
"InstalledModelInfo",
|
||||||
"ComfyUIVersionInfo",
|
"ComfyUIVersionInfo",
|
||||||
|
|
||||||
|
# Import Fail Info Models
|
||||||
|
"ImportFailInfoBulkRequest",
|
||||||
|
"ImportFailInfoBulkResponse",
|
||||||
|
"ImportFailInfoItem",
|
||||||
|
"ImportFailInfoItem1",
|
||||||
|
|
||||||
# Other models
|
# Other models
|
||||||
"Kind",
|
"OperationType",
|
||||||
"StatusStr",
|
"OperationResult",
|
||||||
"ManagerPackInfo",
|
"ManagerPackInfo",
|
||||||
"ManagerPackInstalled",
|
"ManagerPackInstalled",
|
||||||
"SelectedVersion",
|
"SelectedVersion",
|
||||||
@@ -104,6 +119,9 @@ __all__ = [
|
|||||||
"UninstallPackParams",
|
"UninstallPackParams",
|
||||||
"DisablePackParams",
|
"DisablePackParams",
|
||||||
"EnablePackParams",
|
"EnablePackParams",
|
||||||
|
"UpdateAllQueryParams",
|
||||||
|
"UpdateComfyUIQueryParams",
|
||||||
|
"ComfyUISwitchVersionQueryParams",
|
||||||
"QueueStatus",
|
"QueueStatus",
|
||||||
"ManagerMappings",
|
"ManagerMappings",
|
||||||
"ModelMetadata",
|
"ModelMetadata",
|
||||||
@@ -114,6 +132,6 @@ __all__ = [
|
|||||||
"HistoryResponse",
|
"HistoryResponse",
|
||||||
"HistoryListResponse",
|
"HistoryListResponse",
|
||||||
"InstallType",
|
"InstallType",
|
||||||
"OperationType",
|
"SecurityLevel",
|
||||||
"Result",
|
"RiskLevel",
|
||||||
]
|
]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# generated by datamodel-codegen:
|
# generated by datamodel-codegen:
|
||||||
# filename: openapi.yaml
|
# filename: openapi.yaml
|
||||||
# timestamp: 2025-06-14T01:44:21+00:00
|
# timestamp: 2025-07-31T04:52:26+00:00
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -11,252 +11,298 @@ from typing import Any, Dict, List, Optional, Union
|
|||||||
from pydantic import BaseModel, Field, RootModel
|
from pydantic import BaseModel, Field, RootModel
|
||||||
|
|
||||||
|
|
||||||
class Kind(str, Enum):
|
class OperationType(str, Enum):
|
||||||
install = 'install'
|
install = "install"
|
||||||
uninstall = 'uninstall'
|
uninstall = "uninstall"
|
||||||
update = 'update'
|
update = "update"
|
||||||
update_all = 'update-all'
|
update_comfyui = "update-comfyui"
|
||||||
update_comfyui = 'update-comfyui'
|
fix = "fix"
|
||||||
fix = 'fix'
|
disable = "disable"
|
||||||
disable = 'disable'
|
enable = "enable"
|
||||||
enable = 'enable'
|
install_model = "install-model"
|
||||||
install_model = 'install-model'
|
|
||||||
|
|
||||||
|
|
||||||
class StatusStr(str, Enum):
|
class OperationResult(str, Enum):
|
||||||
success = 'success'
|
success = "success"
|
||||||
error = 'error'
|
failed = "failed"
|
||||||
skip = 'skip'
|
skipped = "skipped"
|
||||||
|
error = "error"
|
||||||
|
skip = "skip"
|
||||||
|
|
||||||
|
|
||||||
class TaskExecutionStatus(BaseModel):
|
class TaskExecutionStatus(BaseModel):
|
||||||
status_str: StatusStr = Field(..., description='Overall task execution status')
|
status_str: OperationResult
|
||||||
completed: bool = Field(..., description='Whether the task completed')
|
completed: bool = Field(..., description="Whether the task completed")
|
||||||
messages: List[str] = Field(..., description='Additional status messages')
|
messages: List[str] = Field(..., description="Additional status messages")
|
||||||
|
|
||||||
|
|
||||||
class ManagerMessageName(str, Enum):
|
class ManagerMessageName(str, Enum):
|
||||||
cm_task_completed = 'cm-task-completed'
|
cm_task_completed = "cm-task-completed"
|
||||||
cm_task_started = 'cm-task-started'
|
cm_task_started = "cm-task-started"
|
||||||
cm_queue_status = 'cm-queue-status'
|
cm_queue_status = "cm-queue-status"
|
||||||
|
|
||||||
|
|
||||||
class ManagerPackInfo(BaseModel):
|
class ManagerPackInfo(BaseModel):
|
||||||
id: str = Field(
|
id: str = Field(
|
||||||
...,
|
...,
|
||||||
description='Either github-author/github-repo or name of pack from the registry',
|
description="Either github-author/github-repo or name of pack from the registry",
|
||||||
)
|
)
|
||||||
version: str = Field(..., description='Semantic version or Git commit hash')
|
version: str = Field(..., description="Semantic version or Git commit hash")
|
||||||
ui_id: Optional[str] = Field(None, description='Task ID - generated internally')
|
ui_id: Optional[str] = Field(None, description="Task ID - generated internally")
|
||||||
|
|
||||||
|
|
||||||
class ManagerPackInstalled(BaseModel):
|
class ManagerPackInstalled(BaseModel):
|
||||||
ver: str = Field(
|
ver: str = Field(
|
||||||
...,
|
...,
|
||||||
description='The version of the pack that is installed (Git commit hash or semantic version)',
|
description="The version of the pack that is installed (Git commit hash or semantic version)",
|
||||||
)
|
)
|
||||||
cnr_id: Optional[str] = Field(
|
cnr_id: Optional[str] = Field(
|
||||||
None, description='The name of the pack if installed from the registry'
|
None, description="The name of the pack if installed from the registry"
|
||||||
)
|
)
|
||||||
aux_id: Optional[str] = Field(
|
aux_id: Optional[str] = Field(
|
||||||
None,
|
None,
|
||||||
description='The name of the pack if installed from github (author/repo-name format)',
|
description="The name of the pack if installed from github (author/repo-name format)",
|
||||||
)
|
)
|
||||||
enabled: bool = Field(..., description='Whether the pack is enabled')
|
enabled: bool = Field(..., description="Whether the pack is enabled")
|
||||||
|
|
||||||
|
|
||||||
class SelectedVersion(str, Enum):
|
class SelectedVersion(str, Enum):
|
||||||
latest = 'latest'
|
latest = "latest"
|
||||||
nightly = 'nightly'
|
nightly = "nightly"
|
||||||
|
|
||||||
|
|
||||||
class ManagerChannel(str, Enum):
|
class ManagerChannel(str, Enum):
|
||||||
default = 'default'
|
default = "default"
|
||||||
recent = 'recent'
|
recent = "recent"
|
||||||
legacy = 'legacy'
|
legacy = "legacy"
|
||||||
forked = 'forked'
|
forked = "forked"
|
||||||
dev = 'dev'
|
dev = "dev"
|
||||||
tutorial = 'tutorial'
|
tutorial = "tutorial"
|
||||||
|
|
||||||
|
|
||||||
class ManagerDatabaseSource(str, Enum):
|
class ManagerDatabaseSource(str, Enum):
|
||||||
remote = 'remote'
|
remote = "remote"
|
||||||
local = 'local'
|
local = "local"
|
||||||
cache = 'cache'
|
cache = "cache"
|
||||||
|
|
||||||
|
|
||||||
class ManagerPackState(str, Enum):
|
class ManagerPackState(str, Enum):
|
||||||
installed = 'installed'
|
installed = "installed"
|
||||||
disabled = 'disabled'
|
disabled = "disabled"
|
||||||
not_installed = 'not_installed'
|
not_installed = "not_installed"
|
||||||
import_failed = 'import_failed'
|
import_failed = "import_failed"
|
||||||
needs_update = 'needs_update'
|
needs_update = "needs_update"
|
||||||
|
|
||||||
|
|
||||||
class ManagerPackInstallType(str, Enum):
|
class ManagerPackInstallType(str, Enum):
|
||||||
git_clone = 'git-clone'
|
git_clone = "git-clone"
|
||||||
copy = 'copy'
|
copy = "copy"
|
||||||
cnr = 'cnr'
|
cnr = "cnr"
|
||||||
|
|
||||||
|
|
||||||
|
class SecurityLevel(str, Enum):
|
||||||
|
strong = "strong"
|
||||||
|
normal = "normal"
|
||||||
|
normal_ = "normal-"
|
||||||
|
weak = "weak"
|
||||||
|
|
||||||
|
|
||||||
|
class RiskLevel(str, Enum):
|
||||||
|
block = "block"
|
||||||
|
high_ = "high+"
|
||||||
|
high = "high"
|
||||||
|
middle_ = "middle+"
|
||||||
|
middle = "middle"
|
||||||
|
|
||||||
|
|
||||||
class UpdateState(Enum):
|
class UpdateState(Enum):
|
||||||
false = 'false'
|
false = "false"
|
||||||
true = 'true'
|
true = "true"
|
||||||
|
|
||||||
|
|
||||||
class ManagerPack(ManagerPackInfo):
|
class ManagerPack(ManagerPackInfo):
|
||||||
author: Optional[str] = Field(
|
author: Optional[str] = Field(
|
||||||
None, description="Pack author name or 'Unclaimed' if added via GitHub crawl"
|
None, description="Pack author name or 'Unclaimed' if added via GitHub crawl"
|
||||||
)
|
)
|
||||||
files: Optional[List[str]] = Field(None, description='Files included in the pack')
|
files: Optional[List[str]] = Field(
|
||||||
reference: Optional[str] = Field(
|
None,
|
||||||
None, description='The type of installation reference'
|
description="Repository URLs for installation (typically contains one GitHub URL)",
|
||||||
)
|
)
|
||||||
title: Optional[str] = Field(None, description='The display name of the pack')
|
reference: Optional[str] = Field(
|
||||||
|
None, description="The type of installation reference"
|
||||||
|
)
|
||||||
|
title: Optional[str] = Field(None, description="The display name of the pack")
|
||||||
cnr_latest: Optional[SelectedVersion] = None
|
cnr_latest: Optional[SelectedVersion] = None
|
||||||
repository: Optional[str] = Field(None, description='GitHub repository URL')
|
repository: Optional[str] = Field(None, description="GitHub repository URL")
|
||||||
state: Optional[ManagerPackState] = None
|
state: Optional[ManagerPackState] = None
|
||||||
update_state: Optional[UpdateState] = Field(
|
update_state: Optional[UpdateState] = Field(
|
||||||
None, alias='update-state', description='Update availability status'
|
None, alias="update-state", description="Update availability status"
|
||||||
)
|
)
|
||||||
stars: Optional[int] = Field(None, description='GitHub stars count')
|
stars: Optional[int] = Field(None, description="GitHub stars count")
|
||||||
last_update: Optional[datetime] = Field(None, description='Last update timestamp')
|
last_update: Optional[datetime] = Field(None, description="Last update timestamp")
|
||||||
health: Optional[str] = Field(None, description='Health status of the pack')
|
health: Optional[str] = Field(None, description="Health status of the pack")
|
||||||
description: Optional[str] = Field(None, description='Pack description')
|
description: Optional[str] = Field(None, description="Pack description")
|
||||||
trust: Optional[bool] = Field(None, description='Whether the pack is trusted')
|
trust: Optional[bool] = Field(None, description="Whether the pack is trusted")
|
||||||
install_type: Optional[ManagerPackInstallType] = None
|
install_type: Optional[ManagerPackInstallType] = None
|
||||||
|
|
||||||
|
|
||||||
class InstallPackParams(ManagerPackInfo):
|
class InstallPackParams(ManagerPackInfo):
|
||||||
selected_version: Union[str, SelectedVersion] = Field(
|
selected_version: Union[str, SelectedVersion] = Field(
|
||||||
..., description='Semantic version, Git commit hash, latest, or nightly'
|
..., description="Semantic version, Git commit hash, latest, or nightly"
|
||||||
)
|
)
|
||||||
repository: Optional[str] = Field(
|
repository: Optional[str] = Field(
|
||||||
None,
|
None,
|
||||||
description='GitHub repository URL (required if selected_version is nightly)',
|
description="GitHub repository URL (required if selected_version is nightly)",
|
||||||
)
|
)
|
||||||
pip: Optional[List[str]] = Field(None, description='PyPi dependency names')
|
pip: Optional[List[str]] = Field(None, description="PyPi dependency names")
|
||||||
mode: ManagerDatabaseSource
|
mode: ManagerDatabaseSource
|
||||||
channel: ManagerChannel
|
channel: ManagerChannel
|
||||||
skip_post_install: Optional[bool] = Field(
|
skip_post_install: Optional[bool] = Field(
|
||||||
None, description='Whether to skip post-installation steps'
|
None, description="Whether to skip post-installation steps"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class UpdateAllPacksParams(BaseModel):
|
class UpdateAllPacksParams(BaseModel):
|
||||||
mode: Optional[ManagerDatabaseSource] = None
|
mode: Optional[ManagerDatabaseSource] = None
|
||||||
ui_id: Optional[str] = Field(None, description='Task ID - generated internally')
|
ui_id: Optional[str] = Field(None, description="Task ID - generated internally")
|
||||||
|
|
||||||
|
|
||||||
class UpdatePackParams(BaseModel):
|
class UpdatePackParams(BaseModel):
|
||||||
node_name: str = Field(..., description='Name of the node package to update')
|
node_name: str = Field(..., description="Name of the node package to update")
|
||||||
node_ver: Optional[str] = Field(
|
node_ver: Optional[str] = Field(
|
||||||
None, description='Current version of the node package'
|
None, description="Current version of the node package"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class UpdateComfyUIParams(BaseModel):
|
class UpdateComfyUIParams(BaseModel):
|
||||||
is_stable: Optional[bool] = Field(
|
is_stable: Optional[bool] = Field(
|
||||||
True,
|
True,
|
||||||
description='Whether to update to stable version (true) or nightly (false)',
|
description="Whether to update to stable version (true) or nightly (false)",
|
||||||
)
|
)
|
||||||
target_version: Optional[str] = Field(
|
target_version: Optional[str] = Field(
|
||||||
None,
|
None,
|
||||||
description='Specific version to switch to (for version switching operations)',
|
description="Specific version to switch to (for version switching operations)",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class FixPackParams(BaseModel):
|
class FixPackParams(BaseModel):
|
||||||
node_name: str = Field(..., description='Name of the node package to fix')
|
node_name: str = Field(..., description="Name of the node package to fix")
|
||||||
node_ver: str = Field(..., description='Version of the node package')
|
node_ver: str = Field(..., description="Version of the node package")
|
||||||
|
|
||||||
|
|
||||||
class UninstallPackParams(BaseModel):
|
class UninstallPackParams(BaseModel):
|
||||||
node_name: str = Field(..., description='Name of the node package to uninstall')
|
node_name: str = Field(..., description="Name of the node package to uninstall")
|
||||||
is_unknown: Optional[bool] = Field(
|
is_unknown: Optional[bool] = Field(
|
||||||
False, description='Whether this is an unknown/unregistered package'
|
False, description="Whether this is an unknown/unregistered package"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DisablePackParams(BaseModel):
|
class DisablePackParams(BaseModel):
|
||||||
node_name: str = Field(..., description='Name of the node package to disable')
|
node_name: str = Field(..., description="Name of the node package to disable")
|
||||||
is_unknown: Optional[bool] = Field(
|
is_unknown: Optional[bool] = Field(
|
||||||
False, description='Whether this is an unknown/unregistered package'
|
False, description="Whether this is an unknown/unregistered package"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EnablePackParams(BaseModel):
|
class EnablePackParams(BaseModel):
|
||||||
cnr_id: str = Field(
|
cnr_id: str = Field(
|
||||||
..., description='ComfyUI Node Registry ID of the package to enable'
|
..., description="ComfyUI Node Registry ID of the package to enable"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateAllQueryParams(BaseModel):
|
||||||
|
client_id: str = Field(
|
||||||
|
..., description="Client identifier that initiated the request"
|
||||||
|
)
|
||||||
|
ui_id: str = Field(..., description="Base UI identifier for task tracking")
|
||||||
|
mode: Optional[ManagerDatabaseSource] = None
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateComfyUIQueryParams(BaseModel):
|
||||||
|
client_id: str = Field(
|
||||||
|
..., description="Client identifier that initiated the request"
|
||||||
|
)
|
||||||
|
ui_id: str = Field(..., description="UI identifier for task tracking")
|
||||||
|
stable: Optional[bool] = Field(
|
||||||
|
True,
|
||||||
|
description="Whether to update to stable version (true) or nightly (false)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ComfyUISwitchVersionQueryParams(BaseModel):
|
||||||
|
ver: str = Field(..., description="Version to switch to")
|
||||||
|
client_id: str = Field(
|
||||||
|
..., description="Client identifier that initiated the request"
|
||||||
|
)
|
||||||
|
ui_id: str = Field(..., description="UI identifier for task tracking")
|
||||||
|
|
||||||
|
|
||||||
class QueueStatus(BaseModel):
|
class QueueStatus(BaseModel):
|
||||||
total_count: int = Field(
|
total_count: int = Field(
|
||||||
..., description='Total number of tasks (pending + running)'
|
..., description="Total number of tasks (pending + running)"
|
||||||
)
|
)
|
||||||
done_count: int = Field(..., description='Number of completed tasks')
|
done_count: int = Field(..., description="Number of completed tasks")
|
||||||
in_progress_count: int = Field(..., description='Number of tasks currently running')
|
in_progress_count: int = Field(..., description="Number of tasks currently running")
|
||||||
pending_count: Optional[int] = Field(
|
pending_count: Optional[int] = Field(
|
||||||
None, description='Number of tasks waiting to be executed'
|
None, description="Number of tasks waiting to be executed"
|
||||||
)
|
)
|
||||||
is_processing: bool = Field(..., description='Whether the task worker is active')
|
is_processing: bool = Field(..., description="Whether the task worker is active")
|
||||||
client_id: Optional[str] = Field(
|
client_id: Optional[str] = Field(
|
||||||
None, description='Client ID (when filtered by client)'
|
None, description="Client ID (when filtered by client)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ManagerMappings1(BaseModel):
|
class ManagerMappings1(BaseModel):
|
||||||
title_aux: Optional[str] = Field(None, description='The display name of the pack')
|
title_aux: Optional[str] = Field(None, description="The display name of the pack")
|
||||||
|
|
||||||
|
|
||||||
class ManagerMappings(
|
class ManagerMappings(
|
||||||
RootModel[Optional[Dict[str, List[Union[List[str], ManagerMappings1]]]]]
|
RootModel[Optional[Dict[str, List[Union[List[str], ManagerMappings1]]]]]
|
||||||
):
|
):
|
||||||
root: Optional[Dict[str, List[Union[List[str], ManagerMappings1]]]] = Field(
|
root: Optional[Dict[str, List[Union[List[str], ManagerMappings1]]]] = Field(
|
||||||
None, description='Tuple of [node_names, metadata]'
|
None, description="Tuple of [node_names, metadata]"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ModelMetadata(BaseModel):
|
class ModelMetadata(BaseModel):
|
||||||
name: str = Field(..., description='Name of the model')
|
name: str = Field(..., description="Name of the model")
|
||||||
type: str = Field(..., description='Type of model')
|
type: str = Field(..., description="Type of model")
|
||||||
base: Optional[str] = Field(None, description='Base model type')
|
base: Optional[str] = Field(None, description="Base model type")
|
||||||
save_path: Optional[str] = Field(None, description='Path for saving the model')
|
save_path: Optional[str] = Field(None, description="Path for saving the model")
|
||||||
url: str = Field(..., description='Download URL')
|
url: str = Field(..., description="Download URL")
|
||||||
filename: str = Field(..., description='Target filename')
|
filename: str = Field(..., description="Target filename")
|
||||||
ui_id: Optional[str] = Field(None, description='ID for UI reference')
|
ui_id: Optional[str] = Field(None, description="ID for UI reference")
|
||||||
|
|
||||||
|
|
||||||
class InstallType(str, Enum):
|
class InstallType(str, Enum):
|
||||||
git = 'git'
|
git = "git"
|
||||||
copy = 'copy'
|
copy = "copy"
|
||||||
pip = 'pip'
|
pip = "pip"
|
||||||
|
|
||||||
|
|
||||||
class NodePackageMetadata(BaseModel):
|
class NodePackageMetadata(BaseModel):
|
||||||
title: Optional[str] = Field(None, description='Display name of the node package')
|
title: Optional[str] = Field(None, description="Display name of the node package")
|
||||||
name: Optional[str] = Field(None, description='Repository/package name')
|
name: Optional[str] = Field(None, description="Repository/package name")
|
||||||
files: Optional[List[str]] = Field(None, description='Source URLs for the package')
|
files: Optional[List[str]] = Field(None, description="Source URLs for the package")
|
||||||
description: Optional[str] = Field(
|
description: Optional[str] = Field(
|
||||||
None, description='Description of the node package functionality'
|
None, description="Description of the node package functionality"
|
||||||
)
|
)
|
||||||
install_type: Optional[InstallType] = Field(None, description='Installation method')
|
install_type: Optional[InstallType] = Field(None, description="Installation method")
|
||||||
version: Optional[str] = Field(None, description='Version identifier')
|
version: Optional[str] = Field(None, description="Version identifier")
|
||||||
id: Optional[str] = Field(
|
id: Optional[str] = Field(
|
||||||
None, description='Unique identifier for the node package'
|
None, description="Unique identifier for the node package"
|
||||||
)
|
)
|
||||||
ui_id: Optional[str] = Field(None, description='ID for UI reference')
|
ui_id: Optional[str] = Field(None, description="ID for UI reference")
|
||||||
channel: Optional[str] = Field(None, description='Source channel')
|
channel: Optional[str] = Field(None, description="Source channel")
|
||||||
mode: Optional[str] = Field(None, description='Source mode')
|
mode: Optional[str] = Field(None, description="Source mode")
|
||||||
|
|
||||||
|
|
||||||
class SnapshotItem(RootModel[str]):
|
class SnapshotItem(RootModel[str]):
|
||||||
root: str = Field(..., description='Name of the snapshot')
|
root: str = Field(..., description="Name of the snapshot")
|
||||||
|
|
||||||
|
|
||||||
class Error(BaseModel):
|
class Error(BaseModel):
|
||||||
error: str = Field(..., description='Error message')
|
error: str = Field(..., description="Error message")
|
||||||
|
|
||||||
|
|
||||||
class InstalledPacksResponse(RootModel[Optional[Dict[str, ManagerPackInstalled]]]):
|
class InstalledPacksResponse(RootModel[Optional[Dict[str, ManagerPackInstalled]]]):
|
||||||
@@ -265,142 +311,171 @@ class InstalledPacksResponse(RootModel[Optional[Dict[str, ManagerPackInstalled]]
|
|||||||
|
|
||||||
class HistoryListResponse(BaseModel):
|
class HistoryListResponse(BaseModel):
|
||||||
ids: Optional[List[str]] = Field(
|
ids: Optional[List[str]] = Field(
|
||||||
None, description='List of available batch history IDs'
|
None, description="List of available batch history IDs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class InstalledNodeInfo(BaseModel):
|
class InstalledNodeInfo(BaseModel):
|
||||||
name: str = Field(..., description='Node package name')
|
name: str = Field(..., description="Node package name")
|
||||||
version: str = Field(..., description='Installed version')
|
version: str = Field(..., description="Installed version")
|
||||||
repository_url: Optional[str] = Field(None, description='Git repository URL')
|
repository_url: Optional[str] = Field(None, description="Git repository URL")
|
||||||
install_method: str = Field(
|
install_method: str = Field(
|
||||||
..., description='Installation method (cnr, git, pip, etc.)'
|
..., description="Installation method (cnr, git, pip, etc.)"
|
||||||
)
|
)
|
||||||
enabled: Optional[bool] = Field(
|
enabled: Optional[bool] = Field(
|
||||||
True, description='Whether the node is currently enabled'
|
True, description="Whether the node is currently enabled"
|
||||||
)
|
)
|
||||||
install_date: Optional[datetime] = Field(
|
install_date: Optional[datetime] = Field(
|
||||||
None, description='ISO timestamp of installation'
|
None, description="ISO timestamp of installation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class InstalledModelInfo(BaseModel):
|
class InstalledModelInfo(BaseModel):
|
||||||
name: str = Field(..., description='Model filename')
|
name: str = Field(..., description="Model filename")
|
||||||
path: str = Field(..., description='Full path to model file')
|
path: str = Field(..., description="Full path to model file")
|
||||||
type: str = Field(..., description='Model type (checkpoint, lora, vae, etc.)')
|
type: str = Field(..., description="Model type (checkpoint, lora, vae, etc.)")
|
||||||
size_bytes: Optional[int] = Field(None, description='File size in bytes', ge=0)
|
size_bytes: Optional[int] = Field(None, description="File size in bytes", ge=0)
|
||||||
hash: Optional[str] = Field(None, description='Model file hash for verification')
|
hash: Optional[str] = Field(None, description="Model file hash for verification")
|
||||||
install_date: Optional[datetime] = Field(
|
install_date: Optional[datetime] = Field(
|
||||||
None, description='ISO timestamp when added'
|
None, description="ISO timestamp when added"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ComfyUIVersionInfo(BaseModel):
|
class ComfyUIVersionInfo(BaseModel):
|
||||||
version: str = Field(..., description='ComfyUI version string')
|
version: str = Field(..., description="ComfyUI version string")
|
||||||
commit_hash: Optional[str] = Field(None, description='Git commit hash')
|
commit_hash: Optional[str] = Field(None, description="Git commit hash")
|
||||||
branch: Optional[str] = Field(None, description='Git branch name')
|
branch: Optional[str] = Field(None, description="Git branch name")
|
||||||
is_stable: Optional[bool] = Field(
|
is_stable: Optional[bool] = Field(
|
||||||
False, description='Whether this is a stable release'
|
False, description="Whether this is a stable release"
|
||||||
)
|
)
|
||||||
last_updated: Optional[datetime] = Field(
|
last_updated: Optional[datetime] = Field(
|
||||||
None, description='ISO timestamp of last update'
|
None, description="ISO timestamp of last update"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class OperationType(str, Enum):
|
|
||||||
install = 'install'
|
|
||||||
update = 'update'
|
|
||||||
uninstall = 'uninstall'
|
|
||||||
fix = 'fix'
|
|
||||||
disable = 'disable'
|
|
||||||
enable = 'enable'
|
|
||||||
install_model = 'install-model'
|
|
||||||
|
|
||||||
|
|
||||||
class Result(str, Enum):
|
|
||||||
success = 'success'
|
|
||||||
failed = 'failed'
|
|
||||||
skipped = 'skipped'
|
|
||||||
|
|
||||||
|
|
||||||
class BatchOperation(BaseModel):
|
class BatchOperation(BaseModel):
|
||||||
operation_id: str = Field(..., description='Unique operation identifier')
|
operation_id: str = Field(..., description="Unique operation identifier")
|
||||||
operation_type: OperationType = Field(..., description='Type of operation')
|
operation_type: OperationType
|
||||||
target: str = Field(
|
target: str = Field(
|
||||||
..., description='Target of the operation (node name, model name, etc.)'
|
..., description="Target of the operation (node name, model name, etc.)"
|
||||||
)
|
)
|
||||||
target_version: Optional[str] = Field(
|
target_version: Optional[str] = Field(
|
||||||
None, description='Target version for the operation'
|
None, description="Target version for the operation"
|
||||||
)
|
)
|
||||||
result: Result = Field(..., description='Operation result')
|
result: OperationResult
|
||||||
error_message: Optional[str] = Field(
|
error_message: Optional[str] = Field(
|
||||||
None, description='Error message if operation failed'
|
None, description="Error message if operation failed"
|
||||||
)
|
)
|
||||||
start_time: datetime = Field(
|
start_time: datetime = Field(
|
||||||
..., description='ISO timestamp when operation started'
|
..., description="ISO timestamp when operation started"
|
||||||
)
|
)
|
||||||
end_time: Optional[datetime] = Field(
|
end_time: Optional[datetime] = Field(
|
||||||
None, description='ISO timestamp when operation completed'
|
None, description="ISO timestamp when operation completed"
|
||||||
)
|
)
|
||||||
client_id: Optional[str] = Field(
|
client_id: Optional[str] = Field(
|
||||||
None, description='Client that initiated the operation'
|
None, description="Client that initiated the operation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ComfyUISystemState(BaseModel):
|
class ComfyUISystemState(BaseModel):
|
||||||
snapshot_time: datetime = Field(
|
snapshot_time: datetime = Field(
|
||||||
..., description='ISO timestamp when snapshot was taken'
|
..., description="ISO timestamp when snapshot was taken"
|
||||||
)
|
)
|
||||||
comfyui_version: ComfyUIVersionInfo
|
comfyui_version: ComfyUIVersionInfo
|
||||||
frontend_version: Optional[str] = Field(
|
frontend_version: Optional[str] = Field(
|
||||||
None, description='ComfyUI frontend version if available'
|
None, description="ComfyUI frontend version if available"
|
||||||
)
|
)
|
||||||
python_version: str = Field(..., description='Python interpreter version')
|
python_version: str = Field(..., description="Python interpreter version")
|
||||||
platform_info: str = Field(
|
platform_info: str = Field(
|
||||||
..., description='Operating system and platform information'
|
..., description="Operating system and platform information"
|
||||||
)
|
)
|
||||||
installed_nodes: Optional[Dict[str, InstalledNodeInfo]] = Field(
|
installed_nodes: Optional[Dict[str, InstalledNodeInfo]] = Field(
|
||||||
None, description='Map of installed node packages by name'
|
None, description="Map of installed node packages by name"
|
||||||
)
|
)
|
||||||
installed_models: Optional[Dict[str, InstalledModelInfo]] = Field(
|
installed_models: Optional[Dict[str, InstalledModelInfo]] = Field(
|
||||||
None, description='Map of installed models by name'
|
None, description="Map of installed models by name"
|
||||||
)
|
)
|
||||||
manager_config: Optional[Dict[str, Any]] = Field(
|
manager_config: Optional[Dict[str, Any]] = Field(
|
||||||
None, description='ComfyUI Manager configuration settings'
|
None, description="ComfyUI Manager configuration settings"
|
||||||
|
)
|
||||||
|
comfyui_root_path: Optional[str] = Field(
|
||||||
|
None, description="ComfyUI root installation directory"
|
||||||
|
)
|
||||||
|
model_paths: Optional[Dict[str, List[str]]] = Field(
|
||||||
|
None, description="Map of model types to their configured paths"
|
||||||
|
)
|
||||||
|
manager_version: Optional[str] = Field(None, description="ComfyUI Manager version")
|
||||||
|
security_level: Optional[SecurityLevel] = None
|
||||||
|
network_mode: Optional[str] = Field(
|
||||||
|
None, description="Network mode (online, offline, private)"
|
||||||
|
)
|
||||||
|
cli_args: Optional[Dict[str, Any]] = Field(
|
||||||
|
None, description="Selected ComfyUI CLI arguments"
|
||||||
|
)
|
||||||
|
custom_nodes_count: Optional[int] = Field(
|
||||||
|
None, description="Total number of custom node packages", ge=0
|
||||||
|
)
|
||||||
|
failed_imports: Optional[List[str]] = Field(
|
||||||
|
None, description="List of custom nodes that failed to import"
|
||||||
|
)
|
||||||
|
pip_packages: Optional[Dict[str, str]] = Field(
|
||||||
|
None, description="Map of installed pip packages to their versions"
|
||||||
|
)
|
||||||
|
embedded_python: Optional[bool] = Field(
|
||||||
|
None,
|
||||||
|
description="Whether ComfyUI is running from an embedded Python distribution",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BatchExecutionRecord(BaseModel):
|
class BatchExecutionRecord(BaseModel):
|
||||||
batch_id: str = Field(..., description='Unique batch identifier')
|
batch_id: str = Field(..., description="Unique batch identifier")
|
||||||
start_time: datetime = Field(..., description='ISO timestamp when batch started')
|
start_time: datetime = Field(..., description="ISO timestamp when batch started")
|
||||||
end_time: Optional[datetime] = Field(
|
end_time: Optional[datetime] = Field(
|
||||||
None, description='ISO timestamp when batch completed'
|
None, description="ISO timestamp when batch completed"
|
||||||
)
|
)
|
||||||
state_before: ComfyUISystemState
|
state_before: ComfyUISystemState
|
||||||
state_after: Optional[ComfyUISystemState] = Field(
|
state_after: Optional[ComfyUISystemState] = Field(
|
||||||
None, description='System state after batch execution'
|
None, description="System state after batch execution"
|
||||||
)
|
)
|
||||||
operations: Optional[List[BatchOperation]] = Field(
|
operations: Optional[List[BatchOperation]] = Field(
|
||||||
None, description='List of operations performed in this batch'
|
None, description="List of operations performed in this batch"
|
||||||
)
|
)
|
||||||
total_operations: Optional[int] = Field(
|
total_operations: Optional[int] = Field(
|
||||||
0, description='Total number of operations in batch', ge=0
|
0, description="Total number of operations in batch", ge=0
|
||||||
)
|
)
|
||||||
successful_operations: Optional[int] = Field(
|
successful_operations: Optional[int] = Field(
|
||||||
0, description='Number of successful operations', ge=0
|
0, description="Number of successful operations", ge=0
|
||||||
)
|
)
|
||||||
failed_operations: Optional[int] = Field(
|
failed_operations: Optional[int] = Field(
|
||||||
0, description='Number of failed operations', ge=0
|
0, description="Number of failed operations", ge=0
|
||||||
)
|
)
|
||||||
skipped_operations: Optional[int] = Field(
|
skipped_operations: Optional[int] = Field(
|
||||||
0, description='Number of skipped operations', ge=0
|
0, description="Number of skipped operations", ge=0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportFailInfoBulkRequest(BaseModel):
|
||||||
|
cnr_ids: Optional[List[str]] = Field(
|
||||||
|
None, description="A list of CNR IDs to check."
|
||||||
|
)
|
||||||
|
urls: Optional[List[str]] = Field(
|
||||||
|
None, description="A list of repository URLs to check."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportFailInfoItem1(BaseModel):
|
||||||
|
error: Optional[str] = None
|
||||||
|
traceback: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ImportFailInfoItem(RootModel[Optional[ImportFailInfoItem1]]):
|
||||||
|
root: Optional[ImportFailInfoItem1]
|
||||||
|
|
||||||
|
|
||||||
class QueueTaskItem(BaseModel):
|
class QueueTaskItem(BaseModel):
|
||||||
ui_id: str = Field(..., description='Unique identifier for the task')
|
ui_id: str = Field(..., description="Unique identifier for the task")
|
||||||
client_id: str = Field(..., description='Client identifier that initiated the task')
|
client_id: str = Field(..., description="Client identifier that initiated the task")
|
||||||
kind: Kind = Field(..., description='Type of task being performed')
|
kind: OperationType
|
||||||
params: Union[
|
params: Union[
|
||||||
InstallPackParams,
|
InstallPackParams,
|
||||||
UpdatePackParams,
|
UpdatePackParams,
|
||||||
@@ -415,50 +490,56 @@ class QueueTaskItem(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class TaskHistoryItem(BaseModel):
|
class TaskHistoryItem(BaseModel):
|
||||||
ui_id: str = Field(..., description='Unique identifier for the task')
|
ui_id: str = Field(..., description="Unique identifier for the task")
|
||||||
client_id: str = Field(..., description='Client identifier that initiated the task')
|
client_id: str = Field(..., description="Client identifier that initiated the task")
|
||||||
kind: str = Field(..., description='Type of task that was performed')
|
kind: str = Field(..., description="Type of task that was performed")
|
||||||
timestamp: datetime = Field(..., description='ISO timestamp when task completed')
|
timestamp: datetime = Field(..., description="ISO timestamp when task completed")
|
||||||
result: str = Field(..., description='Task result message or details')
|
result: str = Field(..., description="Task result message or details")
|
||||||
status: Optional[TaskExecutionStatus] = None
|
status: Optional[TaskExecutionStatus] = None
|
||||||
|
batch_id: Optional[str] = Field(
|
||||||
|
None, description="ID of the batch this task belongs to"
|
||||||
|
)
|
||||||
|
end_time: Optional[datetime] = Field(
|
||||||
|
None, description="ISO timestamp when task execution ended"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TaskStateMessage(BaseModel):
|
class TaskStateMessage(BaseModel):
|
||||||
history: Dict[str, TaskHistoryItem] = Field(
|
history: Dict[str, TaskHistoryItem] = Field(
|
||||||
..., description='Map of task IDs to their history items'
|
..., description="Map of task IDs to their history items"
|
||||||
)
|
)
|
||||||
running_queue: List[QueueTaskItem] = Field(
|
running_queue: List[QueueTaskItem] = Field(
|
||||||
..., description='Currently executing tasks'
|
..., description="Currently executing tasks"
|
||||||
)
|
)
|
||||||
pending_queue: List[QueueTaskItem] = Field(
|
pending_queue: List[QueueTaskItem] = Field(
|
||||||
..., description='Tasks waiting to be executed'
|
..., description="Tasks waiting to be executed"
|
||||||
)
|
)
|
||||||
installed_packs: Dict[str, ManagerPackInstalled] = Field(
|
installed_packs: Dict[str, ManagerPackInstalled] = Field(
|
||||||
..., description='Map of currently installed node packages by name'
|
..., description="Map of currently installed node packages by name"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MessageTaskDone(BaseModel):
|
class MessageTaskDone(BaseModel):
|
||||||
ui_id: str = Field(..., description='Task identifier')
|
ui_id: str = Field(..., description="Task identifier")
|
||||||
result: str = Field(..., description='Task result message')
|
result: str = Field(..., description="Task result message")
|
||||||
kind: str = Field(..., description='Type of task')
|
kind: str = Field(..., description="Type of task")
|
||||||
status: Optional[TaskExecutionStatus] = None
|
status: Optional[TaskExecutionStatus] = None
|
||||||
timestamp: datetime = Field(..., description='ISO timestamp when task completed')
|
timestamp: datetime = Field(..., description="ISO timestamp when task completed")
|
||||||
state: TaskStateMessage
|
state: TaskStateMessage
|
||||||
|
|
||||||
|
|
||||||
class MessageTaskStarted(BaseModel):
|
class MessageTaskStarted(BaseModel):
|
||||||
ui_id: str = Field(..., description='Task identifier')
|
ui_id: str = Field(..., description="Task identifier")
|
||||||
kind: str = Field(..., description='Type of task')
|
kind: str = Field(..., description="Type of task")
|
||||||
timestamp: datetime = Field(..., description='ISO timestamp when task started')
|
timestamp: datetime = Field(..., description="ISO timestamp when task started")
|
||||||
state: TaskStateMessage
|
state: TaskStateMessage
|
||||||
|
|
||||||
|
|
||||||
class MessageTaskFailed(BaseModel):
|
class MessageTaskFailed(BaseModel):
|
||||||
ui_id: str = Field(..., description='Task identifier')
|
ui_id: str = Field(..., description="Task identifier")
|
||||||
error: str = Field(..., description='Error message')
|
error: str = Field(..., description="Error message")
|
||||||
kind: str = Field(..., description='Type of task')
|
kind: str = Field(..., description="Type of task")
|
||||||
timestamp: datetime = Field(..., description='ISO timestamp when task failed')
|
timestamp: datetime = Field(..., description="ISO timestamp when task failed")
|
||||||
state: TaskStateMessage
|
state: TaskStateMessage
|
||||||
|
|
||||||
|
|
||||||
@@ -466,11 +547,15 @@ class MessageUpdate(
|
|||||||
RootModel[Union[MessageTaskDone, MessageTaskStarted, MessageTaskFailed]]
|
RootModel[Union[MessageTaskDone, MessageTaskStarted, MessageTaskFailed]]
|
||||||
):
|
):
|
||||||
root: Union[MessageTaskDone, MessageTaskStarted, MessageTaskFailed] = Field(
|
root: Union[MessageTaskDone, MessageTaskStarted, MessageTaskFailed] = Field(
|
||||||
..., description='Union type for all possible WebSocket message updates'
|
..., description="Union type for all possible WebSocket message updates"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class HistoryResponse(BaseModel):
|
class HistoryResponse(BaseModel):
|
||||||
history: Optional[Dict[str, TaskHistoryItem]] = Field(
|
history: Optional[Dict[str, TaskHistoryItem]] = Field(
|
||||||
None, description='Map of task IDs to their history items'
|
None, description="Map of task IDs to their history items"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportFailInfoBulkResponse(RootModel[Optional[Dict[str, ImportFailInfoItem]]]):
|
||||||
|
root: Optional[Dict[str, ImportFailInfoItem]] = None
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,3 +8,4 @@
|
|||||||
7. Adjust the `__init__.py` files in the `data_models` directory to match/export the new data model
|
7. Adjust the `__init__.py` files in the `data_models` directory to match/export the new data model
|
||||||
8. Only then, make the changes to the rest of the codebase
|
8. Only then, make the changes to the rest of the codebase
|
||||||
9. Run the CI tests to verify that the changes are working
|
9. Run the CI tests to verify that the changes are working
|
||||||
|
- The comfyui_manager is a python package that is used to manage the comfyui server. There are two sub-packages `glob` and `legacy`. These represent the current version (`glob`) and the previous version (`legacy`), not including common utilities and data models. When developing, we work in the `glob` package. You can ignore the `legacy` package entirely, unless you have a very good reason to research how things were done in the legacy or prior major versions of the package. But in those cases, you should just look for the sake of knowledge or reflection, not for changing code (unless explicitly asked to do so).
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from comfy.cli_args import args
|
|
||||||
|
|
||||||
SECURITY_MESSAGE_MIDDLE_OR_BELOW = "ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
SECURITY_MESSAGE_MIDDLE = "ERROR: To use this action, a security_level of `normal or below` is required. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
||||||
|
SECURITY_MESSAGE_MIDDLE_P = "ERROR: To use this action, security_level must be `normal or below`, and network_mode must be set to `personal_cloud`. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
||||||
SECURITY_MESSAGE_NORMAL_MINUS = "ERROR: To use this feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
SECURITY_MESSAGE_NORMAL_MINUS = "ERROR: To use this feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
||||||
SECURITY_MESSAGE_GENERAL = "ERROR: This installation is not allowed in this security_level. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
SECURITY_MESSAGE_GENERAL = "ERROR: This installation is not allowed in this security_level. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
||||||
SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in '.safetensors' format is only allowed for models registered in the 'default' channel at this security level. If you want to download this model, set the security level to 'normal-' or lower."
|
SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in '.safetensors' format is only allowed for models registered in the 'default' channel at this security level. If you want to download this model, set the security level to 'normal-' or lower."
|
||||||
@@ -15,9 +15,6 @@ def is_loopback(address):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
is_local_mode = is_loopback(args.listen)
|
|
||||||
|
|
||||||
|
|
||||||
model_dir_name_map = {
|
model_dir_name_map = {
|
||||||
"checkpoints": "checkpoints",
|
"checkpoints": "checkpoints",
|
||||||
"checkpoint": "checkpoints",
|
"checkpoint": "checkpoints",
|
||||||
@@ -37,3 +34,22 @@ model_dir_name_map = {
|
|||||||
"unet": "diffusion_models",
|
"unet": "diffusion_models",
|
||||||
"diffusion_model": "diffusion_models",
|
"diffusion_model": "diffusion_models",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# List of all model directory names used for checking installed models
|
||||||
|
MODEL_DIR_NAMES = [
|
||||||
|
"checkpoints",
|
||||||
|
"loras",
|
||||||
|
"vae",
|
||||||
|
"text_encoders",
|
||||||
|
"diffusion_models",
|
||||||
|
"clip_vision",
|
||||||
|
"embeddings",
|
||||||
|
"diffusers",
|
||||||
|
"vae_approx",
|
||||||
|
"controlnet",
|
||||||
|
"gligen",
|
||||||
|
"upscale_models",
|
||||||
|
"hypernetworks",
|
||||||
|
"photomaker",
|
||||||
|
"classifiers",
|
||||||
|
]
|
||||||
|
|||||||
@@ -41,11 +41,12 @@ from ..common.enums import NetworkMode, SecurityLevel, DBMode
|
|||||||
from ..common import context
|
from ..common import context
|
||||||
|
|
||||||
|
|
||||||
version_code = [4, 0]
|
version_code = [4, 0, 1]
|
||||||
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 '')
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_CHANNEL = "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main"
|
DEFAULT_CHANNEL = "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main"
|
||||||
|
DEFAULT_CHANNEL_LEGACY = "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main"
|
||||||
|
|
||||||
|
|
||||||
default_custom_nodes_path = None
|
default_custom_nodes_path = None
|
||||||
@@ -153,14 +154,8 @@ def check_invalid_nodes():
|
|||||||
cached_config = None
|
cached_config = None
|
||||||
js_path = None
|
js_path = None
|
||||||
|
|
||||||
comfy_ui_required_revision = 1930
|
|
||||||
comfy_ui_required_commit_datetime = datetime(2024, 1, 24, 0, 0, 0)
|
|
||||||
|
|
||||||
comfy_ui_revision = "Unknown"
|
|
||||||
comfy_ui_commit_datetime = datetime(1900, 1, 1, 0, 0, 0)
|
|
||||||
|
|
||||||
channel_dict = None
|
channel_dict = None
|
||||||
valid_channels = {'default', 'local'}
|
valid_channels = {'default', 'local', DEFAULT_CHANNEL, DEFAULT_CHANNEL_LEGACY}
|
||||||
channel_list = None
|
channel_list = None
|
||||||
|
|
||||||
|
|
||||||
@@ -304,18 +299,86 @@ class ManagedResult:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class NormalizedKeyDict:
|
||||||
|
def __init__(self):
|
||||||
|
self._store = {}
|
||||||
|
self._key_map = {}
|
||||||
|
|
||||||
|
def _normalize_key(self, key):
|
||||||
|
if isinstance(key, str):
|
||||||
|
return key.strip().lower()
|
||||||
|
return key
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
norm_key = self._normalize_key(key)
|
||||||
|
self._key_map[norm_key] = key
|
||||||
|
self._store[key] = value
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
norm_key = self._normalize_key(key)
|
||||||
|
original_key = self._key_map[norm_key]
|
||||||
|
return self._store[original_key]
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
norm_key = self._normalize_key(key)
|
||||||
|
original_key = self._key_map.pop(norm_key)
|
||||||
|
del self._store[original_key]
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
return self._normalize_key(key) in self._key_map
|
||||||
|
|
||||||
|
def get(self, key, default=None):
|
||||||
|
return self[key] if key in self else default
|
||||||
|
|
||||||
|
def setdefault(self, key, default=None):
|
||||||
|
if key in self:
|
||||||
|
return self[key]
|
||||||
|
self[key] = default
|
||||||
|
return default
|
||||||
|
|
||||||
|
def pop(self, key, default=None):
|
||||||
|
if key in self:
|
||||||
|
val = self[key]
|
||||||
|
del self[key]
|
||||||
|
return val
|
||||||
|
if default is not None:
|
||||||
|
return default
|
||||||
|
raise KeyError(key)
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return self._store.keys()
|
||||||
|
|
||||||
|
def values(self):
|
||||||
|
return self._store.values()
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
return self._store.items()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._store)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._store)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return repr(self._store)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return dict(self._store)
|
||||||
|
|
||||||
|
|
||||||
class UnifiedManager:
|
class UnifiedManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.installed_node_packages: dict[str, InstalledNodePackage] = {}
|
self.installed_node_packages: dict[str, InstalledNodePackage] = {}
|
||||||
|
|
||||||
self.cnr_inactive_nodes = {} # node_id -> node_version -> fullpath
|
self.cnr_inactive_nodes = NormalizedKeyDict() # node_id -> node_version -> fullpath
|
||||||
self.nightly_inactive_nodes = {} # node_id -> fullpath
|
self.nightly_inactive_nodes = NormalizedKeyDict() # node_id -> fullpath
|
||||||
self.unknown_inactive_nodes = {} # node_id -> repo url * fullpath
|
self.unknown_inactive_nodes = {} # node_id -> repo url * fullpath
|
||||||
self.active_nodes = {} # node_id -> node_version * fullpath
|
self.active_nodes = NormalizedKeyDict() # node_id -> node_version * fullpath
|
||||||
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
|
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
|
||||||
self.cnr_map = {} # node_id -> cnr info
|
self.cnr_map = NormalizedKeyDict() # node_id -> cnr info
|
||||||
self.repo_cnr_map = {} # repo_url -> cnr info
|
self.repo_cnr_map = {} # repo_url -> cnr info
|
||||||
self.custom_node_map_cache = {} # (channel, mode) -> augmented custom node list json
|
self.custom_node_map_cache = {} # (channel, mode) -> augmented custom node list json
|
||||||
self.processed_install = set()
|
self.processed_install = set()
|
||||||
|
|
||||||
def get_module_name(self, x):
|
def get_module_name(self, x):
|
||||||
@@ -721,7 +784,7 @@ class UnifiedManager:
|
|||||||
channel = normalize_channel(channel)
|
channel = normalize_channel(channel)
|
||||||
nodes = await self.load_nightly(channel, mode)
|
nodes = await self.load_nightly(channel, mode)
|
||||||
|
|
||||||
res = {}
|
res = NormalizedKeyDict()
|
||||||
added_cnr = set()
|
added_cnr = set()
|
||||||
for v in nodes.values():
|
for v in nodes.values():
|
||||||
v = v[0]
|
v = v[0]
|
||||||
@@ -1411,7 +1474,7 @@ def identify_node_pack_from_path(fullpath):
|
|||||||
# cnr
|
# cnr
|
||||||
cnr = cnr_utils.read_cnr_info(fullpath)
|
cnr = cnr_utils.read_cnr_info(fullpath)
|
||||||
if cnr is not None:
|
if cnr is not None:
|
||||||
return module_name, cnr['version'], cnr['id'], None
|
return module_name, cnr['version'], cnr['original_name'], None
|
||||||
|
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -1461,7 +1524,10 @@ def get_installed_node_packs():
|
|||||||
if info is None:
|
if info is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
res[info[0]] = { 'ver': info[1], 'cnr_id': info[2], 'aux_id': info[3], 'enabled': False }
|
# NOTE: don't add disabled nodepack if there is enabled nodepack
|
||||||
|
original_name = info[0].split('@')[0]
|
||||||
|
if original_name not in res:
|
||||||
|
res[info[0]] = { 'ver': info[1], 'cnr_id': info[2], 'aux_id': info[3], 'enabled': False }
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -1558,16 +1624,18 @@ def read_config():
|
|||||||
config = configparser.ConfigParser(strict=False)
|
config = configparser.ConfigParser(strict=False)
|
||||||
config.read(context.manager_config_path)
|
config.read(context.manager_config_path)
|
||||||
default_conf = config['default']
|
default_conf = config['default']
|
||||||
manager_util.use_uv = default_conf['use_uv'].lower() == 'true' if 'use_uv' in default_conf else False
|
|
||||||
|
|
||||||
def get_bool(key, default_value):
|
def get_bool(key, default_value):
|
||||||
return default_conf[key].lower() == 'true' if key in default_conf else False
|
return default_conf[key].lower() == 'true' if key 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)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'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', ''),
|
||||||
'use_uv': get_bool('use_uv', False),
|
'use_uv': get_bool('use_uv', True),
|
||||||
'channel_url': default_conf.get('channel_url', DEFAULT_CHANNEL),
|
'channel_url': default_conf.get('channel_url', DEFAULT_CHANNEL),
|
||||||
'default_cache_as_channel_url': get_bool('default_cache_as_channel_url', False),
|
'default_cache_as_channel_url': get_bool('default_cache_as_channel_url', False),
|
||||||
'share_option': default_conf.get('share_option', 'all').lower(),
|
'share_option': default_conf.get('share_option', 'all').lower(),
|
||||||
@@ -1585,16 +1653,20 @@ def read_config():
|
|||||||
}
|
}
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
manager_util.use_uv = False
|
import importlib.util
|
||||||
|
# temporary disable `uv` on Windows by default (https://github.com/Comfy-Org/ComfyUI-Manager/issues/1969)
|
||||||
|
manager_util.use_uv = importlib.util.find_spec("uv") is not None and platform.system() != "Windows"
|
||||||
|
manager_util.bypass_ssl = False
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'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': '',
|
||||||
'use_uv': False,
|
'use_uv': manager_util.use_uv,
|
||||||
'channel_url': DEFAULT_CHANNEL,
|
'channel_url': DEFAULT_CHANNEL,
|
||||||
'default_cache_as_channel_url': False,
|
'default_cache_as_channel_url': False,
|
||||||
'share_option': 'all',
|
'share_option': 'all',
|
||||||
'bypass_ssl': False,
|
'bypass_ssl': manager_util.bypass_ssl,
|
||||||
'file_logging': True,
|
'file_logging': True,
|
||||||
'component_policy': 'workflow',
|
'component_policy': 'workflow',
|
||||||
'update_policy': 'stable-comfyui',
|
'update_policy': 'stable-comfyui',
|
||||||
@@ -1712,16 +1784,6 @@ def try_install_script(url, repo_path, install_cmd, instant_execution=False):
|
|||||||
print(f"\n## ComfyUI-Manager: EXECUTE => {install_cmd}")
|
print(f"\n## ComfyUI-Manager: EXECUTE => {install_cmd}")
|
||||||
code = manager_funcs.run_script(install_cmd, cwd=repo_path)
|
code = manager_funcs.run_script(install_cmd, cwd=repo_path)
|
||||||
|
|
||||||
if platform.system() != "Windows":
|
|
||||||
try:
|
|
||||||
if not os.environ.get('__COMFYUI_DESKTOP_VERSION__') and comfy_ui_commit_datetime.date() < comfy_ui_required_commit_datetime.date():
|
|
||||||
print("\n\n###################################################################")
|
|
||||||
print(f"[WARN] ComfyUI-Manager: Your ComfyUI version ({comfy_ui_revision})[{comfy_ui_commit_datetime.date()}] is too old. Please update to the latest version.")
|
|
||||||
print("[WARN] The extension installation feature may not work properly in the current installed ComfyUI version on Windows environment.")
|
|
||||||
print("###################################################################\n\n")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if code != 0:
|
if code != 0:
|
||||||
if url is None:
|
if url is None:
|
||||||
url = os.path.dirname(repo_path)
|
url = os.path.dirname(repo_path)
|
||||||
@@ -2791,7 +2853,7 @@ async def get_unified_total_nodes(channel, mode, regsitry_cache_mode='cache'):
|
|||||||
|
|
||||||
if cnr_id is not None:
|
if cnr_id is not None:
|
||||||
# cnr or nightly version
|
# cnr or nightly version
|
||||||
cnr_ids.remove(cnr_id)
|
cnr_ids.discard(cnr_id)
|
||||||
updatable = False
|
updatable = False
|
||||||
cnr = unified_manager.cnr_map[cnr_id]
|
cnr = unified_manager.cnr_map[cnr_id]
|
||||||
|
|
||||||
@@ -2955,6 +3017,11 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
info = yaml.load(snapshot_file, Loader=yaml.SafeLoader)
|
info = yaml.load(snapshot_file, Loader=yaml.SafeLoader)
|
||||||
info = info['custom_nodes']
|
info = info['custom_nodes']
|
||||||
|
|
||||||
|
if 'pips' in info and info['pips']:
|
||||||
|
pips = info['pips']
|
||||||
|
else:
|
||||||
|
pips = {}
|
||||||
|
|
||||||
# for cnr restore
|
# for cnr restore
|
||||||
cnr_info = info.get('cnr_custom_nodes')
|
cnr_info = info.get('cnr_custom_nodes')
|
||||||
if cnr_info is not None:
|
if cnr_info is not None:
|
||||||
@@ -3161,6 +3228,8 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
unified_manager.repo_install(repo_url, to_path, instant_execution=True, no_deps=False, return_postinstall=False)
|
unified_manager.repo_install(repo_url, to_path, instant_execution=True, no_deps=False, return_postinstall=False)
|
||||||
cloned_repos.append(repo_name)
|
cloned_repos.append(repo_name)
|
||||||
|
|
||||||
|
manager_util.restore_pip_snapshot(pips, git_helper_extras)
|
||||||
|
|
||||||
# print summary
|
# print summary
|
||||||
for x in cloned_repos:
|
for x in cloned_repos:
|
||||||
print(f"[ INSTALLED ] {x}")
|
print(f"[ INSTALLED ] {x}")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,15 @@ import hashlib
|
|||||||
import folder_paths
|
import folder_paths
|
||||||
from server import PromptServer
|
from server import PromptServer
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from nio import AsyncClient, LoginResponse, UploadResponse
|
||||||
|
matrix_nio_is_available = True
|
||||||
|
except Exception:
|
||||||
|
logging.warning(f"[ComfyUI-Manager] The matrix sharing feature has been disabled because the `matrix-nio` dependency is not installed.\n\tTo use this feature, please run the following command:\n\t{sys.executable} -m pip install matrix-nio\n")
|
||||||
|
matrix_nio_is_available = False
|
||||||
|
|
||||||
|
|
||||||
def extract_model_file_names(json_data):
|
def extract_model_file_names(json_data):
|
||||||
@@ -193,6 +202,14 @@ async def get_esheep_workflow_and_images(request):
|
|||||||
return web.Response(status=200, text=json.dumps(data))
|
return web.Response(status=200, text=json.dumps(data))
|
||||||
|
|
||||||
|
|
||||||
|
@PromptServer.instance.routes.get("/v2/manager/get_matrix_dep_status")
|
||||||
|
async def get_matrix_dep_status(request):
|
||||||
|
if matrix_nio_is_available:
|
||||||
|
return web.Response(status=200, text='available')
|
||||||
|
else:
|
||||||
|
return web.Response(status=200, text='unavailable')
|
||||||
|
|
||||||
|
|
||||||
def set_matrix_auth(json_data):
|
def set_matrix_auth(json_data):
|
||||||
homeserver = json_data['homeserver']
|
homeserver = json_data['homeserver']
|
||||||
username = json_data['username']
|
username = json_data['username']
|
||||||
@@ -332,15 +349,12 @@ async def share_art(request):
|
|||||||
workflowId = upload_workflow_json["workflowId"]
|
workflowId = upload_workflow_json["workflowId"]
|
||||||
|
|
||||||
# check if the user has provided Matrix credentials
|
# check if the user has provided Matrix credentials
|
||||||
if "matrix" in share_destinations:
|
if matrix_nio_is_available and "matrix" in share_destinations:
|
||||||
comfyui_share_room_id = '!LGYSoacpJPhIfBqVfb:matrix.org'
|
comfyui_share_room_id = '!LGYSoacpJPhIfBqVfb:matrix.org'
|
||||||
filename = os.path.basename(asset_filepath)
|
filename = os.path.basename(asset_filepath)
|
||||||
content_type = assetFileType
|
content_type = assetFileType
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from matrix_client.api import MatrixHttpApi
|
|
||||||
from matrix_client.client import MatrixClient
|
|
||||||
|
|
||||||
homeserver = 'matrix.org'
|
homeserver = 'matrix.org'
|
||||||
if matrix_auth:
|
if matrix_auth:
|
||||||
homeserver = matrix_auth.get('homeserver', 'matrix.org')
|
homeserver = matrix_auth.get('homeserver', 'matrix.org')
|
||||||
@@ -348,20 +362,35 @@ async def share_art(request):
|
|||||||
if not homeserver.startswith("https://"):
|
if not homeserver.startswith("https://"):
|
||||||
homeserver = "https://" + homeserver
|
homeserver = "https://" + homeserver
|
||||||
|
|
||||||
client = MatrixClient(homeserver)
|
client = AsyncClient(homeserver, matrix_auth['username'])
|
||||||
try:
|
|
||||||
token = client.login(username=matrix_auth['username'], password=matrix_auth['password'])
|
# Login
|
||||||
if not token:
|
login_resp = await client.login(matrix_auth['password'])
|
||||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
if not isinstance(login_resp, LoginResponse) or not login_resp.access_token:
|
||||||
except Exception:
|
await client.close()
|
||||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
||||||
|
|
||||||
matrix = MatrixHttpApi(homeserver, token=token)
|
# Upload asset
|
||||||
with open(asset_filepath, 'rb') as f:
|
with open(asset_filepath, 'rb') as f:
|
||||||
mxc_url = matrix.media_upload(f.read(), content_type, filename=filename)['content_uri']
|
upload_resp, _maybe_keys = await client.upload(f, content_type=content_type, filename=filename)
|
||||||
|
asset_data = f.seek(0) or f.read() # get size for info below
|
||||||
|
if not isinstance(upload_resp, UploadResponse) or not upload_resp.content_uri:
|
||||||
|
await client.close()
|
||||||
|
return web.json_response({"error": "Failed to upload asset to Matrix."}, content_type='application/json', status=500)
|
||||||
|
mxc_url = upload_resp.content_uri
|
||||||
|
|
||||||
workflow_json_mxc_url = matrix.media_upload(prompt['workflow'], 'application/json', filename='workflow.json')['content_uri']
|
# Upload workflow JSON
|
||||||
|
import io
|
||||||
|
workflow_json_bytes = json.dumps(prompt['workflow']).encode('utf-8')
|
||||||
|
workflow_io = io.BytesIO(workflow_json_bytes)
|
||||||
|
upload_workflow_resp, _maybe_keys = await client.upload(workflow_io, content_type='application/json', filename='workflow.json')
|
||||||
|
workflow_io.seek(0)
|
||||||
|
if not isinstance(upload_workflow_resp, UploadResponse) or not upload_workflow_resp.content_uri:
|
||||||
|
await client.close()
|
||||||
|
return web.json_response({"error": "Failed to upload workflow to Matrix."}, content_type='application/json', status=500)
|
||||||
|
workflow_json_mxc_url = upload_workflow_resp.content_uri
|
||||||
|
|
||||||
|
# Send text message
|
||||||
text_content = ""
|
text_content = ""
|
||||||
if title:
|
if title:
|
||||||
text_content += f"{title}\n"
|
text_content += f"{title}\n"
|
||||||
@@ -369,11 +398,47 @@ async def share_art(request):
|
|||||||
text_content += f"{description}\n"
|
text_content += f"{description}\n"
|
||||||
if credits:
|
if credits:
|
||||||
text_content += f"\ncredits: {credits}\n"
|
text_content += f"\ncredits: {credits}\n"
|
||||||
matrix.send_message(comfyui_share_room_id, text_content)
|
await client.room_send(
|
||||||
matrix.send_content(comfyui_share_room_id, mxc_url, filename, 'm.image')
|
room_id=comfyui_share_room_id,
|
||||||
matrix.send_content(comfyui_share_room_id, workflow_json_mxc_url, 'workflow.json', 'm.file')
|
message_type="m.room.message",
|
||||||
except Exception:
|
content={"msgtype": "m.text", "body": text_content}
|
||||||
logging.exception("An error occurred")
|
)
|
||||||
|
|
||||||
|
# Send image
|
||||||
|
await client.room_send(
|
||||||
|
room_id=comfyui_share_room_id,
|
||||||
|
message_type="m.room.message",
|
||||||
|
content={
|
||||||
|
"msgtype": "m.image",
|
||||||
|
"body": filename,
|
||||||
|
"url": mxc_url,
|
||||||
|
"info": {
|
||||||
|
"mimetype": content_type,
|
||||||
|
"size": len(asset_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Send workflow JSON file
|
||||||
|
await client.room_send(
|
||||||
|
room_id=comfyui_share_room_id,
|
||||||
|
message_type="m.room.message",
|
||||||
|
content={
|
||||||
|
"msgtype": "m.file",
|
||||||
|
"body": "workflow.json",
|
||||||
|
"url": workflow_json_mxc_url,
|
||||||
|
"info": {
|
||||||
|
"mimetype": "application/json",
|
||||||
|
"size": len(workflow_json_bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await client.close()
|
||||||
|
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
return web.json_response({"error": "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500)
|
return web.json_response({"error": "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500)
|
||||||
|
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import concurrent.futures
|
||||||
import folder_paths
|
import folder_paths
|
||||||
|
|
||||||
from comfyui_manager.glob import manager_core as core
|
from comfyui_manager.glob import manager_core as core
|
||||||
from comfyui_manager.glob.constants import model_dir_name_map
|
from comfyui_manager.glob.constants import model_dir_name_map, MODEL_DIR_NAMES
|
||||||
|
|
||||||
|
|
||||||
def get_model_dir(data, show_log=False):
|
def get_model_dir(data, show_log=False):
|
||||||
@@ -72,3 +73,89 @@ def get_model_path(data, show_log=False):
|
|||||||
return os.path.join(base_model, os.path.basename(data["url"]))
|
return os.path.join(base_model, os.path.basename(data["url"]))
|
||||||
else:
|
else:
|
||||||
return os.path.join(base_model, data["filename"])
|
return os.path.join(base_model, data["filename"])
|
||||||
|
|
||||||
|
|
||||||
|
def check_model_installed(json_obj):
|
||||||
|
def is_exists(model_dir_name, filename, url):
|
||||||
|
if filename == "<huggingface>":
|
||||||
|
filename = os.path.basename(url)
|
||||||
|
|
||||||
|
dirs = folder_paths.get_folder_paths(model_dir_name)
|
||||||
|
|
||||||
|
for x in dirs:
|
||||||
|
if os.path.exists(os.path.join(x, filename)):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
total_models_files = set()
|
||||||
|
for x in MODEL_DIR_NAMES:
|
||||||
|
for y in folder_paths.get_filename_list(x):
|
||||||
|
total_models_files.add(y)
|
||||||
|
|
||||||
|
def process_model_phase(item):
|
||||||
|
if (
|
||||||
|
"diffusion" not in item["filename"]
|
||||||
|
and "pytorch" not in item["filename"]
|
||||||
|
and "model" not in item["filename"]
|
||||||
|
):
|
||||||
|
# non-general name case
|
||||||
|
if item["filename"] in total_models_files:
|
||||||
|
item["installed"] = "True"
|
||||||
|
return
|
||||||
|
|
||||||
|
if item["save_path"] == "default":
|
||||||
|
model_dir_name = model_dir_name_map.get(item["type"].lower())
|
||||||
|
if model_dir_name is not None:
|
||||||
|
item["installed"] = str(
|
||||||
|
is_exists(model_dir_name, item["filename"], item["url"])
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
item["installed"] = "False"
|
||||||
|
else:
|
||||||
|
model_dir_name = item["save_path"].split("/")[0]
|
||||||
|
if model_dir_name in folder_paths.folder_names_and_paths:
|
||||||
|
if is_exists(model_dir_name, item["filename"], item["url"]):
|
||||||
|
item["installed"] = "True"
|
||||||
|
|
||||||
|
if "installed" not in item:
|
||||||
|
if item["filename"] == "<huggingface>":
|
||||||
|
filename = os.path.basename(item["url"])
|
||||||
|
else:
|
||||||
|
filename = item["filename"]
|
||||||
|
|
||||||
|
fullpath = os.path.join(
|
||||||
|
folder_paths.models_dir, item["save_path"], filename
|
||||||
|
)
|
||||||
|
|
||||||
|
item["installed"] = "True" if os.path.exists(fullpath) else "False"
|
||||||
|
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(8) as executor:
|
||||||
|
for item in json_obj["models"]:
|
||||||
|
executor.submit(process_model_phase, item)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_whitelist_for_model(item):
|
||||||
|
from comfyui_manager.data_models import ManagerDatabaseSource
|
||||||
|
|
||||||
|
json_obj = await core.get_data_by_mode(ManagerDatabaseSource.cache.value, "model-list.json")
|
||||||
|
|
||||||
|
for x in json_obj.get("models", []):
|
||||||
|
if (
|
||||||
|
x["save_path"] == item["save_path"]
|
||||||
|
and x["base"] == item["base"]
|
||||||
|
and x["filename"] == item["filename"]
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
json_obj = await core.get_data_by_mode(ManagerDatabaseSource.local.value, "model-list.json")
|
||||||
|
|
||||||
|
for x in json_obj.get("models", []):
|
||||||
|
if (
|
||||||
|
x["save_path"] == item["save_path"]
|
||||||
|
and x["base"] == item["base"]
|
||||||
|
and x["filename"] == item["filename"]
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from comfyui_manager.glob import manager_core as core
|
from comfyui_manager.glob import manager_core as core
|
||||||
from comfy.cli_args import args
|
from comfy.cli_args import args
|
||||||
|
from comfyui_manager.data_models import SecurityLevel, RiskLevel, ManagerDatabaseSource
|
||||||
|
|
||||||
|
|
||||||
def is_loopback(address):
|
def is_loopback(address):
|
||||||
@@ -12,24 +13,37 @@ def is_loopback(address):
|
|||||||
|
|
||||||
def is_allowed_security_level(level):
|
def is_allowed_security_level(level):
|
||||||
is_local_mode = is_loopback(args.listen)
|
is_local_mode = is_loopback(args.listen)
|
||||||
|
is_personal_cloud = core.get_config()['network_mode'].lower() == 'personal_cloud'
|
||||||
|
|
||||||
if level == "block":
|
if level == RiskLevel.block.value:
|
||||||
return False
|
return False
|
||||||
elif level == "high":
|
elif level == RiskLevel.high_.value:
|
||||||
if is_local_mode:
|
if is_local_mode:
|
||||||
return core.get_config()["security_level"] in ["weak", "normal-"]
|
return core.get_config()['security_level'] in [SecurityLevel.weak.value, SecurityLevel.normal_.value]
|
||||||
|
elif is_personal_cloud:
|
||||||
|
return core.get_config()['security_level'] == SecurityLevel.weak.value
|
||||||
else:
|
else:
|
||||||
return core.get_config()["security_level"] == "weak"
|
return False
|
||||||
elif level == "middle":
|
elif level == RiskLevel.high.value:
|
||||||
return core.get_config()["security_level"] in ["weak", "normal", "normal-"]
|
if is_local_mode:
|
||||||
|
return core.get_config()['security_level'] in [SecurityLevel.weak.value, SecurityLevel.normal_.value]
|
||||||
|
else:
|
||||||
|
return core.get_config()['security_level'] == SecurityLevel.weak.value
|
||||||
|
elif level == RiskLevel.middle_.value:
|
||||||
|
if is_local_mode or is_personal_cloud:
|
||||||
|
return core.get_config()['security_level'] in [SecurityLevel.weak.value, SecurityLevel.normal.value, SecurityLevel.normal_.value]
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
elif level == RiskLevel.middle.value:
|
||||||
|
return core.get_config()['security_level'] in [SecurityLevel.weak.value, SecurityLevel.normal.value, SecurityLevel.normal_.value]
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def get_risky_level(files, pip_packages):
|
async def get_risky_level(files, pip_packages):
|
||||||
json_data1 = await core.get_data_by_mode("local", "custom-node-list.json")
|
json_data1 = await core.get_data_by_mode(ManagerDatabaseSource.local.value, "custom-node-list.json")
|
||||||
json_data2 = await core.get_data_by_mode(
|
json_data2 = await core.get_data_by_mode(
|
||||||
"cache",
|
ManagerDatabaseSource.cache.value,
|
||||||
"custom-node-list.json",
|
"custom-node-list.json",
|
||||||
channel_url="https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main",
|
channel_url="https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main",
|
||||||
)
|
)
|
||||||
@@ -40,7 +54,7 @@ async def get_risky_level(files, pip_packages):
|
|||||||
|
|
||||||
for x in files:
|
for x in files:
|
||||||
if x not in all_urls:
|
if x not in all_urls:
|
||||||
return "high"
|
return RiskLevel.high_.value
|
||||||
|
|
||||||
all_pip_packages = set()
|
all_pip_packages = set()
|
||||||
for x in json_data1["custom_nodes"] + json_data2["custom_nodes"]:
|
for x in json_data1["custom_nodes"] + json_data2["custom_nodes"]:
|
||||||
@@ -48,6 +62,6 @@ async def get_risky_level(files, pip_packages):
|
|||||||
|
|
||||||
for p in pip_packages:
|
for p in pip_packages:
|
||||||
if p not in all_pip_packages:
|
if p not in all_pip_packages:
|
||||||
return "block"
|
return RiskLevel.block.value
|
||||||
|
|
||||||
return "middle"
|
return RiskLevel.middle_.value
|
||||||
|
|||||||
@@ -222,9 +222,6 @@ function isBeforeFrontendVersion(compareVersion) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const is_legacy_front = () => isBeforeFrontendVersion('1.2.49');
|
|
||||||
const isNotNewManagerUI = () => isBeforeFrontendVersion('1.16.4');
|
|
||||||
|
|
||||||
document.head.appendChild(docStyle);
|
document.head.appendChild(docStyle);
|
||||||
|
|
||||||
var update_comfyui_button = null;
|
var update_comfyui_button = null;
|
||||||
@@ -1517,11 +1514,6 @@ app.registerExtension({
|
|||||||
tooltip: "Share"
|
tooltip: "Share"
|
||||||
}).element
|
}).element
|
||||||
);
|
);
|
||||||
|
|
||||||
const shouldShowLegacyMenuItems = isNotNewManagerUI();
|
|
||||||
if (shouldShowLegacyMenuItems) {
|
|
||||||
app.menu?.settingsGroup.element.before(cmGroup.element);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch(exception) {
|
catch(exception) {
|
||||||
console.log('ComfyUI is outdated. New style menu based features are disabled.');
|
console.log('ComfyUI is outdated. New style menu based features are disabled.');
|
||||||
|
|||||||
@@ -552,6 +552,20 @@ export class ShareDialog extends ComfyDialog {
|
|||||||
this.matrix_destination_checkbox.style.color = "var(--fg-color)";
|
this.matrix_destination_checkbox.style.color = "var(--fg-color)";
|
||||||
this.matrix_destination_checkbox.checked = this.share_option === 'matrix'; //true;
|
this.matrix_destination_checkbox.checked = this.share_option === 'matrix'; //true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
api.fetchApi(`/v2/manager/get_matrix_dep_status`)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(data => {
|
||||||
|
if(data == 'unavailable') {
|
||||||
|
matrix_destination_checkbox_text.style.textDecoration = "line-through";
|
||||||
|
this.matrix_destination_checkbox.disabled = true;
|
||||||
|
this.matrix_destination_checkbox.title = "It has been disabled because the 'matrix-nio' dependency is not installed. Please install this dependency to use the matrix sharing feature.";
|
||||||
|
matrix_destination_checkbox_text.title = "It has been disabled because the 'matrix-nio' dependency is not installed. Please install this dependency to use the matrix sharing feature.";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {});
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
this.comfyworkflows_destination_checkbox = $el("input", { type: 'checkbox', id: "comfyworkflows_destination" }, [])
|
this.comfyworkflows_destination_checkbox = $el("input", { type: 'checkbox', id: "comfyworkflows_destination" }, [])
|
||||||
const comfyworkflows_destination_checkbox_text = $el("label", {}, [" ComfyWorkflows.com"])
|
const comfyworkflows_destination_checkbox_text = $el("label", {}, [" ComfyWorkflows.com"])
|
||||||
this.comfyworkflows_destination_checkbox.style.color = "var(--fg-color)";
|
this.comfyworkflows_destination_checkbox.style.color = "var(--fg-color)";
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -342,15 +344,11 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
["0/70"]
|
["0/70"]
|
||||||
);
|
);
|
||||||
// Additional Inputs Section
|
// Additional Inputs Section
|
||||||
const additionalInputsSection = $el(
|
const additionalInputsSection = $el("div", { style: { ...sectionStyle } }, [
|
||||||
"div",
|
$el("label", { style: labelStyle }, ["3️⃣ Title "]),
|
||||||
{ style: { ...sectionStyle, } },
|
this.TitleInput,
|
||||||
[
|
titleNumDom,
|
||||||
$el("label", { style: labelStyle }, ["3️⃣ Title "]),
|
]);
|
||||||
this.TitleInput,
|
|
||||||
titleNumDom,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
const SubtitleSection = $el("div", { style: sectionStyle }, [
|
const SubtitleSection = $el("div", { style: sectionStyle }, [
|
||||||
$el("label", { style: labelStyle }, ["4️⃣ Subtitle "]),
|
$el("label", { style: labelStyle }, ["4️⃣ Subtitle "]),
|
||||||
this.SubTitleInput,
|
this.SubTitleInput,
|
||||||
@@ -379,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",
|
||||||
{
|
{
|
||||||
@@ -392,11 +390,42 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
this.radioButtonsCheck_lock,
|
this.radioButtonsCheck_lock,
|
||||||
$el("div", { style: { marginLeft: "5px" ,display:'flex',alignItems:'center'} }, [
|
$el(
|
||||||
$el("span", { style: { marginLeft: "5px" } }, ["ON"]),
|
"div",
|
||||||
$el("span", { style: { marginLeft: "20px",marginRight:'10px' ,color:'#fff'} }, ["Price US$"]),
|
{
|
||||||
this.LockInput
|
style: {
|
||||||
]),
|
marginLeft: "5px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
position: "relative",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
$el("span", { style: { marginLeft: "5px" } }, ["ON"]),
|
||||||
|
$el(
|
||||||
|
"span",
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
marginLeft: "20px",
|
||||||
|
marginRight: "10px",
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
["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,
|
||||||
|
]
|
||||||
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
$el(
|
$el(
|
||||||
@@ -404,14 +433,25 @@ 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("span", { style: { marginLeft: "5px" } }, ["OFF"]),
|
$el(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
marginLeft: "5px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[$el("span", { style: { marginLeft: "5px" } }, ["OFF"])]
|
||||||
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
||||||
$el(
|
$el(
|
||||||
"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."]
|
[
|
||||||
|
]
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -432,7 +472,7 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const blockChainSection = $el("div", { style: sectionStyle }, [
|
const blockChainSection = $el("div", { style: sectionStyle }, [
|
||||||
$el("label", { style: labelStyle }, ["7️⃣ Store on blockchain "]),
|
$el("label", { style: labelStyle }, ["8️⃣ Store on blockchain "]),
|
||||||
$el(
|
$el(
|
||||||
"label",
|
"label",
|
||||||
{
|
{
|
||||||
@@ -463,6 +503,139 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
this.ratingRadioButtonsCheck0 = $el("input", {
|
||||||
|
type: "radio",
|
||||||
|
name: "content_rating",
|
||||||
|
value: "0",
|
||||||
|
id: "content_rating0",
|
||||||
|
});
|
||||||
|
this.ratingRadioButtonsCheck1 = $el("input", {
|
||||||
|
type: "radio",
|
||||||
|
name: "content_rating",
|
||||||
|
value: "1",
|
||||||
|
id: "content_rating1",
|
||||||
|
});
|
||||||
|
this.ratingRadioButtonsCheck2 = $el("input", {
|
||||||
|
type: "radio",
|
||||||
|
name: "content_rating",
|
||||||
|
value: "2",
|
||||||
|
id: "content_rating2",
|
||||||
|
});
|
||||||
|
this.ratingRadioButtonsCheck_1 = $el("input", {
|
||||||
|
type: "radio",
|
||||||
|
name: "content_rating",
|
||||||
|
value: "-1",
|
||||||
|
id: "content_rating_1",
|
||||||
|
checked: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// content rating
|
||||||
|
const contentRatingSection = $el("div", { style: sectionStyle }, [
|
||||||
|
$el("label", { style: labelStyle }, ["7️⃣ Content rating "]),
|
||||||
|
$el(
|
||||||
|
"label",
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
marginTop: "10px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
cursor: "pointer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
this.ratingRadioButtonsCheck0,
|
||||||
|
$el("img", {
|
||||||
|
style: {
|
||||||
|
width: "12px",
|
||||||
|
height: "12px",
|
||||||
|
marginLeft: "5px",
|
||||||
|
},
|
||||||
|
src: "https://static.copus.io/images/client/202507/test/b9f17da83b054d53cd0cb4508c2c30dc.png",
|
||||||
|
}),
|
||||||
|
$el("span", { style: { marginLeft: "5px", color: "#fff" } }, [
|
||||||
|
"All ages",
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
$el(
|
||||||
|
"p",
|
||||||
|
{ style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } },
|
||||||
|
["Safe for all viewers; no profanity, violence, or mature themes."]
|
||||||
|
),
|
||||||
|
$el(
|
||||||
|
"label",
|
||||||
|
{ style: { display: "flex", alignItems: "center", cursor: "pointer" } },
|
||||||
|
[
|
||||||
|
this.ratingRadioButtonsCheck1,
|
||||||
|
$el("img", {
|
||||||
|
style: {
|
||||||
|
width: "12px",
|
||||||
|
height: "12px",
|
||||||
|
marginLeft: "5px",
|
||||||
|
},
|
||||||
|
src: "https://static.copus.io/images/client/202507/test/7848bc0d3690671df21c7cf00c4cfc81.png",
|
||||||
|
}),
|
||||||
|
$el("span", { style: { marginLeft: "5px", color: "#fff" } }, [
|
||||||
|
"13+ (Teen)",
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
$el(
|
||||||
|
"p",
|
||||||
|
{ style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } },
|
||||||
|
[
|
||||||
|
"Mild language, light themes, or cartoon violence; no explicit content. ",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
$el(
|
||||||
|
"label",
|
||||||
|
{ style: { display: "flex", alignItems: "center", cursor: "pointer" } },
|
||||||
|
[
|
||||||
|
this.ratingRadioButtonsCheck2,
|
||||||
|
$el("img", {
|
||||||
|
style: {
|
||||||
|
width: "12px",
|
||||||
|
height: "12px",
|
||||||
|
marginLeft: "5px",
|
||||||
|
},
|
||||||
|
src: "https://static.copus.io/images/client/202507/test/bc51839c208d68d91173e43c23bff039.png",
|
||||||
|
}),
|
||||||
|
$el("span", { style: { marginLeft: "5px", color: "#fff" } }, [
|
||||||
|
"18+ (Explicit)",
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
$el(
|
||||||
|
"p",
|
||||||
|
{ style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } },
|
||||||
|
[
|
||||||
|
"Explicit content, including sexual content, strong violence, or intense themes. ",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
$el(
|
||||||
|
"label",
|
||||||
|
{ style: { display: "flex", alignItems: "center", cursor: "pointer" } },
|
||||||
|
[
|
||||||
|
this.ratingRadioButtonsCheck_1,
|
||||||
|
$el("img", {
|
||||||
|
style: {
|
||||||
|
width: "12px",
|
||||||
|
height: "12px",
|
||||||
|
marginLeft: "5px",
|
||||||
|
},
|
||||||
|
src: "https://static.copus.io/images/client/202507/test/5c802fdcaaea4e7bbed37393eec0d5ba.png",
|
||||||
|
}),
|
||||||
|
$el("span", { style: { marginLeft: "5px", color: "#fff" } }, [
|
||||||
|
"Not Rated",
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
$el(
|
||||||
|
"p",
|
||||||
|
{ style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } },
|
||||||
|
["No age rating provided."]
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
// Message Section
|
// Message Section
|
||||||
this.message = $el(
|
this.message = $el(
|
||||||
@@ -526,6 +699,7 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
DescriptionSection,
|
DescriptionSection,
|
||||||
// contestSection,
|
// contestSection,
|
||||||
blockChainSection_lock,
|
blockChainSection_lock,
|
||||||
|
contentRatingSection,
|
||||||
blockChainSection,
|
blockChainSection,
|
||||||
this.message,
|
this.message,
|
||||||
buttonsSection,
|
buttonsSection,
|
||||||
@@ -587,7 +761,9 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
url: data,
|
url: data,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error("make sure your API key is correct and try again later");
|
throw new Error(
|
||||||
|
"make sure your API key is correct and try again later"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e?.response?.status === 413) {
|
if (e?.response?.status === 413) {
|
||||||
@@ -628,8 +804,15 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
subTitle: this.SubTitleInput.value,
|
subTitle: this.SubTitleInput.value,
|
||||||
content: this.descriptionInput.value,
|
content: this.descriptionInput.value,
|
||||||
storeOnChain: this.radioButtonsCheck.checked ? true : false,
|
storeOnChain: this.radioButtonsCheck.checked ? true : false,
|
||||||
lockState:this.radioButtonsCheck_lock.checked ? 2 : 0,
|
lockState: this.radioButtonsCheck_lock.checked ? 2 : 0,
|
||||||
unlockPrice:this.LockInput.value,
|
unlockPrice: this.LockInput.value,
|
||||||
|
rating: this.ratingRadioButtonsCheck0.checked
|
||||||
|
? 0
|
||||||
|
: this.ratingRadioButtonsCheck1.checked
|
||||||
|
? 1
|
||||||
|
: this.ratingRadioButtonsCheck2.checked
|
||||||
|
? 2
|
||||||
|
: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!this.keyInput.value) {
|
if (!this.keyInput.value) {
|
||||||
@@ -644,8 +827,8 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
throw new Error("Title is required");
|
throw new Error("Title is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.radioButtonsCheck_lock.checked){
|
if (this.radioButtonsCheck_lock.checked) {
|
||||||
if (!this.LockInput.value){
|
if (!this.LockInput.value) {
|
||||||
throw new Error("Price is required");
|
throw new Error("Price is required");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -695,23 +878,23 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
"Uploading workflow..."
|
"Uploading workflow..."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res.status && res.data.status && res.data) {
|
if (res.status && res.data.status && res.data) {
|
||||||
localStorage.setItem("copus_token",this.keyInput.value);
|
localStorage.setItem("copus_token", this.keyInput.value);
|
||||||
const { data } = res.data;
|
const { data } = res.data;
|
||||||
if (data) {
|
if (data) {
|
||||||
const url = `${DEFAULT_HOMEPAGE_URL}/work/${data}`;
|
const url = `${DEFAULT_HOMEPAGE_URL}/work/${data}`;
|
||||||
this.message.innerHTML = `Workflow has been shared successfully. <a href="${url}" target="_blank">Click here to view it.</a>`;
|
this.message.innerHTML = `Workflow has been shared successfully. <a href="${url}" target="_blank">Click here to view it.</a>`;
|
||||||
this.previewImage.src = "";
|
this.previewImage.src = "";
|
||||||
this.previewImage.style.display = "none";
|
this.previewImage.style.display = "none";
|
||||||
this.uploadedImages = [];
|
this.uploadedImages = [];
|
||||||
this.allFilesImages = [];
|
this.allFilesImages = [];
|
||||||
this.allFiles = [];
|
this.allFiles = [];
|
||||||
this.TitleInput.value = "";
|
this.TitleInput.value = "";
|
||||||
this.SubTitleInput.value = "";
|
this.SubTitleInput.value = "";
|
||||||
this.descriptionInput.value = "";
|
this.descriptionInput.value = "";
|
||||||
this.selectedFile = null;
|
this.selectedFile = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error("Error sharing workflow: " + e.message);
|
throw new Error("Error sharing workflow: " + e.message);
|
||||||
}
|
}
|
||||||
@@ -757,7 +940,7 @@ export class CopusShareDialog extends ComfyDialog {
|
|||||||
this.element.style.display = "block";
|
this.element.style.display = "block";
|
||||||
this.previewImage.src = "";
|
this.previewImage.src = "";
|
||||||
this.previewImage.style.display = "none";
|
this.previewImage.style.display = "none";
|
||||||
this.keyInput.value = apiToken!=null?apiToken:"";
|
this.keyInput.value = apiToken != null ? apiToken : "";
|
||||||
this.uploadedImages = [];
|
this.uploadedImages = [];
|
||||||
this.allFilesImages = [];
|
this.allFilesImages = [];
|
||||||
this.allFiles = [];
|
this.allFiles = [];
|
||||||
|
|||||||
@@ -714,6 +714,7 @@ export class CustomNodesManager {
|
|||||||
link.href = rowItem.reference;
|
link.href = rowItem.reference;
|
||||||
link.target = '_blank';
|
link.target = '_blank';
|
||||||
link.innerHTML = `<b>${title}</b>`;
|
link.innerHTML = `<b>${title}</b>`;
|
||||||
|
link.title = rowItem.originalData.id;
|
||||||
container.appendChild(link);
|
container.appendChild(link);
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
@@ -1625,17 +1626,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;
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' i
|
|||||||
|
|
||||||
|
|
||||||
DEFAULT_CHANNEL = "https://raw.githubusercontent.com/Comfy-Org/ComfyUI-Manager/main"
|
DEFAULT_CHANNEL = "https://raw.githubusercontent.com/Comfy-Org/ComfyUI-Manager/main"
|
||||||
|
DEFAULT_CHANNEL_LEGACY = "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main"
|
||||||
|
|
||||||
|
|
||||||
default_custom_nodes_path = None
|
default_custom_nodes_path = None
|
||||||
@@ -160,7 +161,7 @@ comfy_ui_revision = "Unknown"
|
|||||||
comfy_ui_commit_datetime = datetime(1900, 1, 1, 0, 0, 0)
|
comfy_ui_commit_datetime = datetime(1900, 1, 1, 0, 0, 0)
|
||||||
|
|
||||||
channel_dict = None
|
channel_dict = None
|
||||||
valid_channels = {'default', 'local'}
|
valid_channels = {'default', 'local', DEFAULT_CHANNEL, DEFAULT_CHANNEL_LEGACY}
|
||||||
channel_list = None
|
channel_list = None
|
||||||
|
|
||||||
|
|
||||||
@@ -304,18 +305,86 @@ class ManagedResult:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class NormalizedKeyDict:
|
||||||
|
def __init__(self):
|
||||||
|
self._store = {}
|
||||||
|
self._key_map = {}
|
||||||
|
|
||||||
|
def _normalize_key(self, key):
|
||||||
|
if isinstance(key, str):
|
||||||
|
return key.strip().lower()
|
||||||
|
return key
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
norm_key = self._normalize_key(key)
|
||||||
|
self._key_map[norm_key] = key
|
||||||
|
self._store[key] = value
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
norm_key = self._normalize_key(key)
|
||||||
|
original_key = self._key_map[norm_key]
|
||||||
|
return self._store[original_key]
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
norm_key = self._normalize_key(key)
|
||||||
|
original_key = self._key_map.pop(norm_key)
|
||||||
|
del self._store[original_key]
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
return self._normalize_key(key) in self._key_map
|
||||||
|
|
||||||
|
def get(self, key, default=None):
|
||||||
|
return self[key] if key in self else default
|
||||||
|
|
||||||
|
def setdefault(self, key, default=None):
|
||||||
|
if key in self:
|
||||||
|
return self[key]
|
||||||
|
self[key] = default
|
||||||
|
return default
|
||||||
|
|
||||||
|
def pop(self, key, default=None):
|
||||||
|
if key in self:
|
||||||
|
val = self[key]
|
||||||
|
del self[key]
|
||||||
|
return val
|
||||||
|
if default is not None:
|
||||||
|
return default
|
||||||
|
raise KeyError(key)
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return self._store.keys()
|
||||||
|
|
||||||
|
def values(self):
|
||||||
|
return self._store.values()
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
return self._store.items()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._store)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._store)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return repr(self._store)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return dict(self._store)
|
||||||
|
|
||||||
|
|
||||||
class UnifiedManager:
|
class UnifiedManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.installed_node_packages: dict[str, InstalledNodePackage] = {}
|
self.installed_node_packages: dict[str, InstalledNodePackage] = {}
|
||||||
|
|
||||||
self.cnr_inactive_nodes = {} # node_id -> node_version -> fullpath
|
self.cnr_inactive_nodes = NormalizedKeyDict() # node_id -> node_version -> fullpath
|
||||||
self.nightly_inactive_nodes = {} # node_id -> fullpath
|
self.nightly_inactive_nodes = NormalizedKeyDict() # node_id -> fullpath
|
||||||
self.unknown_inactive_nodes = {} # node_id -> repo url * fullpath
|
self.unknown_inactive_nodes = {} # node_id -> repo url * fullpath
|
||||||
self.active_nodes = {} # node_id -> node_version * fullpath
|
self.active_nodes = NormalizedKeyDict() # node_id -> node_version * fullpath
|
||||||
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
|
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
|
||||||
self.cnr_map = {} # node_id -> cnr info
|
self.cnr_map = NormalizedKeyDict() # node_id -> cnr info
|
||||||
self.repo_cnr_map = {} # repo_url -> cnr info
|
self.repo_cnr_map = {} # repo_url -> cnr info
|
||||||
self.custom_node_map_cache = {} # (channel, mode) -> augmented custom node list json
|
self.custom_node_map_cache = {} # (channel, mode) -> augmented custom node list json
|
||||||
self.processed_install = set()
|
self.processed_install = set()
|
||||||
|
|
||||||
def get_module_name(self, x):
|
def get_module_name(self, x):
|
||||||
@@ -721,7 +790,7 @@ class UnifiedManager:
|
|||||||
channel = normalize_channel(channel)
|
channel = normalize_channel(channel)
|
||||||
nodes = await self.load_nightly(channel, mode)
|
nodes = await self.load_nightly(channel, mode)
|
||||||
|
|
||||||
res = {}
|
res = NormalizedKeyDict()
|
||||||
added_cnr = set()
|
added_cnr = set()
|
||||||
for v in nodes.values():
|
for v in nodes.values():
|
||||||
v = v[0]
|
v = v[0]
|
||||||
@@ -1557,16 +1626,18 @@ def read_config():
|
|||||||
config = configparser.ConfigParser(strict=False)
|
config = configparser.ConfigParser(strict=False)
|
||||||
config.read(context.manager_config_path)
|
config.read(context.manager_config_path)
|
||||||
default_conf = config['default']
|
default_conf = config['default']
|
||||||
manager_util.use_uv = default_conf['use_uv'].lower() == 'true' if 'use_uv' in default_conf else False
|
|
||||||
|
|
||||||
def get_bool(key, default_value):
|
def get_bool(key, default_value):
|
||||||
return default_conf[key].lower() == 'true' if key in default_conf else False
|
return default_conf[key].lower() == 'true' if key 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)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'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', ''),
|
||||||
'use_uv': get_bool('use_uv', False),
|
'use_uv': get_bool('use_uv', True),
|
||||||
'channel_url': default_conf.get('channel_url', DEFAULT_CHANNEL),
|
'channel_url': default_conf.get('channel_url', DEFAULT_CHANNEL),
|
||||||
'default_cache_as_channel_url': get_bool('default_cache_as_channel_url', False),
|
'default_cache_as_channel_url': get_bool('default_cache_as_channel_url', False),
|
||||||
'share_option': default_conf.get('share_option', 'all').lower(),
|
'share_option': default_conf.get('share_option', 'all').lower(),
|
||||||
@@ -1585,15 +1656,17 @@ def read_config():
|
|||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
manager_util.use_uv = False
|
manager_util.use_uv = False
|
||||||
|
manager_util.bypass_ssl = False
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'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': '',
|
||||||
'use_uv': False,
|
'use_uv': True,
|
||||||
'channel_url': DEFAULT_CHANNEL,
|
'channel_url': DEFAULT_CHANNEL,
|
||||||
'default_cache_as_channel_url': False,
|
'default_cache_as_channel_url': False,
|
||||||
'share_option': 'all',
|
'share_option': 'all',
|
||||||
'bypass_ssl': False,
|
'bypass_ssl': manager_util.bypass_ssl,
|
||||||
'file_logging': True,
|
'file_logging': True,
|
||||||
'component_policy': 'workflow',
|
'component_policy': 'workflow',
|
||||||
'update_policy': 'stable-comfyui',
|
'update_policy': 'stable-comfyui',
|
||||||
@@ -2776,7 +2849,7 @@ async def get_unified_total_nodes(channel, mode, regsitry_cache_mode='cache'):
|
|||||||
|
|
||||||
if cnr_id is not None:
|
if cnr_id is not None:
|
||||||
# cnr or nightly version
|
# cnr or nightly version
|
||||||
cnr_ids.remove(cnr_id)
|
cnr_ids.discard(cnr_id)
|
||||||
updatable = False
|
updatable = False
|
||||||
cnr = unified_manager.cnr_map[cnr_id]
|
cnr = unified_manager.cnr_map[cnr_id]
|
||||||
|
|
||||||
@@ -2940,6 +3013,11 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
info = yaml.load(snapshot_file, Loader=yaml.SafeLoader)
|
info = yaml.load(snapshot_file, Loader=yaml.SafeLoader)
|
||||||
info = info['custom_nodes']
|
info = info['custom_nodes']
|
||||||
|
|
||||||
|
if 'pips' in info and info['pips']:
|
||||||
|
pips = info['pips']
|
||||||
|
else:
|
||||||
|
pips = {}
|
||||||
|
|
||||||
# for cnr restore
|
# for cnr restore
|
||||||
cnr_info = info.get('cnr_custom_nodes')
|
cnr_info = info.get('cnr_custom_nodes')
|
||||||
if cnr_info is not None:
|
if cnr_info is not None:
|
||||||
@@ -3146,6 +3224,8 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
unified_manager.repo_install(repo_url, to_path, instant_execution=True, no_deps=False, return_postinstall=False)
|
unified_manager.repo_install(repo_url, to_path, instant_execution=True, no_deps=False, return_postinstall=False)
|
||||||
cloned_repos.append(repo_name)
|
cloned_repos.append(repo_name)
|
||||||
|
|
||||||
|
manager_util.restore_pip_snapshot(pips, git_helper_extras)
|
||||||
|
|
||||||
# print summary
|
# print summary
|
||||||
for x in cloned_repos:
|
for x in cloned_repos:
|
||||||
print(f"[ INSTALLED ] {x}")
|
print(f"[ INSTALLED ] {x}")
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from ..common import manager_util
|
|||||||
from ..common import cm_global
|
from ..common import cm_global
|
||||||
from ..common import manager_downloader
|
from ..common import manager_downloader
|
||||||
from ..common import context
|
from ..common import context
|
||||||
|
from ..common import manager_security
|
||||||
|
|
||||||
|
|
||||||
logging.info(f"### Loading: ComfyUI-Manager ({core.version_str})")
|
logging.info(f"### Loading: ComfyUI-Manager ({core.version_str})")
|
||||||
@@ -36,7 +37,8 @@ logging.info("[ComfyUI-Manager] network_mode: " + network_mode_description)
|
|||||||
comfy_ui_hash = "-"
|
comfy_ui_hash = "-"
|
||||||
comfyui_tag = None
|
comfyui_tag = None
|
||||||
|
|
||||||
SECURITY_MESSAGE_MIDDLE_OR_BELOW = "ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.\nReference: https://github.com/Comfy-Org/ComfyUI-Manager#security-policy"
|
SECURITY_MESSAGE_MIDDLE = "ERROR: To use this action, a security_level of `normal or below` is required. Please contact the administrator.\nReference: https://github.com/Comfy-Org/ComfyUI-Manager#security-policy"
|
||||||
|
SECURITY_MESSAGE_MIDDLE_P = "ERROR: To use this action, security_level must be `normal or below`, and network_mode must be set to `personal_cloud`. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
||||||
SECURITY_MESSAGE_NORMAL_MINUS = "ERROR: To use this feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.\nReference: https://github.com/Comfy-Org/ComfyUI-Manager#security-policy"
|
SECURITY_MESSAGE_NORMAL_MINUS = "ERROR: To use this feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.\nReference: https://github.com/Comfy-Org/ComfyUI-Manager#security-policy"
|
||||||
SECURITY_MESSAGE_GENERAL = "ERROR: This installation is not allowed in this security_level. Please contact the administrator.\nReference: https://github.com/Comfy-Org/ComfyUI-Manager#security-policy"
|
SECURITY_MESSAGE_GENERAL = "ERROR: This installation is not allowed in this security_level. Please contact the administrator.\nReference: https://github.com/Comfy-Org/ComfyUI-Manager#security-policy"
|
||||||
SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in '.safetensors' format is only allowed for models registered in the 'default' channel at this security level. If you want to download this model, set the security level to 'normal-' or lower."
|
SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in '.safetensors' format is only allowed for models registered in the 'default' channel at this security level. If you want to download this model, set the security level to 'normal-' or lower."
|
||||||
@@ -93,13 +95,27 @@ model_dir_name_map = {
|
|||||||
|
|
||||||
|
|
||||||
def is_allowed_security_level(level):
|
def is_allowed_security_level(level):
|
||||||
|
is_personal_cloud = core.get_config()['network_mode'].lower() == 'personal_cloud'
|
||||||
|
|
||||||
if level == 'block':
|
if level == 'block':
|
||||||
return False
|
return False
|
||||||
|
elif level == 'high+':
|
||||||
|
if is_local_mode:
|
||||||
|
return core.get_config()['security_level'] in ['weak', 'normal-']
|
||||||
|
elif is_personal_cloud:
|
||||||
|
return core.get_config()['security_level'] == 'weak'
|
||||||
|
else:
|
||||||
|
return False
|
||||||
elif level == 'high':
|
elif level == 'high':
|
||||||
if is_local_mode:
|
if is_local_mode:
|
||||||
return core.get_config()['security_level'] in ['weak', 'normal-']
|
return core.get_config()['security_level'] in ['weak', 'normal-']
|
||||||
else:
|
else:
|
||||||
return core.get_config()['security_level'] == 'weak'
|
return core.get_config()['security_level'] == 'weak'
|
||||||
|
elif level == 'middle+':
|
||||||
|
if is_local_mode or is_personal_cloud:
|
||||||
|
return core.get_config()['security_level'] in ['weak', 'normal', 'normal-']
|
||||||
|
else:
|
||||||
|
return False
|
||||||
elif level == 'middle':
|
elif level == 'middle':
|
||||||
return core.get_config()['security_level'] in ['weak', 'normal', 'normal-']
|
return core.get_config()['security_level'] in ['weak', 'normal', 'normal-']
|
||||||
else:
|
else:
|
||||||
@@ -116,7 +132,7 @@ async def get_risky_level(files, pip_packages):
|
|||||||
|
|
||||||
for x in files:
|
for x in files:
|
||||||
if x not in all_urls:
|
if x not in all_urls:
|
||||||
return "high"
|
return "high+"
|
||||||
|
|
||||||
all_pip_packages = set()
|
all_pip_packages = set()
|
||||||
for x in json_data1['custom_nodes'] + json_data2['custom_nodes']:
|
for x in json_data1['custom_nodes'] + json_data2['custom_nodes']:
|
||||||
@@ -126,7 +142,7 @@ async def get_risky_level(files, pip_packages):
|
|||||||
if p not in all_pip_packages:
|
if p not in all_pip_packages:
|
||||||
return "block"
|
return "block"
|
||||||
|
|
||||||
return "middle"
|
return "middle+"
|
||||||
|
|
||||||
|
|
||||||
class ManagerFuncsInComfyUI(core.ManagerFuncs):
|
class ManagerFuncsInComfyUI(core.ManagerFuncs):
|
||||||
@@ -650,7 +666,7 @@ async def task_worker():
|
|||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"[ComfyUI-Manager] ERROR: {e}", file=sys.stderr)
|
logging.error(f"[ComfyUI-Manager] ERROR: {e}")
|
||||||
|
|
||||||
return f"Model installation error: {model_url}"
|
return f"Model installation error: {model_url}"
|
||||||
|
|
||||||
@@ -758,29 +774,29 @@ async def queue_batch(request):
|
|||||||
for x in v:
|
for x in v:
|
||||||
res = await _uninstall_custom_node(x)
|
res = await _uninstall_custom_node(x)
|
||||||
if res.status != 200:
|
if res.status != 200:
|
||||||
failed.add(x[0])
|
failed.add(x['id'])
|
||||||
else:
|
else:
|
||||||
res = await _install_custom_node(x)
|
res = await _install_custom_node(x)
|
||||||
if res.status != 200:
|
if res.status != 200:
|
||||||
failed.add(x[0])
|
failed.add(x['id'])
|
||||||
|
|
||||||
elif k == 'install':
|
elif k == 'install':
|
||||||
for x in v:
|
for x in v:
|
||||||
res = await _install_custom_node(x)
|
res = await _install_custom_node(x)
|
||||||
if res.status != 200:
|
if res.status != 200:
|
||||||
failed.add(x[0])
|
failed.add(x['id'])
|
||||||
|
|
||||||
elif k == 'uninstall':
|
elif k == 'uninstall':
|
||||||
for x in v:
|
for x in v:
|
||||||
res = await _uninstall_custom_node(x)
|
res = await _uninstall_custom_node(x)
|
||||||
if res.status != 200:
|
if res.status != 200:
|
||||||
failed.add(x[0])
|
failed.add(x['id'])
|
||||||
|
|
||||||
elif k == 'update':
|
elif k == 'update':
|
||||||
for x in v:
|
for x in v:
|
||||||
res = await _update_custom_node(x)
|
res = await _update_custom_node(x)
|
||||||
if res.status != 200:
|
if res.status != 200:
|
||||||
failed.add(x[0])
|
failed.add(x['id'])
|
||||||
|
|
||||||
elif k == 'update_comfyui':
|
elif k == 'update_comfyui':
|
||||||
await update_comfyui(None)
|
await update_comfyui(None)
|
||||||
@@ -793,13 +809,13 @@ async def queue_batch(request):
|
|||||||
for x in v:
|
for x in v:
|
||||||
res = await _install_model(x)
|
res = await _install_model(x)
|
||||||
if res.status != 200:
|
if res.status != 200:
|
||||||
failed.add(x[0])
|
failed.add(x['id'])
|
||||||
|
|
||||||
elif k == 'fix':
|
elif k == 'fix':
|
||||||
for x in v:
|
for x in v:
|
||||||
res = await _fix_custom_node(x)
|
res = await _fix_custom_node(x)
|
||||||
if res.status != 200:
|
if res.status != 200:
|
||||||
failed.add(x[0])
|
failed.add(x['id'])
|
||||||
|
|
||||||
with task_worker_lock:
|
with task_worker_lock:
|
||||||
finalize_temp_queue_batch(json_data, failed)
|
finalize_temp_queue_batch(json_data, failed)
|
||||||
@@ -910,8 +926,8 @@ async def update_all(request):
|
|||||||
|
|
||||||
|
|
||||||
async def _update_all(json_data):
|
async def _update_all(json_data):
|
||||||
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_P)
|
||||||
return web.Response(status=403)
|
return web.Response(status=403)
|
||||||
|
|
||||||
with task_worker_lock:
|
with task_worker_lock:
|
||||||
@@ -1056,14 +1072,17 @@ async def fetch_customnode_list(request):
|
|||||||
if channel != 'local':
|
if channel != 'local':
|
||||||
found = 'custom'
|
found = 'custom'
|
||||||
|
|
||||||
for name, url in core.get_channel_dict().items():
|
if channel == core.DEFAULT_CHANNEL or channel == core.DEFAULT_CHANNEL_LEGACY:
|
||||||
if url == channel:
|
channel = 'default'
|
||||||
found = name
|
else:
|
||||||
break
|
for name, url in core.get_channel_dict().items():
|
||||||
|
if url == channel:
|
||||||
|
found = name
|
||||||
|
break
|
||||||
|
|
||||||
channel = found
|
channel = found
|
||||||
|
|
||||||
result = dict(channel=channel, node_packs=node_packs)
|
result = dict(channel=channel, node_packs=node_packs.to_dict())
|
||||||
|
|
||||||
return web.json_response(result, content_type='application/json')
|
return web.json_response(result, content_type='application/json')
|
||||||
|
|
||||||
@@ -1162,7 +1181,7 @@ async def get_snapshot_list(request):
|
|||||||
@routes.get("/v2/snapshot/remove")
|
@routes.get("/v2/snapshot/remove")
|
||||||
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)
|
||||||
return web.Response(status=403)
|
return web.Response(status=403)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1179,8 +1198,8 @@ async def remove_snapshot(request):
|
|||||||
|
|
||||||
@routes.get("/v2/snapshot/restore")
|
@routes.get("/v2/snapshot/restore")
|
||||||
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_P)
|
||||||
return web.Response(status=403)
|
return web.Response(status=403)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1292,6 +1311,65 @@ async def import_fail_info(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
|
@routes.post("/v2/customnode/import_fail_info_bulk")
|
||||||
|
async def import_fail_info_bulk(request):
|
||||||
|
try:
|
||||||
|
json_data = await request.json()
|
||||||
|
|
||||||
|
# Basic validation - ensure we have either cnr_ids or urls
|
||||||
|
if not isinstance(json_data, dict):
|
||||||
|
return web.Response(status=400, text="Request body must be a JSON object")
|
||||||
|
|
||||||
|
if "cnr_ids" not in json_data and "urls" not in json_data:
|
||||||
|
return web.Response(
|
||||||
|
status=400, text="Either 'cnr_ids' or 'urls' field is required"
|
||||||
|
)
|
||||||
|
|
||||||
|
await core.unified_manager.reload('cache')
|
||||||
|
await core.unified_manager.get_custom_nodes('default', 'cache')
|
||||||
|
|
||||||
|
results = {}
|
||||||
|
|
||||||
|
if "cnr_ids" in json_data:
|
||||||
|
if not isinstance(json_data["cnr_ids"], list):
|
||||||
|
return web.Response(status=400, text="'cnr_ids' must be an array")
|
||||||
|
for cnr_id in json_data["cnr_ids"]:
|
||||||
|
if not isinstance(cnr_id, str):
|
||||||
|
results[cnr_id] = {"error": "cnr_id must be a string"}
|
||||||
|
continue
|
||||||
|
module_name = core.unified_manager.get_module_name(cnr_id)
|
||||||
|
if module_name is not None:
|
||||||
|
info = cm_global.error_dict.get(module_name)
|
||||||
|
if info is not None:
|
||||||
|
results[cnr_id] = info
|
||||||
|
else:
|
||||||
|
results[cnr_id] = None
|
||||||
|
else:
|
||||||
|
results[cnr_id] = None
|
||||||
|
|
||||||
|
if "urls" in json_data:
|
||||||
|
if not isinstance(json_data["urls"], list):
|
||||||
|
return web.Response(status=400, text="'urls' must be an array")
|
||||||
|
for url in json_data["urls"]:
|
||||||
|
if not isinstance(url, str):
|
||||||
|
results[url] = {"error": "url must be a string"}
|
||||||
|
continue
|
||||||
|
module_name = core.unified_manager.get_module_name(url)
|
||||||
|
if module_name is not None:
|
||||||
|
info = cm_global.error_dict.get(module_name)
|
||||||
|
if info is not None:
|
||||||
|
results[url] = info
|
||||||
|
else:
|
||||||
|
results[url] = None
|
||||||
|
else:
|
||||||
|
results[url] = None
|
||||||
|
|
||||||
|
return web.json_response(results)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"[ComfyUI-Manager] Error processing bulk import fail info: {e}")
|
||||||
|
return web.Response(status=500, text="Internal server error")
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/v2/manager/queue/reinstall")
|
@routes.post("/v2/manager/queue/reinstall")
|
||||||
async def reinstall_custom_node(request):
|
async def reinstall_custom_node(request):
|
||||||
await uninstall_custom_node(request)
|
await uninstall_custom_node(request)
|
||||||
@@ -1356,8 +1434,8 @@ async def install_custom_node(request):
|
|||||||
|
|
||||||
|
|
||||||
async def _install_custom_node(json_data):
|
async def _install_custom_node(json_data):
|
||||||
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_P)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
# non-nightly cnr is safe
|
# non-nightly cnr is safe
|
||||||
@@ -1462,7 +1540,7 @@ async def _fix_custom_node(json_data):
|
|||||||
|
|
||||||
@routes.post("/v2/customnode/install/git_url")
|
@routes.post("/v2/customnode/install/git_url")
|
||||||
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 web.Response(status=403)
|
||||||
|
|
||||||
@@ -1482,7 +1560,7 @@ async def install_custom_node_git_url(request):
|
|||||||
|
|
||||||
@routes.post("/v2/customnode/install/pip")
|
@routes.post("/v2/customnode/install/pip")
|
||||||
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 web.Response(status=403)
|
||||||
|
|
||||||
@@ -1500,7 +1578,7 @@ async def uninstall_custom_node(request):
|
|||||||
|
|
||||||
async def _uninstall_custom_node(json_data):
|
async def _uninstall_custom_node(json_data):
|
||||||
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)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
node_id = json_data.get('id')
|
node_id = json_data.get('id')
|
||||||
@@ -1526,7 +1604,7 @@ async def update_custom_node(request):
|
|||||||
|
|
||||||
async def _update_custom_node(json_data):
|
async def _update_custom_node(json_data):
|
||||||
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)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
node_id = json_data.get('id')
|
node_id = json_data.get('id')
|
||||||
@@ -1617,8 +1695,8 @@ async def install_model(request):
|
|||||||
|
|
||||||
|
|
||||||
async def _install_model(json_data):
|
async def _install_model(json_data):
|
||||||
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_P)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
# validate request
|
# validate request
|
||||||
@@ -1626,7 +1704,7 @@ async def _install_model(json_data):
|
|||||||
logging.error(f"[ComfyUI-Manager] Invalid model install request is detected: {json_data}")
|
logging.error(f"[ComfyUI-Manager] Invalid model install request is detected: {json_data}")
|
||||||
return web.Response(status=400, text="Invalid model install request is detected")
|
return web.Response(status=400, text="Invalid model install request is detected")
|
||||||
|
|
||||||
if not json_data['filename'].endswith('.safetensors') and not is_allowed_security_level('high'):
|
if not json_data['filename'].endswith('.safetensors') and not is_allowed_security_level('high+'):
|
||||||
models_json = await core.get_data_by_mode('cache', 'model-list.json', 'default')
|
models_json = await core.get_data_by_mode('cache', 'model-list.json', 'default')
|
||||||
|
|
||||||
is_belongs_to_whitelist = False
|
is_belongs_to_whitelist = False
|
||||||
@@ -1783,7 +1861,7 @@ async def get_notice_legacy(request):
|
|||||||
@routes.get("/v2/manager/reboot")
|
@routes.get("/v2/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)
|
||||||
return web.Response(status=403)
|
return web.Response(status=403)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1949,9 +2027,10 @@ if not os.path.exists(context.manager_config_path):
|
|||||||
core.write_config()
|
core.write_config()
|
||||||
|
|
||||||
|
|
||||||
cm_global.register_extension('ComfyUI-Manager',
|
# policy setup
|
||||||
{'version': core.version,
|
manager_security.add_handler_policy(reinstall_custom_node, manager_security.HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NOT_PERSONAL_CLOUD)
|
||||||
'name': 'ComfyUI Manager',
|
manager_security.add_handler_policy(install_custom_node, manager_security.HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NOT_PERSONAL_CLOUD)
|
||||||
'nodes': {},
|
manager_security.add_handler_policy(fix_custom_node, manager_security.HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NOT_PERSONAL_CLOUD)
|
||||||
'description': 'This extension provides the ability to manage custom nodes in ComfyUI.', })
|
manager_security.add_handler_policy(install_custom_node_git_url, manager_security.HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NOT_PERSONAL_CLOUD)
|
||||||
|
manager_security.add_handler_policy(install_custom_node_pip, manager_security.HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NOT_PERSONAL_CLOUD)
|
||||||
|
manager_security.add_handler_policy(install_model, manager_security.HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NOT_PERSONAL_CLOUD)
|
||||||
|
|||||||
@@ -10,6 +10,16 @@ import hashlib
|
|||||||
|
|
||||||
import folder_paths
|
import folder_paths
|
||||||
from server import PromptServer
|
from server import PromptServer
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from nio import AsyncClient, LoginResponse, UploadResponse
|
||||||
|
matrix_nio_is_available = True
|
||||||
|
except Exception:
|
||||||
|
logging.warning(f"[ComfyUI-Manager] The matrix sharing feature has been disabled because the `matrix-nio` dependency is not installed.\n\tTo use this feature, please run the following command:\n\t{sys.executable} -m pip install matrix-nio\n")
|
||||||
|
matrix_nio_is_available = False
|
||||||
|
|
||||||
|
|
||||||
def extract_model_file_names(json_data):
|
def extract_model_file_names(json_data):
|
||||||
@@ -192,6 +202,14 @@ async def get_esheep_workflow_and_images(request):
|
|||||||
return web.Response(status=200, text=json.dumps(data))
|
return web.Response(status=200, text=json.dumps(data))
|
||||||
|
|
||||||
|
|
||||||
|
@PromptServer.instance.routes.get("/v2/manager/get_matrix_dep_status")
|
||||||
|
async def get_matrix_dep_status(request):
|
||||||
|
if matrix_nio_is_available:
|
||||||
|
return web.Response(status=200, text='available')
|
||||||
|
else:
|
||||||
|
return web.Response(status=200, text='unavailable')
|
||||||
|
|
||||||
|
|
||||||
def set_matrix_auth(json_data):
|
def set_matrix_auth(json_data):
|
||||||
homeserver = json_data['homeserver']
|
homeserver = json_data['homeserver']
|
||||||
username = json_data['username']
|
username = json_data['username']
|
||||||
@@ -331,15 +349,12 @@ async def share_art(request):
|
|||||||
workflowId = upload_workflow_json["workflowId"]
|
workflowId = upload_workflow_json["workflowId"]
|
||||||
|
|
||||||
# check if the user has provided Matrix credentials
|
# check if the user has provided Matrix credentials
|
||||||
if "matrix" in share_destinations:
|
if matrix_nio_is_available and "matrix" in share_destinations:
|
||||||
comfyui_share_room_id = '!LGYSoacpJPhIfBqVfb:matrix.org'
|
comfyui_share_room_id = '!LGYSoacpJPhIfBqVfb:matrix.org'
|
||||||
filename = os.path.basename(asset_filepath)
|
filename = os.path.basename(asset_filepath)
|
||||||
content_type = assetFileType
|
content_type = assetFileType
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from matrix_client.api import MatrixHttpApi
|
|
||||||
from matrix_client.client import MatrixClient
|
|
||||||
|
|
||||||
homeserver = 'matrix.org'
|
homeserver = 'matrix.org'
|
||||||
if matrix_auth:
|
if matrix_auth:
|
||||||
homeserver = matrix_auth.get('homeserver', 'matrix.org')
|
homeserver = matrix_auth.get('homeserver', 'matrix.org')
|
||||||
@@ -347,20 +362,35 @@ async def share_art(request):
|
|||||||
if not homeserver.startswith("https://"):
|
if not homeserver.startswith("https://"):
|
||||||
homeserver = "https://" + homeserver
|
homeserver = "https://" + homeserver
|
||||||
|
|
||||||
client = MatrixClient(homeserver)
|
client = AsyncClient(homeserver, matrix_auth['username'])
|
||||||
try:
|
|
||||||
token = client.login(username=matrix_auth['username'], password=matrix_auth['password'])
|
# Login
|
||||||
if not token:
|
login_resp = await client.login(matrix_auth['password'])
|
||||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
if not isinstance(login_resp, LoginResponse) or not login_resp.access_token:
|
||||||
except Exception:
|
await client.close()
|
||||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
||||||
|
|
||||||
matrix = MatrixHttpApi(homeserver, token=token)
|
# Upload asset
|
||||||
with open(asset_filepath, 'rb') as f:
|
with open(asset_filepath, 'rb') as f:
|
||||||
mxc_url = matrix.media_upload(f.read(), content_type, filename=filename)['content_uri']
|
upload_resp, _maybe_keys = await client.upload(f, content_type=content_type, filename=filename)
|
||||||
|
asset_data = f.seek(0) or f.read() # get size for info below
|
||||||
|
if not isinstance(upload_resp, UploadResponse) or not upload_resp.content_uri:
|
||||||
|
await client.close()
|
||||||
|
return web.json_response({"error": "Failed to upload asset to Matrix."}, content_type='application/json', status=500)
|
||||||
|
mxc_url = upload_resp.content_uri
|
||||||
|
|
||||||
workflow_json_mxc_url = matrix.media_upload(prompt['workflow'], 'application/json', filename='workflow.json')['content_uri']
|
# Upload workflow JSON
|
||||||
|
import io
|
||||||
|
workflow_json_bytes = json.dumps(prompt['workflow']).encode('utf-8')
|
||||||
|
workflow_io = io.BytesIO(workflow_json_bytes)
|
||||||
|
upload_workflow_resp, _maybe_keys = await client.upload(workflow_io, content_type='application/json', filename='workflow.json')
|
||||||
|
workflow_io.seek(0)
|
||||||
|
if not isinstance(upload_workflow_resp, UploadResponse) or not upload_workflow_resp.content_uri:
|
||||||
|
await client.close()
|
||||||
|
return web.json_response({"error": "Failed to upload workflow to Matrix."}, content_type='application/json', status=500)
|
||||||
|
workflow_json_mxc_url = upload_workflow_resp.content_uri
|
||||||
|
|
||||||
|
# Send text message
|
||||||
text_content = ""
|
text_content = ""
|
||||||
if title:
|
if title:
|
||||||
text_content += f"{title}\n"
|
text_content += f"{title}\n"
|
||||||
@@ -368,10 +398,45 @@ async def share_art(request):
|
|||||||
text_content += f"{description}\n"
|
text_content += f"{description}\n"
|
||||||
if credits:
|
if credits:
|
||||||
text_content += f"\ncredits: {credits}\n"
|
text_content += f"\ncredits: {credits}\n"
|
||||||
matrix.send_message(comfyui_share_room_id, text_content)
|
await client.room_send(
|
||||||
matrix.send_content(comfyui_share_room_id, mxc_url, filename, 'm.image')
|
room_id=comfyui_share_room_id,
|
||||||
matrix.send_content(comfyui_share_room_id, workflow_json_mxc_url, 'workflow.json', 'm.file')
|
message_type="m.room.message",
|
||||||
except Exception:
|
content={"msgtype": "m.text", "body": text_content}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Send image
|
||||||
|
await client.room_send(
|
||||||
|
room_id=comfyui_share_room_id,
|
||||||
|
message_type="m.room.message",
|
||||||
|
content={
|
||||||
|
"msgtype": "m.image",
|
||||||
|
"body": filename,
|
||||||
|
"url": mxc_url,
|
||||||
|
"info": {
|
||||||
|
"mimetype": content_type,
|
||||||
|
"size": len(asset_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Send workflow JSON file
|
||||||
|
await client.room_send(
|
||||||
|
room_id=comfyui_share_room_id,
|
||||||
|
message_type="m.room.message",
|
||||||
|
content={
|
||||||
|
"msgtype": "m.file",
|
||||||
|
"body": "workflow.json",
|
||||||
|
"url": workflow_json_mxc_url,
|
||||||
|
"info": {
|
||||||
|
"mimetype": "application/json",
|
||||||
|
"size": len(workflow_json_bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await client.close()
|
||||||
|
|
||||||
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return web.json_response({"error": "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500)
|
return web.json_response({"error": "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500)
|
||||||
|
|||||||
@@ -1973,6 +1973,97 @@
|
|||||||
"url": "https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth",
|
"url": "https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth",
|
||||||
"size": "375.0MB"
|
"size": "375.0MB"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "sam2.1_hiera_tiny.pt",
|
||||||
|
"type": "sam2.1",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2.1 hiera model (tiny)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2.1_hiera_tiny.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_tiny.pt",
|
||||||
|
"size": "149.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2.1_hiera_small.pt",
|
||||||
|
"type": "sam2.1",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2.1 hiera model (small)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2.1_hiera_small.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_small.pt",
|
||||||
|
"size": "176.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2.1_hiera_base_plus.pt",
|
||||||
|
"type": "sam2.1",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2.1 hiera model (base+)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2.1_hiera_base_plus.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_base_plus.pt",
|
||||||
|
"size": "309.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2.1_hiera_large.pt",
|
||||||
|
"type": "sam2.1",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2.1 hiera model (large)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2.1_hiera_large.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_large.pt",
|
||||||
|
"size": "857.0MB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "sam2_hiera_tiny.pt",
|
||||||
|
"type": "sam2",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2 hiera model (tiny)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2_hiera_tiny.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_tiny.pt",
|
||||||
|
"size": "149.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2_hiera_small.pt",
|
||||||
|
"type": "sam2",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2 hiera model (small)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2_hiera_small.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_small.pt",
|
||||||
|
"size": "176.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2_hiera_base_plus.pt",
|
||||||
|
"type": "sam2",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2 hiera model (base+)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2_hiera_base_plus.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_base_plus.pt",
|
||||||
|
"size": "309.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2_hiera_large.pt",
|
||||||
|
"type": "sam2",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2 hiera model (large)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2_hiera_large.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt",
|
||||||
|
"size": "857.0MB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "seecoder v1.0",
|
"name": "seecoder v1.0",
|
||||||
"type": "seecoder",
|
"type": "seecoder",
|
||||||
@@ -4006,6 +4097,29 @@
|
|||||||
"size": "649MB"
|
"size": "649MB"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/omnigen2_fp16.safetensors",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "OmniGen2",
|
||||||
|
"save_path": "default",
|
||||||
|
"description": "OmniGen2 diffusion model. This is required for using OmniGen2.",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged",
|
||||||
|
"filename": "omnigen2_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/omnigen2_fp16.safetensors",
|
||||||
|
"size": "7.93GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/qwen_2.5_vl_fp16.safetensors",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "qwen-2.5",
|
||||||
|
"save_path": "default",
|
||||||
|
"description": "text encoder for OmniGen2",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged",
|
||||||
|
"filename": "qwen_2.5_vl_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/text_encoders/qwen_2.5_vl_fp16.safetensors",
|
||||||
|
"size": "7.51GB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "FLUX.1 [Schnell] Diffusion model",
|
"name": "FLUX.1 [Schnell] Diffusion model",
|
||||||
"type": "diffusion_model",
|
"type": "diffusion_model",
|
||||||
@@ -4023,7 +4137,7 @@
|
|||||||
"type": "VAE",
|
"type": "VAE",
|
||||||
"base": "FLUX.1",
|
"base": "FLUX.1",
|
||||||
"save_path": "vae/FLUX1",
|
"save_path": "vae/FLUX1",
|
||||||
"description": "FLUX.1 VAE model",
|
"description": "FLUX.1 VAE model\nNOTE: This VAE model can also be used for image generation with OmniGen2.",
|
||||||
"reference": "https://huggingface.co/black-forest-labs/FLUX.1-schnell",
|
"reference": "https://huggingface.co/black-forest-labs/FLUX.1-schnell",
|
||||||
"filename": "ae.safetensors",
|
"filename": "ae.safetensors",
|
||||||
"url": "https://huggingface.co/black-forest-labs/FLUX.1-schnell/resolve/main/ae.safetensors",
|
"url": "https://huggingface.co/black-forest-labs/FLUX.1-schnell/resolve/main/ae.safetensors",
|
||||||
@@ -4931,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",
|
||||||
@@ -5033,6 +5246,50 @@
|
|||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-fp8.safetensors",
|
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-fp8.safetensors",
|
||||||
"size": "15.7GB"
|
"size": "15.7GB"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "LTX-Video 2B Distilled v0.9.8",
|
||||||
|
"type": "checkpoint",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "checkpoints/LTXV",
|
||||||
|
"description": "LTX-Video 2B distilled model v0.9.8 with improved prompt understanding and detail generation.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
||||||
|
"filename": "ltxv-2b-0.9.8-distilled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-2b-0.9.8-distilled.safetensors",
|
||||||
|
"size": "6.34GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LTX-Video 2B Distilled FP8 v0.9.8",
|
||||||
|
"type": "checkpoint",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "checkpoints/LTXV",
|
||||||
|
"description": "Quantized LTX-Video 2B distilled model v0.9.8 with improved prompt understanding and detail generation, optimized for lower VRAM usage.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
||||||
|
"filename": "ltxv-2b-0.9.8-distilled-fp8.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-2b-0.9.8-distilled-fp8.safetensors",
|
||||||
|
"size": "4.46GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LTX-Video 13B Distilled v0.9.8",
|
||||||
|
"type": "checkpoint",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "checkpoints/LTXV",
|
||||||
|
"description": "LTX-Video 13B distilled model v0.9.8 with improved prompt understanding and detail generation.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
||||||
|
"filename": "ltxv-13b-0.9.8-distilled.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.8-distilled.safetensors",
|
||||||
|
"size": "28.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LTX-Video 13B Distilled FP8 v0.9.8",
|
||||||
|
"type": "checkpoint",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "checkpoints/LTXV",
|
||||||
|
"description": "Quantized LTX-Video 13B distilled model v0.9.8 with improved prompt understanding and detail generation, optimized for lower VRAM usage.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
||||||
|
"filename": "ltxv-13b-0.9.8-distilled-fp8.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.8-distilled-fp8.safetensors",
|
||||||
|
"size": "15.7GB"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "LTX-Video 13B Distilled LoRA v0.9.7",
|
"name": "LTX-Video 13B Distilled LoRA v0.9.7",
|
||||||
"type": "lora",
|
"type": "lora",
|
||||||
@@ -5044,6 +5301,50 @@
|
|||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-lora128.safetensors",
|
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-lora128.safetensors",
|
||||||
"size": "1.33GB"
|
"size": "1.33GB"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "LTX-Video ICLoRA Depth 13B v0.9.7",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "loras",
|
||||||
|
"description": "In-Context LoRA (IC LoRA) for depth-controlled video-to-video generation with precise depth conditioning.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video-ICLoRA-depth-13b-0.9.7",
|
||||||
|
"filename": "ltxv-097-ic-lora-depth-control-comfyui.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video-ICLoRA-depth-13b-0.9.7/resolve/main/ltxv-097-ic-lora-depth-control-comfyui.safetensors",
|
||||||
|
"size": "81.9MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LTX-Video ICLoRA Pose 13B v0.9.7",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "loras",
|
||||||
|
"description": "In-Context LoRA (IC LoRA) for pose-controlled video-to-video generation with precise pose conditioning.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video-ICLoRA-pose-13b-0.9.7",
|
||||||
|
"filename": "ltxv-097-ic-lora-pose-control-comfyui.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video-ICLoRA-pose-13b-0.9.7/resolve/main/ltxv-097-ic-lora-pose-control-comfyui.safetensors",
|
||||||
|
"size": "151MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LTX-Video ICLoRA Canny 13B v0.9.7",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "loras",
|
||||||
|
"description": "In-Context LoRA (IC LoRA) for canny edge-controlled video-to-video generation with precise edge conditioning.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video-ICLoRA-canny-13b-0.9.7",
|
||||||
|
"filename": "ltxv-097-ic-lora-canny-control-comfyui.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video-ICLoRA-canny-13b-0.9.7/resolve/main/ltxv-097-ic-lora-canny-control-comfyui.safetensors",
|
||||||
|
"size": "81.9MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LTX-Video ICLoRA Detailer 13B v0.9.8",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "loras",
|
||||||
|
"description": "A video detailer model on top of LTXV_13B_098_DEV trained on custom data using In-Context LoRA (IC LoRA) method.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video-ICLoRA-detailer-13b-0.9.8",
|
||||||
|
"filename": "ltxv-098-ic-lora-detailer-comfyui.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video-ICLoRA-detailer-13b-0.9.8/resolve/main/ltxv-098-ic-lora-detailer-comfyui.safetensors",
|
||||||
|
"size": "1.31GB"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Latent Bridge Matching for Image Relighting",
|
"name": "Latent Bridge Matching for Image Relighting",
|
||||||
"type": "diffusion_model",
|
"type": "diffusion_model",
|
||||||
@@ -35,7 +35,6 @@ else:
|
|||||||
def current_timestamp():
|
def current_timestamp():
|
||||||
return str(time.time()).split('.')[0]
|
return str(time.time()).split('.')[0]
|
||||||
|
|
||||||
security_check.security_check()
|
|
||||||
|
|
||||||
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
||||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||||
@@ -111,20 +110,15 @@ def check_file_logging():
|
|||||||
|
|
||||||
read_config()
|
read_config()
|
||||||
read_uv_mode()
|
read_uv_mode()
|
||||||
|
security_check.security_check()
|
||||||
check_file_logging()
|
check_file_logging()
|
||||||
|
|
||||||
if sys.version_info < (3, 13):
|
cm_global.pip_overrides = {}
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
|
||||||
else:
|
|
||||||
cm_global.pip_overrides = {}
|
|
||||||
|
|
||||||
if os.path.exists(manager_pip_overrides_path):
|
if os.path.exists(manager_pip_overrides_path):
|
||||||
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||||
cm_global.pip_overrides = json.load(json_file)
|
cm_global.pip_overrides = json.load(json_file)
|
||||||
|
|
||||||
if sys.version_info < (3, 13):
|
|
||||||
cm_global.pip_overrides['numpy'] = 'numpy<2'
|
|
||||||
|
|
||||||
|
|
||||||
if os.path.exists(manager_pip_blacklist_path):
|
if os.path.exists(manager_pip_blacklist_path):
|
||||||
with open(manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
with open(manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||||
|
|||||||
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,25 @@
|
|||||||
{
|
{
|
||||||
"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",
|
||||||
|
"title": "ComfyUI-Unload-Model-Fork",
|
||||||
|
"reference": "https://github.com/joaomede/ComfyUI-Unload-Model-Fork",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/joaomede/ComfyUI-Unload-Model-Fork"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "For unloading a model or all models, using the memory management that is already present in ComfyUI. Copied from [a/https://github.com/willblaschko/ComfyUI-Unload-Models](https://github.com/willblaschko/ComfyUI-Unload-Models) but without the unnecessary extra stuff."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"author": "SanDiegoDude",
|
"author": "SanDiegoDude",
|
||||||
"title": "ComfyUI-HiDream-Sampler [WIP]",
|
"title": "ComfyUI-HiDream-Sampler [WIP]",
|
||||||
|
|||||||
@@ -1,5 +1,668 @@
|
|||||||
{
|
{
|
||||||
"custom_nodes": [
|
"custom_nodes": [
|
||||||
|
{
|
||||||
|
"author": "perilli",
|
||||||
|
"title": "apw_nodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/alessandroperilli/APW_Nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/alessandroperilli/APW_Nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A custom node suite to augment the capabilities of the [a/AP Workflows for ComfyUI](https://perilli.com/ai/comfyui/)\nNOTE: See [a/Open Creative Studio Nodes](https://github.com/alessandroperilli/OCS_Nodes)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "greengerong",
|
||||||
|
"title": "ComfyUI-Lumina-Video [REMOVED]",
|
||||||
|
"reference": "https://github.com/greengerong/ComfyUI-Lumina-Video",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/greengerong/ComfyUI-Lumina-Video"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This is a video generation plugin implementation for ComfyUI based on the Lumina Video model."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "SatadalAI",
|
||||||
|
"title": "Combined Upscale Node for ComfyUI [REMOVED]",
|
||||||
|
"reference": "https://github.com/SatadalAI/SATA_UtilityNode",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/SatadalAI/SATA_UtilityNode"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Combined_Upscale is a custom ComfyUI node designed for high-quality image enhancement workflows. It intelligently combines model-based upscaling with efficient CPU-based resizing, offering granular control over output dimensions and quality. Ideal for asset pipelines, UI prototyping, and generative workflows.\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "netroxin",
|
||||||
|
"title": "Netro_wildcards [REMOVED]",
|
||||||
|
"reference": "https://github.com/netroxin/comfyui_netro_wildcards",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/netroxin/comfyui_netro_wildcards"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Since I used 'simple wildcards' from Vanilla and it no longer works with the new Comfy UI version for me, I created an alternative. This CustomNode takes the entire contents of your wildcards-folder(comfyui wildcards) and creates a node for each one."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "takoyaki1118",
|
||||||
|
"title": "ComfyUI-MangaTools [REMOVED]",
|
||||||
|
"reference": "https://github.com/takoyaki1118/ComfyUI-MangaTools",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/takoyaki1118/ComfyUI-MangaTools"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Manga Panel Detector, Manga Panel Dispatcher, GateImage, MangaPageAssembler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "lucasgattas",
|
||||||
|
"title": "comfyui-egregora-regional [REMOVED]",
|
||||||
|
"reference": "https://github.com/lucasgattas/comfyui-egregora-regional",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/lucasgattas/comfyui-egregora-regional"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Image Tile Split with Region-Aware Prompting for ComfyUI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "lucasgattas",
|
||||||
|
"title": "comfyui-egregora-tiled [REMOVED]",
|
||||||
|
"reference": "https://github.com/lucasgattas/comfyui-egregora-tiled",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/lucasgattas/comfyui-egregora-tiled"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Tiled regional prompting + tiled VAE decode with seam-free blending for ComfyUI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Seedsa",
|
||||||
|
"title": "ComfyUI Fooocus Nodes [REMOVED]",
|
||||||
|
"id": "fooocus-nodes",
|
||||||
|
"reference": "https://github.com/Seedsa/Fooocus_Nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Seedsa/Fooocus_Nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This extension provides image generation features based on Fooocus."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "zhilemann",
|
||||||
|
"title": "ComfyUI-moondream2 [REMOVED]",
|
||||||
|
"reference": "https://github.com/zhilemann/ComfyUI-moondream2",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/zhilemann/ComfyUI-moondream2"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "nodes for nightly moondream2 VLM inference\nsupports only captioning and visual queries at the moment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "shinich39",
|
||||||
|
"title": "comfyui-textarea-is-shit [REMOVED]",
|
||||||
|
"reference": "https://github.com/shinich39/comfyui-textarea-is-shit",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/shinich39/comfyui-textarea-is-shit"
|
||||||
|
],
|
||||||
|
"description": "HTML gives me a textarea like piece of shit.",
|
||||||
|
"install_type": "git-clone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "shinich39",
|
||||||
|
"title": "comfyui-poor-textarea [REMOVED]",
|
||||||
|
"reference": "https://github.com/shinich39/comfyui-poor-textarea",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/shinich39/comfyui-poor-textarea"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Add commentify, indentation, auto-close brackets in textarea."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "InfiniNode",
|
||||||
|
"title": "Comfyui-InfiniNode-Model-Suite [UNSAFE/REMOVED]",
|
||||||
|
"reference": "https://github.com/InfiniNode/Comfyui-InfiniNode-Model-Suite",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/InfiniNode/Comfyui-InfiniNode-Model-Suite"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Welcome to the InfiniNode Model Suite, a custom node pack for ComfyUI that transforms the process of manipulating generative AI models. Our suite is a direct implementation of the 'GUI-Based Key Converter Development Plan,' designed to remove technical barriers for advanced AI practitioners and integrate seamlessly with existing image generation pipelines.[w/This node pack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Avalre",
|
||||||
|
"title": "ComfyUI-avaNodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/Avalre/ComfyUI-avaNodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Avalre/ComfyUI-avaNodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "These nodes were created to personalize/optimize several ComfyUI nodes for my own use. You can replicate the functionality of most of my nodes by some combination of default ComfyUI nodes and custom nodes from other developers."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Alectriciti",
|
||||||
|
"title": "comfyui-creativeprompts [REMOVED]",
|
||||||
|
"reference": "https://github.com/Alectriciti/comfyui-creativeprompts",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Alectriciti/comfyui-creativeprompts"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A creative alternative to dynamicprompts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "flybirdxx",
|
||||||
|
"title": "ComfyUI Sliding Window [REMOVED]",
|
||||||
|
"reference": "https://github.com/PixWizardry/ComfyUI_Sliding_Window",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/PixWizardry/ComfyUI_Sliding_Window"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This set of nodes provides a powerful sliding window or 'tiling' technique for processing long videos and animations in ComfyUI. It allows you to work on animations that are longer than your VRAM would typically allow by breaking the job into smaller, overlapping chunks and seamlessly blending them back together."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "SykkoAtHome",
|
||||||
|
"title": "Sykko Tools for ComfyUI [REMOVED]",
|
||||||
|
"reference": "https://github.com/SykkoAtHome/ComfyUI_SykkoTools",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/SykkoAtHome/ComfyUI_SykkoTools"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Utilities for working with camera animations inside ComfyUI. The repository currently provides a node for loading camera motion from ASCII FBX files and a corresponding command line helper for debugging."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "hananbeer",
|
||||||
|
"title": "node_dev - ComfyUI Node Development Helper [REMOVED]",
|
||||||
|
"reference": "https://github.com/hananbeer/node_dev",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/hananbeer/node_dev"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Browse to this endpoint to reload custom nodes for more streamlined development:\nhttp://127.0.0.1:8188/node_dev/reload/<module_name>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Charonartist",
|
||||||
|
"title": "Comfyui_gemini_tts_node [REMOVED]",
|
||||||
|
"reference": "https://github.com/Charonartist/Comfyui_gemini_tts_node",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Charonartist/Comfyui_gemini_tts_node"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This custom node is a ComfyUI node for generating speech from text using the Gemini 2.5 Flash Preview TTS API."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "squirrel765",
|
||||||
|
"title": "lorasubdirectory [REMOVED]",
|
||||||
|
"reference": "https://github.com/andrewsthomasj/lorasubdirectory",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/andrewsthomasj/lorasubdirectory"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "only show dropdown of loras ina a given subdirectory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "shingo1228",
|
||||||
|
"title": "ComfyUI-send-Eagle(slim) [REVMOED]",
|
||||||
|
"id": "send-eagle",
|
||||||
|
"reference": "https://github.com/shingo1228/ComfyUI-send-eagle-slim",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/shingo1228/ComfyUI-send-eagle-slim"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Nodes:Send Webp Image to Eagle. This is an extension node for ComfyUI that allows you to send generated images in webp format to Eagle. This extension node is a re-implementation of the Eagle linkage functions of the previous ComfyUI-send-Eagle node, focusing on the functions required for this node."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "shingo1228",
|
||||||
|
"title": "ComfyUI-SDXL-EmptyLatentImage [REVMOED]",
|
||||||
|
"id": "sdxl-emptylatent",
|
||||||
|
"reference": "https://github.com/shingo1228/ComfyUI-SDXL-EmptyLatentImage",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/shingo1228/ComfyUI-SDXL-EmptyLatentImage"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Nodes:SDXL Empty Latent Image. An extension node for ComfyUI that allows you to select a resolution from the pre-defined json files and output a Latent Image."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "chaunceyyann",
|
||||||
|
"title": "ComfyUI Image Processing Nodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/chaunceyyann/comfyui-image-processing-nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/chaunceyyann/comfyui-image-processing-nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A collection of custom nodes for ComfyUI focused on image processing operations."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "OgreLemonSoup",
|
||||||
|
"title": "Gallery&Tabs [DEPRECATED]",
|
||||||
|
"id": "LoadImageGallery",
|
||||||
|
"reference": "https://github.com/OgreLemonSoup/ComfyUI-Load-Image-Gallery",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/OgreLemonSoup/ComfyUI-Load-Image-Gallery"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Adds a gallery to the Load Image node and tabs for Load Checkpoint/Lora/etc nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "11dogzi",
|
||||||
|
"title": "Qwen-Image ComfyUI [REMOVED]",
|
||||||
|
"reference": "https://github.com/11dogzi/Comfyui-Qwen-Image",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/11dogzi/Comfyui-Qwen-Image"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This is a custom node package that integrates the Qwen-Image model into ComfyUI."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "BAIS1C",
|
||||||
|
"title": "ComfyUI-AudioDuration [REMOVED]",
|
||||||
|
"reference": "https://github.com/BAIS1C/ComfyUI_BASICDancePoser",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/BAIS1C/ComfyUI_BASICDancePoser"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Node to extract Dance poses from Music to control Video Generations.\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "BAIS1C",
|
||||||
|
"title": "ComfyUI_BASICSAdvancedDancePoser [REMOVED]",
|
||||||
|
"reference": "https://github.com/BAIS1C/ComfyUI_BASICSAdvancedDancePoser",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/BAIS1C/ComfyUI_BASICSAdvancedDancePoser"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Professional COCO-WholeBody 133-keypoint dance animation system for ComfyUI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "fablestudio",
|
||||||
|
"title": "ComfyUI-Showrunner-Utils [REMOVED]",
|
||||||
|
"reference": "https://github.com/fablestudio/ComfyUI-Showrunner-Utils",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/fablestudio/ComfyUI-Showrunner-Utils"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Align Face, Generate Timestamp, GetMostCommonColors, Alpha Crop and Position Image, Shrink Image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "skayka",
|
||||||
|
"title": "ComfyUI-DreamFit [REMOVED]",
|
||||||
|
"reference": "https://github.com/skayka/ComfyUI-DreamFit",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/skayka/ComfyUI-DreamFit"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Garment-centric human generation nodes for ComfyUI using DreamFit with Flux.\nDreamFit is a powerful adapter system that enhances Flux models with garment-aware generation capabilities, enabling high-quality fashion and clothing generation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "domenecmiralles",
|
||||||
|
"title": "obobo_nodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/domenecmiralles/obobo_nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/domenecmiralles/obobo_nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A collection of custom nodes for ComfyUI that provide various input and output capabilities."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "NicholasKao1029",
|
||||||
|
"title": "comfyui-pixxio [REMOVED]",
|
||||||
|
"reference": "https://github.com/NicholasKao1029/comfyui-pixxio",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/NicholasKao1029/comfyui-pixxio"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Auto-Upload Image to Pixxio Collection, Load Image from Pixx.io"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "ComfyUI-Workflow",
|
||||||
|
"title": "ComfyUI OpenAI Nodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/ComfyUI-Workflow/ComfyUI-OpenAI",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/ComfyUI-Workflow/ComfyUI-OpenAI"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "By utilizing OpenAI's powerful vision models, this node enables you to incorporate state-of-the-art image understanding into your ComfyUI projects with minimal setup."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "dionren",
|
||||||
|
"title": "Export Workflow With Cyuai Api Available Nodes [REMOVED]",
|
||||||
|
"id": "comfyUI-Pro-Export-Tool",
|
||||||
|
"reference": "https://github.com/dionren/ComfyUI-Pro-Export-Tool",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/dionren/ComfyUI-Pro-Export-Tool"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This is a node to convert workflows to cyuai api available nodes."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "1H-hobit",
|
||||||
|
"title": "ComfyUI_InternVL3 [REMOVED]",
|
||||||
|
"reference": "https://github.com/1H-hobit/ComfyUI_InternVL3",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/1H-hobit/ComfyUI_InternVL3"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI for [a/InternVL](https://github.com/OpenGVLab/InternVL)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "spacepxl",
|
||||||
|
"title": "ComfyUI-Florence-2 [DEPRECATED]",
|
||||||
|
"id": "florence2-spacepxl",
|
||||||
|
"reference": "https://github.com/spacepxl/ComfyUI-Florence-2",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/spacepxl/ComfyUI-Florence-2"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "[a/https://huggingface.co/microsoft/Florence-2-large-ft](https://huggingface.co/microsoft/Florence-2-large-ft)\nLarge or base model, support for captioning and bbox task modes, more coming soon."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "xxxxxxxxxxxc",
|
||||||
|
"title": "flux-kontext-diff-merge [REMOVED]",
|
||||||
|
"reference": "https://github.com/xxxxxxxxxxxc/flux-kontext-diff-merge",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/xxxxxxxxxxxc/flux-kontext-diff-merge"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Preserve image quality with flux-kontext-diff-merge. This ComfyUI node merges only changed areas from AI edits, ensuring clarity and detail."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "TechnoByteJS",
|
||||||
|
"title": "TechNodes [REMOVED]",
|
||||||
|
"id": "technodes",
|
||||||
|
"reference": "https://github.com/TechnoByteJS/ComfyUI-TechNodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/TechnoByteJS/ComfyUI-TechNodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI nodes for merging, testing and more.\nNOTE: SDNext Merge, VAE Merge, MBW Layers, Repeat VAE, Quantization."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "DDDDEEP",
|
||||||
|
"title": "ComfyUI-DDDDEEP [REMOVED]",
|
||||||
|
"reference": "https://github.com/DDDDEEP/ComfyUI-DDDDEEP",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/DDDDEEP/ComfyUI-DDDDEEP"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: AutoWidthHeight, ReturnIntSeed, OppositeBool, PromptItemCollection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "manifestations",
|
||||||
|
"title": "ComfyUI Ethnic Outfits Custom Nodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/manifestations/comfyui-outfits",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/manifestations/comfyui-outfits"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Custom ComfyUI nodes for generating outfit prompts representing diverse ethnicities, cultures, and regions. Uses extensible JSON data for clothing, accessories, and poses, with “random/disabled” dropdowns for flexibility. Advanced prompt engineering is supported via Ollama LLM integration. Easily add new regions, ethnicities, or cultures by updating data files and creating lightweight node wrappers. Designed for artists, researchers, and developers seeking culturally rich, customizable prompt generation in ComfyUI workflows."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "MitoshiroPJ",
|
||||||
|
"title": "ComfyUI Slothful Attention [REMOVED]",
|
||||||
|
"reference": "https://github.com/MitoshiroPJ/comfyui_slothful_attention",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/MitoshiroPJ/comfyui_slothful_attention"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This custom node allow controlling output without training. The reducing method is similar to [a/Spatial-Reduction Attention](https://paperswithcode.com/method/spatial-reduction-attention)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "MitoshiroPJ",
|
||||||
|
"title": "comfyui_focal_sampler [REMOVED]",
|
||||||
|
"reference": "https://github.com/MitoshiroPJ/comfyui_focal_sampler",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/MitoshiroPJ/comfyui_focal_sampler"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Apply additional sampling to specific area"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "manifestations",
|
||||||
|
"title": "ComfyUI Ethnic Outfit & Prompt Enhancer Nodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/manifestations/comfyui-indian-outfit",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/manifestations/comfyui-indian-outfit"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Features:\n* Extensive options for Indian, Indonesian, and international clothing, jewelry, accessories, and styles\n* Multiple jewelry and accessory fields (with material support: gold, diamond, silver, leather, beads, etc.)\n* Support for tattoos, henna, hair styles, poses, shot types, lighting, and photography genres\n* Seamless prompt expansion using your own Ollama LLM instance\n* Modular, extensible JSON data files for easy customization"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "coVISIONSld",
|
||||||
|
"title": "ComfyUI-OmniGen2 [REMOVED]",
|
||||||
|
"reference": "https://github.com/coVISIONSld/ComfyUI-OmniGen2",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/coVISIONSld/ComfyUI-OmniGen2"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI-OmniGen2 is a custom node package for the OmniGen2 model, enabling advanced text-to-image generation and visual understanding."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "S4MUEL-404",
|
||||||
|
"title": "ComfyUI-S4Tool-Image-Overlay [REMOVED]",
|
||||||
|
"reference": "https://github.com/S4MUEL-404/ComfyUI-S4Tool-Image-Overlay",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/S4MUEL-404/ComfyUI-S4Tool-Image-Overlay"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Quickly set up image overlay effects"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "akspa0",
|
||||||
|
"title": "ComfyUI-FapMixPlus [REMOVED]",
|
||||||
|
"reference": "https://github.com/akspa0/ComfyUI-FapMixPlus",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/akspa0/ComfyUI-FapMixPlus"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This is an audio processing script that applies soft limiting, optional loudness normalization, and optional slicing for transcription. It can also produce stereo-mixed outputs with optional audio appended to the end. The script organizes processed files into structured folders with sanitized filenames and retains original timestamps for continuity."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "RedmondAI",
|
||||||
|
"title": "comfyui-tools [UNSAFE]",
|
||||||
|
"reference": "https://github.com/RedmondAI/comfyui-tools",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/RedmondAI/comfyui-tools"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Custom extensions for ComfyUI used by the Redmond3D VFX team.[w/This node pack has a vulnerability that allows it to create files at arbitrary paths.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "S4MUEL-404",
|
||||||
|
"title": "Image Position Blend [REMOVED]",
|
||||||
|
"id": "ComfyUI-Image-Position-Blend",
|
||||||
|
"version": "1.1",
|
||||||
|
"reference": "https://github.com/S4MUEL-404/ComfyUI-Image-Position-Blend",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/S4MUEL-404/ComfyUI-Image-Position-Blend"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A custom node for conveniently adjusting the overlay position of two images."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "S4MUEL-404",
|
||||||
|
"title": "ComfyUI-Text-On-Image [REMOVED]",
|
||||||
|
"id": "ComfyUI-Text-On-Image",
|
||||||
|
"reference": "https://github.com/S4MUEL-404/ComfyUI-Text-On-Image",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/S4MUEL-404/ComfyUI-Text-On-Image"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A custom node for ComfyUI that allows users to add text overlays to images with customizable size, font, position, and shadow."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "S4MUEL-404",
|
||||||
|
"title": "ComfyUI-Prompts-Selector [REMOVED]",
|
||||||
|
"reference": "https://github.com/S4MUEL-404/ComfyUI-Prompts-Selector",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/S4MUEL-404/ComfyUI-Prompts-Selector"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Quickly select preset prompts and merge them"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "juntaosun",
|
||||||
|
"title": "ComfyUI_open_nodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/juntaosun/ComfyUI_open_nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/juntaosun/ComfyUI_open_nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI open nodes by juntaosun."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "perilli",
|
||||||
|
"title": "apw_nodes [DEPRECATED]",
|
||||||
|
"reference": "https://github.com/alessandroperilli/apw_nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/alessandroperilli/apw_nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A custom node suite to augment the capabilities of the [a/AP Workflows for ComfyUI](https://perilli.com/ai/comfyui/)[w/'APW_Nodes' has been newly added in place of 'apw_nodes'.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "markuryy",
|
||||||
|
"title": "ComfyUI Spiritparticle Nodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/markuryy/comfyui-spiritparticle",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/markuryy/comfyui-spiritparticle"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A node pack by spiritparticle."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "SpaceKendo",
|
||||||
|
"title": "Text to video for Stable Video Diffusion in ComfyUI [REMOVED]",
|
||||||
|
"id": "svd-txt2vid",
|
||||||
|
"reference": "https://github.com/SpaceKendo/ComfyUI-svd_txt2vid",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/SpaceKendo/ComfyUI-svd_txt2vid"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This is node replaces the init_image conditioning for the [a/Stable Video Diffusion](https://github.com/Stability-AI/generative-models) image to video model with text embeds, together with a conditioning frame. The conditioning frame is a set of latents."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "vovler",
|
||||||
|
"title": "ComfyUI Civitai Helper Extension [REMOVED]",
|
||||||
|
"reference": "https://github.com/vovler/comfyui-civitaihelper",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/vovler/comfyui-civitaihelper"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI extension for parsing Civitai PNG workflows and automatically downloading missing models"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "DriftJohnson",
|
||||||
|
"title": "DJZ-Nodes [REMOVED]",
|
||||||
|
"id": "DJZ-Nodes",
|
||||||
|
"reference": "https://github.com/MushroomFleet/DJZ-Nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/MushroomFleet/DJZ-Nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "AspectSize and other nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "DriftJohnson",
|
||||||
|
"title": "KokoroTTS Node [REMOVED]",
|
||||||
|
"reference": "https://github.com/MushroomFleet/DJZ-KokoroTTS",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/MushroomFleet/DJZ-KokoroTTS"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This node provides advanced text-to-speech functionality powered by KokoroTTS. Follow the instructions below to install, configure, and use the node within your portable ComfyUI installation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "MushroomFleet",
|
||||||
|
"title": "DJZ-Pedalboard [REMOVED]",
|
||||||
|
"reference": "https://github.com/MushroomFleet/DJZ-Pedalboard",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/MushroomFleet/DJZ-Pedalboard"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This project provides a collection of custom nodes designed for enhanced audio effects in ComfyUI. With an intuitive pedalboard interface, users can easily integrate and manipulate various audio effects within their workflows."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "MushroomFleet",
|
||||||
|
"title": "SVG Suite for ComfyUI [REMOVED]",
|
||||||
|
"reference": "https://github.com/MushroomFleet/svg-suite",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/MushroomFleet/svg-suite"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "SVG Suite is an advanced set of nodes for converting images to SVG in ComfyUI, expanding upon the functionality of ComfyUI-ToSVG."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "joeriben",
|
||||||
|
"title": "AI4ArtsEd Ollama Prompt Node [DEPRECATED]",
|
||||||
|
"reference": "https://github.com/joeriben/ai4artsed_comfyui",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/joeriben/ai4artsed_comfyui"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Experimental nodes for ComfyUI. For more, see [a/https://kubi-meta.de/ai4artsed](https://kubi-meta.de/ai4artsed) A custom ComfyUI node for stylistic and cultural transformation of input text using local LLMs served via Ollama. This node allows you to combine a free-form prompt (e.g. translation, poetic recoding, genre shift) with externally supplied text in the ComfyUI graph. The result is processed via an Ollama-hosted model and returned as plain text."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "bento234",
|
||||||
|
"title": "ComfyUI-bento-toolbox [REMOVED]",
|
||||||
|
"reference": "https://github.com/bento234/ComfyUI-bento-toolbox",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/bento234/ComfyUI-bento-toolbox"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Tile Prompt Distributor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "yichengup",
|
||||||
|
"title": "ComfyUI-VideoBlender [REMOVED]",
|
||||||
|
"reference": "https://github.com/yichengup/ComfyUI-VideoBlender",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/yichengup/ComfyUI-VideoBlender"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Video clip mixing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "xl0",
|
||||||
|
"title": "latent-tools [REMOVED]",
|
||||||
|
"reference": "https://github.com/xl0/latent-tools",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/xl0/latent-tools"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Visualize and manipulate the latent space in ComfyUI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Conor-Collins",
|
||||||
|
"title": "ComfyUI-CoCoTools [REMOVED]",
|
||||||
|
"reference": "https://github.com/Conor-Collins/coco_tools",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Conor-Collins/coco_tools"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A set of custom nodes for ComfyUI providing advanced image processing, file handling, and utility functions."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "theUpsider",
|
||||||
|
"title": "ComfyUI-Logic [DEPRECATED]",
|
||||||
|
"id": "comfy-logic",
|
||||||
|
"reference": "https://github.com/theUpsider/ComfyUI-Logic",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/theUpsider/ComfyUI-Logic"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "An extension to ComfyUI that introduces logic nodes and conditional rendering capabilities."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Malloc-pix",
|
||||||
|
"title": "comfyui_qwen2.4_vl_node [REMOVED]",
|
||||||
|
"reference": "https://github.com/Malloc-pix/comfyui_qwen2.4_vl_node",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Malloc-pix/comfyui_qwen2.4_vl_node"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: CogVLM2 Captioner, CLIP Dynamic Text Encode(cy)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "inyourdreams-studio",
|
||||||
|
"title": "ComfyUI-RBLM [REMOVED]",
|
||||||
|
"reference": "https://github.com/inyourdreams-studio/comfyui-rblm",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/inyourdreams-studio/comfyui-rblm"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A custom node pack for ComfyUI that provides text manipulation nodes."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"author": "dream-computing",
|
"author": "dream-computing",
|
||||||
"title": "SyntaxNodes - Image Processing Effects for ComfyUI [REMOVED]",
|
"title": "SyntaxNodes - Image Processing Effects for ComfyUI [REMOVED]",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,219 @@
|
|||||||
{
|
{
|
||||||
"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",
|
||||||
|
"type": "sam2.1",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2.1 hiera model (tiny)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2.1_hiera_tiny.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_tiny.pt",
|
||||||
|
"size": "149.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2.1_hiera_small.pt",
|
||||||
|
"type": "sam2.1",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2.1 hiera model (small)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2.1_hiera_small.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_small.pt",
|
||||||
|
"size": "176.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2.1_hiera_base_plus.pt",
|
||||||
|
"type": "sam2.1",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2.1 hiera model (base+)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2.1_hiera_base_plus.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_base_plus.pt",
|
||||||
|
"size": "309.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2.1_hiera_large.pt",
|
||||||
|
"type": "sam2.1",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2.1 hiera model (large)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2.1_hiera_large.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_large.pt",
|
||||||
|
"size": "857.0MB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "sam2_hiera_tiny.pt",
|
||||||
|
"type": "sam2",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2 hiera model (tiny)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2_hiera_tiny.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_tiny.pt",
|
||||||
|
"size": "149.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2_hiera_small.pt",
|
||||||
|
"type": "sam2",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2 hiera model (small)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2_hiera_small.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_small.pt",
|
||||||
|
"size": "176.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2_hiera_base_plus.pt",
|
||||||
|
"type": "sam2",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2 hiera model (base+)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2_hiera_base_plus.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_base_plus.pt",
|
||||||
|
"size": "309.0MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sam2_hiera_large.pt",
|
||||||
|
"type": "sam2",
|
||||||
|
"base": "SAM",
|
||||||
|
"save_path": "sams",
|
||||||
|
"description": "Segmenty Anything SAM 2 hiera model (large)",
|
||||||
|
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||||
|
"filename": "sam2_hiera_large.pt",
|
||||||
|
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt",
|
||||||
|
"size": "857.0MB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/omnigen2_fp16.safetensors",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "OmniGen2",
|
||||||
|
"save_path": "default",
|
||||||
|
"description": "OmniGen2 diffusion model. This is required for using OmniGen2.",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged",
|
||||||
|
"filename": "omnigen2_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/omnigen2_fp16.safetensors",
|
||||||
|
"size": "7.93GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/qwen_2.5_vl_fp16.safetensors",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "qwen-2.5",
|
||||||
|
"save_path": "default",
|
||||||
|
"description": "text encoder for OmniGen2",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged",
|
||||||
|
"filename": "qwen_2.5_vl_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/text_encoders/qwen_2.5_vl_fp16.safetensors",
|
||||||
|
"size": "7.51GB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Latent Bridge Matching for Image Relighting",
|
"name": "Latent Bridge Matching for Image Relighting",
|
||||||
"type": "diffusion_model",
|
"type": "diffusion_model",
|
||||||
@@ -473,224 +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"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "XLabs-AI/realism_lora.safetensors",
|
|
||||||
"type": "lora",
|
|
||||||
"base": "FLUX.1",
|
|
||||||
"save_path": "xlabs/loras",
|
|
||||||
"description": "A checkpoint with trained LoRAs for FLUX.1-dev model by Black Forest Labs",
|
|
||||||
"reference": "https://huggingface.co/XLabs-AI/flux-lora-collection",
|
|
||||||
"filename": "realism_lora.safetensors",
|
|
||||||
"url": "https://huggingface.co/XLabs-AI/flux-lora-collection/resolve/main/realism_lora.safetensors",
|
|
||||||
"size": "44.8MB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "XLabs-AI/art_lora.safetensors",
|
|
||||||
"type": "lora",
|
|
||||||
"base": "FLUX.1",
|
|
||||||
"save_path": "xlabs/loras",
|
|
||||||
"description": "A checkpoint with trained LoRAs for FLUX.1-dev model by Black Forest Labs",
|
|
||||||
"reference": "https://huggingface.co/XLabs-AI/flux-lora-collection",
|
|
||||||
"filename": "art_lora.safetensors",
|
|
||||||
"url": "https://huggingface.co/XLabs-AI/flux-lora-collection/resolve/main/scenery_lora.safetensors",
|
|
||||||
"size": "44.8MB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "XLabs-AI/mjv6_lora.safetensors",
|
|
||||||
"type": "lora",
|
|
||||||
"base": "FLUX.1",
|
|
||||||
"save_path": "xlabs/loras",
|
|
||||||
"description": "A checkpoint with trained LoRAs for FLUX.1-dev model by Black Forest Labs",
|
|
||||||
"reference": "https://huggingface.co/XLabs-AI/flux-lora-collection",
|
|
||||||
"filename": "mjv6_lora.safetensors",
|
|
||||||
"url": "https://huggingface.co/XLabs-AI/flux-lora-collection/resolve/main/mjv6_lora.safetensors",
|
|
||||||
"size": "44.8MB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "XLabs-AI/flux-ip-adapter",
|
|
||||||
"type": "lora",
|
|
||||||
"base": "FLUX.1",
|
|
||||||
"save_path": "xlabs/ipadapters",
|
|
||||||
"description": "A checkpoint with trained LoRAs for FLUX.1-dev model by Black Forest Labs",
|
|
||||||
"reference": "https://huggingface.co/XLabs-AI/flux-ip-adapter",
|
|
||||||
"filename": "ip_adapter.safetensors",
|
|
||||||
"url": "https://huggingface.co/XLabs-AI/flux-ip-adapter/resolve/main/ip_adapter.safetensors",
|
|
||||||
"size": "982MB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "stabilityai/SD3.5-Large-Controlnet-Blur",
|
|
||||||
"type": "controlnet",
|
|
||||||
"base": "SD3.5",
|
|
||||||
"save_path": "controlnet/SD3.5",
|
|
||||||
"description": "Blur Controlnet model for SD3.5 Large",
|
|
||||||
"reference": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets",
|
|
||||||
"filename": "sd3.5_large_controlnet_blur.safetensors",
|
|
||||||
"url": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets/resolve/main/sd3.5_large_controlnet_blur.safetensors",
|
|
||||||
"size": "8.65GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stabilityai/SD3.5-Large-Controlnet-Canny",
|
|
||||||
"type": "controlnet",
|
|
||||||
"base": "SD3.5",
|
|
||||||
"save_path": "controlnet/SD3.5",
|
|
||||||
"description": "Canny Controlnet model for SD3.5 Large",
|
|
||||||
"reference": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets",
|
|
||||||
"filename": "sd3.5_large_controlnet_canny.safetensors",
|
|
||||||
"url": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets/resolve/main/sd3.5_large_controlnet_canny.safetensors",
|
|
||||||
"size": "8.65GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stabilityai/SD3.5-Large-Controlnet-Depth",
|
|
||||||
"type": "controlnet",
|
|
||||||
"base": "SD3.5",
|
|
||||||
"save_path": "controlnet/SD3.5",
|
|
||||||
"description": "Depth Controlnet model for SD3.5 Large",
|
|
||||||
"reference": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets",
|
|
||||||
"filename": "sd3.5_large_controlnet_depth.safetensors",
|
|
||||||
"url": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets/resolve/main/sd3.5_large_controlnet_depth.safetensors",
|
|
||||||
"size": "8.65GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 2B v0.9 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.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.safetensors",
|
|
||||||
"size": "9.37GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InstantX/FLUX.1-dev-IP-Adapter",
|
|
||||||
"type": "IP-Adapter",
|
|
||||||
"base": "FLUX.1",
|
|
||||||
"save_path": "ipadapter-flux",
|
|
||||||
"description": "FLUX.1-dev-IP-Adapter",
|
|
||||||
"reference": "https://huggingface.co/InstantX/FLUX.1-dev-IP-Adapter",
|
|
||||||
"filename": "ip-adapter.bin",
|
|
||||||
"url": "https://huggingface.co/InstantX/FLUX.1-dev-IP-Adapter/resolve/main/ip-adapter.bin",
|
|
||||||
"size": "5.29GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/sigclip_vision_384 (patch14_384)",
|
|
||||||
"type": "clip_vision",
|
|
||||||
"base": "sigclip",
|
|
||||||
"save_path": "clip_vision",
|
|
||||||
"description": "This clip vision model is required for FLUX.1 Redux.",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/sigclip_vision_384/tree/main",
|
|
||||||
"filename": "sigclip_vision_patch14_384.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/sigclip_vision_384/resolve/main/sigclip_vision_patch14_384.safetensors",
|
|
||||||
"size": "857MB"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,6 +331,26 @@
|
|||||||
],
|
],
|
||||||
"description": "Dynamic Node examples for ComfyUI",
|
"description": "Dynamic Node examples for ComfyUI",
|
||||||
"install_type": "git-clone"
|
"install_type": "git-clone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Jonathon-Doran",
|
||||||
|
"title": "remote-combo-demo",
|
||||||
|
"reference": "https://github.com/Jonathon-Doran/remote-combo-demo",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Jonathon-Doran/remote-combo-demo"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
179
openapi.yaml
179
openapi.yaml
@@ -18,6 +18,14 @@ security: []
|
|||||||
# Common API components
|
# Common API components
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
|
OperationType:
|
||||||
|
type: string
|
||||||
|
enum: [install, uninstall, update, update-comfyui, fix, disable, enable, install-model]
|
||||||
|
description: Type of operation or task being performed
|
||||||
|
OperationResult:
|
||||||
|
type: string
|
||||||
|
enum: [success, failed, skipped, error, skip]
|
||||||
|
description: Result status of an operation (failed/error and skipped/skip are aliases)
|
||||||
# Core Task Queue Models
|
# Core Task Queue Models
|
||||||
QueueTaskItem:
|
QueueTaskItem:
|
||||||
type: object
|
type: object
|
||||||
@@ -29,9 +37,7 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
description: Client identifier that initiated the task
|
description: Client identifier that initiated the task
|
||||||
kind:
|
kind:
|
||||||
type: string
|
$ref: '#/components/schemas/OperationType'
|
||||||
description: Type of task being performed
|
|
||||||
enum: [install, uninstall, update, update-all, update-comfyui, fix, disable, enable, install-model]
|
|
||||||
params:
|
params:
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: '#/components/schemas/InstallPackParams'
|
- $ref: '#/components/schemas/InstallPackParams'
|
||||||
@@ -65,14 +71,19 @@ components:
|
|||||||
description: Task result message or details
|
description: Task result message or details
|
||||||
status:
|
status:
|
||||||
$ref: '#/components/schemas/TaskExecutionStatus'
|
$ref: '#/components/schemas/TaskExecutionStatus'
|
||||||
|
batch_id:
|
||||||
|
type: [string, 'null']
|
||||||
|
description: ID of the batch this task belongs to
|
||||||
|
end_time:
|
||||||
|
type: [string, 'null']
|
||||||
|
format: date-time
|
||||||
|
description: ISO timestamp when task execution ended
|
||||||
required: [ui_id, client_id, kind, timestamp, result]
|
required: [ui_id, client_id, kind, timestamp, result]
|
||||||
TaskExecutionStatus:
|
TaskExecutionStatus:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
status_str:
|
status_str:
|
||||||
type: string
|
$ref: '#/components/schemas/OperationResult'
|
||||||
enum: [success, error, skip]
|
|
||||||
description: Overall task execution status
|
|
||||||
completed:
|
completed:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether the task completed
|
description: Whether the task completed
|
||||||
@@ -223,6 +234,14 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
enum: [git-clone, copy, cnr]
|
enum: [git-clone, copy, cnr]
|
||||||
description: Type of installation used for the pack
|
description: Type of installation used for the pack
|
||||||
|
SecurityLevel:
|
||||||
|
type: string
|
||||||
|
enum: [strong, normal, normal-, weak]
|
||||||
|
description: Security level configuration (from most to least restrictive)
|
||||||
|
RiskLevel:
|
||||||
|
type: string
|
||||||
|
enum: [block, high+, high, middle+, middle]
|
||||||
|
description: Risk classification for operations
|
||||||
ManagerPack:
|
ManagerPack:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/ManagerPackInfo'
|
- $ref: '#/components/schemas/ManagerPackInfo'
|
||||||
@@ -235,7 +254,7 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
description: Files included in the pack
|
description: Repository URLs for installation (typically contains one GitHub URL)
|
||||||
reference:
|
reference:
|
||||||
type: string
|
type: string
|
||||||
description: The type of installation reference
|
description: The type of installation reference
|
||||||
@@ -366,6 +385,46 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
description: ComfyUI Node Registry ID of the package to enable
|
description: ComfyUI Node Registry ID of the package to enable
|
||||||
required: [cnr_id]
|
required: [cnr_id]
|
||||||
|
# Query Parameter Models
|
||||||
|
UpdateAllQueryParams:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
client_id:
|
||||||
|
type: string
|
||||||
|
description: Client identifier that initiated the request
|
||||||
|
ui_id:
|
||||||
|
type: string
|
||||||
|
description: Base UI identifier for task tracking
|
||||||
|
mode:
|
||||||
|
$ref: '#/components/schemas/ManagerDatabaseSource'
|
||||||
|
required: [client_id, ui_id]
|
||||||
|
UpdateComfyUIQueryParams:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
client_id:
|
||||||
|
type: string
|
||||||
|
description: Client identifier that initiated the request
|
||||||
|
ui_id:
|
||||||
|
type: string
|
||||||
|
description: UI identifier for task tracking
|
||||||
|
stable:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
description: Whether to update to stable version (true) or nightly (false)
|
||||||
|
required: [client_id, ui_id]
|
||||||
|
ComfyUISwitchVersionQueryParams:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
ver:
|
||||||
|
type: string
|
||||||
|
description: Version to switch to
|
||||||
|
client_id:
|
||||||
|
type: string
|
||||||
|
description: Client identifier that initiated the request
|
||||||
|
ui_id:
|
||||||
|
type: string
|
||||||
|
description: UI identifier for task tracking
|
||||||
|
required: [ver, client_id, ui_id]
|
||||||
# Queue Status Models
|
# Queue Status Models
|
||||||
QueueStatus:
|
QueueStatus:
|
||||||
type: object
|
type: object
|
||||||
@@ -580,9 +639,7 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
description: Unique operation identifier
|
description: Unique operation identifier
|
||||||
operation_type:
|
operation_type:
|
||||||
type: string
|
$ref: '#/components/schemas/OperationType'
|
||||||
description: Type of operation
|
|
||||||
enum: [install, update, uninstall, fix, disable, enable, install-model]
|
|
||||||
target:
|
target:
|
||||||
type: string
|
type: string
|
||||||
description: Target of the operation (node name, model name, etc.)
|
description: Target of the operation (node name, model name, etc.)
|
||||||
@@ -590,9 +647,7 @@ components:
|
|||||||
type: [string, 'null']
|
type: [string, 'null']
|
||||||
description: Target version for the operation
|
description: Target version for the operation
|
||||||
result:
|
result:
|
||||||
type: string
|
$ref: '#/components/schemas/OperationResult'
|
||||||
description: Operation result
|
|
||||||
enum: [success, failed, skipped]
|
|
||||||
error_message:
|
error_message:
|
||||||
type: [string, 'null']
|
type: [string, 'null']
|
||||||
description: Error message if operation failed
|
description: Error message if operation failed
|
||||||
@@ -640,6 +695,45 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
description: ComfyUI Manager configuration settings
|
description: ComfyUI Manager configuration settings
|
||||||
|
comfyui_root_path:
|
||||||
|
type: [string, 'null']
|
||||||
|
description: ComfyUI root installation directory
|
||||||
|
model_paths:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: Map of model types to their configured paths
|
||||||
|
manager_version:
|
||||||
|
type: [string, 'null']
|
||||||
|
description: ComfyUI Manager version
|
||||||
|
security_level:
|
||||||
|
$ref: '#/components/schemas/SecurityLevel'
|
||||||
|
network_mode:
|
||||||
|
type: [string, 'null']
|
||||||
|
description: Network mode (online, offline, private)
|
||||||
|
cli_args:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
description: Selected ComfyUI CLI arguments
|
||||||
|
custom_nodes_count:
|
||||||
|
type: [integer, 'null']
|
||||||
|
description: Total number of custom node packages
|
||||||
|
minimum: 0
|
||||||
|
failed_imports:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: List of custom nodes that failed to import
|
||||||
|
pip_packages:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
description: Map of installed pip packages to their versions
|
||||||
|
embedded_python:
|
||||||
|
type: [boolean, 'null']
|
||||||
|
description: Whether ComfyUI is running from an embedded Python distribution
|
||||||
required: [snapshot_time, comfyui_version, python_version, platform_info]
|
required: [snapshot_time, comfyui_version, python_version, platform_info]
|
||||||
BatchExecutionRecord:
|
BatchExecutionRecord:
|
||||||
type: object
|
type: object
|
||||||
@@ -688,6 +782,39 @@ components:
|
|||||||
minimum: 0
|
minimum: 0
|
||||||
default: 0
|
default: 0
|
||||||
required: [batch_id, start_time, state_before]
|
required: [batch_id, start_time, state_before]
|
||||||
|
|
||||||
|
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
|
||||||
@@ -923,6 +1050,32 @@ paths:
|
|||||||
description: Processing started
|
description: Processing started
|
||||||
'201':
|
'201':
|
||||||
description: Processing already in progress
|
description: Processing already in progress
|
||||||
|
|
||||||
|
/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.
|
||||||
|
|
||||||
/v2/manager/queue/reset:
|
/v2/manager/queue/reset:
|
||||||
get:
|
get:
|
||||||
summary: Reset queue
|
summary: Reset queue
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-manager"
|
name = "comfyui-manager"
|
||||||
license = { text = "GPL-3.0-only" }
|
license = { text = "GPL-3.0-only" }
|
||||||
version = "4.0.0-beta.4"
|
version = "4.0.1-beta.5"
|
||||||
requires-python = ">= 3.9"
|
requires-python = ">= 3.9"
|
||||||
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."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -27,7 +27,7 @@ classifiers = [
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"GitPython",
|
"GitPython",
|
||||||
"PyGithub",
|
"PyGithub",
|
||||||
"matrix-client==0.4.0",
|
# "matrix-nio",
|
||||||
"transformers",
|
"transformers",
|
||||||
"huggingface-hub>0.20",
|
"huggingface-hub>0.20",
|
||||||
"typer",
|
"typer",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
GitPython
|
GitPython
|
||||||
PyGithub
|
PyGithub
|
||||||
matrix-client==0.4.0
|
# matrix-nio
|
||||||
transformers
|
transformers
|
||||||
huggingface-hub>0.20
|
huggingface-hub>0.20
|
||||||
typer
|
typer
|
||||||
|
|||||||
15
scanner.py
15
scanner.py
@@ -255,13 +255,13 @@ def clone_or_pull_git_repository(git_url):
|
|||||||
repo.git.submodule('update', '--init', '--recursive')
|
repo.git.submodule('update', '--init', '--recursive')
|
||||||
print(f"Pulling {repo_name}...")
|
print(f"Pulling {repo_name}...")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Pulling {repo_name} failed: {e}")
|
print(f"Failed to pull '{repo_name}': {e}")
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
Repo.clone_from(git_url, repo_dir, recursive=True)
|
Repo.clone_from(git_url, repo_dir, recursive=True)
|
||||||
print(f"Cloning {repo_name}...")
|
print(f"Cloning {repo_name}...")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Cloning {repo_name} failed: {e}")
|
print(f"Failed to clone '{repo_name}': {e}")
|
||||||
|
|
||||||
|
|
||||||
def update_custom_nodes():
|
def update_custom_nodes():
|
||||||
@@ -496,8 +496,15 @@ def gen_json(node_info):
|
|||||||
nodes_in_url, metadata_in_url = data[git_url]
|
nodes_in_url, metadata_in_url = data[git_url]
|
||||||
nodes = set(nodes_in_url)
|
nodes = set(nodes_in_url)
|
||||||
|
|
||||||
for x, desc in node_list_json.items():
|
try:
|
||||||
nodes.add(x.strip())
|
for x, desc in node_list_json.items():
|
||||||
|
nodes.add(x.strip())
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\nERROR: Invalid json format '{node_list_json_path}'")
|
||||||
|
print("------------------------------------------------------")
|
||||||
|
print(e)
|
||||||
|
print("------------------------------------------------------")
|
||||||
|
node_list_json = {}
|
||||||
|
|
||||||
metadata_in_url['title_aux'] = title
|
metadata_in_url['title_aux'] = title
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user