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:
2026-08-29 14:21:13 +02:00
co-authored by Claude Opus 5
parent c2312e6632
commit a4ea1dd0d6
5 changed files with 456 additions and 2 deletions
+13
View File
@@ -356,6 +356,14 @@ def _free_port(host: str, start: int) -> int:
def cmd_serve(args: argparse.Namespace) -> int:
# At a terminal this is a dashboard with the engine as a child of it. The
# import is here rather than at the top because it is only ever needed on
# that path, and `serve` in a container must not pay for it.
if not args.plain and sys.stdout.isatty() and sys.stdin.isatty():
from fluksio.tui import run_tui
return run_tui(args)
data_dir = _data_dir(args.data_dir, args.shared)
for flag, name in CONCURRENCY_FLAGS.items():
value = getattr(args, flag, None)
@@ -566,6 +574,11 @@ def _parser() -> argparse.ArgumentParser:
metavar="N",
help="python worker processes (default 4, FLOW_MAX_WORKERS)",
)
serve.add_argument(
"--plain",
action="store_true",
help="the log stream rather than the dashboard (the default with no terminal)",
)
serve.add_argument(
"--gpus",
type=_at_least(0),