irisandClaude Opus 5 d8570f4d5a Ask each machine about its own limits, not this one about all of them
`ClaudeUsage` read `~/.claude/.credentials.json` on the machine running the
backend and asked the API about that account, once, globally. But a session
runs wherever its setup says, so the numbers on the usage screen belonged
to the backend's account rather than to the account that spent the tokens.

That is not a rounding error in the layout this project is aiming at.
`ai-server` belongs on the host; the host has no `claude` CLI at all and
the VM is a remote. So the screen would have reported "is Claude Code
logged in on this machine?" while every session ran fine on a machine whose
limits nobody could see. It looked correct only because backend and CLI
happen to be the same box today.

Usage is now per machine, asked through the same `Transport` the sessions
use -- `ssh host sh -c 'cat $HOME/...'` for a remote, unchanged for the
local one. `$HOME` is left for the far shell to expand, since a path built
here is this machine's home directory and over ssh that is somebody else's.
Machines with no Claude provider are not asked and get no row: they have no
Claude limits, and a row about them would be a fact about nothing.

The snapshot gains the states it could not say. `available` plus an `error`
string made three different situations look identical, and the one that
suffered was the harmless one: a machine nobody has logged in on is a
decision somebody made, with nothing to fix, and it read as broken.
`notLoggedIn`, `unreachable` and `failed` are now distinct, and which one a
failed read is gets decided in `why_no_credentials` rather than at the call
site.

Supporting changes: `ssh::command` builds a `std::process::Command` that
tokio converts from, so a blocking caller can use the one place that knows
what a correct ssh invocation is; `Transport::capture_blocking` is that
caller's door. The cache is keyed by machine and service rather than by
position, since the set is no longer fixed at startup -- a positional cache
would hand one machine's numbers to another the moment a setup was added.
A cached snapshot still picks up a rename immediately, because the name has
nothing to do with the poll interval.

Verified against a real ssh setup (loopback, per AGENTS.md) with five
machines, all four states seen: local `ok`, loopback-over-ssh `ok`,
unreachable host `unreachable` carrying ssh's own message, a claude-less
machine correctly absent, and -- with the backend's HOME emptied -- local
`notLoggedIn` while the ssh machine still reported real windows, which is
the production shape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VETa8afmpWaYezLCqJhDB8
2026-08-29 06:12:17 -04:00
2026-08-24 20:32:18 -04:00
S
Description
No description provided
27 MiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 54%
Kotlin 43.6%
Shell 2.4%