Fetch a run's artifacts from the CLI
`save_artifact` had no download counterpart: the run detail listed a run's files and nothing in `fluksio --help` fetched one. `fluksio artifacts RUN` lists them, `fluksio artifacts RUN NAME` writes one — under the name the node saved it as, since the message name is chosen for the graph. The run detail now carries that filename, which it held in the table and did not report. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Hra4ndWMCLU5F3KjUuVAc
This commit is contained in:
@@ -923,6 +923,43 @@ def cmd_runs(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _artifacts(client: Client, args: argparse.Namespace) -> int:
|
||||
"""List a run's files, or write one of them here."""
|
||||
handle = RunHandle(client, args.run_id, client.run(args.run_id))
|
||||
rows = handle.artifacts
|
||||
if not args.name:
|
||||
if not rows:
|
||||
_say("This run produced no artifacts.")
|
||||
return 0
|
||||
for row in rows:
|
||||
named = row.get("filename") or ""
|
||||
_say(
|
||||
f"{str(row['name']):<24} {row['size']:>10} B "
|
||||
f"{row.get('media_type', ''):<24} {named}"
|
||||
)
|
||||
return 0
|
||||
data = handle.download(args.name)
|
||||
# The name it was written under reads better than the message's, which is
|
||||
# chosen for the graph; `--out` beats both.
|
||||
match = next((row for row in rows if row.get("name") == args.name), {})
|
||||
out = Path(args.out or match.get("filename") or args.name)
|
||||
out.write_bytes(data)
|
||||
_say(f"{out} {len(data)} bytes")
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_artifacts(args: argparse.Namespace) -> int:
|
||||
try:
|
||||
with _client_for(args, retries=0) as client:
|
||||
return _artifacts(client, args)
|
||||
except KeyError as exc:
|
||||
return _fail(str(exc.args[0]))
|
||||
except (SyncError, ApiError) as exc:
|
||||
return _fail(str(exc))
|
||||
except httpx.HTTPError as exc:
|
||||
return _unreachable(exc)
|
||||
|
||||
|
||||
def cmd_flavors(args: argparse.Namespace) -> int:
|
||||
"""The named sizes a node can ask for."""
|
||||
try:
|
||||
@@ -1286,6 +1323,21 @@ def add_parsers(subparsers: Any) -> None:
|
||||
with_engine(parser, local=True)
|
||||
parser.set_defaults(func=cmd_runs)
|
||||
|
||||
parser = subparsers.add_parser(
|
||||
"artifacts", help="the files a run produced; name one to download it"
|
||||
)
|
||||
parser.add_argument("run_id")
|
||||
parser.add_argument("name", nargs="?", default="")
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--out",
|
||||
default="",
|
||||
metavar="PATH",
|
||||
help="where to write it (default: the name it was saved under)",
|
||||
)
|
||||
with_engine(parser, local=True)
|
||||
parser.set_defaults(func=cmd_artifacts)
|
||||
|
||||
parser = subparsers.add_parser(
|
||||
"flavors", help="the named resource sizes a node can ask for"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user