Documentation
¶
Overview ¶
Package usage exposes provider subscription allowances (Claude, Codex) read from a Redline service running on the same host, mapped onto a DevX-owned wire shape so Redline's model never leaks into DevX clients.
Index ¶
Constants ¶
const ( // DashboardTimeout bounds a dashboard read when the caller's context carries // no deadline; the read is a DB-only query so it should never come close. DashboardTimeout = 5 * time.Second // RefreshTimeout bounds one provider refresh when the caller's context // carries no deadline. Redline allows 10s per upstream fetch. RefreshTimeout = 12 * time.Second )
const DefaultBaseURL = "http://127.0.0.1:7436"
DefaultBaseURL is where Redline listens by default.
const DefaultPollInterval = 30 * time.Second
DefaultPollInterval matches the plan's usage.poll_interval default; Redline's dashboard read is DB-only so this is cheap. cmd/root.go and web.UsageOptionsFromGlobalConfig build their viper/option defaults from this so there is one source of truth for the interval.
const MinPollInterval = 5 * time.Second
MinPollInterval is the floor for usage.poll_interval: anything below this (including a misconfigured value that parses to a tiny duration, like a bare number of nanoseconds) is clamped up rather than hammering Redline.
const RefreshMinSpacing = 15 * time.Second
RefreshMinSpacing is the minimum time between two provider refreshes; extra requests reuse the cached usage.
Variables ¶
var ErrTimeout = errors.New("redline did not respond in time")
ErrTimeout reports that Redline was reachable but did not answer within the call's deadline, which is a different diagnosis from it not running.
ErrUnauthorized reports that Redline rejected the API token.
var ErrUnreachable = errors.New("redline unreachable")
ErrUnreachable reports that the Redline service could not be contacted, which normally means it is not running on this host.
Functions ¶
func DiscoverToken ¶
DiscoverToken resolves the Redline API token: the REDLINE_API_TOKEN environment variable wins, then an explicit tokenFile, then the platform default beside redline.yaml. It also returns the path the token came from (empty for the environment variable) so callers can name it in a "token not found at …" message.
Types ¶
type Client ¶
Client talks to a Redline service over its loopback HTTP API. Prefer NewClient, which validates the base URL before any token is put on the wire.
func NewClient ¶
NewClient validates baseURL and returns a Client for it. An empty baseURL means DefaultBaseURL. Non-loopback hosts are refused unless allowRemote is set, so a mistyped URL cannot ship the Redline bearer token elsewhere, and an allowed remote host must speak https so the token is never sent in the clear. The returned BaseURL keeps only the scheme and host.
type Poller ¶
type Poller struct {
Client *Client
Interval time.Duration
// KeepStaleFor is how long the last good providers survive while Redline is
// unavailable. Defaults to 15 minutes.
KeepStaleFor time.Duration
// Now defaults to time.Now; tests inject a clock.
Now func() time.Time
// OnChange is called whenever the marshalled payload differs from the
// previous one.
OnChange func(Usage)
// Logger receives state transitions and refresh failures. A nil Logger
// discards them, which is what a Bubble Tea TUI needs: stray writes to
// stderr corrupt the display.
Logger *log.Logger
// contains filtered or unexported fields
}
Poller keeps a cached Usage snapshot fresh by polling Redline's dashboard. The zero value is usable except for Client, which must be set.
func (*Poller) Poll ¶
Poll performs a single dashboard read and stores the result. It is single flighted: concurrent callers queue so OnChange can never deliver a payload older than Current(). Only the owner of the Poller should call it; other callers want Current, which never blocks on Redline.
func (*Poller) Refresh ¶
func (p *Poller) Refresh(ctx context.Context) (RefreshResult, error)
Refresh asks Redline to fetch live usage for every known provider and then re-reads the dashboard. It returns Throttled without contacting Redline when a refresh is already in flight or one ran less than RefreshMinSpacing ago.
The returned error joins the per-provider refresh failures; the dashboard re-read still happens and Usage is still populated, so a caller can surface the error while showing fresh-as-possible data.
ctx only aborts the caller's wait: the Redline calls and the cache write run on the poller's own context, so a disconnected browser can never blank the shared state for everybody else.
func (*Poller) Start ¶
Start polls Redline immediately and then every Interval until ctx is done. It returns as soon as the background loop is running; the returned channel is closed when the loop has exited, so Shutdown can wait for an in-flight poll. ctx also becomes the poller's own lifecycle context, which is what Refresh and the loop use for their Redline calls.
Starting an already-started Poller panics: two loops over one cache is a programming error, not a runtime condition.
type Provider ¶
type Provider struct {
ID string `json:"id"`
Provider string `json:"provider"`
Label string `json:"label"`
State ProviderState `json:"state"`
// Error is populated only when State is ProviderError (no usable snapshot).
// Redline's per-account error text can contain filesystem paths, so it is
// deliberately dropped for the ok/stale cases where a usable snapshot exists
// alongside it. Capped at 200 runes.
Error string `json:"error,omitempty"`
Source string `json:"source,omitempty"`
ObservedAt time.Time `json:"observed_at"`
// Primary is the account-scoped window shown without a click; nil when the
// account exposes no account-scoped window at all. When non-nil its Key
// always matches exactly one Windows entry (the labels differ: "5h"/"wk"
// here versus the expanded-view label in Windows).
Primary *Window `json:"primary,omitempty"`
Windows []Window `json:"windows"`
}
Provider is one Redline provider account (for example "claude-main").
type ProviderState ¶
type ProviderState string
ProviderState is one provider account's state. It marshals as a plain JSON string.
const ( // ProviderOK means the snapshot is present and fresh. ProviderOK ProviderState = "ok" // ProviderStale means Redline flagged the snapshot as older than its // max_snapshot_age. ProviderStale ProviderState = "stale" // ProviderError means there is no usable snapshot for this account. ProviderError ProviderState = "error" )
type RefreshResult ¶
type RefreshResult struct {
// Usage is the state after the refresh, or the cached state when throttled.
Usage Usage
// Throttled is true when the call did not contact Redline because another
// refresh is in flight or one ran less than RefreshMinSpacing ago.
Throttled bool
// RetryAfter is how long until a refresh would be accepted; only meaningful
// when Throttled.
RetryAfter time.Duration
}
RefreshResult reports the outcome of Refresh.
type State ¶
type State string
State is the top-level usage state. It marshals as a plain JSON string.
const ( // StateLoading is reported until the first poll completes. StateLoading State = "loading" // StateOK means the last Redline read succeeded. StateOK State = "ok" StateUnavailable State = "unavailable" // StateDisabled means provider usage is turned off by configuration. StateDisabled State = "disabled" )
type Usage ¶
type Usage struct {
State State `json:"state"`
// Message is human-readable text for the unavailable and disabled states.
Message string `json:"message,omitempty"`
// UpdatedAt is when DevX last read Redline successfully.
UpdatedAt time.Time `json:"updated_at"`
Providers []Provider `json:"providers"`
}
Usage is the top-level payload DevX serves to its clients.
Values returned by Poller.Current, Poller.Poll and Poller.OnChange are copies the caller owns: Providers, Windows and Primary may be read, sorted or edited in place without affecting the poller's cache.
type Window ¶
type Window struct {
Key string `json:"key"`
Label string `json:"label"`
Scope string `json:"scope"`
// Remaining is the fraction of the allowance left, 0..1.
Remaining float64 `json:"remaining"`
ResetsAt time.Time `json:"resets_at"`
PeriodSeconds int64 `json:"period_seconds,omitempty"`
ResetInferred bool `json:"reset_inferred,omitempty"`
}
Window is one allowance window (account- or model-scoped).