Open a dashboard when serve is run at a terminal
`fluksio serve` printed a log stream and nothing else, so watching an engine meant a second terminal running `status --watch`, and stopping or pairing it meant a third. At a terminal it now opens a dashboard: the health and flow overview `status` draws, the recent runs as a table, and the engine's own output in a pane below — which is what the earlier decision against this was protecting, and it is still all there. The engine is a child process running `serve --plain`, not a thread, so it outlives the dashboard: q leaves it running and says so, s and r stop and restart it, c cancels the selected run and e pairs with a portal. An engine already serving this directory is adopted rather than duplicated, and it can be stopped from here only because the pidfile and the token together prove it is this installation's. `--plain` and no terminal both keep the old behaviour, which is what the container and CI run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Hra4ndWMCLU5F3KjUuVAc
This commit is contained in:
@@ -567,11 +567,51 @@ def test_run_and_sweep_take_what_to_sync() -> None:
|
||||
assert parser.parse_args(["sweep", "train"]).sync == []
|
||||
|
||||
|
||||
def test_the_dashboard_runs_the_engine_as_a_child_of_itself(monkeypatch) -> None:
|
||||
"""At a terminal `serve` is a dashboard; the engine is a plain serve.
|
||||
|
||||
Every flag is passed through, so what the child runs with is what serve
|
||||
was asked for — and `--plain` is what stops it opening a second one.
|
||||
"""
|
||||
import sys
|
||||
|
||||
from fluksio import cli
|
||||
from fluksio.tui import child_argv
|
||||
|
||||
argv = child_argv(["serve", "--port", "8123", "--gpus", "1"])
|
||||
assert argv[:3] == [sys.executable, "-m", "fluksio.cli"]
|
||||
assert argv[3:] == ["serve", "--port", "8123", "--gpus", "1", "--plain"]
|
||||
# Already plain: told once, not twice.
|
||||
assert child_argv(["serve", "--plain"])[3:] == ["serve", "--plain"]
|
||||
|
||||
opened: list[str] = []
|
||||
monkeypatch.setattr(
|
||||
"fluksio.tui.run_tui", lambda args: opened.append("tui") or 0, raising=False
|
||||
)
|
||||
monkeypatch.setattr(sys.stdout, "isatty", lambda: True, raising=False)
|
||||
monkeypatch.setattr(sys.stdin, "isatty", lambda: True, raising=False)
|
||||
|
||||
parser = cli._parser()
|
||||
assert cli.cmd_serve(parser.parse_args(["serve"])) == 0
|
||||
assert opened == ["tui"]
|
||||
|
||||
# `--plain` goes past it, which is what the child and every container does.
|
||||
# Nothing else of serve runs here, so it fails on the data directory it is
|
||||
# given rather than opening a dashboard.
|
||||
opened.clear()
|
||||
monkeypatch.setattr(
|
||||
cli, "_data_dir", lambda *a, **k: (_ for _ in ()).throw(SystemExit(3))
|
||||
)
|
||||
with __import__("pytest").raises(SystemExit):
|
||||
cli.cmd_serve(parser.parse_args(["serve", "--plain"]))
|
||||
assert opened == []
|
||||
|
||||
|
||||
def test_a_serving_engine_records_itself_until_it_stops(tmp_path) -> None:
|
||||
"""A pid nobody is running is the same as no pidfile at all."""
|
||||
import os
|
||||
|
||||
from fluksio.cli import PIDFILE, read_pidfile, write_pidfile
|
||||
from fluksio.cli import read_pidfile, write_pidfile
|
||||
|
||||
assert read_pidfile(tmp_path) is None
|
||||
|
||||
@@ -586,7 +626,7 @@ def test_a_serving_engine_records_itself_until_it_stops(tmp_path) -> None:
|
||||
assert read_pidfile(tmp_path) is None
|
||||
|
||||
|
||||
def test_who_holds_the_port_is_told_apart_by_the_token(tmp_path) -> None:
|
||||
def test_who_holds_the_port_is_told_apart_by_the_token() -> None:
|
||||
"""Only this directory's own engine may be reported as already up.
|
||||
|
||||
The token is signed with this directory's secret key, so an engine that
|
||||
|
||||
Reference in New Issue
Block a user