Show usage per machine, and say why a machine has none

The server now reports limits per machine, so the screen has to as well:
one card per machine that offers a paid service, named by the machine
first, because these are one account's numbers and which account is decided
by which box ran the session.

It also has to say which of four things happened, and the reason for
splitting them shows up here rather than in the data. A machine nobody has
logged in on is working exactly as somebody set it up, so it reads as a
plain statement in ordinary text -- marking it would be the interface
nagging about a decision already made, and would dilute the marks that do
mean something. Only "couldn't reach it" and "the endpoint refused" are
coloured as faults, and they say different things because they need
different things done. The old screen drew all three in the error colour.

No machine offering a paid service is not an error either: it says so
instead of drawing nothing.

The app also stopped parsing: `available` no longer exists and
`getBoolean` on a missing key throws, so this had to land with the server
change rather than after it. An older backend sending no `state` is read as
"failed" rather than "ok", since an empty card drawn as healthy is the
worse failure.

Looked at running, against five machines: local reporting notLoggedIn with
the backend's HOME emptied, loopback-over-ssh returning real windows beside
it, an unreachable host showing ssh's own message in red, and a machine
with no Claude provider correctly absent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VETa8afmpWaYezLCqJhDB8
This commit is contained in:
irisandClaude Opus 5 committed 2026-08-29 06:18:46 -04:00
1 parent 6166b1f626
commit 206319045d
4 files changed
+116 -24

No files matched your search

@@ -418,9 +418,21 @@ data class UsageWindow(
data class UsageSnapshot(
val provider: String,
val available: Boolean,
/** Stable id of the machine these numbers belong to. */
val setup: String,
/** That machine's current label. */
val setupName: String,
/**
* What came back: "ok", "notLoggedIn", "unreachable" or "failed".
*
* Four rather than a flag, because the screen has to treat them differently. "notLoggedIn" is a
* machine somebody chose not to put an account on -- a fact, not a fault -- while the other two
* are faults worth chasing. Collapsing them made a healthy setup read as broken.
*/
val state: String,
/** Why, for the two states that are faults. Absent otherwise. */
val detail: String?,
val windows: List<UsageWindow>,
val error: String?,
)
/** The backend caches; refreshing more often than its poll interval just re-reads the cache. */
@@ -429,8 +441,12 @@ fun fetchUsage(settings: ServerSettings): List<UsageSnapshot> =
connection.jsonObjects { snapshot ->
UsageSnapshot(
provider = snapshot.getString("provider"),
available = snapshot.getBoolean("available"),
error = snapshot.optString("error").ifEmpty { null },
setup = snapshot.optString("setup"),
setupName = snapshot.optString("setupName"),
// Unknown to an older backend, and unknown is not "fine": defaulting to "ok"
// would draw an empty card as a healthy one.
state = snapshot.optString("state").ifEmpty { "failed" },
detail = snapshot.optString("detail").ifEmpty { null },
windows =
snapshot.getJSONArray("windows").mapObjects { window ->
UsageWindow(