flux-pro-cli 2026.4.5.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.
@@ -0,0 +1,9 @@
1
+ # AceDataCloud API Token
2
+ # Get yours at https://platform.acedata.cloud
3
+ ACEDATACLOUD_API_TOKEN=
4
+
5
+ # Optional: Custom API base URL (default: https://api.acedata.cloud)
6
+ # ACEDATACLOUD_API_BASE_URL=https://api.acedata.cloud
7
+
8
+ # Optional: Request timeout in seconds (default: 1800)
9
+ # FLUX_REQUEST_TIMEOUT=1800
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.0] - 2025-01-21
6
+
7
+ ### Added
8
+
9
+ - Initial release of Flux CLI
10
+ - `generate` command for text-to-image generation
11
+ - `edit` command for image editing with text prompts
12
+ - `task` command for querying task status
13
+ - `tasks` command for batch task queries
14
+ - `wait` command for polling task completion
15
+ - `models` command to list available Flux models
16
+ - `aspect-ratios` command to list available aspect ratios
17
+ - `config` command to show current configuration
18
+ - Support for all Flux models: flux-dev, flux-pro, flux-pro-1.1, flux-pro-1.1-ultra, flux-kontext-pro, flux-kontext-max
19
+ - `--json` flag for machine-readable output on all commands
20
+ - `--token` option and `ACEDATACLOUD_API_TOKEN` env var for authentication
21
+ - Rich terminal formatting for human-readable output
22
+ - Docker support via Dockerfile and docker-compose.yaml
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 AceDataCloud
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.1
2
+ Name: flux-pro-cli
3
+ Version: 2026.4.5.0
4
+ Summary: CLI tool for Flux AI Image Generation via AceDataCloud API
5
+ Project-URL: Homepage, https://github.com/AceDataCloud/FluxCli
6
+ Project-URL: Repository, https://github.com/AceDataCloud/FluxCli
7
+ Project-URL: Issues, https://github.com/AceDataCloud/FluxCli/issues
8
+ Project-URL: Changelog, https://github.com/AceDataCloud/FluxCli/blob/main/CHANGELOG.md
9
+ Author-email: AceDataCloud <support@acedata.cloud>
10
+ Maintainer-email: AceDataCloud <support@acedata.cloud>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: acedata,ai,cli,command-line,flux,generation,image
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Multimedia :: Graphics
24
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: click>=8.1.0
27
+ Requires-Dist: httpx>=0.27.0
28
+ Requires-Dist: pydantic>=2.0.0
29
+ Requires-Dist: python-dotenv>=1.0.0
30
+ Requires-Dist: rich>=13.0.0
31
+ Provides-Extra: all
32
+ Requires-Dist: flux-pro-cli[dev,release,test]; extra == 'all'
33
+ Provides-Extra: dev
34
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
35
+ Requires-Dist: pre-commit>=3.7.0; extra == 'dev'
36
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
37
+ Provides-Extra: release
38
+ Requires-Dist: build>=1.2.0; extra == 'release'
39
+ Requires-Dist: twine>=6.1.0; extra == 'release'
40
+ Provides-Extra: test
41
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
42
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'test'
43
+ Requires-Dist: pytest>=8.0.0; extra == 'test'
44
+ Requires-Dist: respx>=0.21.0; extra == 'test'
45
+ Description-Content-Type: text/markdown
46
+
47
+ # Flux CLI
48
+
49
+ A command-line tool for AI image generation and editing using [Flux](https://blackforestlabs.ai/) through the [AceDataCloud](https://platform.acedata.cloud) platform.
50
+
51
+ ## Features
52
+
53
+ - **Text-to-Image Generation** — Generate images from text prompts with multiple Flux models
54
+ - **Image Editing** — Edit existing images using text descriptions (kontext models)
55
+ - **Task Management** — Query status, batch check, and poll for completion
56
+ - **Multiple Models** — flux-dev, flux-pro, flux-pro-1.1, flux-pro-1.1-ultra, flux-kontext-pro, flux-kontext-max
57
+ - **Rich Output** — Beautiful terminal formatting with `--json` for scripting
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ pip install flux-pro-cli
63
+ ```
64
+
65
+ ## Quick Start
66
+
67
+ ```bash
68
+ # Set your API token
69
+ export ACEDATACLOUD_API_TOKEN=your_token_here
70
+
71
+ # Generate an image
72
+ flux generate "A majestic mountain landscape at golden hour, photorealistic"
73
+
74
+ # Generate with a specific model
75
+ flux generate "Cyberpunk city with neon lights" -m flux-pro-1.1
76
+
77
+ # Generate with aspect ratio (ultra/kontext models)
78
+ flux generate "Minimalist logo of a phoenix" -m flux-pro-1.1-ultra -s 16:9
79
+
80
+ # Edit an existing image
81
+ flux edit "Add sunglasses to the person" --image-url https://example.com/photo.jpg
82
+
83
+ # Edit with max context model
84
+ flux edit "Change background to sunset beach" --image-url https://example.com/img.png -m flux-kontext-max
85
+
86
+ # Check task status
87
+ flux task abc123-def456
88
+
89
+ # Wait for task completion
90
+ flux wait abc123 --interval 5 --timeout 300
91
+
92
+ # Batch check multiple tasks
93
+ flux tasks task-1 task-2 task-3
94
+ ```
95
+
96
+ ## Commands
97
+
98
+ | Command | Description |
99
+ |---------|-------------|
100
+ | `generate` | Generate an image from a text prompt |
101
+ | `edit` | Edit an existing image with a text prompt |
102
+ | `task` | Query a single task status |
103
+ | `tasks` | Query multiple tasks at once |
104
+ | `wait` | Wait for a task to complete |
105
+ | `models` | List available Flux models |
106
+ | `aspect-ratios` | List available aspect ratios |
107
+ | `config` | Show current configuration |
108
+
109
+ ## Models
110
+
111
+ | Model | Type | Size Input | Notes |
112
+ |-------|------|-----------|-------|
113
+ | `flux-dev` | Dev | Pixels | Fast, good balance (default) |
114
+ | `flux-pro` | Pro | Pixels | Higher quality |
115
+ | `flux-pro-1.1` | Pro | Pixels | Better prompt following |
116
+ | `flux-pro-1.1-ultra` | Ultra | Aspect ratio | Highest quality |
117
+ | `flux-kontext-pro` | Kontext | Aspect ratio | Best for editing/style transfer |
118
+ | `flux-kontext-max` | Kontext | Aspect ratio | Max context for complex edits |
119
+
120
+ ## JSON Output
121
+
122
+ All commands support `--json` for machine-readable output:
123
+
124
+ ```bash
125
+ flux generate "A red car" --json | jq '.task_id'
126
+ flux task abc123 --json | jq '.data[0].image_url'
127
+ ```
128
+
129
+ ## Configuration
130
+
131
+ | Environment Variable | Description | Default |
132
+ |---------------------|-------------|---------|
133
+ | `ACEDATACLOUD_API_TOKEN` | API authentication token | (required) |
134
+ | `ACEDATACLOUD_API_BASE_URL` | API base URL | `https://api.acedata.cloud` |
135
+ | `FLUX_REQUEST_TIMEOUT` | Request timeout in seconds | `1800` |
136
+
137
+ You can also use a `.env` file or pass `--token` directly.
138
+
139
+ ## Docker
140
+
141
+ ```bash
142
+ docker compose run flux-cli generate "A beautiful sunset"
143
+ ```
144
+
145
+ ## Development
146
+
147
+ ```bash
148
+ # Install with dev dependencies
149
+ pip install -e ".[all]"
150
+
151
+ # Run tests
152
+ pytest
153
+
154
+ # Run linter
155
+ ruff check .
156
+ ruff format --check .
157
+ ```
158
+
159
+ ## License
160
+
161
+ MIT License - see [LICENSE](LICENSE) for details.
162
+
163
+ ## Links
164
+
165
+ - [AceDataCloud Platform](https://platform.acedata.cloud)
166
+ - [API Documentation](https://docs.acedata.cloud)
167
+ - [Flux by Black Forest Labs](https://blackforestlabs.ai/)
@@ -0,0 +1,121 @@
1
+ # Flux CLI
2
+
3
+ A command-line tool for AI image generation and editing using [Flux](https://blackforestlabs.ai/) through the [AceDataCloud](https://platform.acedata.cloud) platform.
4
+
5
+ ## Features
6
+
7
+ - **Text-to-Image Generation** — Generate images from text prompts with multiple Flux models
8
+ - **Image Editing** — Edit existing images using text descriptions (kontext models)
9
+ - **Task Management** — Query status, batch check, and poll for completion
10
+ - **Multiple Models** — flux-dev, flux-pro, flux-pro-1.1, flux-pro-1.1-ultra, flux-kontext-pro, flux-kontext-max
11
+ - **Rich Output** — Beautiful terminal formatting with `--json` for scripting
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pip install flux-pro-cli
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ # Set your API token
23
+ export ACEDATACLOUD_API_TOKEN=your_token_here
24
+
25
+ # Generate an image
26
+ flux generate "A majestic mountain landscape at golden hour, photorealistic"
27
+
28
+ # Generate with a specific model
29
+ flux generate "Cyberpunk city with neon lights" -m flux-pro-1.1
30
+
31
+ # Generate with aspect ratio (ultra/kontext models)
32
+ flux generate "Minimalist logo of a phoenix" -m flux-pro-1.1-ultra -s 16:9
33
+
34
+ # Edit an existing image
35
+ flux edit "Add sunglasses to the person" --image-url https://example.com/photo.jpg
36
+
37
+ # Edit with max context model
38
+ flux edit "Change background to sunset beach" --image-url https://example.com/img.png -m flux-kontext-max
39
+
40
+ # Check task status
41
+ flux task abc123-def456
42
+
43
+ # Wait for task completion
44
+ flux wait abc123 --interval 5 --timeout 300
45
+
46
+ # Batch check multiple tasks
47
+ flux tasks task-1 task-2 task-3
48
+ ```
49
+
50
+ ## Commands
51
+
52
+ | Command | Description |
53
+ |---------|-------------|
54
+ | `generate` | Generate an image from a text prompt |
55
+ | `edit` | Edit an existing image with a text prompt |
56
+ | `task` | Query a single task status |
57
+ | `tasks` | Query multiple tasks at once |
58
+ | `wait` | Wait for a task to complete |
59
+ | `models` | List available Flux models |
60
+ | `aspect-ratios` | List available aspect ratios |
61
+ | `config` | Show current configuration |
62
+
63
+ ## Models
64
+
65
+ | Model | Type | Size Input | Notes |
66
+ |-------|------|-----------|-------|
67
+ | `flux-dev` | Dev | Pixels | Fast, good balance (default) |
68
+ | `flux-pro` | Pro | Pixels | Higher quality |
69
+ | `flux-pro-1.1` | Pro | Pixels | Better prompt following |
70
+ | `flux-pro-1.1-ultra` | Ultra | Aspect ratio | Highest quality |
71
+ | `flux-kontext-pro` | Kontext | Aspect ratio | Best for editing/style transfer |
72
+ | `flux-kontext-max` | Kontext | Aspect ratio | Max context for complex edits |
73
+
74
+ ## JSON Output
75
+
76
+ All commands support `--json` for machine-readable output:
77
+
78
+ ```bash
79
+ flux generate "A red car" --json | jq '.task_id'
80
+ flux task abc123 --json | jq '.data[0].image_url'
81
+ ```
82
+
83
+ ## Configuration
84
+
85
+ | Environment Variable | Description | Default |
86
+ |---------------------|-------------|---------|
87
+ | `ACEDATACLOUD_API_TOKEN` | API authentication token | (required) |
88
+ | `ACEDATACLOUD_API_BASE_URL` | API base URL | `https://api.acedata.cloud` |
89
+ | `FLUX_REQUEST_TIMEOUT` | Request timeout in seconds | `1800` |
90
+
91
+ You can also use a `.env` file or pass `--token` directly.
92
+
93
+ ## Docker
94
+
95
+ ```bash
96
+ docker compose run flux-cli generate "A beautiful sunset"
97
+ ```
98
+
99
+ ## Development
100
+
101
+ ```bash
102
+ # Install with dev dependencies
103
+ pip install -e ".[all]"
104
+
105
+ # Run tests
106
+ pytest
107
+
108
+ # Run linter
109
+ ruff check .
110
+ ruff format --check .
111
+ ```
112
+
113
+ ## License
114
+
115
+ MIT License - see [LICENSE](LICENSE) for details.
116
+
117
+ ## Links
118
+
119
+ - [AceDataCloud Platform](https://platform.acedata.cloud)
120
+ - [API Documentation](https://docs.acedata.cloud)
121
+ - [Flux by Black Forest Labs](https://blackforestlabs.ai/)
@@ -0,0 +1 @@
1
+ """Flux CLI - AI Image Generation via AceDataCloud API."""
@@ -0,0 +1,5 @@
1
+ """Allow running as python -m flux_cli."""
2
+
3
+ from flux_cli.main import cli
4
+
5
+ cli()
File without changes
@@ -0,0 +1,165 @@
1
+ """Image generation and editing commands."""
2
+
3
+ import click
4
+
5
+ from flux_cli.core.client import get_client
6
+ from flux_cli.core.exceptions import FluxError
7
+ from flux_cli.core.output import (
8
+ ASPECT_RATIOS,
9
+ DEFAULT_MODEL,
10
+ FLUX_MODELS,
11
+ print_error,
12
+ print_image_result,
13
+ print_json,
14
+ )
15
+
16
+
17
+ @click.command()
18
+ @click.argument("prompt")
19
+ @click.option(
20
+ "-m",
21
+ "--model",
22
+ type=click.Choice(FLUX_MODELS),
23
+ default=DEFAULT_MODEL,
24
+ help="Flux model to use.",
25
+ )
26
+ @click.option(
27
+ "-s",
28
+ "--size",
29
+ default=None,
30
+ help="Image size: pixels (e.g. 1024x1024) or aspect ratio (e.g. 16:9).",
31
+ )
32
+ @click.option(
33
+ "-n",
34
+ "--count",
35
+ default=None,
36
+ type=int,
37
+ help="Number of images to generate.",
38
+ )
39
+ @click.option("--callback-url", default=None, help="Webhook callback URL.")
40
+ @click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
41
+ @click.pass_context
42
+ def generate(
43
+ ctx: click.Context,
44
+ prompt: str,
45
+ model: str,
46
+ size: str | None,
47
+ count: int | None,
48
+ callback_url: str | None,
49
+ output_json: bool,
50
+ ) -> None:
51
+ """Generate an image from a text prompt.
52
+
53
+ PROMPT is a detailed description of the image to generate.
54
+
55
+ \b
56
+ Examples:
57
+ flux generate "A majestic mountain at golden hour, photorealistic"
58
+ flux generate "Cyberpunk city with neon lights" -m flux-pro-1.1
59
+ flux generate "Minimalist logo of a phoenix" -m flux-pro-1.1-ultra -s 16:9
60
+ """
61
+ client = get_client(ctx.obj.get("token"))
62
+ try:
63
+ payload: dict[str, object] = {
64
+ "action": "generate",
65
+ "prompt": prompt,
66
+ "model": model,
67
+ "size": size,
68
+ "count": count,
69
+ "callback_url": callback_url,
70
+ }
71
+
72
+ result = client.generate_image(**payload) # type: ignore[arg-type]
73
+ if output_json:
74
+ print_json(result)
75
+ else:
76
+ print_image_result(result)
77
+ except FluxError as e:
78
+ print_error(e.message)
79
+ raise SystemExit(1) from e
80
+
81
+
82
+ @click.command()
83
+ @click.argument("prompt")
84
+ @click.option(
85
+ "--image-url",
86
+ required=True,
87
+ help="URL of the image to edit.",
88
+ )
89
+ @click.option(
90
+ "-m",
91
+ "--model",
92
+ type=click.Choice(FLUX_MODELS),
93
+ default="flux-kontext-pro",
94
+ help="Flux model to use (default: flux-kontext-pro).",
95
+ )
96
+ @click.option(
97
+ "-s",
98
+ "--size",
99
+ default=None,
100
+ help="Output size: pixels or aspect ratio.",
101
+ )
102
+ @click.option("--callback-url", default=None, help="Webhook callback URL.")
103
+ @click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
104
+ @click.pass_context
105
+ def edit(
106
+ ctx: click.Context,
107
+ prompt: str,
108
+ image_url: str,
109
+ model: str,
110
+ size: str | None,
111
+ callback_url: str | None,
112
+ output_json: bool,
113
+ ) -> None:
114
+ """Edit an existing image with a text prompt.
115
+
116
+ PROMPT describes the desired changes to the image.
117
+
118
+ \b
119
+ Examples:
120
+ flux edit "Add sunglasses" --image-url https://example.com/photo.jpg
121
+ flux edit "Change background to sunset" --image-url https://example.com/img.png -m flux-kontext-max
122
+ """
123
+ client = get_client(ctx.obj.get("token"))
124
+ try:
125
+ payload: dict[str, object] = {
126
+ "action": "edit",
127
+ "prompt": prompt,
128
+ "image_url": image_url,
129
+ "model": model,
130
+ "size": size,
131
+ "callback_url": callback_url,
132
+ }
133
+
134
+ result = client.edit_image(**payload) # type: ignore[arg-type]
135
+ if output_json:
136
+ print_json(result)
137
+ else:
138
+ print_image_result(result)
139
+ except FluxError as e:
140
+ print_error(e.message)
141
+ raise SystemExit(1) from e
142
+
143
+
144
+ @click.command("aspect-ratios")
145
+ def aspect_ratios() -> None:
146
+ """List available aspect ratios for ultra/kontext models."""
147
+ from rich.table import Table
148
+
149
+ from flux_cli.core.output import console
150
+
151
+ table = Table(title="Available Aspect Ratios")
152
+ table.add_column("Ratio", style="bold cyan")
153
+ table.add_column("Orientation")
154
+
155
+ for ratio in ASPECT_RATIOS:
156
+ w, h = ratio.split(":")
157
+ if int(w) > int(h):
158
+ orientation = "Landscape"
159
+ elif int(w) < int(h):
160
+ orientation = "Portrait"
161
+ else:
162
+ orientation = "Square"
163
+ table.add_row(ratio, orientation)
164
+
165
+ console.print(table)
@@ -0,0 +1,31 @@
1
+ """Info and utility commands."""
2
+
3
+ import click
4
+
5
+ from flux_cli.core.config import settings
6
+ from flux_cli.core.output import console, print_models
7
+
8
+
9
+ @click.command()
10
+ def models() -> None:
11
+ """List available Flux models."""
12
+ print_models()
13
+
14
+
15
+ @click.command()
16
+ def config() -> None:
17
+ """Show current configuration."""
18
+ from rich.table import Table
19
+
20
+ table = Table(title="Flux CLI Configuration")
21
+ table.add_column("Setting", style="bold cyan")
22
+ table.add_column("Value")
23
+
24
+ table.add_row("API Base URL", settings.api_base_url)
25
+ table.add_row(
26
+ "API Token",
27
+ f"{settings.api_token[:8]}..." if settings.api_token else "[red]Not set[/red]",
28
+ )
29
+ table.add_row("Request Timeout", f"{settings.request_timeout}s")
30
+
31
+ console.print(table)
@@ -0,0 +1,141 @@
1
+ """Task management commands."""
2
+
3
+ import time
4
+
5
+ import click
6
+
7
+ from flux_cli.core.client import get_client
8
+ from flux_cli.core.exceptions import FluxError
9
+ from flux_cli.core.output import print_error, print_json, print_success, print_task_result
10
+
11
+
12
+ @click.command()
13
+ @click.argument("task_id")
14
+ @click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
15
+ @click.pass_context
16
+ def task(
17
+ ctx: click.Context,
18
+ task_id: str,
19
+ output_json: bool,
20
+ ) -> None:
21
+ """Query a single task status.
22
+
23
+ TASK_ID is the task ID returned from generate/edit commands.
24
+
25
+ Examples:
26
+
27
+ flux task abc123-def456
28
+ """
29
+ client = get_client(ctx.obj.get("token"))
30
+ try:
31
+ result = client.query_task(id=task_id, action="retrieve")
32
+ if output_json:
33
+ print_json(result)
34
+ else:
35
+ print_task_result(result)
36
+ except FluxError as e:
37
+ print_error(e.message)
38
+ raise SystemExit(1) from e
39
+
40
+
41
+ @click.command("tasks")
42
+ @click.argument("task_ids", nargs=-1, required=True)
43
+ @click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
44
+ @click.pass_context
45
+ def tasks_batch(
46
+ ctx: click.Context,
47
+ task_ids: tuple[str, ...],
48
+ output_json: bool,
49
+ ) -> None:
50
+ """Query multiple tasks at once.
51
+
52
+ TASK_IDS are space-separated task IDs.
53
+
54
+ Examples:
55
+
56
+ flux tasks abc123 def456 ghi789
57
+ """
58
+ client = get_client(ctx.obj.get("token"))
59
+ try:
60
+ result = client.query_task(ids=list(task_ids), action="retrieve_batch")
61
+ if output_json:
62
+ print_json(result)
63
+ else:
64
+ print_task_result(result)
65
+ except FluxError as e:
66
+ print_error(e.message)
67
+ raise SystemExit(1) from e
68
+
69
+
70
+ @click.command()
71
+ @click.argument("task_id")
72
+ @click.option(
73
+ "--interval",
74
+ type=int,
75
+ default=5,
76
+ help="Polling interval in seconds (default: 5).",
77
+ )
78
+ @click.option(
79
+ "--timeout",
80
+ "max_timeout",
81
+ type=int,
82
+ default=600,
83
+ help="Maximum wait time in seconds (default: 600).",
84
+ )
85
+ @click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
86
+ @click.pass_context
87
+ def wait(
88
+ ctx: click.Context,
89
+ task_id: str,
90
+ interval: int,
91
+ max_timeout: int,
92
+ output_json: bool,
93
+ ) -> None:
94
+ """Wait for a task to complete, polling periodically.
95
+
96
+ TASK_ID is the task ID to monitor.
97
+
98
+ Examples:
99
+
100
+ flux wait abc123
101
+
102
+ flux wait abc123 --interval 10 --timeout 300
103
+ """
104
+ client = get_client(ctx.obj.get("token"))
105
+ elapsed = 0
106
+
107
+ try:
108
+ while elapsed < max_timeout:
109
+ result = client.query_task(id=task_id, action="retrieve")
110
+ data = result.get("data", {})
111
+
112
+ if isinstance(data, list) and data:
113
+ item = data[0]
114
+ elif isinstance(data, dict):
115
+ item = data
116
+ else:
117
+ item = {}
118
+
119
+ state = item.get("state", item.get("status", ""))
120
+ if state in ("succeeded", "completed", "complete", "failed", "error"):
121
+ if output_json:
122
+ print_json(result)
123
+ else:
124
+ if state in ("failed", "error"):
125
+ print_error(f"Task {task_id} failed.")
126
+ else:
127
+ print_success(f"Task {task_id} completed!")
128
+ print_task_result(result)
129
+ return
130
+
131
+ if not output_json:
132
+ click.echo(f"Status: {state or 'pending'} (waited {elapsed}s)...", err=True)
133
+
134
+ time.sleep(interval)
135
+ elapsed += interval
136
+
137
+ print_error(f"Timeout: task {task_id} did not complete within {max_timeout}s")
138
+ raise SystemExit(1)
139
+ except FluxError as e:
140
+ print_error(e.message)
141
+ raise SystemExit(1) from e
File without changes