@webfueler/oc-dash 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -144
- package/dist/assets/index-B9io-Y-k.js +9 -0
- package/dist/assets/{index-HDE3yxEk.css → index-CCkSH4wy.css} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/index.js +15 -0
- package/dist-server/models.js +67 -0
- package/docs/screenshot.png +0 -0
- package/package.json +14 -2
- package/dist/assets/index-BGry4XSt.js +0 -9
package/README.md
CHANGED
|
@@ -1,176 +1,122 @@
|
|
|
1
1
|
# oc-dash
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Single package: Hono backend on Node 22, React + Vite + TypeScript frontend,
|
|
7
|
-
plain CSS, inline-SVG activity chart. No chart library, no CSS framework.
|
|
8
|
-
|
|
9
|
-
## What it shows
|
|
10
|
-
|
|
11
|
-
- **Cost hero** — one hero card with the range's total cost (USD, money-green),
|
|
12
|
-
a `≈ $X/day` figure where the range spans full days (omitted for today), and
|
|
13
|
-
a compact stat strip below it: tokens, prompts · steps, sessions ·
|
|
14
|
-
subagents, active days · streak. When the stats endpoint is unavailable, the
|
|
15
|
-
hero carries a fallback badge and the strip shows totals computed from the
|
|
16
|
-
session list instead.
|
|
17
|
-
- **Sessions table** — every session in the range with title, agent, model
|
|
18
|
-
chip (full `provider/model · variant` string on hover), own cost, **incl.
|
|
19
|
-
subagents** (recursive: parent cost plus the cost of all descendants at any
|
|
20
|
-
depth), tokens, last activity, and outcome. Child sessions nest under their
|
|
21
|
-
parent, **collapsed by default**: clicking a parent row (or focusing it and
|
|
22
|
-
pressing Enter/Space) expands its subagents, and Expand all / Collapse all
|
|
23
|
-
buttons sit in the section head. Tree state resets when the range changes.
|
|
24
|
-
Rows whose outcome is `succeeded` render a dim check; `failed` and
|
|
25
|
-
`interrupted` keep loud badges. A project filter is built from the session
|
|
26
|
-
directories.
|
|
27
|
-
- **Models table** — steps, tokens, and cost per model, with an `unpriced`
|
|
28
|
-
flag on models that burn tokens but report zero cost.
|
|
29
|
-
- **Activity chart** — steps per day, drawn as inline SVG: bars are
|
|
30
|
-
width-capped and slot-centered, dotted gridlines mark 25/50/75% of the peak,
|
|
31
|
-
zero-step days stay visible as stubs, and the Today range shows the trailing
|
|
32
|
-
7 days with the in-range day accented (`/api/summary` gains an additive,
|
|
33
|
-
Today-only `contextActivity` field for this).
|
|
34
|
-
|
|
35
|
-
Refreshes every 30 seconds while the tab is visible; refreshes immediately
|
|
36
|
-
when the tab becomes visible again.
|
|
37
|
-
|
|
38
|
-
## Requirements
|
|
39
|
-
|
|
40
|
-
- Node 22+
|
|
41
|
-
- npm
|
|
42
|
-
- A running local opencode2 service (the app only ever issues read-only GETs
|
|
43
|
-
and never stops or restarts the service)
|
|
44
|
-
|
|
45
|
-
## Run without cloning
|
|
3
|
+
Track exactly what AI usage costs in opencode2. oc-dash is a local dashboard
|
|
4
|
+
over the service API that shows spend session by session, subagents included,
|
|
5
|
+
under any time range and filter combination.
|
|
46
6
|
|
|
47
|
-
|
|
48
|
-
npx @webfueler/oc-dash
|
|
49
|
-
```
|
|
7
|
+
## What it looks like
|
|
50
8
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
`PORT=4022 npx @webfueler/oc-dash`).
|
|
9
|
+
One page: a cost hero, project and model filters with a filtered totals card,
|
|
10
|
+
then the sessions table, the models table, and an activity chart.
|
|
54
11
|
|
|
55
|
-
|
|
12
|
+

|
|
56
13
|
|
|
57
|
-
|
|
58
|
-
npm i -g @webfueler/oc-dash
|
|
59
|
-
oc-dash
|
|
60
|
-
```
|
|
14
|
+
## What you can learn
|
|
61
15
|
|
|
62
|
-
The
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
16
|
+
- The total spend for a range, with a per-day average when the range covers
|
|
17
|
+
full days, plus tokens, prompts, steps, sessions, subagents, and streaks.
|
|
18
|
+
- Spend per project and per model under combined filters. Pick one or both;
|
|
19
|
+
the filtered totals card recomputes for the cut on screen.
|
|
20
|
+
- Subagent spend. Child sessions nest under their parent, and the Incl.
|
|
21
|
+
subagents column rolls descendant cost into the parent at any depth.
|
|
22
|
+
- Which sessions cost the most. Click the Own cost, Incl. subagents, or
|
|
23
|
+
Tokens header to sort; each cycles default, ascending, descending.
|
|
24
|
+
- Which models burned tokens at zero reported cost. The Models table flags
|
|
25
|
+
them `unpriced`.
|
|
66
26
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
27
|
+
## Reading the numbers
|
|
28
|
+
|
|
29
|
+
- A number served by the stats endpoint is exact for the window and filter
|
|
30
|
+
cut on screen.
|
|
31
|
+
- When a cut cannot be served, the card sums the session rows instead and
|
|
32
|
+
marks the result `≈` (approximate). The fallback is labeled, never silent.
|
|
33
|
+
- If stats and the session rows disagree about a model, the card says so
|
|
34
|
+
instead of showing either number.
|
|
35
|
+
- Stats totals exclude compaction usage. Session rows and the directory
|
|
36
|
+
fallback include it, so the two totals can differ.
|
|
37
|
+
- Costs are list-price estimates from models.dev, not your bill, and models
|
|
38
|
+
that burn tokens at zero reported cost are undercounted.
|
|
70
39
|
|
|
71
|
-
|
|
72
|
-
`curl -fsSL https://opencode.ai/install | bash` (more options at
|
|
73
|
-
https://opencode.ai).
|
|
40
|
+
## Quick start
|
|
74
41
|
|
|
75
|
-
|
|
42
|
+
Requires Node 22+ with npm, and a running opencode2 service from the
|
|
43
|
+
compatible beta line. oc-dash only issues read-only GETs and never starts,
|
|
44
|
+
stops, or restarts the service.
|
|
76
45
|
|
|
77
46
|
```sh
|
|
78
|
-
|
|
47
|
+
npx @webfueler/oc-dash
|
|
79
48
|
```
|
|
80
49
|
|
|
81
|
-
|
|
50
|
+
Serves the dashboard on http://localhost:4021. Override the port with `PORT`,
|
|
51
|
+
for example `PORT=4022 npx @webfueler/oc-dash`.
|
|
82
52
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
```
|
|
53
|
+
When no registered service answers the startup probe, oc-dash prints a short
|
|
54
|
+
message and exits non-zero. Start the service with `opencode2 serve --service`.
|
|
86
55
|
|
|
87
|
-
|
|
88
|
-
|
|
56
|
+
No opencode2 yet? Install the beta line with
|
|
57
|
+
`npm install -g @opencode-ai/cli@beta`; it puts the `opencode2` command on
|
|
58
|
+
your PATH. Exact money needs the stats route, which the beta line serves
|
|
59
|
+
(`@latest` does not yet). To skip `npx`, `npm i -g @webfueler/oc-dash` puts
|
|
60
|
+
`oc-dash` on your PATH.
|
|
89
61
|
|
|
90
|
-
##
|
|
62
|
+
## Using the dashboard
|
|
91
63
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
```
|
|
64
|
+
Ranges sit in the top bar: Today, 7 days, 30 days, All. A range switch
|
|
65
|
+
refetches and collapses the session tree.
|
|
95
66
|
|
|
96
|
-
|
|
97
|
-
and
|
|
67
|
+
The project and model filters sit above the tables, each with an All entry
|
|
68
|
+
and live counts. Type to search: projects match their full path, models their
|
|
69
|
+
display name and raw id. The model filter ignores reasoning variants; held
|
|
70
|
+
values survive range switches with a truthful 0 when nothing matches.
|
|
98
71
|
|
|
99
|
-
|
|
72
|
+
The filtered totals card appears whenever either filter is active. Its money
|
|
73
|
+
comes from the stats engine when the exact cut can be served; otherwise it is
|
|
74
|
+
the row sum, marked `≈`. A project filter on its own unlocks extra stats
|
|
75
|
+
tiles (prompts, steps, activity, any positive compaction gap); a model filter
|
|
76
|
+
hides them.
|
|
100
77
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
78
|
+
Sessions nest under their parent, collapsed by default. Click a parent row or
|
|
79
|
+
press Enter/Space to expand it; Expand all and Collapse all sit in the
|
|
80
|
+
section head. Own cost is the session's own spend, Incl. subagents adds every
|
|
81
|
+
descendant. The model chip shows the display name, with the raw
|
|
82
|
+
`providerID/id · variant` on hover.
|
|
105
83
|
|
|
106
|
-
|
|
107
|
-
|
|
84
|
+
If the stats endpoint is down, the hero carries a fallback badge and totals
|
|
85
|
+
from the session rows, the filtered card labels its money approximate, and
|
|
86
|
+
the Models table stays empty. The dashboard refreshes every 30 seconds while
|
|
87
|
+
the tab is visible, and right away when it becomes visible again.
|
|
108
88
|
|
|
109
|
-
##
|
|
89
|
+
## Known limits
|
|
110
90
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
91
|
+
- The opencode2 API and client are beta; a service update can change
|
|
92
|
+
behavior. Exact money needs a service that serves `session.stats`.
|
|
93
|
+
- The session walk stops after 50 pages of 100 rows. When it truncates, the
|
|
94
|
+
dashboard warns and a directory filter falls back to the approximate row sum.
|
|
95
|
+
- The service endpoint is discovered once and cached. If the service comes
|
|
96
|
+
back on a new port, restart the dashboard.
|
|
97
|
+
- Without a healthy service at startup, oc-dash exits. It never starts or
|
|
98
|
+
restarts one.
|
|
114
99
|
|
|
115
|
-
|
|
116
|
-
model detection, and the client-side fallback totals.
|
|
100
|
+
## Development
|
|
117
101
|
|
|
118
|
-
|
|
102
|
+
Stack: Hono on Node 22 serves the API and built frontend from one process.
|
|
103
|
+
Frontend: React + Vite + TypeScript, plain CSS, inline-SVG chart. Tests use
|
|
104
|
+
vitest across the server routes, walk, and client logic; lint uses eslint.
|
|
119
105
|
|
|
120
106
|
```sh
|
|
121
|
-
npm
|
|
107
|
+
npm install
|
|
108
|
+
npm run dev # API on 4021, Vite on 5273, /api proxied to 4021
|
|
109
|
+
npm run build # typecheck, bundle the frontend, compile the server
|
|
110
|
+
npm run start # serve the build on 4021
|
|
111
|
+
npm test # vitest
|
|
112
|
+
npm run lint # eslint
|
|
122
113
|
```
|
|
123
114
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
| `GET /api/health` | Dashboard health plus the opencode service URL and health |
|
|
129
|
-
| `GET /api/summary?range=today\|7d\|30d\|all` | `session.stats` for the resolved window, with `tools="summary"` and the machine's local IANA timezone |
|
|
130
|
-
| `GET /api/sessions?range=today\|7d\|30d\|all` | Cursor-paginated `GET /api/session` walk (limit 100, capped at 50 pages), windowed by `time.updated >= range start` |
|
|
131
|
-
|
|
132
|
-
Range mapping: `today` starts at local midnight; `7d` and `30d` start now
|
|
133
|
-
minus N×24h; `all` omits `from`/`to` so stats fall back to the earliest
|
|
134
|
-
message. Nesting, filtering, and cost rollup happen in the frontend.
|
|
135
|
-
|
|
136
|
-
If the stats endpoint is unavailable, `/api/summary` answers with
|
|
137
|
-
`{ "degraded": true, "reason": ... }` instead of an error page, and the UI
|
|
138
|
-
falls back to totals computed from the session list (marked as degraded).
|
|
115
|
+
The backend talks to the service through `@opencode/client`, pinned to an
|
|
116
|
+
exact beta build, and is discover-only. See
|
|
117
|
+
[docs/API.md](https://github.com/webfueler/oc-dash/blob/main/docs/API.md) for
|
|
118
|
+
the backend routes, the range mapping, and the project layout.
|
|
139
119
|
|
|
140
|
-
##
|
|
120
|
+
## License
|
|
141
121
|
|
|
142
|
-
|
|
143
|
-
discover-only: `Service.discover()` finds a healthy registered service and
|
|
144
|
-
never starts, stops, or restarts one. If nothing healthy is registered when
|
|
145
|
-
the dashboard starts, it prints a short message (start `opencode serve
|
|
146
|
-
--service`) and exits. If the service dies later, the UI shows its degraded
|
|
147
|
-
state instead of crashing. Calls go through the typed client with a raw-fetch
|
|
148
|
-
fallback (`Service.headers(endpoint)` auth attached).
|
|
149
|
-
|
|
150
|
-
The discovered endpoint is cached for the life of the dashboard process, so
|
|
151
|
-
if the opencode service comes back on a different port, restart the
|
|
152
|
-
dashboard too.
|
|
153
|
-
|
|
154
|
-
## Estimate caveats (read before quoting numbers)
|
|
155
|
-
|
|
156
|
-
- Session costs and the stats totals are **list-price estimates** based on
|
|
157
|
-
models.dev pricing data, not your actual bill.
|
|
158
|
-
- Providers without price data are **undercounted**: models flagged
|
|
159
|
-
`unpriced` in the Models table burn real tokens but report zero cost, so
|
|
160
|
-
real spend is higher than shown.
|
|
161
|
-
- The stats endpoint adds **compaction usage** (not attributed to any
|
|
162
|
-
session) to the totals, so KPI totals can legitimately exceed the sum of
|
|
163
|
-
the session rollups in the table.
|
|
164
|
-
- Subagent spend rolls up into the parent row; the opencode2 TUI does not
|
|
165
|
-
show this.
|
|
166
|
-
|
|
167
|
-
## Project layout
|
|
168
|
-
|
|
169
|
-
```
|
|
170
|
-
server/ Hono backend (entry, service connection, range mapping)
|
|
171
|
-
src/ React frontend (components, tree/rollup logic, formatting)
|
|
172
|
-
src/*.test.ts Unit tests (vitest)
|
|
173
|
-
bin/oc-dash.js npx launcher (runs the compiled server)
|
|
174
|
-
dist/ Built frontend (gitignored)
|
|
175
|
-
dist-server/ Compiled server (gitignored)
|
|
176
|
-
```
|
|
122
|
+
MIT. Source, issues, and releases: https://github.com/webfueler/oc-dash.
|