ap-client 0.5.3.dev5__tar.gz → 0.5.3.dev7__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.
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/PKG-INFO +3 -1
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/irepo_sdk.py +2 -2
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/ossutil_commands.py +176 -44
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/ossutil_io.py +19 -1
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/pyproject.toml +3 -1
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/__init__.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/akpool_commands.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/api.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/cli.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/config.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/dataset_commands.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/dataset_register.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/exporter.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/fs_commands.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/instance_commands.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/managed_ak.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/manifest_view.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/profile_commands.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/repo_read_guard.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/share_api.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/split_publish.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/tbb.py +0 -0
- {ap_client-0.5.3.dev5 → ap_client-0.5.3.dev7}/ap_client/waiter.py +0 -0
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ap-client
|
|
3
|
-
Version: 0.5.3.
|
|
3
|
+
Version: 0.5.3.dev7
|
|
4
4
|
Summary: Agent Platform API Client & CLI
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: click>=8.0.0
|
|
6
7
|
Requires-Dist: irepo-artifact==0.2.1
|
|
7
8
|
Requires-Dist: pyyaml>=6.0
|
|
8
9
|
Requires-Dist: requests>=2.28.0
|
|
9
10
|
Requires-Dist: rich>=13.0.0
|
|
10
11
|
Requires-Dist: typer>=0.9.0
|
|
12
|
+
Requires-Dist: urllib3>=1.26
|
|
11
13
|
Requires-Dist: websockets>=13.0
|
|
12
14
|
Provides-Extra: all
|
|
13
15
|
Requires-Dist: instance-repo[oss]<2.0.0,>=1.3.5.dev3; extra == 'all'
|
|
@@ -1001,9 +1001,9 @@ def load_ossutil_repo() -> tuple[Any, dict]:
|
|
|
1001
1001
|
cfg = get_config(verbose=_api._verbose_override, cluster=_api._cluster_override)
|
|
1002
1002
|
cluster = _cluster_from_headers(cfg.headers)
|
|
1003
1003
|
if not cluster:
|
|
1004
|
-
raise ConfigurationError("Set --cluster / AP_CLUSTER before using ap
|
|
1004
|
+
raise ConfigurationError("Set --cluster / AP_CLUSTER before using ap oss")
|
|
1005
1005
|
if not cfg.token_key:
|
|
1006
|
-
raise ConfigurationError("Set AP_API_KEY before using ap
|
|
1006
|
+
raise ConfigurationError("Set AP_API_KEY before using ap oss")
|
|
1007
1007
|
transport = Transport(cfg.base_url, cfg.token_key, env=_env_header(cfg.headers))
|
|
1008
1008
|
if cfg.verbose:
|
|
1009
1009
|
# STS material must stay redacted even when general HTTP redaction is disabled.
|
|
@@ -4,11 +4,11 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import threading
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Optional
|
|
7
|
+
from typing import Annotated, Optional
|
|
8
8
|
|
|
9
9
|
import typer
|
|
10
10
|
|
|
11
|
-
from .ossutil_io import OssBinding, OssFiles, OssUsageError,
|
|
11
|
+
from .ossutil_io import OssBinding, OssFiles, OssUsageError, safe_error
|
|
12
12
|
|
|
13
13
|
ossutil_app = typer.Typer(
|
|
14
14
|
help="OSS file operations within the server-configured storage prefix", no_args_is_help=True
|
|
@@ -16,7 +16,7 @@ ossutil_app = typer.Typer(
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def register(app: typer.Typer) -> None:
|
|
19
|
-
app.add_typer(ossutil_app, name="
|
|
19
|
+
app.add_typer(ossutil_app, name="oss")
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def _files() -> OssFiles:
|
|
@@ -218,38 +218,67 @@ def oss_cat(
|
|
|
218
218
|
_fail(exc)
|
|
219
219
|
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
221
|
+
Recursive = Annotated[bool, typer.Option("--recursive", "-r")]
|
|
222
|
+
Force = Annotated[bool, typer.Option("--force", "-f", help="Overwrite without confirmation")]
|
|
223
|
+
Update = Annotated[bool, typer.Option("--update", "-u", help="Copy only missing or older targets")]
|
|
224
|
+
Jobs = Annotated[int, typer.Option("--jobs", "--job", "-j", min=1, max=10000)]
|
|
225
|
+
Parallel = Annotated[int, typer.Option("--parallel", min=1, max=10000)]
|
|
226
|
+
BigFileThreshold = Annotated[int, typer.Option("--bigfile-threshold", min=0)]
|
|
227
|
+
PartSize = Annotated[Optional[int], typer.Option("--part-size", min=1)]
|
|
228
|
+
CheckpointDir = Annotated[Optional[Path], typer.Option("--checkpoint-dir")]
|
|
229
|
+
Include = Annotated[Optional[list[str]], typer.Option("--include")]
|
|
230
|
+
Exclude = Annotated[Optional[list[str]], typer.Option("--exclude")]
|
|
231
|
+
OnlyCurrentDir = Annotated[bool, typer.Option("--only-current-dir")]
|
|
232
|
+
DisableIgnoreError = Annotated[bool, typer.Option("--disable-ignore-error")]
|
|
233
|
+
DisableCrc64 = Annotated[bool, typer.Option("--disable-crc64")]
|
|
234
|
+
DisableAllSymlink = Annotated[bool, typer.Option("--disable-all-symlink")]
|
|
235
|
+
ByteRange = Annotated[
|
|
236
|
+
Optional[str], typer.Option("--range", help="Download bytes: START-END, START-, or -END")
|
|
237
|
+
]
|
|
238
|
+
Meta = Annotated[
|
|
239
|
+
Optional[str], typer.Option("--meta", help="Object metadata: header:value#header:value")
|
|
240
|
+
]
|
|
241
|
+
StartTime = Annotated[Optional[int], typer.Option("--start-time", min=0)]
|
|
242
|
+
EndTime = Annotated[Optional[int], typer.Option("--end-time", min=0)]
|
|
243
|
+
EncodingType = Annotated[Optional[str], typer.Option("--encoding-type")]
|
|
244
|
+
Payer = Annotated[Optional[str], typer.Option("--payer")]
|
|
245
|
+
OutputFormat = Annotated[Optional[str], typer.Option("--output-format", "--format")]
|
|
246
|
+
|
|
247
|
+
#: Operand forms that are always local paths. Everything else (including a bare
|
|
248
|
+
#: relative path) addresses the repo-config prefix.
|
|
249
|
+
_LOCAL_PREFIXES = ("./", "../", "/", "~")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _looks_local(value: str) -> bool:
|
|
253
|
+
return value in (".", "..", "-") or value.startswith(_LOCAL_PREFIXES)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _require_remote(value: str, argument: str) -> None:
|
|
257
|
+
if not value.startswith("oss://") and _looks_local(value):
|
|
258
|
+
raise OssUsageError(
|
|
259
|
+
f"{argument} must be an oss:// URI or a repo-relative path; "
|
|
260
|
+
"use get or put for local files"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _require_local(value: str, argument: str) -> None:
|
|
265
|
+
if value.startswith("oss://"):
|
|
266
|
+
raise OssUsageError(f"{argument} must be a local path; use cp for OSS-to-OSS copies")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _transfer(direction: str, src: str, dst: str, ctx: typer.Context) -> None:
|
|
270
|
+
options = ctx.params
|
|
271
|
+
recursive = bool(options.get("recursive"))
|
|
272
|
+
force = bool(options.get("force"))
|
|
273
|
+
update = bool(options.get("update"))
|
|
274
|
+
only_current_dir = bool(options.get("only_current_dir"))
|
|
275
|
+
disable_ignore_error = bool(options.get("disable_ignore_error"))
|
|
276
|
+
disable_crc64 = bool(options.get("disable_crc64"))
|
|
277
|
+
disable_all_symlink = bool(options.get("disable_all_symlink"))
|
|
278
|
+
byte_range = options.get("byte_range")
|
|
279
|
+
meta = options.get("meta")
|
|
280
|
+
start_time = options.get("start_time")
|
|
281
|
+
end_time = options.get("end_time")
|
|
253
282
|
lock = threading.Lock()
|
|
254
283
|
|
|
255
284
|
def progress(path, done, total):
|
|
@@ -275,12 +304,23 @@ def oss_cp(
|
|
|
275
304
|
typer.echo("Enter y or n", err=True)
|
|
276
305
|
|
|
277
306
|
try:
|
|
278
|
-
fmt = _format(output_format)
|
|
307
|
+
fmt = _format(options.get("output_format"))
|
|
279
308
|
if start_time is not None and end_time is not None and start_time > end_time:
|
|
280
309
|
raise OssUsageError("--start-time cannot be later than --end-time")
|
|
281
|
-
|
|
310
|
+
if direction == "download":
|
|
311
|
+
_require_remote(src, "REMOTE")
|
|
312
|
+
_require_local(dst, "LOCAL")
|
|
313
|
+
elif direction == "upload":
|
|
314
|
+
_require_local(src, "LOCAL")
|
|
315
|
+
_require_remote(dst, "REMOTE")
|
|
316
|
+
else:
|
|
317
|
+
_require_remote(src, "SRC")
|
|
318
|
+
_require_remote(dst, "DST")
|
|
282
319
|
files = _files()
|
|
283
|
-
|
|
320
|
+
from .irepo_sdk import _is_verbose
|
|
321
|
+
|
|
322
|
+
files.verbose = _is_verbose()
|
|
323
|
+
_configure(files, options.get("encoding_type"), options.get("payer"))
|
|
284
324
|
files.disable_crc64 = disable_crc64
|
|
285
325
|
files.disable_all_symlink = disable_all_symlink
|
|
286
326
|
files.byte_range = None
|
|
@@ -309,20 +349,24 @@ def oss_cp(
|
|
|
309
349
|
"--meta requires standard content headers or x-oss-meta-* headers"
|
|
310
350
|
)
|
|
311
351
|
files.write_headers[key] = value
|
|
312
|
-
files.multipart_threshold
|
|
352
|
+
files.multipart_threshold = options.get("bigfile_threshold", 100 * 1024 * 1024)
|
|
353
|
+
files.part_size = options.get("part_size")
|
|
354
|
+
checkpoint_dir = options.get("checkpoint_dir")
|
|
313
355
|
if checkpoint_dir is not None:
|
|
314
|
-
|
|
356
|
+
# ctx.params carries the raw value; Typer only converts to Path in the callback.
|
|
357
|
+
files.state_dir = Path(checkpoint_dir)
|
|
315
358
|
result = files.cp(
|
|
316
359
|
src,
|
|
317
360
|
dst,
|
|
361
|
+
direction=direction,
|
|
318
362
|
recursive=recursive,
|
|
319
363
|
overwrite=force,
|
|
320
|
-
jobs=jobs,
|
|
321
|
-
parallel=parallel,
|
|
364
|
+
jobs=options.get("jobs", 3),
|
|
365
|
+
parallel=options.get("parallel", 4),
|
|
322
366
|
progress=progress,
|
|
323
367
|
update=update,
|
|
324
|
-
include=tuple(include or ()),
|
|
325
|
-
exclude=tuple(exclude or ()),
|
|
368
|
+
include=tuple(options.get("include") or ()),
|
|
369
|
+
exclude=tuple(options.get("exclude") or ()),
|
|
326
370
|
only_current_dir=only_current_dir,
|
|
327
371
|
stop_on_error=disable_ignore_error,
|
|
328
372
|
confirm=None if force or update else confirm,
|
|
@@ -335,3 +379,91 @@ def oss_cp(
|
|
|
335
379
|
_emit(result, fmt)
|
|
336
380
|
if result["failed_count"]:
|
|
337
381
|
raise typer.Exit(1)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
@ossutil_app.command("get")
|
|
385
|
+
def oss_get(
|
|
386
|
+
ctx: typer.Context,
|
|
387
|
+
remote: str = typer.Argument(..., help="Remote oss:// URI or repo-relative path"),
|
|
388
|
+
local: str = typer.Argument(".", help="Local file or directory to write"),
|
|
389
|
+
recursive: Recursive = False,
|
|
390
|
+
force: Force = False,
|
|
391
|
+
update: Update = False,
|
|
392
|
+
jobs: Jobs = 3,
|
|
393
|
+
parallel: Parallel = 4,
|
|
394
|
+
bigfile_threshold: BigFileThreshold = 100 * 1024 * 1024,
|
|
395
|
+
part_size: PartSize = None,
|
|
396
|
+
checkpoint_dir: CheckpointDir = None,
|
|
397
|
+
include: Include = None,
|
|
398
|
+
exclude: Exclude = None,
|
|
399
|
+
only_current_dir: OnlyCurrentDir = False,
|
|
400
|
+
disable_ignore_error: DisableIgnoreError = False,
|
|
401
|
+
disable_crc64: DisableCrc64 = False,
|
|
402
|
+
byte_range: ByteRange = None,
|
|
403
|
+
start_time: StartTime = None,
|
|
404
|
+
end_time: EndTime = None,
|
|
405
|
+
encoding_type: EncodingType = None,
|
|
406
|
+
payer: Payer = None,
|
|
407
|
+
output_format: OutputFormat = None,
|
|
408
|
+
):
|
|
409
|
+
"""Download objects to the local filesystem. Requires private-network access."""
|
|
410
|
+
_transfer("download", remote, local, ctx)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
@ossutil_app.command("put")
|
|
414
|
+
def oss_put(
|
|
415
|
+
ctx: typer.Context,
|
|
416
|
+
local: str = typer.Argument(..., help="Local file or directory to upload"),
|
|
417
|
+
remote: str = typer.Argument(..., help="Remote oss:// URI or repo-relative path"),
|
|
418
|
+
recursive: Recursive = False,
|
|
419
|
+
force: Force = False,
|
|
420
|
+
update: Update = False,
|
|
421
|
+
jobs: Jobs = 3,
|
|
422
|
+
parallel: Parallel = 4,
|
|
423
|
+
bigfile_threshold: BigFileThreshold = 100 * 1024 * 1024,
|
|
424
|
+
part_size: PartSize = None,
|
|
425
|
+
checkpoint_dir: CheckpointDir = None,
|
|
426
|
+
include: Include = None,
|
|
427
|
+
exclude: Exclude = None,
|
|
428
|
+
only_current_dir: OnlyCurrentDir = False,
|
|
429
|
+
disable_ignore_error: DisableIgnoreError = False,
|
|
430
|
+
disable_crc64: DisableCrc64 = False,
|
|
431
|
+
disable_all_symlink: DisableAllSymlink = False,
|
|
432
|
+
meta: Meta = None,
|
|
433
|
+
start_time: StartTime = None,
|
|
434
|
+
end_time: EndTime = None,
|
|
435
|
+
encoding_type: EncodingType = None,
|
|
436
|
+
payer: Payer = None,
|
|
437
|
+
output_format: OutputFormat = None,
|
|
438
|
+
):
|
|
439
|
+
"""Upload a local file or directory to OSS."""
|
|
440
|
+
_transfer("upload", local, remote, ctx)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
@ossutil_app.command("cp")
|
|
444
|
+
def oss_cp(
|
|
445
|
+
ctx: typer.Context,
|
|
446
|
+
src: str = typer.Argument(..., help="Remote oss:// URI or repo-relative path"),
|
|
447
|
+
dst: str = typer.Argument(..., help="Remote oss:// URI or repo-relative path"),
|
|
448
|
+
recursive: Recursive = False,
|
|
449
|
+
force: Force = False,
|
|
450
|
+
update: Update = False,
|
|
451
|
+
jobs: Jobs = 3,
|
|
452
|
+
parallel: Parallel = 4,
|
|
453
|
+
bigfile_threshold: BigFileThreshold = 100 * 1024 * 1024,
|
|
454
|
+
part_size: PartSize = None,
|
|
455
|
+
checkpoint_dir: CheckpointDir = None,
|
|
456
|
+
include: Include = None,
|
|
457
|
+
exclude: Exclude = None,
|
|
458
|
+
only_current_dir: OnlyCurrentDir = False,
|
|
459
|
+
disable_ignore_error: DisableIgnoreError = False,
|
|
460
|
+
disable_crc64: DisableCrc64 = False,
|
|
461
|
+
meta: Meta = None,
|
|
462
|
+
start_time: StartTime = None,
|
|
463
|
+
end_time: EndTime = None,
|
|
464
|
+
encoding_type: EncodingType = None,
|
|
465
|
+
payer: Payer = None,
|
|
466
|
+
output_format: OutputFormat = None,
|
|
467
|
+
):
|
|
468
|
+
"""Copy objects within OSS. Use get/put for local transfers."""
|
|
469
|
+
_transfer("copy", src, dst, ctx)
|
|
@@ -245,6 +245,9 @@ class _RoutingBucket:
|
|
|
245
245
|
|
|
246
246
|
|
|
247
247
|
def safe_error(exc: Exception) -> str:
|
|
248
|
+
if isinstance(exc, ImportError):
|
|
249
|
+
# ImportError messages only name the missing module; nothing sensitive is included.
|
|
250
|
+
return f"{type(exc).__name__}: {exc}"
|
|
248
251
|
if isinstance(exc, (OssUsageError, OssOperationError, OSError)):
|
|
249
252
|
return str(exc)
|
|
250
253
|
# oss2 exceptions may stringify signed requests and arbitrary response payloads.
|
|
@@ -274,6 +277,7 @@ class OssFiles:
|
|
|
274
277
|
self.write_headers: dict[str, str] = {}
|
|
275
278
|
self.byte_range = None
|
|
276
279
|
self.disable_all_symlink = False
|
|
280
|
+
self.verbose = False
|
|
277
281
|
if oss_module is None:
|
|
278
282
|
from instance_repo.store.oss import _import_oss2
|
|
279
283
|
|
|
@@ -791,6 +795,7 @@ class OssFiles:
|
|
|
791
795
|
src: str,
|
|
792
796
|
dst: str,
|
|
793
797
|
*,
|
|
798
|
+
direction: str | None = None,
|
|
794
799
|
recursive: bool,
|
|
795
800
|
overwrite: bool,
|
|
796
801
|
jobs: int,
|
|
@@ -805,7 +810,12 @@ class OssFiles:
|
|
|
805
810
|
start_time: int | None = None,
|
|
806
811
|
end_time: int | None = None,
|
|
807
812
|
) -> dict:
|
|
808
|
-
direction
|
|
813
|
+
if direction is None:
|
|
814
|
+
# Library convenience for direct callers; the CLI commands pass the
|
|
815
|
+
# direction explicitly so relative remote paths stay unambiguous.
|
|
816
|
+
direction = direction_for(src, dst)
|
|
817
|
+
elif direction not in ("upload", "download", "copy"):
|
|
818
|
+
raise OssUsageError("direction must be upload, download or copy")
|
|
809
819
|
remote_src = self.resolve(src, directory=recursive) if direction != "upload" else None
|
|
810
820
|
remote_dst = None
|
|
811
821
|
if direction != "download":
|
|
@@ -991,6 +1001,14 @@ class OssFiles:
|
|
|
991
1001
|
iter(items()), transfer, jobs if recursive else 1, stop_on_error
|
|
992
1002
|
):
|
|
993
1003
|
if error:
|
|
1004
|
+
if self.verbose:
|
|
1005
|
+
# Frames only: exception text may embed signed URLs or STS material.
|
|
1006
|
+
import sys
|
|
1007
|
+
import traceback
|
|
1008
|
+
|
|
1009
|
+
sys.stderr.write("Traceback (most recent call last):\n")
|
|
1010
|
+
sys.stderr.writelines(traceback.format_tb(error.__traceback__))
|
|
1011
|
+
sys.stderr.write(f"error: {safe_error(error)}\n")
|
|
994
1012
|
failed.append({"path": name, "error": safe_error(error)})
|
|
995
1013
|
elif copied:
|
|
996
1014
|
completed.append(name)
|
|
@@ -4,12 +4,14 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ap-client"
|
|
7
|
-
version = "0.5.3.
|
|
7
|
+
version = "0.5.3.dev7"
|
|
8
8
|
description = "Agent Platform API Client & CLI"
|
|
9
9
|
readme = { text = "A lightweight Python SDK and command line interface for Agent Platform. It provides helpers for configuring API access and managing templates, datasets, jobs, and groups.", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
dependencies = [
|
|
12
12
|
"typer>=0.9.0",
|
|
13
|
+
"click>=8.0.0",
|
|
14
|
+
"urllib3>=1.26",
|
|
13
15
|
"requests>=2.28.0",
|
|
14
16
|
"rich>=13.0.0",
|
|
15
17
|
"PyYAML>=6.0",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|