Put resources where somebody would look for them

`resources` was reachable from the SDK and the API and nowhere else, and
`node_queued` was published to nothing at all — so a node waiting for a machine
looked exactly like a node that had hung, which is the failure the event was
added for.

The node panel gets a Resources section: a size by name, the numbers for a node
that wants its own, and how long it is expected to take. Picking a flavor drops
the numbers, because saying both is two answers and the engine refuses it.

A queued node draws a neutral dot rather than one of the three status colours —
it is not running, it did not go well, and it did not go wrong; it is idle with
a reason, which the tooltip gives.

And a Workers screen, which is the first UI for any of this: every machine the
engine can reach with what is free of each, what is attached, what a cluster
has been asked for, and the sizes, editable. `fluksio status` grew a line of
the same.

The demo's training node already preferred a GPU worker, which was exactly the
declaration that used to be dropped, so it now says how much of that machine it
takes as well.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6HeySA27EkGANZN95QySW
This commit is contained in:
2026-08-27 09:32:08 +02:00
co-authored by Claude Opus 5
parent 40f8ad378d
commit 7d87dfffdd
12 changed files with 1156 additions and 7 deletions
@@ -42,6 +42,14 @@ type FlowEvent =
source?: ValueSource
}
| { type: "node_started"; node: string }
| {
type: "node_queued"
flow?: string
node: string
run?: string
detail?: string
ts?: number
}
| {
type: "node_executed"
flow?: string
@@ -179,6 +187,14 @@ function connect() {
source: message.source,
})
break
case "node_queued":
// Waiting for a machine, which looks exactly like hung from outside.
// `node_started` overwrites this, so nothing has to clear it.
liveStore.setStatus(message.node, {
status: "queued",
detail: message.detail,
})
break
case "node_started":
liveStore.setStatus(message.node, { status: "running" })
break