Repository navigation
fix(tui): name each row's provider once the pool serves more than one - #392
Merged
MagicalTux merged 2 commits intoSep 15, 2026
Merged
MagicalTux merged 2 commits into
MagicalTux merged 2 commits into
Conversation
The account column beside the name shows `a.type`, which is `oauth` on every subscription row. In a single-provider pool that is at least a distinction from an API key. In a mixed pool it is the one fact the operator already had. What it needed to say instead is which provider the row belongs to, because one person's ChatGPT and Claude subscriptions are usually the same email. A pool holding both lists that address twice, identically, and nothing on the row tells them apart — including the row the operator is about to act on with `s`. Observed on an eight-account pool: three Anthropic, five Codex, two addresses appearing on both sides. So when more than one provider is present the column names the provider, and its width follows the labels actually there, so the longer one is not truncated. A single-provider pool keeps the column it has today, since repeating one name down every row would trade one uninformative column for another. `providerOf` gains its return type on the way past, which takes provider.js from 16 strict-mode diagnostics to 13. Reverting src/ turns two of the four added tests red; the other two are the single-provider guards, which hold either way by design. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jgautheron
added a commit
to jgautheron/teamclaude
that referenced
this pull request
Sep 15, 2026
The account row's width budget reserved an 8-column type cell whatever the column held. Since KarpelesLab#392 a mixed pool writes the provider there, padded to the longest label present, and `Anthropic` is 9, so every row in a mixed pool ran 2 columns past the budget. fitLine then cut the tail: the reset label of the last bar, or the whole `$` tag behind a `⊘ Fable`. It only shows on a row that carries every reserved tag itself, which is how the budget's other slack hid it. The row and the budget now take the width from one helper. Claude-Session: https://claude.ai/code/session_01YCbpDukDtRkSth9MkN7gFQ
jgautheron
added a commit
to jgautheron/teamclaude
that referenced
this pull request
Sep 15, 2026
The account row's width budget reserved an 8-column type cell whatever the column held. Since KarpelesLab#392 a mixed pool writes the provider there, padded to the longest label present, and `Anthropic` is 9, so every row in a mixed pool ran 2 columns past the budget. fitLine then cut the tail: the reset label of the last bar, or the whole `$` tag behind a `⊘ Fable`. It only shows on a row that carries every reserved tag itself, which is how the budget's other slack hid it. The row and the budget now take the width from one helper.
MagicalTux
added a commit
that referenced
this pull request
Sep 20, 2026
* fix(tui): budget the provider column at the width it draws The account row's width budget reserved an 8-column type cell whatever the column held. Since #392 a mixed pool writes the provider there, padded to the longest label present, and `Anthropic` is 9, so every row in a mixed pool ran 2 columns past the budget. fitLine then cut the tail: the reset label of the last bar, or the whole `$` tag behind a `⊘ Fable`. It only shows on a row that carries every reserved tag itself, which is how the budget's other slack hid it. The row and the budget now take the width from one helper. * fix(tui): draw a subscription account's own bars before its first reading The row category keyed on readings alone, so an OAuth account that had not answered a request or a probe yet drew the API-key `Tok`/`Req` pair, and was budgeted with the metered rows: its empty bars sat at a different width from the subscription rows around it. A subscription meters Ses/Wk whether or not it has reported yet, so the category now also follows the auth kind. * feat(tui): mark each pool's current account in a mixed fleet A pool holding Claude and Codex accounts rotates each provider on a cursor of its own, but ► read the manager's single `currentIndex`, which names whichever pool moved last. The other pool showed no current account at all, and switching one side with `s` took the marker away from the other. A mixed pool now marks the account each provider's cursor names, the same answer `currentAccounts` already gives in /teamclaude/status. That can be a shared API-key account, since a key serves either pool. The set is read once per frame. A single-provider pool is unchanged. Attach mode reads the same answer from the status payload. A name alone cannot tell a shared API key from a Codex login at the same address, so the payload now also carries `currentIndexes`, each pool's current account by position in `accounts`. Attach mode prefers it, falls back to the name matched on provider, and against a server that sends neither marks its one cursor as before. * feat(tui): draw a two-provider pool as two panes, side by side A pool holding Claude and Codex accounts is one table today, each row naming its provider, so the table grows by the sum of both pools, and one person's two subscriptions, usually the same address, sit next to each other told apart by a single column. The dashboard now draws one pane per provider, Anthropic on the left and Codex on the right, so the table is as tall as the larger pool. The pane titles take the spacer line the table always had, so the split costs no height. A pane drops the type column, since its title names the provider, reserves route cells only for its own provider's routes, and caps its bars narrower. Each pane is laid out with the per-category budget the full table uses. Width is dealt out in the order a pane spends it: bars wide enough for their reset label, whole names, then wider bars. Both panes reach one step before either starts the next, a step that cannot be met in full is shared in proportion to what each pane still wants, so a pane with three bars and a `⊘ Fable` tag does not cut its names while the other pads. The table splits only when both panes reach the first step and keep every bar the rows draw in one column; otherwise it stays one column, now grouped by provider, which is also the order selection walks. Like the bars in one column, the split can change when a `⊘` tag appears near that width. A Codex plan need not state a five-hour limit. When the Codex accounts in a list have all reported and none states one, the list draws no Ses bar and starts at Wk. An account that has not reported yet keeps the column, so it does not come and go at startup. * docs(usage): describe the two-provider panes and the currentIndexes status field The panes change what an operator sees on a wide terminal and the status payload gains a field, but neither was documented. usage.md now says when the account table splits into one pane per provider (two providers, 127 columns or more, every bar still drawable), that each pool has its own current marker, when the Ses bar is dropped, and what currentIndexes holds. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Mark Karpeles <magicaltux@gmail.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Merged
MagicalTux
added a commit
that referenced
this pull request
Sep 20, 2026
Thirty-six commits since 1.1.20. Several change what a client sees on a failure, so read the first section before upgrading a shared deployment. Behaviour changes #439 a 401 from upstream is no longer relayed to the client: the request fails over like a 403, and an API-key account or an OAuth account with no refresh token leaves rotation (it used to be picked again on every request). With nothing left the client gets the proxy's own error #439 a path under /teamclaude/ that no control route claims — a typo, or the wrong verb — answers 404 locally instead of being forwarded upstream under a fleet credential #429 the synthetic 429's retry-after is the real reset of the windows blocking the request's candidate accounts, not a flat 60s, and the message counts only those candidates; #408 names accounts that need a re-login instead of calling them "at quota" #438 session pins are per conversation (session id plus a digest of the first message), so a session's subagents spread across accounts. `sessions.items[].id` in status is the composite key, load is counted per conversation, and a persisted concurrency cap re-learns #378 a `thread: continue` bound for a per-account third-party upstream is refused with the 400 Anthropic gives, so the client resends the whole conversation; `messageThreads: true` opts a relay out #434 a 429 whose x-codex-* headers show a spent account-wide window holds the account like an Anthropic rejection; a spent model-scoped bucket only moves the request #437 a Codex response head is awaited for five minutes (Anthropic unchanged) #411 idle keep-alive connections are held 120s on both listeners #389 with session distribution on, requests carrying no session id rotate on a cursor of their own instead of all resting on the current account #405 `defaultClientMode` ("mitm" | "base-url") sets what `run` and `env` do without a flag; `--mitm` / `--no-mitm` decide per launch, and in base-URL mode `env` unsets a stale proxy export naming this proxy #439 route `--bucket` is validated; an array `switchThreshold` reads as the default with one line saying so Features #419 an MCP management endpoint at POST /teamclaude/mcp, off unless `proxy.mcp` is "read" or "full"; a named client key is read-only even in full mode, and with no proxy key it serves only this machine #428 per-account `switchThreshold`, a number or a per-bucket table #406 a Claude+Codex pool is drawn as two panes on a wide terminal, each with its own current marker; #392 names the provider in a mixed list; #418 lets the operator arrange the list (`displayOrder`); #376 draws loopback-served accounts last; #435 shows the percentage beside a bar's countdown; #394 shows the running version in the header #430 free Codex rate-limit reset credits in status, the TUI and the dashboard #385 `proxy.terminalOnly` tunnels chatgpt.com so ChatGPT Desktop stays out Fixes #404 a TUI paint can no longer block the proxy (stdout non-blocking, frames dropped while the terminal is behind); #410 a dead terminal no longer takes the proxy with it, and SIGHUP shuts down cleanly #433 token usage is booked from Codex Responses streams #386 #387 #388 the Codex five-hour window is read from the model-scoped family and the usage probe, and extra limits are named from their entries #431 a headerless 429 that follows the request is retried once #432 #439 startup and collaborator log lines reach the TUI's activity pane and log file instead of the covered terminal #415 #403 #439 reload mirrors `priority`, `disabled`, `stripRequestFields` onto the config entry, and a reload during a removal does not re-add it #439 the Host check uses the address actually bound; sx.org calls time out #381 the dashboard polls status before asking for a key #403 session outcome accounting classifies the decoded path; account names in daemon log lines are sanitised Tooling #371 #372 #373 `npm run typecheck` (tsc over the JS sources) in CI, with a strict-mode ratchet: per-file strict diagnostics may not grow past the pre-merge commit (2006 at introduction, 1735 now) #401 docker workflow actions bumped Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The account column beside the name shows
a.type, which isoauthon everysubscription row. In a single-provider pool that is at least a distinction from an API
key. In a mixed pool it is the one fact the operator already had.
What it needed to say instead is which provider the row belongs to — because one
person's ChatGPT and Claude subscriptions are usually the same email. A pool holding both
lists that address twice, identically, and nothing on the row tells them apart, including
the row the operator is about to act on with
s.Observed on an eight-account pool (three Anthropic, five Codex, two addresses on both
sides) — before:
Rows 2 and 3 are the same person's two subscriptions. Rows 1 and 4 likewise. After:
What changed
When more than one provider is present the column names the provider, and its width
follows the labels actually there, so the longer one is not truncated.
A single-provider pool keeps the column it has today — repeating one name down every row
would trade one uninformative column for another.
providerOfgains its return type on the way past, which takesprovider.jsfrom 16strict-mode diagnostics to 13;
tui.jsis unchanged at 225.Tests
npm test1792 pass / 0 fail,npm run lintandnpm run typecheckclean.Reverting
src/turns two of the four added tests red; the other two are thesingle-provider guards, which hold either way by design.
Independent of the other open PRs — different file, no conflict.