qall 0.1.0__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.
qall/__init__.py ADDED
@@ -0,0 +1,35 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import logging
16
+
17
+ formatter = logging.Formatter(
18
+ fmt="[%(asctime)s] %(levelname)s | %(name)s | %(message)s",
19
+ datefmt="%Y-%m-%d %H:%M:%S",
20
+ )
21
+
22
+ console_handler = logging.StreamHandler()
23
+ console_handler.setFormatter(formatter)
24
+
25
+ logger = logging.getLogger(__name__)
26
+ logger.setLevel(logging.INFO)
27
+ logger.addHandler(console_handler)
28
+
29
+ # Optional: Prevent logs from propagating to the global root logger
30
+ # to avoid duplicate logs if the end-user also configures logging.
31
+ logger.propagate = False
32
+
33
+ from qorus.sdk import task, workflow, run
34
+
35
+ __all__ = ["task", "workflow", "run"]
qall/cli/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
qall/cli/artifact.py ADDED
@@ -0,0 +1,53 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+
16
+ from qorus.core import artifact
17
+
18
+ artifact_cli = typer.Typer(
19
+ name="artifact",
20
+ help="Inspect and download generated execution artifacts.",
21
+ no_args_is_help=True,
22
+ )
23
+
24
+
25
+ @artifact_cli.command("ls")
26
+ def list_artifacts(
27
+ workflow_run_id: str = typer.Argument(
28
+ ..., help="The ID of the workflow run to list artifacts for."
29
+ )
30
+ ):
31
+ typer.echo("Available artifacts:")
32
+
33
+ artifacts = artifact.list_artifacts(workflow_run_id)
34
+
35
+ if not artifacts:
36
+ typer.echo(" No artifacts found.")
37
+ return
38
+
39
+ for name in artifacts:
40
+ typer.echo(f"- {typer.style(name, bold=True)}")
41
+
42
+
43
+ @artifact_cli.command("info")
44
+ def artifact_info(
45
+ artifact_id: str = typer.Argument(
46
+ ..., help="The unique identifier tracking hash of the generated asset."
47
+ )
48
+ ):
49
+ """Display download links, sizing, and origin execution contexts for a specific task artifact."""
50
+ art = artifact.get_artifact(artifact_id=artifact_id)
51
+ typer.echo(
52
+ f"Artifact Name: {art.name} | Task Run Origin: {art.task_run_id} | URL: {art.url}"
53
+ )
qall/cli/checkpoint.py ADDED
@@ -0,0 +1,22 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+
16
+ import qorus.core as core
17
+
18
+ checkpoint_cli = typer.Typer(
19
+ name="checkpoint",
20
+ help="Inspect generated execution task checkpoints.",
21
+ no_args_is_help=True,
22
+ )
qall/cli/config.py ADDED
@@ -0,0 +1,44 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+
16
+ from typing import Optional
17
+
18
+ import qorus.core as core
19
+
20
+ config_cli = typer.Typer(
21
+ name="config",
22
+ help="Manage local configuration profile configurations.",
23
+ no_args_is_help=True,
24
+ )
25
+
26
+
27
+ @config_cli.command("init")
28
+ def config_init(
29
+ provider: Optional[str] = typer.Option(
30
+ "scaleway", "--provider", help="Default target cloud provider name."
31
+ ),
32
+ erase: bool = typer.Option(
33
+ False, "--erase", help="Overwrite existing configuration files if present."
34
+ ),
35
+ ):
36
+ """Initialize a default .qorus.yml project layout file inside the current directory boundary."""
37
+ res = core.init_config(provider_name=provider, erase_exists=erase)
38
+
39
+ if res is None:
40
+ typer.echo("Configuration file already exists. Use --erase to force overwrite.")
41
+ else:
42
+ typer.echo(
43
+ "Project workspace configuration initialized successfully (.qorus.yml)."
44
+ )
qall/cli/daemon.py ADDED
@@ -0,0 +1,217 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import typer
16
+
17
+ from typing import Optional
18
+
19
+ from qorus.core.daemon import (
20
+ start_daemon,
21
+ run_task,
22
+ get_daemon_status,
23
+ stop_daemon,
24
+ restart_daemon,
25
+ )
26
+
27
+ daemon_cli = typer.Typer(
28
+ name="daemon",
29
+ help="Manage daemon",
30
+ no_args_is_help=True,
31
+ )
32
+
33
+
34
+ @daemon_cli.command("start")
35
+ def daemon_start(
36
+ port: int = typer.Option(50053, "--port", "-p", help="gRPC port for the daemon"),
37
+ image: str = typer.Option(
38
+ None,
39
+ "--image",
40
+ "-i",
41
+ help="Daemon Docker image to use",
42
+ ),
43
+ block_registry: str = typer.Option(
44
+ None,
45
+ "--block-dir",
46
+ "-b",
47
+ help="Path to block registry directory",
48
+ ),
49
+ artifact_registry: str = typer.Option(
50
+ None,
51
+ "--artifact-dir",
52
+ "-a",
53
+ help="Path to artifact registry directory",
54
+ ),
55
+ worker_provider: str = typer.Option(
56
+ None,
57
+ "--worker-provider",
58
+ "-w",
59
+ help="Name of the worker provider",
60
+ ),
61
+ ):
62
+ """
63
+ Start a local Qorus daemon in a Docker container.
64
+ """
65
+ try:
66
+ start_daemon(
67
+ port=port,
68
+ block_registry_path=block_registry,
69
+ artifact_registry_path=artifact_registry,
70
+ image=image,
71
+ worker_provider=worker_provider,
72
+ )
73
+ except KeyboardInterrupt:
74
+ typer.echo("Daemon stopped.")
75
+ except Exception as e:
76
+ typer.echo(f"Error: {e}", err=True)
77
+ raise typer.Exit(code=1)
78
+
79
+
80
+ @daemon_cli.command("stop")
81
+ def daemon_stop(
82
+ port: int = typer.Option(50053, "--port", "-p", help="gRPC port for the daemon"),
83
+ ):
84
+ try:
85
+ stop_daemon(port)
86
+ except Exception as e:
87
+ typer.echo(f"Error: {e}", err=True)
88
+ raise typer.Exit(code=1)
89
+
90
+
91
+ @daemon_cli.command("restart")
92
+ def daemon_restart(
93
+ port: int = typer.Option(50053, "--port", "-p", help="gRPC port for the daemon"),
94
+ ):
95
+ try:
96
+ restart_daemon(port)
97
+ except Exception as e:
98
+ typer.echo(f"Error: {e}", err=True)
99
+ raise typer.Exit(code=1)
100
+
101
+
102
+ @daemon_cli.command("run")
103
+ def daemon_run(
104
+ task_hash: str = typer.Argument(
105
+ ..., help="Hash of the task stored in registry to execute."
106
+ ),
107
+ artifact_hash: str = typer.Option(
108
+ None,
109
+ "--artifact-hash",
110
+ "-a",
111
+ help="Hash of the input artifact stored in registry for the task execution.",
112
+ ),
113
+ daemon_image: str = typer.Option(
114
+ None,
115
+ "--daemon-image",
116
+ "-i",
117
+ help="Daemon Docker image to use",
118
+ ),
119
+ daemon_address: Optional[str] = typer.Option(
120
+ None,
121
+ "--daemon-address",
122
+ "-d",
123
+ help="Daemon gRPC address (default: QORUS_DAEMON_ADDRESS env var or localhost:50053)",
124
+ ),
125
+ block_registry: str = typer.Option(
126
+ None,
127
+ "--block-dir",
128
+ "-b",
129
+ help="Path to block registry directory",
130
+ ),
131
+ artifact_registry: str = typer.Option(
132
+ None,
133
+ "--artifact-dir",
134
+ "-a",
135
+ help="Path to artifact registry directory",
136
+ ),
137
+ worker_provider: str = typer.Option(
138
+ None,
139
+ "--worker-provider",
140
+ "-w",
141
+ help="Name of the worker provider",
142
+ ),
143
+ timeout: int = typer.Option(
144
+ 300, "--timeout", "-t", help="Timeout for the task execution."
145
+ ),
146
+ ):
147
+ """
148
+ Orders a local Daemon instance to start and execute a given task.
149
+ """
150
+ try:
151
+ artifacts = run_task(
152
+ task_hash=task_hash,
153
+ input_artifact_hash=artifact_hash,
154
+ daemon_address=daemon_address,
155
+ block_registry=block_registry,
156
+ artifact_registry=artifact_registry,
157
+ daemon_image=daemon_image,
158
+ timeout=timeout,
159
+ worker_provider=worker_provider,
160
+ )
161
+
162
+ if artifacts:
163
+ typer.echo(f"Task completed successfully!")
164
+ typer.echo(f"Output artifacts: {len(artifacts)}")
165
+
166
+ for artifact in artifacts:
167
+ typer.echo(f" - {artifact.hash}")
168
+
169
+ except Exception as e:
170
+ typer.echo(f"Error: {e}", err=True)
171
+ raise typer.Exit(code=1)
172
+
173
+
174
+ @daemon_cli.command("status")
175
+ def daemon_status(
176
+ daemon_image: str = typer.Option(
177
+ None,
178
+ "--daemon-image",
179
+ "-i",
180
+ help="Daemon Docker image to use",
181
+ ),
182
+ daemon_address: Optional[str] = typer.Option(
183
+ None,
184
+ "--daemon-address",
185
+ "-d",
186
+ help="Daemon gRPC address (default: QORUS_DAEMON_ADDRESS env var or localhost:50053)",
187
+ ),
188
+ block_registry: str = typer.Option(
189
+ None,
190
+ "--block-dir",
191
+ "-b",
192
+ help="Path to block registry directory",
193
+ ),
194
+ artifact_registry: str = typer.Option(
195
+ None,
196
+ "--artifact-dir",
197
+ "-a",
198
+ help="Path to artifact registry directory",
199
+ ),
200
+ ):
201
+ """
202
+ Check the status of the local daemon container.
203
+ """
204
+ try:
205
+ status = get_daemon_status(
206
+ daemon_image=daemon_image,
207
+ daemon_address=daemon_address,
208
+ block_registry=block_registry,
209
+ artifact_registry=artifact_registry,
210
+ )
211
+
212
+ if status:
213
+ typer.echo(f"status: {status}")
214
+
215
+ except Exception as e:
216
+ typer.echo(f"Error: {e}", err=True)
217
+ raise typer.Exit(code=1)
qall/cli/log.py ADDED
@@ -0,0 +1,22 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+
16
+ import qorus.core as core
17
+
18
+ log_cli = typer.Typer(
19
+ name="logs",
20
+ help="Manage workflow and task logs generation.",
21
+ no_args_is_help=True,
22
+ )
qall/cli/main.py ADDED
@@ -0,0 +1,159 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+ import logging
16
+ import os
17
+
18
+ from typing import Optional
19
+
20
+ import qorus.core as core
21
+
22
+ from .artifact import artifact_cli
23
+ from .provider import provider_cli
24
+ from .registry import registry_cli
25
+ from .log import log_cli
26
+ from .workflow import workflow_cli
27
+ from .resource import resource_cli
28
+ from .checkpoint import checkpoint_cli
29
+ from .config import config_cli
30
+ from .daemon import daemon_cli
31
+ from .task import task_cli
32
+ from .worker import worker_cli
33
+ from .utils import parse_extra_args
34
+
35
+ _DEFAULT_LOGGING_LEVEL = logging.INFO
36
+
37
+ cli = typer.Typer(
38
+ name="qorus",
39
+ help="A python tool for modern hybrid and quantum workflows.",
40
+ no_args_is_help=True,
41
+ )
42
+
43
+ cli.add_typer(workflow_cli)
44
+ cli.add_typer(registry_cli)
45
+ cli.add_typer(artifact_cli)
46
+ cli.add_typer(provider_cli)
47
+ cli.add_typer(config_cli)
48
+ cli.add_typer(resource_cli)
49
+ cli.add_typer(log_cli)
50
+ cli.add_typer(daemon_cli)
51
+ cli.add_typer(task_cli)
52
+ cli.add_typer(worker_cli)
53
+ cli.add_typer(checkpoint_cli)
54
+
55
+
56
+ def _setup_logging():
57
+ logging_level_str = os.getenv("QORUS_LOGGING_LEVEL")
58
+
59
+ if not logging_level_str and not _DEFAULT_LOGGING_LEVEL:
60
+ return
61
+
62
+ if logging_level_str:
63
+ mapping = logging.getLevelNamesMapping()
64
+ logging_level = mapping.get(logging_level_str.upper())
65
+ else:
66
+ logging_level = _DEFAULT_LOGGING_LEVEL
67
+
68
+ logging.basicConfig(level=logging_level)
69
+
70
+
71
+ _setup_logging()
72
+
73
+
74
+ @cli.command("run")
75
+ def run(
76
+ entrypoint: str = typer.Argument(
77
+ ..., help="Path to the Python script containing tasks."
78
+ ),
79
+ local: bool = typer.Option(
80
+ False, "--local", help="Wether to run locally or remotely"
81
+ ),
82
+ input_artifact_hash: Optional[str] = typer.Option(
83
+ None,
84
+ "--input-artifact-hash",
85
+ help="The artifact's hash the entrypoint task may require to execute.",
86
+ ),
87
+ name: Optional[str] = typer.Option(
88
+ None,
89
+ "--name",
90
+ help="Name of the workflow to declare. Script file name if not provided",
91
+ ),
92
+ version: Optional[str] = typer.Option(
93
+ "latest", "--version", "-v", help="Version tag for the target push."
94
+ ),
95
+ profile: Optional[str] = typer.Option(
96
+ None, "--profile", "-p", help="Target configuration profile name."
97
+ ),
98
+ ):
99
+ """Compile, delta-push, provision hardware and run an arbitrary hybrid workflow script instantly."""
100
+ typer.echo(f"Initiating compilation run for: {entrypoint}")
101
+ run_obj = core.create_workflow_run(
102
+ entrypoint=entrypoint,
103
+ workflow_name=name,
104
+ workflow_version=version,
105
+ profile_name=profile,
106
+ local_only=local,
107
+ input_artifact_hash=input_artifact_hash,
108
+ )
109
+ typer.echo(f"Workflow run ID: {run_obj.id}")
110
+
111
+
112
+ @cli.command("fix")
113
+ def fix(
114
+ entrypoint: str = typer.Argument(
115
+ ..., help="Path to the Python script containing tasks."
116
+ ),
117
+ ):
118
+ run_obj = core.fix_workflow_run(entrypoint=entrypoint)
119
+
120
+
121
+ @cli.command(
122
+ "login", context_settings={"allow_extra_args": True, "ignore_unknown_options": True}
123
+ )
124
+ def login(
125
+ ctx: typer.Context, # Typer Context is mandatory to capture extra unkown arguments
126
+ provider_name: str = typer.Argument(
127
+ ..., help="Name of the cloud provider (e.g., scaleway)."
128
+ ),
129
+ registry_domain: str = typer.Argument(
130
+ ..., help="Domain URI of the content-addressed registry server."
131
+ ),
132
+ ):
133
+ creds = parse_extra_args(ctx.args)
134
+
135
+ if not creds:
136
+ typer.echo(
137
+ "Warning: No dynamic credentials flags passed (e.g. --api-key=XYZ).",
138
+ err=True,
139
+ )
140
+
141
+ core.login_provider(provider_name=provider_name, credentials=creds)
142
+ core.login_registry(registry_domain=registry_domain, credentials=creds)
143
+ typer.echo("Successfully authenticated against both endpoints.")
144
+
145
+
146
+ @cli.command("logout")
147
+ def logout():
148
+ old_provider = core.logout_provider()
149
+
150
+ if old_provider:
151
+ typer.echo(f"Dropped provider credentials successfully from {old_provider}")
152
+
153
+ old_registry = core.logout_registry()
154
+
155
+ if old_registry:
156
+ typer.echo(f"Dropped provider credentials successfully from {old_registry}")
157
+
158
+ if not old_provider and not old_registry:
159
+ typer.echo(f"Already logout")
qall/cli/provider.py ADDED
@@ -0,0 +1,60 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+
16
+ import qorus.core as core
17
+
18
+ from .utils import parse_extra_args
19
+
20
+ provider_cli = typer.Typer(
21
+ name="provider",
22
+ help="Configure compute provider environments.",
23
+ no_args_is_help=True,
24
+ )
25
+
26
+
27
+ @provider_cli.command(
28
+ "login", context_settings={"allow_extra_args": True, "ignore_unknown_options": True}
29
+ )
30
+ def provider_login(
31
+ ctx: typer.Context,
32
+ provider_name: str = typer.Argument(
33
+ ..., help="Compute provider identity token identifier (e.g., scaleway)."
34
+ ),
35
+ ):
36
+ """Authenticate the local instance against a provider control plane."""
37
+ creds = parse_extra_args(ctx.args)
38
+
39
+ if not creds:
40
+ typer.echo(
41
+ "Warning: No dynamic credentials flags passed (e.g. --api-key=XYZ).",
42
+ err=True,
43
+ )
44
+
45
+ core.login_provider(
46
+ provider_name=provider_name,
47
+ credentials=creds,
48
+ )
49
+
50
+ typer.echo(f"Provider session initialized for: {provider_name}")
51
+
52
+
53
+ @provider_cli.command("logout")
54
+ def provider_logout():
55
+ old_provider = core.logout_provider()
56
+
57
+ if old_provider:
58
+ typer.echo(f"Dropped provider credentials successfully from {old_provider}")
59
+ else:
60
+ typer.echo(f"Already logout")
qall/cli/registry.py ADDED
@@ -0,0 +1,145 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+ import os
16
+
17
+ import qorus.core as core
18
+
19
+ from pathlib import Path
20
+
21
+ from qorus.specification import WorkflowSpec, WorkerSpec
22
+
23
+ from .utils import parse_extra_args
24
+
25
+ registry_cli = typer.Typer(
26
+ name="registry",
27
+ help="Interact with containerless content-addressed task registries.",
28
+ no_args_is_help=True,
29
+ )
30
+
31
+
32
+ @registry_cli.command("push")
33
+ def registry_push(
34
+ file: Path = typer.Argument(
35
+ ..., help="Path to the Python entrypoint script file containing your tasks."
36
+ ),
37
+ local: bool = typer.Option(
38
+ False,
39
+ "--local",
40
+ help="Wether to store in a local block registry or a remote one.",
41
+ ),
42
+ worker: bool = typer.Option(
43
+ False,
44
+ "--worker",
45
+ help="Whether to push a worker spec instead of a workflow spec.",
46
+ ),
47
+ name: bool = typer.Option(
48
+ None,
49
+ "--name",
50
+ "--n",
51
+ help="Name of the registry entry.",
52
+ ),
53
+ version: bool = typer.Option(
54
+ None,
55
+ "--version",
56
+ "--v",
57
+ help="Version of the registry entry.",
58
+ ),
59
+ ):
60
+ """Statically compile code statements, analyze the DAG, and delta-push missing blocks to the registry."""
61
+
62
+ if not os.path.exists(file):
63
+ typer.echo(f"Cannot found file {file}")
64
+ return
65
+
66
+ typer.echo(f"Compiling local Merkle DAG elements from script: {file}")
67
+
68
+ name = name or file.stem
69
+ version = version or "latest"
70
+
71
+ if worker:
72
+ worker_spec = WorkerSpec(file)
73
+ tag, dag = core.push_worker_on_registry(
74
+ worker_spec=worker_spec,
75
+ worker_name=name,
76
+ worker_version=version,
77
+ local_only=local,
78
+ )
79
+ else:
80
+ workflow_spec = WorkflowSpec(file)
81
+ tag, dag = core.push_workflow_on_registry(
82
+ workflow_spec=workflow_spec,
83
+ workflow_name=name,
84
+ workflow_version=version,
85
+ local_only=local,
86
+ )
87
+
88
+ typer.echo(f"Registry synchronization push complete: {tag}, {dag}")
89
+
90
+
91
+ @registry_cli.command("pull")
92
+ def registry_pull(
93
+ uri: str = typer.Argument(
94
+ ..., help="The canonical target URI formatted as 'name:version' to download."
95
+ )
96
+ ):
97
+ """Download the full underlying Merkle DAG block graph from a remote registry into the local cache."""
98
+ if ":" not in uri:
99
+ name, version = uri, "latest"
100
+ else:
101
+ name, version = uri.split(":", 1)
102
+ core.pull_from_registry(workflow_name=name, workflow_version=version)
103
+ typer.echo(f"Blocks downloaded and cached locally for URI: {name}:{version}")
104
+
105
+
106
+ @registry_cli.command(
107
+ "login", context_settings={"allow_extra_args": True, "ignore_unknown_options": True}
108
+ )
109
+ def registry_login(
110
+ ctx: typer.Context,
111
+ registry_domain: str = typer.Argument(
112
+ ..., help="Target registry hostname URL endpoint."
113
+ ),
114
+ ):
115
+ """Authenticate and store login tokens for a specific content-addressed task registry server."""
116
+ creds = parse_extra_args(ctx.args)
117
+
118
+ if not creds:
119
+ typer.echo(
120
+ "Warning: No dynamic credentials flags passed (e.g. --api-key=XYZ).",
121
+ err=True,
122
+ )
123
+
124
+ core.login_registry(
125
+ registry_domain=registry_domain,
126
+ credentials=creds,
127
+ )
128
+ typer.echo(
129
+ f"Registry token persistent mapping written for domain: {registry_domain}"
130
+ )
131
+
132
+
133
+ @registry_cli.command("logout")
134
+ def registry_logout():
135
+ old_registry = core.logout_registry()
136
+
137
+ if old_registry:
138
+ typer.echo(f"Dropped registry credentials successfully from {old_registry}")
139
+ else:
140
+ typer.echo(f"Already logout")
141
+
142
+
143
+ @registry_cli.command("clear")
144
+ def registry_clear():
145
+ core.clear_registry()
qall/cli/resource.py ADDED
@@ -0,0 +1,28 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+
16
+ import qorus.core as core
17
+
18
+ resource_cli = typer.Typer(
19
+ name="resource",
20
+ help="Inspect hardware topologies and execution modalities.",
21
+ no_args_is_help=True,
22
+ )
23
+
24
+
25
+ @resource_cli.command("ls")
26
+ def resource_list():
27
+ """Poll the compute provider to list all accessible CPU, GPU, and physical/emulated QPU hardware."""
28
+ typer.echo("Polling cloud control plane for active available hardware...")
qall/cli/task.py ADDED
@@ -0,0 +1,113 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import os
15
+ import typer
16
+ import os
17
+
18
+ from typing import Optional
19
+
20
+ from qorus.core.runtime import execute_task, ExecutionMode
21
+
22
+ task_cli = typer.Typer(
23
+ name="task",
24
+ help="Manage task execution engine",
25
+ no_args_is_help=True,
26
+ )
27
+
28
+
29
+ @task_cli.command("run")
30
+ def run(
31
+ task_hash: str,
32
+ task_run_id: str,
33
+ artifact_hash: str = typer.Option(
34
+ None,
35
+ "--artifact-hash",
36
+ "-a",
37
+ help="Hash of the artifact to use as input for the task",
38
+ ),
39
+ block_registry: str = typer.Option(
40
+ None,
41
+ "--block-registry",
42
+ "-l",
43
+ help="Local registry directory",
44
+ ),
45
+ daemon_address: str = typer.Option(
46
+ None,
47
+ "--daemon-address",
48
+ "-d",
49
+ help="Daemon gRPC address (default: localhost:50053)",
50
+ ),
51
+ mode: Optional[str] = typer.Option(
52
+ "daemon",
53
+ "--mode",
54
+ "-m",
55
+ help="Execution mode: 'local' for daemonless, 'daemon' for daemon-based",
56
+ ),
57
+ worker_address: Optional[list[str]] = typer.Option(
58
+ None,
59
+ "--worker-address",
60
+ "-w",
61
+ help="Worker gRPC address",
62
+ ),
63
+ ):
64
+ """
65
+ Execute a task. Made for intra-container execution.
66
+ """
67
+
68
+ try:
69
+ execution_mode = ExecutionMode(mode.lower())
70
+ except ValueError:
71
+ typer.echo(
72
+ f"Error: Invalid execution mode '{mode}'. Must be 'local' or 'daemon'.",
73
+ err=True,
74
+ )
75
+ raise typer.Exit(code=1)
76
+
77
+ typer.echo(f"[Runtime] Executing in {execution_mode} mode")
78
+
79
+ if not daemon_address:
80
+ daemon_address = os.environ.get("QORUS_DAEMON_ADDRESS", "127.0.0.1:50053")
81
+
82
+ task_output = execute_task(
83
+ task_hash=task_hash,
84
+ task_run_id=task_run_id,
85
+ daemon_address=daemon_address,
86
+ block_registry=block_registry,
87
+ artifact_hash=artifact_hash,
88
+ mode=execution_mode,
89
+ worker_addresses=worker_address,
90
+ )
91
+
92
+ typer.echo(f"[Runtime] Task done!")
93
+
94
+ if task_output.return_code == 0:
95
+ log = f" Task {task_hash} successful! " + (
96
+ "Output artifact hash: " + task_output.output_artifact_hash
97
+ if task_output.output_artifact_hash
98
+ else "No output (no artifact created)."
99
+ )
100
+ else:
101
+ log = f" Task {task_hash} failed with code {task_output.return_code}."
102
+
103
+ typer.echo("[Runtime]" + log)
104
+
105
+ typer.echo("[Runtime]Task logs:")
106
+ typer.echo("[Runtime]" + "-" * 20)
107
+ typer.echo("[Runtime] stdout:")
108
+ typer.echo(task_output.stdout.replace("\n", "\n[Runtime]\t"))
109
+ typer.echo("[Runtime]" + "-" * 20)
110
+ typer.echo("[Runtime] stderr:")
111
+ typer.echo("[Runtime] " + task_output.stderr.replace("\n", "\n[Runtime]\t"))
112
+
113
+ raise typer.Exit(code=task_output.return_code)
qall/cli/utils.py ADDED
@@ -0,0 +1,45 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from typing import List
15
+
16
+
17
+ def parse_extra_args(args: List[str]) -> dict:
18
+ """
19
+ Parses unkown arbitrary extra CLI options flags into a clean Python dictionary.
20
+ Handles both '--key=value' and '--key value' parameter syntaxes.
21
+ """
22
+ extra_dict = {}
23
+ i = 0
24
+ while i < len(args):
25
+ arg = args[i]
26
+ if arg.startswith("--"):
27
+ # Case A: Syntax option is '--key=value'
28
+ if "=" in arg:
29
+ key, val = arg.split("=", 1)
30
+ extra_dict[key.lstrip("-")] = val
31
+ i += 1
32
+ # Case B: Syntax option is '--key value'
33
+ else:
34
+ key = arg.lstrip("-")
35
+ if i + 1 < len(args) and not args[i + 1].startswith("-"):
36
+ extra_dict[key] = args[i + 1]
37
+ i += 2
38
+ else:
39
+ # Fallback flag default value if standalone
40
+ extra_dict[key] = "True"
41
+ i += 1
42
+ else:
43
+ # Skip invalid positions or unformatted extra strings
44
+ i += 1
45
+ return extra_dict
qall/cli/worker.py ADDED
@@ -0,0 +1,42 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+
16
+ from typing import Optional
17
+
18
+ from qorus.core.runtime import execute_worker
19
+
20
+ worker_cli = typer.Typer(
21
+ name="worker",
22
+ help="Manage worker execution engine",
23
+ no_args_is_help=True,
24
+ )
25
+
26
+
27
+ @worker_cli.command("run")
28
+ def run(
29
+ worker_hash: str,
30
+ port: int = typer.Option(None, "--port", "-p", help="gRPC port for the worker"),
31
+ block_registry: str = typer.Option(
32
+ None,
33
+ "--block-registry",
34
+ "-l",
35
+ help="Local registry directory",
36
+ ),
37
+ ):
38
+ execute_worker(
39
+ worker_hash=worker_hash,
40
+ port=port,
41
+ block_registry=block_registry,
42
+ )
qall/cli/workflow.py ADDED
@@ -0,0 +1,79 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import typer
15
+
16
+ import qorus.core as core
17
+
18
+ workflow_cli = typer.Typer(
19
+ name="workflow",
20
+ help="Manage and control lifecycle workflows.",
21
+ no_args_is_help=True,
22
+ )
23
+
24
+
25
+ @workflow_cli.command("create")
26
+ def workflow_create(
27
+ uri: str = typer.Argument(
28
+ ..., help="Target canonical URI string formatted as 'name:version'."
29
+ ),
30
+ profile: str = typer.Argument(
31
+ ..., help="The resource profile name to apply to this orchestration model."
32
+ ),
33
+ ):
34
+ """Register or update a formal workflow definition inside the cloud provider control plane infrastructure."""
35
+ if ":" not in uri:
36
+ typer.echo(
37
+ "Error: URI must match the 'name:version' format constraint.", err=True
38
+ )
39
+ raise typer.Exit(code=1)
40
+ name, version = uri.split(":", 1)
41
+
42
+ wf = core.create_workflow(
43
+ workflow_name=name, workflow_version=version, profile=profile
44
+ )
45
+ typer.echo(
46
+ f"Workflow reference compiled on provider ecosystem. Endpoint: {wf.endpoint}"
47
+ )
48
+
49
+
50
+ @workflow_cli.command("info")
51
+ def workflow_info(
52
+ workflow_id: str = typer.Argument(
53
+ ..., help="The unique tracking identifier of the target workflow."
54
+ )
55
+ ):
56
+ """Retrieve deep structure info and infrastructure metrics for a registered workflow asset."""
57
+ # Assuming core tracking retrieval logic or printing simple data structures
58
+ typer.echo(
59
+ f"Fetching structural information boundaries for workflow ID: {workflow_id}"
60
+ )
61
+ raise NotImplementedError()
62
+
63
+
64
+ @workflow_cli.command("ls")
65
+ def workflow_list():
66
+ """List all available workflows deployed across the active provider endpoint layout."""
67
+ typer.echo("Listing available architecture workflows...")
68
+ raise NotImplementedError()
69
+
70
+
71
+ @workflow_cli.command("run")
72
+ def workflow_run(
73
+ workflow_id: str = typer.Argument(
74
+ ..., help="The structural tracking ID of the pre-compiled workflow."
75
+ )
76
+ ):
77
+ """Execute a pre-compiled workflow asset that has already been registered on the cloud platform."""
78
+ run_obj = core.run_workflow(workflow_id=workflow_id)
79
+ typer.echo(f"Workflow run triggered execution sequence. Run ID: {run_obj.id}")
@@ -0,0 +1,30 @@
1
+ qiskit-scaleway-worker:
2
+ provider: scaleway
3
+ version: 0.1.0
4
+ hash: xxxxxxxxxxxxxxxxx
5
+ input_formats:
6
+ - qiskit.QuantumCircuit
7
+ perceval-scaleway-worker:
8
+ provider: scaleway
9
+ version: 0.1.0
10
+ hash: xxxxxxxxxxxxxxxxx
11
+ input_formats:
12
+ - perceval.Circuit
13
+ cirq-scaleway-worker:
14
+ provider: scaleway
15
+ version: 0.1.0
16
+ hash: xxxxxxxxxxxxxxxxx
17
+ input_formats:
18
+ - cirq.Circuit
19
+ cirq_local_qc_worker:
20
+ provider: local
21
+ version: latest
22
+ hash: zdpuAu5wZRLMPj8SMBQG5h85zmG6ypYKN8KD7Gg8UThTSCwwC
23
+ input_formats:
24
+ - cirq.Circuit
25
+ qiskit_local_qc_worker:
26
+ provider: local
27
+ version: latest
28
+ hash: zdpuAkXnJzuDVo3DfVxPZ7GnHpbkZXpn9YP7GTFFrqGrzhDYz
29
+ input_formats:
30
+ - qiskit.QuantumCircuit
@@ -0,0 +1,13 @@
1
+ # Copyright 2026 Scaleway
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
@@ -0,0 +1,30 @@
1
+ qiskit-scaleway-worker:
2
+ provider: scaleway
3
+ version: 0.1.0
4
+ hash: xxxxxxxxxxxxxxxxx
5
+ input_formats:
6
+ - qiskit.QuantumCircuit
7
+ perceval-scaleway-worker:
8
+ provider: scaleway
9
+ version: 0.1.0
10
+ hash: xxxxxxxxxxxxxxxxx
11
+ input_formats:
12
+ - perceval.Circuit
13
+ cirq-scaleway-worker:
14
+ provider: scaleway
15
+ version: 0.1.0
16
+ hash: xxxxxxxxxxxxxxxxx
17
+ input_formats:
18
+ - cirq.Circuit
19
+ cirq_local_qc_worker:
20
+ provider: local
21
+ version: latest
22
+ hash: zdpuAu5wZRLMPj8SMBQG5h85zmG6ypYKN8KD7Gg8UThTSCwwC
23
+ input_formats:
24
+ - cirq.Circuit
25
+ qiskit_local_qc_worker:
26
+ provider: local
27
+ version: latest
28
+ hash: zdpuAkXnJzuDVo3DfVxPZ7GnHpbkZXpn9YP7GTFFrqGrzhDYz
29
+ input_formats:
30
+ - qiskit.QuantumCircuit
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.3
2
+ Name: qall
3
+ Version: 0.1.0
4
+ Summary: A python tool for modern hybrid and quantum workflows.
5
+ Requires-Dist: cloudpickle==3.1.2
6
+ Requires-Dist: dataclasses-json>=0.6.7
7
+ Requires-Dist: docker>=7.1.0
8
+ Requires-Dist: grpcio>=1.81.1
9
+ Requires-Dist: keyring>=25.7.0
10
+ Requires-Dist: matplotlib>=3.11.0
11
+ Requires-Dist: pyan3>=2.5.0
12
+ Requires-Dist: pyyaml>=6.0.3
13
+ Requires-Dist: qio>=0.2.2
14
+ Requires-Dist: rustworkx==0.17.1
15
+ Requires-Dist: typer>=0.26.7
16
+ Requires-Dist: lmdb>=2.3.0
17
+ Requires-Dist: retry==0.9.2
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+
21
+ # Hybrid computing as a Service
22
+
23
+ ## Protobuf
24
+
25
+ To re-generate registry client/server API:
26
+
27
+ ```bash
28
+ cd protobuf/
29
+
30
+ make registry
31
+ ```
32
+
33
+ To re-generate daemon client/server API:
34
+
35
+ ```bash
36
+ cd protobuf/
37
+
38
+ make daemon
39
+ ```
40
+
41
+ ## Registry server
42
+
43
+ To test registry server:
44
+ ```bash
45
+ cd qall-registry-server/
46
+
47
+ make test
48
+ ```
49
+
50
+ To build registry server:
51
+ ```bash
52
+ cd qall-registry-server/
53
+
54
+ make build
55
+ ```
56
+
57
+ To start local instructure
58
+ ```bash
59
+ cd infrastructure/local/
60
+
61
+ make start
62
+ ```
63
+
64
+ ## Registry client
65
+
66
+ To install registry client:
67
+ ```bash
68
+ cd qall-registry-client/
69
+
70
+ pip install .
71
+ ```
72
+
73
+ To test registry client:
74
+ ```bash
75
+ cd qall-registry-client/
76
+
77
+ export qall_REGISTRY_URL=localhost:50051; pytest -vvv tests/
78
+ ```
79
+
80
+ ## Daemon server
81
+
82
+ To run daemon server:
83
+ ```bash
84
+ make build
85
+
86
+ qall daemon start
87
+ ```
88
+
89
+ ## Daemon client
90
+
91
+ To install daemon client:
92
+ ```bash
93
+ cd qall-daemon-client/
94
+
95
+ pip install .
96
+ ```
97
+
98
+ ## Scaleway HCaaS client
99
+ TODO
100
+
101
+ ## qall (SDK)
102
+
103
+ To install qall sdk:
104
+ ```bash
105
+ cd qall/
106
+
107
+ pip install .
108
+ ```
109
+
110
+ To push a project to the registry:
111
+ ```bash
112
+ cd qall/
113
+
114
+ pip install .
115
+
116
+ qall registry push examples/basic_hierarchy/my_worflow.py
117
+
118
+ qall daemon start
119
+
120
+ qall daemon run <root hash>
121
+ ```
@@ -0,0 +1,22 @@
1
+ qall/__init__.py,sha256=4zQjELaG-rnReGhj5sZOPw41tIbval0icnDYw54qrs0,1149
2
+ qall/cli/__init__.py,sha256=qY1dcrxE8XuqOxGK8YcTJ0vYOLPv_Bptz53NiqMYQtI,573
3
+ qall/cli/artifact.py,sha256=7dFdeDwPm4AVVXI5p-CeNAgpMlxVeu2tFLBiSSeuUOU,1647
4
+ qall/cli/checkpoint.py,sha256=iLyv9i6DyZmiMES7yo6tcMhyrCLnM-n3xo9EjbpyTJM,753
5
+ qall/cli/config.py,sha256=QhOvMW1MyhvKlujhN_JJtpScFih86I3auM98WqJhHNQ,1487
6
+ qall/cli/daemon.py,sha256=fWXVkzDQadLISz52qNa4HLRG3GW9Ln6QYV5ZAOzwjEM,5828
7
+ qall/cli/log.py,sha256=vyXT2Gx9j1h7DLl3MwR-84GFrjYUtAMKTh14bcO7Qr0,736
8
+ qall/cli/main.py,sha256=dUCXAAZEKjzZ1fFvAC_EFc8SZl3Rvkp8CHQ49sFj93A,4743
9
+ qall/cli/provider.py,sha256=5k6E4sv_Dz0iR4OzI6496td1iq42ZWhRsVlybaaFxRE,1761
10
+ qall/cli/registry.py,sha256=kFXTzfBYTh78b8SeCPNUC85wB4xisJvj5o8fJSuEtC0,4220
11
+ qall/cli/resource.py,sha256=3QcLpKD2qC9D0W76uvBuMC89J2ePHxO8bwb4IqTJrxE,992
12
+ qall/cli/task.py,sha256=pTi3jFzzfjnkOZau1CSgosJYV1Mq-4Ib6e7TAvNVAeM,3310
13
+ qall/cli/utils.py,sha256=f76y3MYeL0TycuTPXTIuz-z8_rZRgePoiejM4QUiWFY,1662
14
+ qall/cli/worker.py,sha256=rguYE8IJw5fJbBfsvlAgqiFSO-wC9ibT3nZ0Qs5-vvY,1188
15
+ qall/cli/workflow.py,sha256=uSWnaD34eEjSUOe3hC0tdXjPk4wwqiqXqz7cLrl9qIw,2727
16
+ qall/data/worker_catalog.yml,sha256=PAuCpQBBFEGzDb1_ESgAzpCyETFfWPFr05bHUBVhues,690
17
+ qall/visualization/__init__.py,sha256=qY1dcrxE8XuqOxGK8YcTJ0vYOLPv_Bptz53NiqMYQtI,573
18
+ qall-0.1.0.data/purelib/worker_catalog.yml,sha256=PAuCpQBBFEGzDb1_ESgAzpCyETFfWPFr05bHUBVhues,690
19
+ qall-0.1.0.dist-info/WHEEL,sha256=a0ta4TK7UKMB0EbDesmi2mn0Djkly7aj_7Tu2R7SseE,81
20
+ qall-0.1.0.dist-info/entry_points.txt,sha256=BqU8hoxdqEfdJJ9hyX20-DUFBHAFa2IEVqkq0Fjk2MM,44
21
+ qall-0.1.0.dist-info/METADATA,sha256=4kZMV7O1iP0OYh1WWpvYu8Fw6iL7HZHH4ZZekD-OyTQ,1718
22
+ qall-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.33
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ qall = qall.cli.main:cli
3
+