confyui 0.1.0__tar.gz

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.
confyui-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,152 @@
1
+ Metadata-Version: 2.3
2
+ Name: confyui
3
+ Version: 0.1.0
4
+ Summary: KpihX sovereign local image generation and editing daemon over ComfyUI (txt2img, img2img, inpaint; XPU-first)
5
+ Author: KpihX
6
+ Author-email: KpihX <kapoivha@gmail.com>
7
+ Requires-Dist: fastapi>=0.159.1
8
+ Requires-Dist: httpx>=0.28.1
9
+ Requires-Dist: huggingface-hub>=2.2.0
10
+ Requires-Dist: pillow>=13.0.0
11
+ Requires-Dist: psutil>=7.2.2
12
+ Requires-Dist: python-dotenv>=1.2.4
13
+ Requires-Dist: rich>=15.0.0
14
+ Requires-Dist: typer>=0.27.3
15
+ Requires-Dist: uvicorn>=0.54.0
16
+ Requires-Dist: torch>=2.8 ; extra == 'cpu'
17
+ Requires-Dist: torchvision>=0.23 ; extra == 'cpu'
18
+ Requires-Dist: torch>=2.8 ; extra == 'cuda'
19
+ Requires-Dist: torchvision>=0.23 ; extra == 'cuda'
20
+ Requires-Dist: transformers>=4.56 ; extra == 'cuda'
21
+ Requires-Dist: accelerate>=1.10.1 ; extra == 'cuda'
22
+ Requires-Dist: huggingface-hub>=0.34.4 ; extra == 'cuda'
23
+ Requires-Dist: safetensors>=0.6.2 ; extra == 'cuda'
24
+ Requires-Dist: torch>=2.8 ; extra == 'xpu'
25
+ Requires-Dist: torchvision>=0.23 ; extra == 'xpu'
26
+ Requires-Dist: transformers>=4.56 ; extra == 'xpu'
27
+ Requires-Dist: accelerate>=1.10.1 ; extra == 'xpu'
28
+ Requires-Dist: huggingface-hub>=0.34.4 ; extra == 'xpu'
29
+ Requires-Dist: safetensors>=0.6.2 ; extra == 'xpu'
30
+ Requires-Python: >=3.12
31
+ Provides-Extra: cpu
32
+ Provides-Extra: cuda
33
+ Provides-Extra: xpu
34
+ Description-Content-Type: text/markdown
35
+
36
+ # confyui
37
+
38
+ KπX sovereign local image generation and editing daemon over ComfyUI.
39
+
40
+ Hardware-accelerated on Intel Arc (XPU) via PyTorch XPU wheels, with portable CPU and CUDA extras.
41
+
42
+ ## Philosophy
43
+
44
+ - **0 Magic:** The CLI talks to ComfyUI's standard REST API via structured JSON graphs.
45
+ - **0 Manual Start:** The first call (`generate`, `edit`, `inpaint`) automatically starts ComfyUI in the background.
46
+ - **0 Wasted RAM:** An idle watchdog shuts ComfyUI down after `CONFYUI_IDLE_TIMEOUT` seconds of inactivity (default: 30 minutes).
47
+ - **0 Hardcoding:** Paths expand `$HOME` at runtime and are 100% overridable via environment variables.
48
+
49
+ ---
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ # Clone or navigate to the repository
55
+ cd ~/Labs/KpihX-Labs/AI/confyui
56
+
57
+ # Intel Arc workstation (XPU)
58
+ make sync-xpu
59
+ # or: uv sync --extra xpu
60
+
61
+ # Generic CPU fallback
62
+ make sync-cpu
63
+
64
+ # NVIDIA CUDA host
65
+ make sync-cuda
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Environment Variables
71
+
72
+ | Variable | Default | Description |
73
+ |---|---|---|
74
+ | `CONFYUI_DIR` | `~/RAW/comfyui-local/ComfyUI` | Path to the ComfyUI checkout |
75
+ | `CONFYUI_PYTHON` | `$CONFYUI_DIR/.venv/bin/python` | Python interpreter used to run ComfyUI |
76
+ | `CONFYUI_HOST` | `127.0.0.1` | Bind address |
77
+ | `CONFYUI_PORT` | `8188` | Port |
78
+ | `CONFYUI_IDLE_TIMEOUT` | `1800` | Seconds of inactivity before auto-shutdown |
79
+ | `CONFYUI_SPAWN_TIMEOUT` | `90` | Max seconds to wait for daemon readiness |
80
+ | `CONFYUI_STATE_DIR` | `~/.cache/confyui` | Directory for PID, logs, and heartbeat |
81
+ | `CONFYUI_DEFAULT_CKPT` | `Realistic_Vision_V5.1_noVAE.safetensors` | Default checkpoint for txt2img & img2img |
82
+ | `CONFYUI_INPAINT_CKPT` | `Realistic_Vision_V5.1_fp16-no-ema-inpainting.safetensors` | Default inpainting checkpoint |
83
+
84
+ ---
85
+
86
+ ## Usage
87
+
88
+ ### 1. Text-to-Image (`generate`)
89
+
90
+ ```bash
91
+ # Minimal
92
+ uv run confyui generate "a red panda wearing a top hat on the moon, cinematic lighting"
93
+
94
+ # Full options
95
+ uv run confyui generate "a red panda wearing a top hat on the moon" \
96
+ --negative "blurry, low quality, artifacts" \
97
+ --width 512 \
98
+ --height 512 \
99
+ --steps 20 \
100
+ --cfg 7.0 \
101
+ --seed 42 \
102
+ --sampler euler \
103
+ --scheduler normal \
104
+ --model "Realistic_Vision_V5.1_noVAE.safetensors" \
105
+ --out panda_moon.png
106
+ ```
107
+
108
+ ### 2. Image-to-Image (`edit`)
109
+
110
+ Modifies an existing image based on a prompt. The source image is kept and only altered proportionally to `--denoise` (0.0 = unchanged, 0.45-0.60 = balanced modification, 1.0 = total repaint):
111
+
112
+ ```bash
113
+ uv run confyui edit panda_moon.png "make the red panda wear a golden astronaut suit" \
114
+ --denoise 0.50 \
115
+ --steps 20 \
116
+ --out panda_astronaut.png
117
+ ```
118
+
119
+ ### 3. Inpainting (`inpaint`)
120
+
121
+ Repaints only the masked region (white pixels in the mask are regenerated, black pixels preserved):
122
+
123
+ ```bash
124
+ uv run confyui inpaint portrait.png torso_mask.png "she wears a long red silk dress" \
125
+ --denoise 1.0 \
126
+ --steps 25 \
127
+ --out portrait_red_dress.png
128
+ ```
129
+
130
+ ### 4. Daemon Management
131
+
132
+ ```bash
133
+ # Inspect status, uptime, PID, and idle time remaining
134
+ uv run confyui status
135
+
136
+ # List installed checkpoints
137
+ uv run confyui models
138
+
139
+ # Stop the daemon immediately to free RAM and VRAM
140
+ uv run confyui stop
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Development
146
+
147
+ ```bash
148
+ make help # Show available targets
149
+ make check # Lint (ruff), format check, compile, pyright, pytest
150
+ make smoke # Fast sanity check of CLI and imports
151
+ make demo # Run a real 12-step test generation
152
+ ```
@@ -0,0 +1,117 @@
1
+ # confyui
2
+
3
+ KπX sovereign local image generation and editing daemon over ComfyUI.
4
+
5
+ Hardware-accelerated on Intel Arc (XPU) via PyTorch XPU wheels, with portable CPU and CUDA extras.
6
+
7
+ ## Philosophy
8
+
9
+ - **0 Magic:** The CLI talks to ComfyUI's standard REST API via structured JSON graphs.
10
+ - **0 Manual Start:** The first call (`generate`, `edit`, `inpaint`) automatically starts ComfyUI in the background.
11
+ - **0 Wasted RAM:** An idle watchdog shuts ComfyUI down after `CONFYUI_IDLE_TIMEOUT` seconds of inactivity (default: 30 minutes).
12
+ - **0 Hardcoding:** Paths expand `$HOME` at runtime and are 100% overridable via environment variables.
13
+
14
+ ---
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ # Clone or navigate to the repository
20
+ cd ~/Labs/KpihX-Labs/AI/confyui
21
+
22
+ # Intel Arc workstation (XPU)
23
+ make sync-xpu
24
+ # or: uv sync --extra xpu
25
+
26
+ # Generic CPU fallback
27
+ make sync-cpu
28
+
29
+ # NVIDIA CUDA host
30
+ make sync-cuda
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Environment Variables
36
+
37
+ | Variable | Default | Description |
38
+ |---|---|---|
39
+ | `CONFYUI_DIR` | `~/RAW/comfyui-local/ComfyUI` | Path to the ComfyUI checkout |
40
+ | `CONFYUI_PYTHON` | `$CONFYUI_DIR/.venv/bin/python` | Python interpreter used to run ComfyUI |
41
+ | `CONFYUI_HOST` | `127.0.0.1` | Bind address |
42
+ | `CONFYUI_PORT` | `8188` | Port |
43
+ | `CONFYUI_IDLE_TIMEOUT` | `1800` | Seconds of inactivity before auto-shutdown |
44
+ | `CONFYUI_SPAWN_TIMEOUT` | `90` | Max seconds to wait for daemon readiness |
45
+ | `CONFYUI_STATE_DIR` | `~/.cache/confyui` | Directory for PID, logs, and heartbeat |
46
+ | `CONFYUI_DEFAULT_CKPT` | `Realistic_Vision_V5.1_noVAE.safetensors` | Default checkpoint for txt2img & img2img |
47
+ | `CONFYUI_INPAINT_CKPT` | `Realistic_Vision_V5.1_fp16-no-ema-inpainting.safetensors` | Default inpainting checkpoint |
48
+
49
+ ---
50
+
51
+ ## Usage
52
+
53
+ ### 1. Text-to-Image (`generate`)
54
+
55
+ ```bash
56
+ # Minimal
57
+ uv run confyui generate "a red panda wearing a top hat on the moon, cinematic lighting"
58
+
59
+ # Full options
60
+ uv run confyui generate "a red panda wearing a top hat on the moon" \
61
+ --negative "blurry, low quality, artifacts" \
62
+ --width 512 \
63
+ --height 512 \
64
+ --steps 20 \
65
+ --cfg 7.0 \
66
+ --seed 42 \
67
+ --sampler euler \
68
+ --scheduler normal \
69
+ --model "Realistic_Vision_V5.1_noVAE.safetensors" \
70
+ --out panda_moon.png
71
+ ```
72
+
73
+ ### 2. Image-to-Image (`edit`)
74
+
75
+ Modifies an existing image based on a prompt. The source image is kept and only altered proportionally to `--denoise` (0.0 = unchanged, 0.45-0.60 = balanced modification, 1.0 = total repaint):
76
+
77
+ ```bash
78
+ uv run confyui edit panda_moon.png "make the red panda wear a golden astronaut suit" \
79
+ --denoise 0.50 \
80
+ --steps 20 \
81
+ --out panda_astronaut.png
82
+ ```
83
+
84
+ ### 3. Inpainting (`inpaint`)
85
+
86
+ Repaints only the masked region (white pixels in the mask are regenerated, black pixels preserved):
87
+
88
+ ```bash
89
+ uv run confyui inpaint portrait.png torso_mask.png "she wears a long red silk dress" \
90
+ --denoise 1.0 \
91
+ --steps 25 \
92
+ --out portrait_red_dress.png
93
+ ```
94
+
95
+ ### 4. Daemon Management
96
+
97
+ ```bash
98
+ # Inspect status, uptime, PID, and idle time remaining
99
+ uv run confyui status
100
+
101
+ # List installed checkpoints
102
+ uv run confyui models
103
+
104
+ # Stop the daemon immediately to free RAM and VRAM
105
+ uv run confyui stop
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Development
111
+
112
+ ```bash
113
+ make help # Show available targets
114
+ make check # Lint (ruff), format check, compile, pyright, pytest
115
+ make smoke # Fast sanity check of CLI and imports
116
+ make demo # Run a real 12-step test generation
117
+ ```
@@ -0,0 +1,126 @@
1
+ [project]
2
+ name = "confyui"
3
+ version = "0.1.0"
4
+ description = "KpihX sovereign local image generation and editing daemon over ComfyUI (txt2img, img2img, inpaint; XPU-first)"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "KpihX", email = "kapoivha@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "fastapi>=0.159.1",
12
+ "httpx>=0.28.1",
13
+ "huggingface-hub>=2.2.0",
14
+ "pillow>=13.0.0",
15
+ "psutil>=7.2.2",
16
+ "python-dotenv>=1.2.4",
17
+ "rich>=15.0.0",
18
+ "typer>=0.27.3",
19
+ "uvicorn>=0.54.0",
20
+ ]
21
+
22
+ # ── Hardware extras (mutually exclusive) ─────────────────────────────────────
23
+ # Usage: bare sync = client only, plus exactly one accelerator backend.
24
+ # uv sync client only (remote-style, no torch)
25
+ # uv sync --extra cpu + CPU torch
26
+ # uv sync --extra xpu + Intel Arc (XPU) torch
27
+ # uv sync --extra cuda + NVIDIA CUDA torch
28
+ [project.optional-dependencies]
29
+ cpu = [
30
+ "torch>=2.8",
31
+ "torchvision>=0.23",
32
+ ]
33
+ xpu = [
34
+ "torch>=2.8",
35
+ "torchvision>=0.23",
36
+ "transformers>=4.56",
37
+ "accelerate>=1.10.1",
38
+ "huggingface_hub>=0.34.4",
39
+ "safetensors>=0.6.2",
40
+ ]
41
+ cuda = [
42
+ "torch>=2.8",
43
+ "torchvision>=0.23",
44
+ "transformers>=4.56",
45
+ "accelerate>=1.10.1",
46
+ "huggingface_hub>=0.34.4",
47
+ "safetensors>=0.6.2",
48
+ ]
49
+
50
+ [project.scripts]
51
+ confyui = "confyui.cli:app"
52
+
53
+ [build-system]
54
+ requires = ["uv_build>=0.10.3,<0.11.0"]
55
+ build-backend = "uv_build"
56
+
57
+ [tool.uv.build-backend]
58
+ module-name = "confyui"
59
+
60
+ # ── Resolution ───────────────────────────────────────────────────────────────
61
+ # On Intel Arc, unsafe-best-match makes the XPU wheels rank correctly against
62
+ # PyPI during resolution. Removing it is how PyTorch silently resolves to a
63
+ # CUDA build and the model ends up on the CPU. Rationale and full doctrine:
64
+ # k-ai/references/pyproject-hardware.md, template: AI/nobg/pyproject.toml.
65
+ [tool.uv]
66
+ package = true
67
+ index-strategy = "unsafe-best-match"
68
+ # One accelerator backend per environment, never a mixture.
69
+ conflicts = [
70
+ [{ extra = "cpu" }, { extra = "xpu" }, { extra = "cuda" }],
71
+ ]
72
+
73
+ # ── PyTorch indexes ──────────────────────────────────────────────────────────
74
+ # `explicit = true` keeps an index out of general resolution so the base
75
+ # client-only sync is unaffected; the XPU index is left non-explicit on
76
+ # purpose — that combination is the one measured working on this machine.
77
+ [[tool.uv.index]]
78
+ name = "pytorch-cpu"
79
+ url = "https://download.pytorch.org/whl/cpu"
80
+ explicit = true
81
+
82
+ [[tool.uv.index]]
83
+ name = "pytorch-xpu"
84
+ url = "https://download.pytorch.org/whl/xpu"
85
+
86
+ [[tool.uv.index]]
87
+ name = "pytorch-cuda"
88
+ url = "https://download.pytorch.org/whl/cu130"
89
+ explicit = true
90
+
91
+ [tool.uv.sources]
92
+ # This binding is what actually selects the accelerator.
93
+ torch = [
94
+ { index = "pytorch-cpu", extra = "cpu" },
95
+ { index = "pytorch-xpu", extra = "xpu" },
96
+ { index = "pytorch-cuda", extra = "cuda" },
97
+ ]
98
+ torchvision = [
99
+ { index = "pytorch-cpu", extra = "cpu" },
100
+ { index = "pytorch-xpu", extra = "xpu" },
101
+ { index = "pytorch-cuda", extra = "cuda" },
102
+ ]
103
+
104
+ # ── Development ──────────────────────────────────────────────────────────────
105
+ [dependency-groups]
106
+ dev = [
107
+ "pytest",
108
+ "pytest-cov",
109
+ "ruff",
110
+ ]
111
+
112
+ [tool.ruff]
113
+ line-length = 120
114
+ target-version = "py312"
115
+
116
+ [tool.ruff.lint.flake8-bugbear]
117
+ # `typer.Argument(...)` and `typer.Option(...)` are immutable factories, not mutable
118
+ # default values, so B008's concern does not apply to them. Declared once here instead of
119
+ # a `# noqa: B008` on every parameter that typer requires inline in the signature.
120
+ extend-immutable-calls = ["typer.Argument", "typer.Option"]
121
+
122
+ [tool.pyright]
123
+ typeCheckingMode = "basic"
124
+
125
+ [tool.pytest.ini_options]
126
+ testpaths = ["tests"]
@@ -0,0 +1,10 @@
1
+ """KπX sovereign local image generation & editing daemon over ComfyUI."""
2
+
3
+ # Note: per sovereign standard, package version is defined exclusively in
4
+ # pyproject.toml to eliminate desynchronization risks. Do NOT add __version__ here.
5
+
6
+ from confyui.client import ComfyClient
7
+ from confyui.config import Config
8
+ from confyui.server import ServerManager
9
+
10
+ __all__ = ["ComfyClient", "Config", "ServerManager"]
@@ -0,0 +1,203 @@
1
+ """confyui CLI — Typer-based interface to the sovereign ComfyUI daemon.
2
+
3
+ Provides `generate` (txt2img), `edit` (img2img), `inpaint`, `status`, `stop`, and `models`.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+
10
+ import typer
11
+ from rich.console import Console
12
+ from rich.table import Table
13
+
14
+ from confyui.client import ComfyClient
15
+ from confyui.config import Config
16
+ from confyui.server import ServerManager
17
+
18
+ app = typer.Typer(
19
+ name="confyui",
20
+ help="KπX sovereign local image generation & editing daemon over ComfyUI (XPU-first).",
21
+ no_args_is_help=True,
22
+ )
23
+ console = Console()
24
+
25
+
26
+ @app.command()
27
+ def generate(
28
+ prompt: str = typer.Argument(..., help="Text prompt to generate"),
29
+ negative: str = typer.Option("", "--negative", "-n", help="Negative prompt"),
30
+ width: int = typer.Option(512, "--width", "-w", help="Image width in pixels"),
31
+ height: int = typer.Option(512, "--height", "-h", help="Image height in pixels"),
32
+ steps: int = typer.Option(20, "--steps", "-s", help="Sampling steps"),
33
+ cfg: float = typer.Option(7.0, "--cfg", help="Classifier-free guidance scale"),
34
+ seed: int | None = typer.Option(None, "--seed", help="Random seed for reproducibility"),
35
+ sampler: str = typer.Option("euler", "--sampler", help="Sampler name (euler, dpmpp_2m, etc.)"),
36
+ scheduler: str = typer.Option("normal", "--scheduler", help="Scheduler name (normal, karras, etc.)"),
37
+ model: str | None = typer.Option(None, "--model", "-m", help="Checkpoint filename"),
38
+ out: Path | None = typer.Option(None, "--out", "-o", help="Destination path for output image"),
39
+ timeout: float = typer.Option(300.0, "--timeout", help="Max seconds to wait"),
40
+ ) -> None:
41
+ """Generate an image from text (txt2img)."""
42
+ client = ComfyClient()
43
+ with console.status("[bold green]Generating image..."):
44
+ dest = client.generate(
45
+ prompt=prompt,
46
+ negative_prompt=negative,
47
+ width=width,
48
+ height=height,
49
+ steps=steps,
50
+ cfg=cfg,
51
+ seed=seed,
52
+ sampler_name=sampler,
53
+ scheduler=scheduler,
54
+ model=model,
55
+ out=out,
56
+ timeout=timeout,
57
+ )
58
+ console.print(f"[bold green]✓[/bold green] Saved: [cyan]{dest}[/cyan] ({dest.stat().st_size:,} bytes)")
59
+
60
+
61
+ @app.command()
62
+ def edit(
63
+ source: Path = typer.Argument(..., help="Path to source image to modify"),
64
+ prompt: str = typer.Argument(..., help="Text prompt describing the desired modification"),
65
+ negative: str = typer.Option("", "--negative", "-n", help="Negative prompt"),
66
+ denoise: float = typer.Option(
67
+ 0.55, "--denoise", "-d", help="Denoising strength (0.0 = untouched, 1.0 = full rewrite)"
68
+ ),
69
+ steps: int = typer.Option(20, "--steps", "-s", help="Sampling steps"),
70
+ cfg: float = typer.Option(7.0, "--cfg", help="Classifier-free guidance scale"),
71
+ seed: int | None = typer.Option(None, "--seed", help="Random seed for reproducibility"),
72
+ sampler: str = typer.Option("euler", "--sampler", help="Sampler name"),
73
+ scheduler: str = typer.Option("normal", "--scheduler", help="Scheduler name"),
74
+ model: str | None = typer.Option(None, "--model", "-m", help="Checkpoint filename"),
75
+ out: Path | None = typer.Option(None, "--out", "-o", help="Destination path"),
76
+ timeout: float = typer.Option(300.0, "--timeout", help="Max seconds to wait"),
77
+ ) -> None:
78
+ """Edit an existing image with a text prompt (img2img)."""
79
+ client = ComfyClient()
80
+ with console.status(f"[bold green]Editing {source.name} (denoise={denoise})..."):
81
+ dest = client.edit(
82
+ source=source,
83
+ prompt=prompt,
84
+ negative_prompt=negative,
85
+ denoise=denoise,
86
+ steps=steps,
87
+ cfg=cfg,
88
+ seed=seed,
89
+ sampler_name=sampler,
90
+ scheduler=scheduler,
91
+ model=model,
92
+ out=out,
93
+ timeout=timeout,
94
+ )
95
+ console.print(f"[bold green]✓[/bold green] Saved: [cyan]{dest}[/cyan] ({dest.stat().st_size:,} bytes)")
96
+
97
+
98
+ @app.command()
99
+ def inpaint(
100
+ source: Path = typer.Argument(..., help="Path to source image"),
101
+ mask: Path = typer.Argument(..., help="Path to binary mask (white = regenerate, black = preserve)"),
102
+ prompt: str = typer.Argument(..., help="Text prompt for the masked region"),
103
+ negative: str = typer.Option("", "--negative", "-n", help="Negative prompt"),
104
+ denoise: float = typer.Option(1.0, "--denoise", "-d", help="Denoising strength for masked region"),
105
+ steps: int = typer.Option(25, "--steps", "-s", help="Sampling steps"),
106
+ cfg: float = typer.Option(7.0, "--cfg", help="Classifier-free guidance scale"),
107
+ seed: int | None = typer.Option(None, "--seed", help="Random seed"),
108
+ sampler: str = typer.Option("euler", "--sampler", help="Sampler name"),
109
+ scheduler: str = typer.Option("normal", "--scheduler", help="Scheduler name"),
110
+ model: str | None = typer.Option(None, "--model", "-m", help="Inpainting checkpoint filename"),
111
+ out: Path | None = typer.Option(None, "--out", "-o", help="Destination path"),
112
+ timeout: float = typer.Option(300.0, "--timeout", help="Max seconds to wait"),
113
+ ) -> None:
114
+ """Inpaint a region defined by a mask."""
115
+ client = ComfyClient()
116
+ with console.status(f"[bold green]Inpainting {source.name}..."):
117
+ dest = client.inpaint(
118
+ source=source,
119
+ mask=mask,
120
+ prompt=prompt,
121
+ negative_prompt=negative,
122
+ denoise=denoise,
123
+ steps=steps,
124
+ cfg=cfg,
125
+ seed=seed,
126
+ sampler_name=sampler,
127
+ scheduler=scheduler,
128
+ model=model,
129
+ out=out,
130
+ timeout=timeout,
131
+ )
132
+ console.print(f"[bold green]✓[/bold green] Saved: [cyan]{dest}[/cyan] ({dest.stat().st_size:,} bytes)")
133
+
134
+
135
+ @app.command()
136
+ def status() -> None:
137
+ """Show the status of the ComfyUI background daemon."""
138
+ mgr = ServerManager()
139
+ st = mgr.status()
140
+
141
+ table = Table(title="confyui Daemon Status")
142
+ table.add_column("Property", style="bold cyan")
143
+ table.add_column("Value")
144
+
145
+ table.add_row("HTTP Server", "[green]UP[/green]" if st["http_up"] else "[red]DOWN[/red]")
146
+ table.add_row("Supervisor Process", "[green]ALIVE[/green]" if st["running"] else "[dim]STOPPED[/dim]")
147
+ table.add_row("PID", str(st["pid"]) if st["pid"] else "—")
148
+ table.add_row("Base URL", st["base_url"])
149
+ table.add_row("Idle Timeout", f"{st['idle_timeout_sec']}s")
150
+
151
+ rem = st["idle_remaining_sec"]
152
+ if rem is not None:
153
+ table.add_row("Idle Remaining", f"{rem}s before auto-shutdown")
154
+ else:
155
+ table.add_row("Idle Remaining", "—")
156
+
157
+ table.add_row("ComfyUI Dir", st["comfyui_dir"])
158
+ table.add_row("Log File", st["log_file"])
159
+
160
+ console.print(table)
161
+
162
+
163
+ @app.command()
164
+ def stop() -> None:
165
+ """Stop the running ComfyUI daemon and free RAM/VRAM immediately."""
166
+ mgr = ServerManager()
167
+ if mgr.stop():
168
+ console.print("[bold yellow]✓[/bold yellow] ComfyUI daemon stopped.")
169
+ else:
170
+ console.print("[dim]ComfyUI daemon is not running.[/dim]")
171
+
172
+
173
+ @app.command()
174
+ def models() -> None:
175
+ """List available checkpoint models in ComfyUI."""
176
+ client = ComfyClient()
177
+ ckpts = client.list_checkpoints()
178
+ if not ckpts:
179
+ console.print("[yellow]No checkpoints found in ComfyUI checkpoints directory.[/yellow]")
180
+ return
181
+
182
+ table = Table(title="Available Checkpoints")
183
+ table.add_column("#", style="dim")
184
+ table.add_column("Model Checkpoint", style="cyan")
185
+
186
+ cfg = Config.from_env()
187
+ for idx, c in enumerate(ckpts, 1):
188
+ suffix = ""
189
+ if c == cfg.default_ckpt:
190
+ suffix = " [bold green](default)[/bold green]"
191
+ elif c == cfg.default_inpaint_ckpt:
192
+ suffix = " [bold blue](default inpaint)[/bold blue]"
193
+ table.add_row(str(idx), c + suffix)
194
+
195
+ console.print(table)
196
+
197
+
198
+ def main() -> None:
199
+ app()
200
+
201
+
202
+ if __name__ == "__main__":
203
+ main()