.TH CMDUSE 1 "2026-10" "cmd-usage 0.7.8" "User Commands"
.SH NAME
cmduse \- live Command Code (commandcode.ai) usage dashboard and reporter
.br
ocuse \- OpenCode Go/Zen usage (Go's usage endpoint plus local opencode data)
.br
arkuse \- Volcengine ARK Agent Plan usage in AFP (control\-plane meters plus local opencode data)
.SH SYNOPSIS
.B cmduse
[\fIOPTIONS\fR]
.br
.B cmduse \-1
[\fIOPTIONS\fR]
.br
.B cmduse daily
[\fB\-\-days\fR \fIn\fR] [\fB\-\-json\fR] [\fB\-\-csv\fR] [\fB\-\-local\fR]
.br
.B cmduse hourly
[\fB\-\-hours\fR \fIn\fR] [\fB\-\-json\fR] [\fB\-\-csv\fR] [\fB\-\-local\fR]
.br
.B cmduse model
[\fB\-\-days\fR \fIn\fR | \fB\-\-since\fR \fIISO\fR]
[\fB\-\-json\fR] [\fB\-\-csv\fR]
.br
.B cmduse session
[\fB\-\-days\fR \fIn\fR | \fB\-\-since\fR \fIISO\fR]
[\fB\-\-json\fR] [\fB\-\-csv\fR]
.br
.B cmduse models
[\fB\-\-json\fR] [\fB\-\-gated\fR]
.br
.B cmduse plans
[\fB\-\-json\fR]
.br
.B cmduse statusline
[\fB\-\-json\fR]
.br
.B cmduse mcp
.br
.B cmduse config set
\fIkey\fR=\fIvalue\fR [\fIkey\fR=\fIvalue\fR ...]
.br
.B cmduse \-V
.br
.B cmduse \-h
.br
.B ocuse
[\fIOPTIONS\fR]
.SH DESCRIPTION
.B cmduse
is a terminal client for the Command Code (commandcode.ai) metered AI API. It
fetches the account's plan, remaining monthly credits, and the 5\-hour and
weekly rolling spending windows, and renders them as a live in\-place
dashboard. It also produces account\-wide usage reports (all harnesses, via
the usage API), offline reports from local CLI logs, a template\-driven
statusline for prompts, and a listing of live models. The same crate also
ships a second binary,
.BR ocuse ,
which tracks OpenCode Go and OpenCode Zen; it shares this manual's
presentation and is documented under
.BR OCUSE .
A third binary,
.BR arkuse ,
tracks a Volcengine ARK Agent Plan in the plan's own unit (AFP) and is
documented under
.BR ARKUSE .
.PP
Authentication is read from
.IR ~/.commandcode/auth.json ,
the login state written by the official Command Code CLI (\fBcmd login\fR).
The file is a JSON object containing an \fBapiKey\fR field. There is no
separate cmduse login; without that file every network command fails with an
error naming the missing key.
.SH EXIT STATUS
.TP
0
Success.
.TP
1
Runtime failure (network, auth, config write, report fetch).
.TP
2
Argument parse error; a message was printed to stderr.
.SH SUBCOMMANDS
With no subcommand, \fBcmduse\fR runs the live dashboard in watch mode. With
\fB\-1\fR it fetches once and prints.
.TP
.B daily
Account usage by UTC day for the last \fI\-\-days\fR N days (default 7, max
365, today included). Account\-wide means all harnesses (CLI, IDE, web,
API) that spent against the same account. Days are derived by fetching the
cumulative summary at each UTC midnight and differencing adjacent
boundaries; requests are pooled 8 at a time in parallel. With
\fB\-\-local\fR it instead reads only this machine's CLI sessions from
.IR ~/.commandcode/projects
(offline, no API key required, misses other harnesses). Days are bucketed
in UTC unless \fB\-\-tz\fR is given, which applies to both the API and the
local path. \fB\-\-json\fR emits one object
\fI{"scope","total","days"}\fR where \fIdays\fR maps each date to its totals.
\fB\-\-csv\fR emits
\fIday,requests,tokens_in,tokens_out,cache_read,cache_write,cost_usd\fR
with a final \fItotal\fR row.
.TP
.B hourly
Same cumulative\-differencing method as \fBdaily\fR but over hour
boundaries (UTC unless \fB\-\-tz\fR) for the last \fI\-\-hours\fR N hours
(default 24, max 168). \fB\-\-local\fR reads CLI\-log hour buckets offline
and honors \fB\-\-tz\fR. \fB\-\-json\fR emits one object
\fI{"scope","source","hours"}\fR; \fB\-\-csv\fR emits
\fIhour,requests,tokens_in,tokens_out,cost_usd\fR.
.TP
.B model
Local usage grouped by model, read from
.IR ~/.commandcode/projects
(offline; the usage API exposes no per\-model split). All\-time by default:
\fB\-\-days\fR \fIn\fR or \fB\-\-since\fR \fIISO\fR bounds the window, and
\fB\-\-json\fR reports \fIsource\fR + \fIsince\fR alongside \fImodels\fR.
\fB\-\-json\fR and \fB\-\-csv\fR supported; the \fITokens\fR column sums
input, output, cache read, and cache write.
.TP
.B session
Local usage grouped by project/session directory, same offline file.
\fB\-\-json\fR and \fB\-\-csv\fR supported.
.TP
.B models
Live model list from the provider endpoint
.I GET https://api.commandcode.ai/provider/v1/models
\-\- the same source the account plan gating uses. Unfiltered by plan
unless \fB\-\-gated\fR is given, in which case only the current plan's
allowed models are shown (see the OPTIONS entry for \fB\-\-gated\fR).
Text rows are sorted by display name as
.IR "NAME CONTEXT ID" ,
where CONTEXT is a human K/M count or "\-" when the API omits it.
\fB\-\-json\fR prints one JSON object per model per line with keys id, name,
context_length, owned_by.
.TP
.B plans
Print the plan comparison table (name, price, monthly credits, 5\-hour and
weekly caps), marking the current plan with a leading "*" matched by exact
display name, so "individual\-goat" does not mark the Go row. The current
plan is fetched best\-effort; with no key or network no row is marked.
For the JSON form, see \fB\-\-json\fR under OPTIONS.
.TP
.B statusline
Fetch once and print a single compact line (no trailing newline) built from
the configured statusline template. Suitable for shell prompts, tmux
status, and headless scripts. On any API error prints
.I "cmduse: <error>"
to stdout and exits 1. \fB\-\-json\fR prints one JSON object:
plan, monthlyRemaining, monthlyCap, fiveHourUsed, fiveHourCap, weeklyUsed,
weeklyCap (all dollars as JSON numbers; plan is the human name such as
"GOAT").
.TP
.B mcp
Run an MCP (Model Context Protocol) stdio server: newline\-delimited
JSON\-RPC on stdin/stdout, diagnostics on stderr. Exposes seven tools \(
usage, plans, models, daily, hourly, session, model \) whose output is the same
text the corresponding subcommands print. Tool execution errors are returned as
results with isError true, not JSON\-RPC errors. Intended for MCP hosts
such as Zed's context_servers ("command": "cmduse", "args": ["mcp"]).
.TP
.B config set \fIkey\fR=\fIvalue\fR ...
Merge the given settings into
.I ~/.config/cmd\-usage/config.json
(or
.IR $XDG_CONFIG_HOME/cmd\-usage/config.json )
and print the resulting values. Keys and valid ranges:
.RS
.TP
interval=\fIs\fR
refresh seconds, 1\(en86400.
.TP
width=\fIn\fR
bar width, 5\(en200.
.TP
burst_on=\fIbool\fR
show the spend\-burst sparkline row in watch mode (default true).
.TP
burst=\fIn\fR
sparkline sample count, 5\(en240 (default 40).
.TP
sl=\fIstring\fR
statusline template, see STATUSLINE.
.TP
sl_colors=\fIbool\fR
ANSI colors in the statusline (default true).
.TP
sl_ascii=\fIbool\fR
use "#\-" bars instead of the "\[ha]"/dash glyphs in the statusline (default
false).
.RE
Unknown keys, non\-numeric values, and out\-of\-range values abort with a
message and exit 2; nothing is written.
.SH OPTIONS
Options accept \fB\-\-name value\fR, \fB\-\-name=value\fR, and an attached short
value (\fB\-i30\fR, \fB\-w40\fR).
.TP
.B \-1, \-\-once
Fetch once, print, exit. Without this, watch mode runs forever until
Ctrl\-C. With \fB\-\-json\fR it prints one JSON object (plan, status,
periodEnd, monthlyCredits, monthlyCap, purchasedCredits, freeCredits,
fiveHour, weekly, summary, error) instead of the text dashboard.
.TP
.B \-p, \-\-plain
No ANSI colors and no live redraw (used with \fB\-1\fR for scripts/pipes).
In watch mode it keeps the plain text render but still redraws in place.
Colors are also dropped automatically on a non\-terminal stdout or when
\fBNO_COLOR\fR is set.
The plain render carries the same fields as the colored dashboard: period end,
monthly quota (remaining / cap), a Monthly allowance row with its
"$X by now" pro\-rata figure, "window N% elapsed" on every window row, and the
"on pace to hit cap in ..." burn\-rate warning when the current rate reaches
the cap before the window resets.
.TP
.B \-i, \-\-interval \fIdur\fR
Refresh interval (1\(en86400 seconds). Accepts a unit suffix:
.I 30s ", " 5m ", " 1h ", " 2d
(bare number = seconds). Overrides config \fBinterval_secs\fR.
.TP
.B \-w, \-\-bar\-width \fIn\fR
Progress bar width (5\(en200). Overrides config \fBbar_width\fR.
.TP
.B \-b, \-\-bursts \fIn\fR
Show the spend\-burst sparkline for this run with N samples (5\(en240);
implies it on regardless of config.
.TP
.B \-\-tz \fI\(+-HH:MM\fR
.BR daily " / " hourly :
bucket by this fixed UTC offset instead of UTC. Applies to the account API
path and the \fB\-\-local\fR log path alike. Accepts
.I +05:30 ", " -08:00 ", " +0530 ", " -8 .
.TP
.B \-\-days, \-\-last, \-l \fIn\fR
.BR daily : number of days back (1\(en365).
.TP
.B \-\-hours \fIn\fR
.BR hourly : number of hours back (1\(en168).
.TP
.B \-\-json
Machine\-readable output where the subcommand supports it. Conflicts with
\fB\-\-csv\fR (supplying both exits 2).
.TP
.B \-\-csv
CSV output for \fBdaily\fR, \fBhourly\fR, \fBmodel\fR, and \fBsession\fR.
Fields containing a comma, quote, or newline are quoted per RFC 4180.
.TP
.B \-\-gated
.BR models :
keep only models the current plan may use. Uses the plan id plus purchased
and free credits; purchased/free credits unlock everything, an unknown plan
shows everything. The gate is a close approximation of the API's (the
server enforces the real limit), so a misclassification could show or hide
one model. With \fB\-\-json\fR it does not filter: every model is emitted
with additional \fBallowed\fR (bool) and \fBreason\fR (string) fields. If the
bundled gating snapshot is older than 30 days a warning is printed to stderr.
.TP
.B \-\-dismiss\-update
Acknowledge the current update notice: records the newest crates.io version
(same as
.BR "config set dismissed_update=<ver>" )
and exits. The notice stays hidden until an even newer version appears.
.TP
.B \-W, \-\-watch
Force watch mode (the default). Conflicts with
.BR \-1 ;
supplying both exits 2.
.TP
.B watch
Bare\-word alias for
.BR \-W .
.TP
.B \-\-local
.BR daily " / " hourly :
read only this machine's CLI logs; no network.
.TP
.B \-V, \-\-version
Print \fBcmduse <version>\fR and exit 0.
.TP
.B \-h, \-\-help
Print usage and exit 0. Accepts "help" as a bare word too.
.TP
.B config
Config subcommand; see SUBCOMMANDS.
.SH THE LIVE DASHBOARD
With no subcommand, \fBcmduse\fR renders a frame and rewrites it in place
every interval. All line content is clipped to the terminal width and the
frame never exceeds what the window holds, so it cannot wrap or scroll
during normal operation.
.SS Layout (full mode)
The frame is, top to bottom:
.RS
.nf
Command Code Usage . GOAT . active
Period ends 2026-09-27
Credits $44.15 / $70.00 monthly . $0.00 purchased . $0.00 free
Usage windows
Monthly 36.9% <bar> $25.85 / $70.00 . resets in 18d 0h . window 42% elapsed . $29.40 by now
5-hour 11.1% <bar> $1.55 / $14.00 . resets in 2h 22m . window 59% elapsed
Weekly 55.7% <bar> $19.61 / $35.00 . resets in 1d 18h . window 75% elapsed
This billing period
Requests 2.7K . Cost $26.63 . Tokens 402.1M in / 721.2K out . Success 100%
.fi
.RE
When enabled and there was nonzero spend in the last samples, an extra
spend\-burst row precedes the last (status) line.
.PP
Line semantics:
.TP
Header
Plan display name (see below) and subscription status.
.TP
Period ends
The UTC billing cycle end date, from the subscription's
\fBcurrentPeriodEnd\fR. Exactly the first 10 characters (YYYY\-MM\-DD).
.TP
Credits
Remaining monthly credits, purchased credits, and free credits. The cap
shown is from the local plan table (see PLAN TABLE), not from the API.
.TP
Usage windows
Three rows. Bar fill is used/cap; the percentage is used/cap; "used / cap"
is in dollars; "resets in" is wall time until the window rolls;
"window N% elapsed" is time since the window start divided by its length.
Monthly also carries "$X by now": the monthly cap pro\-rated to that elapsed
share (cap x elapsed%), i.e. what an even burn would have consumed by this
point \- compare it against the actual used figure. It is Monthly\-only: the
5\-hour and weekly caps are throttles rather than budgets, so a pro\-rata
figure there would read as a spend target.
Monthly's used is derived: monthly cap minus remaining credits. Its cap and
period come from the plan table and the subscription period. 5\-hour and
weekly data (used, cap, resetAt) come verbatim from the API
\fBwindowLimits\fR response and are never derived.
.TP
On pace warning
When a window is at least 10% elapsed AND the current spend rate
(used since window start, divided by seconds since window start) projects
hitting the cap before the window resets, the row gains
\fI. on pace to hit cap in <Xh Ym>\fR. Under 10% elapsed no projection is
shown (a flat extrapolation from minutes of data is noise). The projection
assumes a flat spend rate; bursty sessions shift it.
.TP
This billing period
Cumulative request count, cost, input/output tokens (human K/M compact) and
success rate, from the usage summary endpoint.
.SS Window math (exact)
A window of length L seconds ends at instant R (epoch ms, from the API or,
for monthly, the subscription period end parsed as ISO 8601 UTC). Its start
is R \- L. Elapsed percent is
.IR "(now \- start) / L ",
clamped to 0\(en100, rounded to a whole percent. "Resets in" is R \- now,
rendered as "Nd Nh" when a day or more, "Nh Nm" when an hour or more, "Nm"
when a minute or more, else "<1m". When now >= R it renders "resetting..."
(the next fetch carries the fresh window). Pace ETA is
.IR "used / (now \- start) ",
giving a dollars\-per\-second rate; seconds\-to\-cap is
.IR "(cap \- used) / rate";
it is shown only when that lands before R and the 10% gate passes. ISO
dates parse as UTC when suffixed with Z or lacking a suffix, and honor an
explicit +HH:MM or \-HH:MM (or basic +HHMM) offset by shifting the civil
reading; a time with no offset is read as UTC.
.SS Compact mode
The terminal size is re\-queried every refresh. When the window is smaller
than 40 columns by 16 rows the full frame is replaced by a single content
line:
.RS
.nf
GOAT $44.15/$70.00 . 5h $1.55
.fi
.RE
followed by the status line. When the window is resized back over the
threshold the full frame returns, in place. Even a very small window (a few
columns) keeps redrawing the clipped compact line stably; there is no exit
and no shredding.
.SS Status line and countdown
The final frame line is the status line:
.I "refreshing every <N>s . ctrl-c to quit"
Between refreshes, each second the line rewrites in place as
.I "refreshing every <N>s . next refresh in <K>s . ctrl-c to quit"
.IR "K" " = N\-1 .. 1".
Both lines are clipped to the terminal width if needed; a line exactly at
the width is legal (no wrap), longer content is truncated at the width.
.SS Spend\-burst sparkline
On by default; disabled with
.BR "config set burst_on=false" .
It is one row, labeled
.I "spend bursts (<N>s samples) "
followed by the history rendered as an 8\-level bar chart. Each bar is the
dollar delta of the 5\-hour window's cumulative used between two consecutive
refreshes, so a tall bar means spend during that interval and a flat line
means idle. Sample count is config \fBburst_samples\fR (default 40) or
\fB\-b\fR on the command line; at a 5s interval 40 samples is about 3
minutes. The row is hidden while every retained delta is zero, so idle
screens stay quiet. History is session\-only: it starts empty each run and
never shows spend from earlier sessions. Zero scale renders an all\-low
row.
.SH PLAN TABLE
Plan detection lowercases the API plan id and matches substrings in this
order: "enterprise" -> Enterprise, "provider" -> Provider,
"team" -> Team Pro, "max" with "20" -> Max 20x, "max" without
-> Max 10x, "goat" -> GOAT, "pro" -> Pro, "go" -> Go,
anything else -> Free.
.PP
Monthly credit pool by plan (single shared pool, verified empirically; the
docs' per\-model allowances are not what the API meters):
.IP Go
$10
.IP GOAT
$70
.IP Pro
$80
.IP Max 10x
$150
.IP Max 20x
$300
.IP Team Pro
$40
.IP Enterprise / Provider
none (pay\-as\-you\-go; no monthly cap row)
.PP
The 5\-hour and weekly caps (e.g. $14 / $35 on GOAT) are NOT in this table;
they come from the API
.B windowLimits
response and must not be derived client\-side.
.SH STATUSLINE
The statusline template supports the placeholders
.I {plan} {credits} {cap} {credits_bar} {5h_bar} {5h_pct} {5h_used} {5h_cap} {5h_eta}
.I {wk_bar} {wk_pct} {wk_used} {wk_cap} {wk_eta}
. Unknown placeholders are dropped; an unclosed brace passes through;
multi\-line templates are allowed. \fBsl_colors=false\fR strips ANSI after
rendering; \fBsl_ascii=true\fR renders bars with "#" and "\-" instead of the
box glyphs. {credits} is remaining monthly credits, {cap} the plan monthly
cap. {5h_eta} and {wk_eta} render the pace warning text ("on pace to hit cap
in 2h 3m") or an empty string when no warning applies. The same template
engine renders both the statusline subcommand and any statusline template in
a shell prompt.
.SH API ENDPOINTS
All requests go to
.IR https://api.commandcode.ai ,
with header
.I Authorization: Bearer <apiKey>
and Accept: application/json. Endpoints:
.IP "GET /alpha/billing/subscriptions"
plan id, status, current period start/end.
.IP "GET /alpha/billing/credits"
credits object (monthly/purchased/free) and windowLimits (fiveHour, weekly;
each with used, cap, resetAt in ms).
.IP "GET /alpha/usage/summary"
cumulative totals: count, cost, tokens in/out, success rate.
.IP "GET /alpha/usage/summary?since=<ISO>"
same shape, cumulative since the ISO instant (used by daily/hourly
differencing).
.IP "GET /provider/v1/models"
live model list; each entry has id, and may have name, context_length,
owned_by.
.PP
Transient failures (TLS handshake drops, timeouts, EOF, 429, and any 5xx)
are retried up to 5 times with an exponential backoff (1s base, 8s cap,
jitter); a hard 15s per\-attempt timeout applies. A 429's numeric
\fBRetry\-After\fR is honored (capped at 60s) in place of the computed backoff. A watch\-mode fetch that
still fails renders a minimal error frame:
.RS
.nf
Command Code Usage . fetch failed
error: <joined per-endpoint errors>
retrying on next refresh
.fi
.RE
and the next interval retries; no fake zero data is shown.
.SH FILES
.TP
.I ~/.commandcode/auth.json
Read; API key. Written by the Command Code CLI.
.TP
.I ~/.commandcode/projects
Read; per\-project CLI usage logs used by \fBdaily\fR/\fBhourly\fR
\fB\-\-local\fR, \fBmodel\fR, \fBsession\fR. JSON lines.
.TP
.I ~/.cache/cmd\-usage/last\-check
Written; JSON recording the last update\-check time and newest known version.
Deleting it forces a fresh check on the next run.
.TP
.I ~/.config/cmd\-usage/config.json
Config, or
.I $XDG_CONFIG_HOME/cmd\-usage/config.json
when XDG_CONFIG_HOME is set. Missing/corrupt files silently fall back to
defaults (a corrupt file prints a warning to stderr). Keys:
.RS
.TP
interval_secs
integer 1\(en86400, default 5.
.TP
bar_width
integer 5\(en200, default 20.
.TP
burst_enabled
boolean, default true.
.TP
burst_samples
integer 5\(en240, default 40.
.TP
notify_on_cap
boolean, default true. In watch mode fire a desktop notification once when a
window crosses into overflow.
.TP
statusline_template
string, default "{plan} {credits}/{cap} . 5h {5h_bar} . wk {wk_bar}".
.TP
statusline_colors
boolean, default true.
.TP
statusline_ascii
boolean, default false.
.TP
check_updates
boolean, default true. When false, never contact crates.io to check for a
newer \fBcmd\-usage\fR.
.TP
dismissed_update
version string or null. Suppresses the update notice for exactly this
version; a newer release shows again.
.RE
Unknown keys are preserved on config set (the file is rewritten from the
merged struct, so unknown keys are actually dropped on the next
.I "config set"
write; hand\-editing extra keys is tolerated until a config set rewrites the
file).
.SH ENVIRONMENT
.TP
.B XDG_CONFIG_HOME
Base for the config file when set.
.TP
.B HOME
Base for auth, projects, config (fallback), and cache paths when
XDG_CONFIG_HOME is unset.
.TP
.B NO_COLOR
When set to a non\-empty value, ANSI colors are suppressed (no\-color.org).
Colors are also suppressed automatically when stdout is not a terminal, so
piping a report or `cmduse \-1` never leaks SGR escapes.
.SH EXAMPLES
.nf
cmduse # live dashboard, 5s refresh
cmduse -1 # one shot
cmduse -1 -p # plain one shot (scripts)
cmduse -i 30 -w 40 # slower refresh, wider bars
cmduse -b 120 # sparkline with 120 samples
cmduse daily --days 14 # account usage, two weeks
cmduse daily --local # this machine only
cmduse hourly --hours 6
cmduse models
cmduse statusline
cmduse config set interval=10 burst_on=false
.fi
.SH BUGS / NOTES
Rendering edge cases were observed against real terminals:
.IP \(bu
A frame taller than the terminal scrolls on refresh; that is why watch mode
switches to compact mode under 40x16 and clips every line to the width.
.IP \(bu
Never add a trailing newline after the frame's last line and never write a
bare newline at the very bottom row of the window; both desync the in\-place
redraw in real terminals.
.IP \(bu
Account and local dailies/hourlies bucket in UTC by default; pass
\fB\-\-tz\fR to bucket by a fixed offset. Without it, a session near a UTC
midnight boundary splits across two day rows.
.IP \(bu
If the API returns a plan id not in the local plan table, the dashboard labels
it "Free" with no monthly cap and prints a one\-time warning to stderr; update
\fIcore/plans.json\fR to teach it the new plan.
.SH OCUSE
The same crate also ships a second binary,
.BR ocuse ,
which tracks OpenCode Go (the Go subscription) and OpenCode Zen
(pay\-as\-you\-go) with the same CLI shape as \fBcmduse\fR. OpenCode Go
exposes the console's own usage endpoint,
.IR GET\ /zen/go/v1/usage ,
which reports the three account meters \(em
.I rolling
(5\-hour),
.IR weekly ,
and
.I monthly
\(em each a percent of its limit and a reset instant, so
.B ocuse
reads those for its window rows (the credential from
.IR $OCUSE_API_KEY ", " $OPENCODE_API_KEY ,
else the live credential opencode itself is using \(em
.B credential
in opencode's store, where v2 and
.B /connect
write it \(em with the legacy
.I auth.json
as the fallback for a v1 install).
It reports what actually ran on this machine from opencode's own store for
everything the endpoint does not carry \(em the per\-model table,
\fBdaily\fR/\fBhourly\fR, tokens \(em priced by opencode itself, against the
token rates and per\-model limits the OpenCode docs publish (the generated
catalogue
.IR core/zen.json ).
The endpoint's figures win wherever both exist: the server's 5\-hour window is
a block with its own reset, its week is anchored to Monday 00:00 UTC, its month
is the subscription anniversary, and it meters with its own price basis, none
of which is reproducible from the store. With no key, no network, or
\fB\-\-no\-api\fR, the windows fall back to a local estimate and the dashboard
labels them as such \(em and names the reason under the \fBGO\fR heading when
there is one (\fIusage API: HTTP 401 api_key_expired\fR), because the estimate
alone cannot tell a missing key from an expired one. The same line rides in
\fB\-\-json\fR as \fIaccountError\fR (\fBnull\fR when the endpoint answered, and
for \fB\-\-no\-api\fR, which is a choice rather than a fault). An expired Go
key is not renewed by signing in again: it has to be replaced in the OpenCode
console.
.PP
Go meters per model: each model has a monthly dollar limit, and its 5\-hour,
weekly, and monthly windows are fixed shares of that limit (20%, 50%, and
100%). Go ships as two plans that share one token price list but grant
different per\-model limits: Go and Go Plus. Neither the store nor the usage
endpoint names the plan the account is on, so the plan is explicit
(\fB\-\-plan\fR) and defaults to
.BR go .
.SS ocuse synopsis
.B ocuse
[\fIOPTIONS\fR]
.br
.B ocuse \-1
[\fB\-\-json\fR] [\fIOPTIONS\fR]
.br
.B ocuse model
[\fIid\fR] [\fB\-\-since\fR \fIISO\fR] [\fB\-\-json\fR | \fB\-\-csv\fR] [\fIOPTIONS\fR]
.br
.B ocuse daily
[\fB\-\-days\fR \fIn\fR] [\fB\-\-json\fR | \fB\-\-csv\fR] [\fIOPTIONS\fR]
.br
.B ocuse hourly
[\fB\-\-hours\fR \fIn\fR] [\fB\-\-json\fR | \fB\-\-csv\fR] [\fIOPTIONS\fR]
.br
.B ocuse session
[\fB\-\-since\fR \fIISO\fR] [\fB\-\-json\fR | \fB\-\-csv\fR]
.br
.B ocuse models
[\fB\-\-gated\fR] [\fB\-\-json\fR]
.br
.B ocuse plans
[\fB\-\-json\fR]
.br
.B ocuse statusline
[\fB\-\-json\fR] [\fIOPTIONS\fR]
.br
.B ocuse mcp
.br
.B ocuse \-V
.br
.B ocuse \-h
.SS ocuse commands
.TP
.B (no command)
Run the live dashboard in watch mode: a frame redrawn in place every 10
seconds (colour, severity gauges, and the spend\-burst sparkline) until
Ctrl\-C. The frame and countdown reuse \fBcmduse\fR's in\-place redraw
contract, so a shrinking frame never scrolls.
.TP
.B \-1, \-\-once
Fetch once, print, exit. With \fB\-\-json\fR it prints the JSON projection
instead of the text dashboard.
.TP
.B \-\-json
Machine\-readable projection (one object): \fIsource\fR, \fIgeneratedAt\fR,
\fIperiodStart\fR (and its alias \fIperiodStartAt\fR, both epoch ms),
\fIperiodEndAt\fR (the resolved end of the period in epoch ms, \fBnull\fR for
\fB\-\-window all\fR, whose range has no boundary), \fIperiodInferred\fR,
\fIaccount\fR (the three account meters from Go's usage endpoint \(em
\fIrolling\fR, \fIweekly\fR, \fImonthly\fR, each \fI{status, percent,
resetsAt}\fR with \fIresetsAt\fR in epoch ms \(em or \fBnull\fR when the
endpoint was not reached), \fItotals\fR (each of month, fiveHour,
weekly carrying cost, requests, and tokens), and the per\-model breakdown
\fImodels\fR. A bare \fB\-\-json\fR also leaves the watch loop, so it prints
and exits rather than redrawing.
.TP
.B model [\fIid\fR]
Local usage grouped by model. With no \fIid\fR, the full model table; with an
\fIid\fR, that model's limit, its month/5\-hour/weekly spend, and its share of
period spend. An unused model prints that it has no usage in the period.
\fB\-\-since\fR \fIISO\fR pins the window start (an exact instant, outranking
\fB\-\-window\fR and \fB\-\-period\-start\fR). \fB\-\-json\fR emits one object
\fI{"scope","source","sinceMs","models"}\fR whose \fImodels\fR map each id to
requests, tokensIn, tokensOut, cacheRead, costUsd and its limit; \fB\-\-csv\fR
emits \fImodel,requests,tokens_in,tokens_out,cache_read,cost_usd\fR.
.TP
.B daily
Spend per day (oldest first) over the read window (\fB\-\-days\fR), bucketed in
the display timezone (\fB\-\-tz\fR). \fB\-\-json\fR emits
\fI{"scope","source","total","days"}\fR; \fB\-\-csv\fR emits
\fIday,requests,tokens_in,tokens_out,cache_read,cost_usd\fR with a final
\fItotal\fR row.
.TP
.B hourly
Spend per hour, same bucketing as \fBdaily\fR but over the last \fB\-\-hours\fR
N hours (default 24, max 168). \fB\-\-json\fR emits
\fI{"scope","source","total","hours"}\fR; \fB\-\-csv\fR emits
\fIhour,requests,tokens_in,tokens_out,cache_read,cost_usd\fR with a final
\fItotal\fR row.
.TP
.B session
Spend per session, newest first (top 20). \fB\-\-since\fR \fIISO\fR pins the
window start. \fB\-\-json\fR emits \fI{"scope","source","sessions"}\fR (each
entry adding the last\-used instant and the distinct\-model count);
\fB\-\-csv\fR emits
\fIsession,requests,tokens_in,tokens_out,cache_read,cost_usd\fR.
.TP
.B models
The Go catalogue for the selected plan: each model's name, id, \fB$\fR/1M
in/out/cache\-read rates, the plan's monthly limit, and a \fIGRANTED\fR column.
Without \fB\-\-gated\fR every catalogued model is listed and marked;
\fB\-\-gated\fR keeps only the models the plan grants (present in the plan's
allowance map). \fB\-\-json\fR prints one JSON object per line with keys id,
name, input, output, cacheRead, limit (null when not granted) and granted.
Needs no database; works offline.
.TP
.B plans
Print the Go/Zen catalogue: both Go plans (label, price, and each model's
monthly limit) beside the shared token rates, then a line for the Zen
pay\-as\-you\-go models. \fB\-\-json\fR emits one object with
\fIextractedAt\fR, \fIwindows\fR (the 5\-hour/weekly/monthly shares),
\fIplans\fR (id, label, priceUsd, allowanceByModel), and the Go and Zen rate
tables. Needs no database; works offline.
.TP
.B statusline
Print one compact line: month spend, 5\-hour spend, and the top model.
\fB\-\-json\fR prints one object: plan, period, monthSpent, monthCap,
fiveHourSpent, fiveHourCap, weeklySpent, weeklyCap, model.
.TP
.B mcp
Run an MCP stdio server; see \fBocuse mcp server\fR below.
.TP
.B \-V, \-\-version
Print \fBocuse <version>\fR and exit 0.
.TP
.B \-h, \-\-help
Print usage and exit 0.
.SS ocuse options
.TP
.B \-\-db \fIpath\fR
opencode store path. Default \fBOPENCODE_DB\fR, else
.IR ~/.local/share/opencode/opencode.db .
Opened read\-only, so a running opencode is unaffected; a missing or
unreadable store exits 1 with a hint.
.TP
.B \-\-plan \fIid\fR
Which Go plan to measure against: \fBgo\fR (default) or \fBgo-plus\fR. There
is deliberately no auto\-detection: the usage endpoint reports percentages
rather than a plan id, and the local store records no subscription, so the plan
must be given. An unknown id exits 2 and lists the known ids.
.TP
.B \-\-no\-api
Do not call OpenCode Go's usage endpoint: report the local estimate only.
Useful offline, or to compare the local windows against the account's.
.TP
.B \-\-days \fIn\fR
Days of history to read (default 120). Bounds the
\fBdaily\fR window and the range the MCP tools scan.
.TP
.B \-\-hours \fIn\fR
Hours of history for \fBhourly\fR (default 24, max 168).
.TP
.B \-\-gated
models: keep only the models the selected plan grants.
.TP
.B \-\-window \fIw\fR
Reporting window for the dashboard and statusline: \fBmonth\fR (default, the
calendar month in UTC), \fBall\fR, or \fI<n>d\fR. Anything else exits 2.
.TP
.B \-\-period-start \fIYYYY\-MM\-DD\fR
Pin the period start (midnight UTC) instead of deriving it from
\fB\-\-window\fR.
.TP
.B \-\-infer\-anniversary
Infer the period start from the first Go request ever seen, instead of the
calendar month. Opt\-in, because sparse history infers badly; ignored when
\fB\-\-period-start\fR or \fB\-\-since\fR is given. The JSON's
\fIperiodInferred\fR reports which
was used.
.TP
.B \-\-since \fIISO\fR
Window start for \fBmodel\fR/\fBsession\fR as an exact ISO\-8601 instant
(\fI2026\-08\-27T00:00:00Z\fR). Outranks \fB\-\-window\fR and
\fB\-\-period\-start\fR; an unparseable value exits 2.
.TP
.B \-\-json
Machine\-readable output for every subcommand that has one: the dashboard
(\fB\-1\fR), \fBdaily\fR, \fBhourly\fR, \fBmodel\fR, \fBsession\fR,
\fBmodels\fR, \fBplans\fR, \fBstatusline\fR. Never coloured.
.TP
.B \-\-csv
CSV output for \fBdaily\fR, \fBhourly\fR, \fBmodel\fR and \fBsession\fR
(the columns carry the store's fields; there is no cache\-write column).
Never coloured. Supplying both \fB\-\-json\fR and \fB\-\-csv\fR exits 2.
.TP
.B \-\-rows \fIn\fR
Table rows to print (default 15).
.TP
.B \-\-plain
No colour and no gauges. Colour is also dropped automatically on a
non\-terminal stdout or when \fBNO_COLOR\fR is set.
.TP
.B \-\-tz \fI\(+-HH:MM\fR
Display timezone for the \fBdaily\fR/\fBhourly\fR buckets. Accepts
.IR +05:30 ", " -08:00 ", " +0530 .
Anything else exits 2.
.SS ocuse data and windows
When Go's usage endpoint answers, the header's three window lines are the
account's own meters (the shares and resets \fBopencode.ai/console\fR shows),
each with this store's own spend beside the server's share of the
limit \(em priced on the documented peak/off\-peak schedule, because opencode
records the off\-peak sheet for every turn and would read about 40% under the
console on a peak\-heavy day \(em the two need not agree, so the share carries its own \fIof limit\fR label
\(em and the billing period comes from the endpoint's monthly reset instant \(em
the subscription anniversary, which is why the period line reads
\fIGo billing month\fR. No dollar denominator is printed: any ceiling
derived from one side contradicts the other \(em \fIspend / share\fR offered
\fI$40\fR against a documented \fI$80\fR monthly allowance, and the summed
per\-model limits were the old \fI$215\fR. The burn still gets an \fIon pace
to hit cap in\fR ETA, and the monthly row \(em the one budget \(em its
\fI$X by now\fR pro\-rata figure.
Each model's table row still compares its own spend
against its monthly limit; the table's \fI5H\fR/\fIWK\fR columns are local
rolling windows (this model's spend over the last 5 hours / 7 days), and the
dashboard says so rather than letting them read as a second opinion.
.TP
Without the endpoint \(em no Go key, no network, or \fB\-\-no\-api\fR \(em the
window lines are a local estimate and are labelled as one: the rows carry
spend alone, since limits are per model and no account\-wide pool exists to
divide by. Only the monthly period has a real end, so that row alone gains
a reset countdown and its \fIwindow N% elapsed\fR share; the rolling 5\-hour
and weekly rows show no countdown. The period defaults to the calendar month, with
\fB\-\-period\-start\fR pinning the real anniversary and
\fB\-\-infer\-anniversary\fR guessing it from history. The header splits period tokens
\fIin / out\fR and names the resolved end date. A model whose spend
crosses its limit is flagged \fILIMIT EXCEEDED\fR. There is no
\fISuccess N%\fR figure \(em opencode's store records no per\-request success
or error signal. Limits and rates come from
.IR core/zen.json ,
regenerated from OpenCode's own docs sources with
.BR "bun scripts/extract\-zen.ts" .
.SS ocuse mcp server
\fBocuse mcp\fR runs an MCP stdio server (newline\-delimited JSON\-RPC on
stdin/stdout, diagnostics on stderr) exposing seven tools \(em \fIusage\fR,
\fIplans\fR, \fImodels\fR, \fIdaily\fR, \fIhourly\fR, \fIsession\fR,
\fImodel\fR \) whose output matches the corresponding subcommands. Tool
execution errors are returned as results with \fIisError\fR true, never as
JSON\-RPC errors. stdout carries protocol traffic only.
.SS ocuse files
.TP
.I ~/.local/share/opencode/opencode.db
Read\-only; opencode's own message store (v1 \fBmessage\fR, or v2
\fBsession_message\fR after the migration), the only per\-model cost source.
.TP
.I $OPENCODE_DB
Overrides the store path (equivalently, \fB\-\-db\fR).
.TP
.I ~/.local/share/opencode/auth.json
Read (never written) as the fallback Go key \(em the legacy v1 location of the
credential \fB/connect\fR stores; on v2 the live one is the \fBcredential\fR row
of opencode's store, which is read first. \fB$OCUSE_API_KEY\fR and
\fB$OPENCODE_API_KEY\fR override both.
.SS arkuse
.B arkuse
tracks a Volcengine ARK Agent Plan and reports in
.BR AFP ,
the plan's own unit, never in dollars or yuan: one AFP is ¥0.002 at every
tier and a model's coefficient converts tokens into it \(em
\fIAFP = (input + cache_read + output) \(mu coef / 10,000\fR. Cache reads are
billed; \fIreasoning\fR is not added again because it already sits inside
the output count. Coefficients, tier gates, promo dates and the plan table
are hand\-maintained in
.IR core/ark.json ,
because the publisher documents them as prose.
.SS arkuse synopsis
.B arkuse
[\fIOPTIONS\fR]
.br
.B arkuse \-1
[\fB\-\-json\fR] [\fIOPTIONS\fR]
.br
.B arkuse model
[\fIid\fR] [\fB\-\-since\fR \fIISO\fR] [\fB\-\-json\fR | \fB\-\-csv\fR] [\fIOPTIONS\fR]
.br
.B arkuse daily
[\fB\-\-days\fR \fIn\fR] [\fB\-\-json\fR | \fB\-\-csv\fR] [\fIOPTIONS\fR]
.br
.B arkuse hourly
[\fB\-\-hours\fR \fIn\fR] [\fB\-\-json\fR | \fB\-\-csv\fR] [\fIOPTIONS\fR]
.br
.B arkuse session
[\fB\-\-since\fR \fIISO\fR] [\fB\-\-json\fR | \fB\-\-csv\fR]
.br
.B arkuse models
[\fB\-\-gated\fR] [\fB\-\-json\fR]
.br
.B arkuse plans
[\fB\-\-json\fR]
.br
.B arkuse statusline
[\fB\-\-json\fR] [\fIOPTIONS\fR]
.br
.B arkuse mcp
.br
.B arkuse \-V
.br
.B arkuse \-h
.SS arkuse commands
Every subcommand, machine format, watch frame, config key, update check and
cap notification mirrors
.BR cmduse 's,
with AFP standing where the other binaries put money.
.TP
.B (no command), \fB\-1\fR
Live watch, or one frame with \fB\-1\fR. The dashboard shows the plan's own
5\-hour / daily / weekly / monthly meter rows, each with the server's share of
the limit and this store's AFP beside it labelled \fIlocal\fR, then a model
block with each model's coefficient, AFP, requests and share of period AFP.
No per\-model allowance is invented: ARK limits the plan.
.TP
.B daily, hourly, session, model
The same reports as
.BR cmduse .
\fB\-\-csv\fR columns are
\fIday|hour|session|model,requests,tokens_in,tokens_out,cache_read,afp\fR
(no cache\-write column, and AFP replaces cost). \fBmodel\fR with no id prints
the period's per\-model table.
.TP
.B models
The ARK catalogue with the coefficient in force today (a promo halves it until
its date), the derived ¥/1M tokens, the minimum tier and whether the selected
plan grants the model. \fB\-\-gated\fR keeps only what the plan grants;
\fB\-\-json\fR is one object per line. Needs no store.
.TP
.B plans
The personal tiers (price, monthly AFP quota, and the 5\-hour / daily / weekly
quotas), the active promos, the flat AFP rate, and the enterprise seat price
list. Enterprise AFP is metered per seat and the personal usage API does not
expose it, so only prices appear. Needs no store.
.TP
.B statusline
One compact line: period AFP, 5\-hour AFP, weekly AFP and the top model.
\fB\-\-json\fR mirrors cmduse's keys (\fImonthSpent\fR,
\fIfiveHourSpent\fR, \fIweeklySpent\fR, the caps \fBnull\fR, \fImodel\fR)
with an extra \fIafp\fR block carrying the monthly yuan figure and the
control plane's \fIaccount\fR object.
.TP
.B mcp
Run an MCP stdio server exposing the same seven tools \(em \fIusage\fR,
\fIplans\fR, \fImodels\fR, \fIdaily\fR, \fIhourly\fR, \fIsession\fR,
\fImodel\fR \) whose output matches the subcommands.
.SS arkuse options
The shared options are those under
.BR "ocuse options" ,
with these differences.
.TP
.B \-\-plan \fItier\fR
The ARK tier: \fIsmall\fR, \fImedium\fR, \fIlarge\fR or \fImax\fR. Default:
the profile's own \fIplan_tier\fR, else \fImedium\fR. An unknown tier exits 2
and lists the known ones.
.TP
.B \-\-profile \fIname\fR
The \fBarkcli\fR control\-plane profile to query. Default
\fB$ARKUSE_PROFILE\fR, else the first profile whose type is
\fIagent\-plan\fR. An unknown name degrades to the local estimate with the
control plane's own reason on the dashboard.
.TP
.B \-\-no\-api
Skip \fBarkcli\fR entirely and report the local estimate only. The tier then
comes from \fB\-\-plan\fR or the catalogue default, and no profile is
resolved.
.SS arkuse data and windows
Two sources. \fBarkcli usage plan\fR returns the plan's own meters with their
reset instants \(em the only figures that see every machine and harness on the
subscription \(em and this store (\fIopencode.db\fR, provider
\fIagent\-plan\fR only) supplies the per\-model attribution and the local
estimate. The server's windows are not reproducible locally: the 5\-hour
window is a block with its own reset, the week is anchored to Monday 00:00, and
the month is the subscription anniversary, so rows are cut at
\fIreset \(mi window\fR and period dates print in the server's own offset.
A text\-only workload has no daily window; only media and Harness items do.
When no meter answers the rows fall back to a rolling cut and the block is
labelled \fIestimated locally\fR with the reason beside it.
.PP
The plan's semantics are surfaced rather than assumed: no downgrade; on an
upgrade the monthly quota is pro\-rated while the 5\-hour and weekly windows
reset to the new tier immediately; plain API calls against the plan base URL
are forbidden (ban risk); the quota is shared across every harness; and
postpaid overflow is opt\-in. Because ARK limits the plan rather than a model,
\fInotify_on_cap\fR fires per window meter.
.SS arkuse files
.TP
.I ~/.config/arkuse/config.json
The same keys as cmduse's config, set with \fBarkuse config set\fR.
.TP
.I $OPENCODE_DB, \-\-db
The store path; opened read\-only.
.SH SEE ALSO
.BR cmd (1),
the Command Code CLI for login and authentication.
.br
.BR ocuse ,
this crate's OpenCode Go/Zen twin, documented above under
.BR OCUSE .
.br
.BR arkuse ,
this crate's ARK Agent Plan twin, documented above under
.BR ARKUSE .