Add fluksio status, and ask for a run's parameters at a terminal

Two halves of the same gap: the CLI could start work but not show you any.

`fluksio status` draws the home screen's top half in a terminal — health and
what is wrong with it, every flow with its state and node count, and the
recent runs and failures under them. `--watch` keeps it there. Rich does the
drawing; it was already installed under fastapi's own CLI, and is named now
because a command depends on it.

`fluksio run` with no parameters at a terminal asks for them, one line per
declared input with its declared value in brackets — so Enter through the lot
is what running the defaults looks like, and an artifact input takes the
`@run:` spelling the engine now resolves. A scripted run is untouched: passing
any parameter, or piping the command, skips the questions, as does --defaults.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019V5bsYGNxcgPs4xXmTPx69
This commit is contained in:
2026-08-25 07:42:09 +02:00
co-authored by Claude Opus 5
parent 93374a310e
commit 4941c2787c
12 changed files with 312 additions and 16 deletions
+50
View File
@@ -255,3 +255,53 @@ def test_ctrl_c_while_waiting_cancels_the_run(monkeypatch) -> None:
args = _parser().parse_args(["run", "train", "--local", "--no-sync"])
assert cli.cmd_run(args, []) == 130
assert cancelled == ["run-1"]
def test_an_artifact_input_may_be_named_rather_than_pasted() -> None:
"""The engine resolves either spelling; the CLI just stops mangling them."""
import json
from fluksio.sdk.cli import _coerce
assert _coerce("@run:123-abc.dataset", "artifact") == "@run:123-abc.dataset"
digest = "sha256:" + "a1" * 32
assert _coerce(digest, "artifact") == digest
# A reference a script already holds still arrives as the object it is.
reference = {"digest": digest, "size": 3}
assert _coerce(json.dumps(reference), "artifact") == reference
def test_the_run_prompt_keeps_declared_values_for_anything_left_blank() -> None:
"""Enter through the lot is what running with the defaults looks like."""
from unittest.mock import patch
from fluksio.sdk.cli import _ask_params
definition = {
"inputs": [
{"spec": {"name": "lr", "dtype": "float"}, "initial": 0.05},
{"spec": {"name": "epochs", "dtype": "int"}, "initial": 10},
]
}
with patch("builtins.input", side_effect=["", ""]):
# Nothing sent: an input the caller leaves out keeps what it declares,
# which is the same contract the browser's dialog has.
assert _ask_params(definition) == {}
with patch("builtins.input", side_effect=["0.01", "50"]):
assert _ask_params(definition) == {"lr": 0.01, "epochs": 50}
def test_the_run_prompt_names_an_answer_of_the_wrong_type() -> None:
from unittest.mock import patch
import pytest
from fluksio.sdk import SyncError
from fluksio.sdk.cli import _ask_params
definition = {"inputs": [{"spec": {"name": "lr", "dtype": "float"}}]}
with patch("builtins.input", side_effect=["fast"]):
with pytest.raises(SyncError, match="lr"):
_ask_params(definition)