comfy-cli 0.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- comfy_cli/__init__.py +0 -0
- comfy_cli/__main__.py +4 -0
- comfy_cli/cmdline.py +374 -0
- comfy_cli/command/__init__.py +2 -0
- comfy_cli/command/custom_nodes/__init__.py +1 -0
- comfy_cli/command/custom_nodes/command.py +401 -0
- comfy_cli/command/install.py +97 -0
- comfy_cli/command/models/models.py +162 -0
- comfy_cli/command/run.py +2 -0
- comfy_cli/config_manager.py +95 -0
- comfy_cli/constants.py +50 -0
- comfy_cli/env_checker.py +171 -0
- comfy_cli/logging.py +37 -0
- comfy_cli/meta_data.py +155 -0
- comfy_cli/tracking.py +69 -0
- comfy_cli/ui.py +94 -0
- comfy_cli/utils.py +55 -0
- comfy_cli/workspace_manager.py +167 -0
- comfy_cli-0.0.8.dist-info/LICENSE +674 -0
- comfy_cli-0.0.8.dist-info/METADATA +907 -0
- comfy_cli-0.0.8.dist-info/RECORD +24 -0
- comfy_cli-0.0.8.dist-info/WHEEL +5 -0
- comfy_cli-0.0.8.dist-info/entry_points.txt +4 -0
- comfy_cli-0.0.8.dist-info/top_level.txt +1 -0
comfy_cli/__init__.py
ADDED
|
File without changes
|
comfy_cli/__main__.py
ADDED
comfy_cli/cmdline.py
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
import uuid
|
|
6
|
+
import webbrowser
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
import questionary
|
|
10
|
+
import typer
|
|
11
|
+
from rich import print
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from typing_extensions import Annotated, List
|
|
14
|
+
|
|
15
|
+
from comfy_cli import constants, env_checker, logging, tracking, ui, utils
|
|
16
|
+
from comfy_cli.command import custom_nodes
|
|
17
|
+
from comfy_cli.command import install as install_inner
|
|
18
|
+
from comfy_cli.command import run as run_inner
|
|
19
|
+
from comfy_cli.command.models import models as models_command
|
|
20
|
+
from comfy_cli.config_manager import ConfigManager
|
|
21
|
+
from comfy_cli.env_checker import EnvChecker, check_comfy_server_running
|
|
22
|
+
from comfy_cli.meta_data import MetadataManager
|
|
23
|
+
from comfy_cli.workspace_manager import WorkspaceManager
|
|
24
|
+
|
|
25
|
+
app = typer.Typer()
|
|
26
|
+
workspace_manager = WorkspaceManager()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main():
|
|
30
|
+
app()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@app.callback(invoke_without_command=True)
|
|
34
|
+
def entry(
|
|
35
|
+
ctx: typer.Context,
|
|
36
|
+
workspace: Optional[str] = typer.Option(
|
|
37
|
+
default=None, show_default=False, help="Path to ComfyUI workspace"
|
|
38
|
+
),
|
|
39
|
+
recent: Optional[bool] = typer.Option(
|
|
40
|
+
default=False, show_default=False, is_flag=True, help="Execute from recent path"
|
|
41
|
+
),
|
|
42
|
+
here: Optional[bool] = typer.Option(
|
|
43
|
+
default=False,
|
|
44
|
+
show_default=False,
|
|
45
|
+
is_flag=True,
|
|
46
|
+
help="Execute from current path",
|
|
47
|
+
),
|
|
48
|
+
):
|
|
49
|
+
if ctx.invoked_subcommand is None:
|
|
50
|
+
print(ctx.get_help())
|
|
51
|
+
ctx.exit()
|
|
52
|
+
|
|
53
|
+
ctx.ensure_object(dict) # Ensure that ctx.obj exists and is a dict
|
|
54
|
+
workspace_manager.update_context(ctx, workspace, recent, here)
|
|
55
|
+
init()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def init():
|
|
59
|
+
# TODO(yoland): after this
|
|
60
|
+
metadata_manager = MetadataManager()
|
|
61
|
+
start_time = time.time()
|
|
62
|
+
metadata_manager.scan_dir()
|
|
63
|
+
end_time = time.time()
|
|
64
|
+
logging.setup_logging()
|
|
65
|
+
tracking.prompt_tracking_consent()
|
|
66
|
+
|
|
67
|
+
print(f"scan_dir took {end_time - start_time:.2f} seconds to run")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@app.command(help="Download and install ComfyUI and ComfyUI-Manager")
|
|
71
|
+
@tracking.track_command()
|
|
72
|
+
def install(
|
|
73
|
+
ctx: typer.Context,
|
|
74
|
+
url: Annotated[str, typer.Option(show_default=False)] = constants.COMFY_GITHUB_URL,
|
|
75
|
+
manager_url: Annotated[
|
|
76
|
+
str, typer.Option(show_default=False)
|
|
77
|
+
] = constants.COMFY_MANAGER_GITHUB_URL,
|
|
78
|
+
restore: Annotated[
|
|
79
|
+
bool,
|
|
80
|
+
lambda: typer.Option(
|
|
81
|
+
default=False,
|
|
82
|
+
help="Restore dependencies for installed ComfyUI if not installed",
|
|
83
|
+
),
|
|
84
|
+
] = False,
|
|
85
|
+
skip_manager: Annotated[
|
|
86
|
+
bool, typer.Option(help="Skip installing the manager component")
|
|
87
|
+
] = False,
|
|
88
|
+
amd: Annotated[bool, typer.Option(help="Install for AMD gpu")] = False,
|
|
89
|
+
commit: Annotated[str, typer.Option(help="Specify commit hash for ComfyUI")] = None,
|
|
90
|
+
):
|
|
91
|
+
checker = EnvChecker()
|
|
92
|
+
|
|
93
|
+
# In the case of installation, since it involves installing in a non-existent path, get_workspace_path is not used.
|
|
94
|
+
specified_workspace = ctx.obj.get(constants.CONTEXT_KEY_WORKSPACE)
|
|
95
|
+
use_recent = ctx.obj.get(constants.CONTEXT_KEY_RECENT)
|
|
96
|
+
use_here = ctx.obj.get(constants.CONTEXT_KEY_HERE)
|
|
97
|
+
|
|
98
|
+
if specified_workspace:
|
|
99
|
+
workspace_path = specified_workspace
|
|
100
|
+
elif use_recent:
|
|
101
|
+
workspace_path = workspace_manager.config_manager.get(
|
|
102
|
+
constants.CONFIG_KEY_RECENT_WORKSPACE
|
|
103
|
+
)
|
|
104
|
+
elif use_here:
|
|
105
|
+
workspace_path = os.getcwd()
|
|
106
|
+
else: # For installation, if not explicitly specified, it will only install in the default path.
|
|
107
|
+
workspace_path = os.path.expanduser("~/comfy")
|
|
108
|
+
|
|
109
|
+
if checker.python_version.major < 3:
|
|
110
|
+
print(
|
|
111
|
+
"[bold red]Python version 3.6 or higher is required to run ComfyUI.[/bold red]"
|
|
112
|
+
)
|
|
113
|
+
print(
|
|
114
|
+
f"You are currently using Python version {env_checker.format_python_version(checker.python_version)}."
|
|
115
|
+
)
|
|
116
|
+
if checker.currently_in_comfy_repo:
|
|
117
|
+
console = Console()
|
|
118
|
+
# TODO: warn user that you are teh
|
|
119
|
+
|
|
120
|
+
torch_mode = None
|
|
121
|
+
if amd:
|
|
122
|
+
torch_mode = "amd"
|
|
123
|
+
|
|
124
|
+
install_inner.execute(
|
|
125
|
+
url,
|
|
126
|
+
manager_url,
|
|
127
|
+
workspace_path,
|
|
128
|
+
restore,
|
|
129
|
+
skip_manager,
|
|
130
|
+
torch_mode,
|
|
131
|
+
commit=commit,
|
|
132
|
+
)
|
|
133
|
+
workspace_manager.set_recent_workspace(workspace_path)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def update(self):
|
|
137
|
+
_env_checker = EnvChecker()
|
|
138
|
+
print(f"Updating ComfyUI in {self.workspace}...")
|
|
139
|
+
os.chdir(self.workspace)
|
|
140
|
+
subprocess.run(["git", "pull"], check=True)
|
|
141
|
+
subprocess.run(
|
|
142
|
+
[sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], check=True
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# @app.command(help="Run workflow file")
|
|
147
|
+
# @tracking.track_command()
|
|
148
|
+
# def run(
|
|
149
|
+
# workflow_file: Annotated[str, typer.Option(help="Path to the workflow file.")],
|
|
150
|
+
# ):
|
|
151
|
+
# run_inner.execute(workflow_file)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def validate_comfyui(_env_checker):
|
|
155
|
+
if _env_checker.comfy_repo is None:
|
|
156
|
+
print(
|
|
157
|
+
f"[bold red]If ComfyUI is not installed, this feature cannot be used.[/bold red]"
|
|
158
|
+
)
|
|
159
|
+
raise typer.Exit(code=1)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def launch_comfyui(_env_checker, _config_manager, extra, background=False):
|
|
163
|
+
validate_comfyui(_env_checker)
|
|
164
|
+
|
|
165
|
+
if background:
|
|
166
|
+
if _config_manager.background is not None and utils.is_running(
|
|
167
|
+
_config_manager.background[2]
|
|
168
|
+
):
|
|
169
|
+
print(
|
|
170
|
+
f"[bold red]ComfyUI is already running in background.\nYou cannot start more than one background service.[/bold red]\n"
|
|
171
|
+
)
|
|
172
|
+
raise typer.Exit(code=1)
|
|
173
|
+
|
|
174
|
+
port = 8188
|
|
175
|
+
listen = "127.0.0.1"
|
|
176
|
+
|
|
177
|
+
if extra is not None:
|
|
178
|
+
for i in range(len(extra) - 1):
|
|
179
|
+
if extra[i] == "--port":
|
|
180
|
+
port = extra[i + 1]
|
|
181
|
+
if listen[i] == "--listen":
|
|
182
|
+
listen = extra[i + 1]
|
|
183
|
+
|
|
184
|
+
if check_comfy_server_running(port):
|
|
185
|
+
print(
|
|
186
|
+
f"[bold red]The {port} port is already in use. A new ComfyUI server cannot be launched.\n[bold red]\n"
|
|
187
|
+
)
|
|
188
|
+
raise typer.Exit(code=1)
|
|
189
|
+
|
|
190
|
+
if len(extra) > 0:
|
|
191
|
+
extra = ["--"] + extra
|
|
192
|
+
else:
|
|
193
|
+
extra = []
|
|
194
|
+
|
|
195
|
+
cmd = [
|
|
196
|
+
"comfy",
|
|
197
|
+
f'--workspace={os.path.join(os.getcwd(), "..")}',
|
|
198
|
+
"launch",
|
|
199
|
+
] + extra
|
|
200
|
+
|
|
201
|
+
process = subprocess.Popen(
|
|
202
|
+
cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
|
203
|
+
)
|
|
204
|
+
print(
|
|
205
|
+
f"[bold yellow]Run ComfyUI in the background.[/bold yellow] ({listen}:{port})"
|
|
206
|
+
)
|
|
207
|
+
_config_manager.config["DEFAULT"][
|
|
208
|
+
constants.CONFIG_KEY_BACKGROUND
|
|
209
|
+
] = f"{(listen, port, process.pid)}"
|
|
210
|
+
_config_manager.write_config()
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
env_path = _env_checker.get_isolated_env()
|
|
214
|
+
reboot_path = None
|
|
215
|
+
|
|
216
|
+
new_env = os.environ.copy()
|
|
217
|
+
|
|
218
|
+
if env_path is not None:
|
|
219
|
+
session_path = os.path.join(
|
|
220
|
+
_config_manager.get_config_path(), "tmp", str(uuid.uuid4())
|
|
221
|
+
)
|
|
222
|
+
new_env["__COMFY_CLI_SESSION__"] = session_path
|
|
223
|
+
|
|
224
|
+
# To minimize the possibility of leaving residue in the tmp directory, use files instead of directories.
|
|
225
|
+
reboot_path = os.path.join(session_path + ".reboot")
|
|
226
|
+
|
|
227
|
+
extra = extra if extra is not None else []
|
|
228
|
+
|
|
229
|
+
while True:
|
|
230
|
+
subprocess.run([sys.executable, "main.py"] + extra, env=new_env, check=False)
|
|
231
|
+
|
|
232
|
+
if not os.path.exists(reboot_path):
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
os.remove(reboot_path)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@app.command(help="Stop background ComfyUI")
|
|
239
|
+
def stop():
|
|
240
|
+
_config_manager = ConfigManager()
|
|
241
|
+
|
|
242
|
+
if constants.CONFIG_KEY_BACKGROUND not in _config_manager.config["DEFAULT"]:
|
|
243
|
+
print(f"[bold red]No ComfyUI is running in the background.[/bold red]\n")
|
|
244
|
+
raise typer.Exit(code=1)
|
|
245
|
+
|
|
246
|
+
bg_info = _config_manager.background
|
|
247
|
+
is_killed = utils.kill_all(bg_info[2])
|
|
248
|
+
|
|
249
|
+
print(
|
|
250
|
+
f"[bold yellow]Background ComfyUI is stopped.[/bold yellow] ({bg_info[0]}:{bg_info[1]})"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
_config_manager.remove_background()
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@app.command(help="Launch ComfyUI: ?[--background] ?[-- <extra args ...>]")
|
|
257
|
+
@tracking.track_command()
|
|
258
|
+
def launch(
|
|
259
|
+
ctx: typer.Context,
|
|
260
|
+
background: Annotated[
|
|
261
|
+
bool, typer.Option(help="Launch ComfyUI in background")
|
|
262
|
+
] = False,
|
|
263
|
+
extra: List[str] = typer.Argument(None),
|
|
264
|
+
):
|
|
265
|
+
_env_checker = EnvChecker()
|
|
266
|
+
_config_manager = ConfigManager()
|
|
267
|
+
|
|
268
|
+
resolved_workspace = workspace_manager.get_workspace_path(ctx)
|
|
269
|
+
if not resolved_workspace:
|
|
270
|
+
print(
|
|
271
|
+
"\nComfyUI is not available.\nTo install ComfyUI, you can run:\n\n\tcomfy install\n\n",
|
|
272
|
+
file=sys.stderr,
|
|
273
|
+
)
|
|
274
|
+
raise typer.Exit(code=1)
|
|
275
|
+
|
|
276
|
+
print(f"\nLaunching ComfyUI from: {resolved_workspace}\n")
|
|
277
|
+
|
|
278
|
+
os.chdir(resolved_workspace + "/ComfyUI")
|
|
279
|
+
_env_checker.check() # update environment checks
|
|
280
|
+
|
|
281
|
+
# Update the recent workspace
|
|
282
|
+
workspace_manager.set_recent_workspace(resolved_workspace)
|
|
283
|
+
|
|
284
|
+
launch_comfyui(_env_checker, _config_manager, extra, background=background)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@app.command("set-default", help="Set default workspace")
|
|
288
|
+
@tracking.track_command()
|
|
289
|
+
def set_default(workspace_path: str):
|
|
290
|
+
workspace_path = os.path.expanduser(workspace_path)
|
|
291
|
+
comfy_path = os.path.join(workspace_path, "ComfyUI")
|
|
292
|
+
if not os.path.exists(comfy_path):
|
|
293
|
+
print(
|
|
294
|
+
f"Invalid workspace path: {workspace_path}\nThe workspace path must contain 'ComfyUI'."
|
|
295
|
+
)
|
|
296
|
+
raise typer.Exit(code=1)
|
|
297
|
+
|
|
298
|
+
workspace_manager.set_default_workspace(workspace_path)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@app.command(help="Show which ComfyUI is selected.")
|
|
302
|
+
@tracking.track_command()
|
|
303
|
+
def which(ctx: typer.Context):
|
|
304
|
+
comfy_path = workspace_manager.get_workspace_path(ctx)
|
|
305
|
+
if not os.path.exists(comfy_path) or not os.path.exists(
|
|
306
|
+
os.path.join(comfy_path, "ComfyUI")
|
|
307
|
+
):
|
|
308
|
+
print(
|
|
309
|
+
f"ComfyUI not found, please run 'comfy install', run 'comfy' in a ComfyUI directory, or specify the workspace path with '--workspace'."
|
|
310
|
+
)
|
|
311
|
+
raise typer.Exit(code=1)
|
|
312
|
+
|
|
313
|
+
print(f"Target ComfyUI path: {comfy_path}")
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@app.command(help="Print out current environment variables.")
|
|
317
|
+
@tracking.track_command()
|
|
318
|
+
def env():
|
|
319
|
+
_env_checker = EnvChecker()
|
|
320
|
+
_env_checker.print()
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@app.command(hidden=True)
|
|
324
|
+
@tracking.track_command()
|
|
325
|
+
def nodes():
|
|
326
|
+
print(
|
|
327
|
+
"\n[bold red] No such command, did you mean 'comfy node' instead?[/bold red]\n"
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@app.command(hidden=True)
|
|
332
|
+
@tracking.track_command()
|
|
333
|
+
def models():
|
|
334
|
+
print(
|
|
335
|
+
"\n[bold red] No such command, did you mean 'comfy model' instead?[/bold red]\n"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
@app.command(help="Provide feedback on the Comfy CLI tool.")
|
|
340
|
+
@tracking.track_command()
|
|
341
|
+
def feedback():
|
|
342
|
+
print("Feedback Collection for Comfy CLI Tool\n")
|
|
343
|
+
|
|
344
|
+
# General Satisfaction
|
|
345
|
+
general_satisfaction_score = ui.prompt_select(
|
|
346
|
+
question="On a scale of 1 to 5, how satisfied are you with the Comfy CLI tool? (1 being very dissatisfied and 5 being very satisfied)",
|
|
347
|
+
choices=["1", "2", "3", "4", "5"],
|
|
348
|
+
)
|
|
349
|
+
tracking.track_event(
|
|
350
|
+
"feedback_general_satisfaction", {"score": general_satisfaction_score}
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Usability and User Experience
|
|
354
|
+
usability_satisfaction_score = ui.prompt_select(
|
|
355
|
+
question="On a scale of 1 to 5, how satisfied are you with the usability and user experience of the Comfy CLI tool? (1 being very dissatisfied and 5 being very satisfied)",
|
|
356
|
+
choices=["1", "2", "3", "4", "5"],
|
|
357
|
+
)
|
|
358
|
+
tracking.track_event(
|
|
359
|
+
"feedback_usability_satisfaction", {"score": usability_satisfaction_score}
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
# Additional Feature-Specific Feedback
|
|
363
|
+
if questionary.confirm(
|
|
364
|
+
"Do you want to provide additional feature-specific feedback on our GitHub page?"
|
|
365
|
+
).ask():
|
|
366
|
+
tracking.track_event("feedback_additional")
|
|
367
|
+
webbrowser.open("https://github.com/Comfy-Org/comfy-cli/issues/new/choose")
|
|
368
|
+
|
|
369
|
+
print("Thank you for your feedback!")
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
app.add_typer(models_command.app, name="model", help="Manage models.")
|
|
373
|
+
app.add_typer(custom_nodes.app, name="node", help="Manage custom nodes.")
|
|
374
|
+
app.add_typer(custom_nodes.manager_app, name="manager", help="Manager ComfyUI-Manager.")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .command import app, manager_app
|