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 ADDED
File without changes
comfy_cli/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from comfy_cli.cmdline import main
2
+
3
+ if __name__ == "__main__": # pragma: nocover
4
+ main()
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,2 @@
1
+ from . import custom_nodes
2
+ from . import install
@@ -0,0 +1 @@
1
+ from .command import app, manager_app