A minimalist, offline dashboard for Devin Desktop's cached session metadata and account quota. It uses the local SQLite state.vscdb database, no third-party dependencies.
macOS:
python3 devin_dashboard.pyWindows:
py devin_dashboard.pyThe app opens http://localhost:8787 automatically. Keep the terminal running while using the dashboard and press Ctrl-C to stop it.
Useful options:
python3 devin_dashboard.py --no-browser
python3 devin_dashboard.py --port 9000
python3 devin_dashboard.py --db "/path/to/state.vscdb"The database is auto-detected at:
- macOS:
~/Library/Application Support/Devin/User/globalStorage/state.vscdb - Windows:
%APPDATA%\\Devin\\User\\globalStorage\\state.vscdb, then%LOCALAPPDATA% - Linux:
~/.config/Devin/User/globalStorage/state.vscdb
- Searchable, sortable session metadata table.
- Session title, provider, workspace, user-message count, per-session ACU consumed, estimated cost, input/output/cached token counts, active runtime, model generation time, created time, and updated time.
- ACU consumed and monthly limit, billing window, and overage balance.
- Current-month totals: session count, tokens in, tokens out, and top models by tokens, ACU, and session count.
- Optional estimated dollar spend. Enter a local
$ / ACUprice (default 1.25) and a monthly budget (default $75, which equals 60 ACU at the default price); the monthly budget drives the ACU limit. Both values are saved only in the browser's local storage. - Manual refresh and optional 60-second auto-refresh.
- Per-row share menu: Copy JSON copies the full session record (pretty-printed, including the estimated cost) to the clipboard, and Download .md file saves the session details (with the estimated cost) as a Markdown file named after the title and created date.
All data is read at request time from two local SQLite databases, both opened read-only:
- Devin Desktop state —
state.vscdb, a VS Code-style key/value store whoseItemTableholds JSON blobs keyed by strings such aswindsurf.acp.sessioninfo.<sessionId>. - Devin CLI sessions —
~/.local/share/devin/cli/sessions.db(override with--cli-db), whosemessage_nodestable stores one row per chat message with achat_messageJSON column.
The server merges both sources in read_state() and exposes the result as a single JSON payload at GET /api/data. The browser then renders everything client-side from that payload; no computation happens against a remote service.
- Plan card — Plan name comes from the cached plan blob (
windsurf.reactSettings.cachedPlanInfoData*, falling back towindsurf.settings.cachedPlanInfo) instate.vscdb. Estimated cost is computed in the browser asACU consumed × $/ACUusing the locally configured price. - ACU consumed card — Two numbers are compared: the server ACU (
plan.consumed) and the local ACU. The server value is taken from thewindsurfAuthStatusblob: itsuserStatusProtoBinaryBase64field is base64-decoded and parsed as a raw protobuf message, reading fixed64 field 19 (ACU consumed) and field 20 (ACU limit); these override the cached-plan values when present. The local ACU is aggregated server-side fromsessions.dbby summingmetadata.committed_acu_costover all deduplicated messages whosecreated_atfalls inside the plan's billing window (derived from the plan blob'sstartTimestamp/endTimestamp). The card displaysmax(server, local)and shows the difference as "other". The progress bar and percentage useplan.limit. - Sessions this month card — Computed entirely in the browser: sessions whose
createdAt(from the session-info blob's_meta["cognition.ai/createdAt"]or thewindsurf.acp.session/session_start_time/<id>key) falls in the current calendar month. Token totals suminputTokens/outputTokensover those sessions. - Model metrics card — Also browser-side, over the same current-month sessions. The model per session comes from
sessions.db: thesessions.modelcolumn, overridden by the most recently observedmetadata.generation_modelin that session's messages.
Each row is one session, assembled by joining two sources on the session slug (the last /-separated segment of the session ID):
- From
state.vscdb:windsurf.acp.sessioninfo.<id>provides title, provider (explicitproviderIdor parsed from the ID), andcwd;info.updatedAtprovides Last updated;_meta["cognition.ai/createdAt"](or the session-start-time key) provides Created;windsurf.acp.session/userMessageCount/<id>(falling back to_meta["cognition.ai/userMessageCount"]) provides the user-message count;windsurfSpace.sessionWorkspace/<id>provides the workspace label (or the basename ofcwd). - From
sessions.db, aggregated per session with a SQL query overmessage_nodes(deduplicated bymessage_id): ACU (SUMofmetadata.committed_acu_cost), tokens in/out (SUMofmetrics.input_tokens+cache_read_tokens+cache_creation_tokens/metrics.output_tokens), cached tokens (SUMofmetrics.cache_read_tokens+cache_creation_tokens), model generation time (SUMofmetrics.total_time_ms), and active runtime. Note thatmetrics.input_tokensis only the uncached remainder of each request's prompt — the full input per request includes the two cache fields, matching the ATIF transcript'stotal_prompt_tokens. Active runtime sums the gaps between consecutive messages, capped at one hour each, and skips gaps preceding user messages, so idle time between sessions of work is excluded. Sessions with no local CLI rows (e.g.devin-cloud) show—for these columns. - Estimated cost column — computed in the browser as
session ACU × $/ACU; it is a local estimate, not a billing record.
Server-side, sessions whose title is (untitled) with zero user messages are hidden, and the list is sorted by last-updated time. The browser then applies your search text (matched against title, directory, session ID, provider, workspace, and model) and column sorting on top of that list.
- The server binds to
localhostby default and serves no external assets. - The app opens the SQLite database read-only and never reads
windsurfAuthStatus, API keys, or credentials. - Per-session ACU, token usage, active runtime, and model generation time are read from the local Devin CLI session store (
~/.local/share/devin/cli/sessions.db, override with--cli-db). Tokens in sums each request's full input (metrics.input_tokens+metrics.cache_read_tokens+metrics.cache_creation_tokens), sinceinput_tokensalone is the uncached remainder; tokens out sumsmetrics.output_tokens; cached tokens combinecache_read_tokensandcache_creation_tokens. Duplicate message snapshots are deduplicated by message ID. Active runtime excludes gaps before user messages and caps any single gap at one hour; model generation time sumsmetrics.total_time_ms. Sessions without local CLI data (for exampledevin-cloudsessions) show—. The dollar amount is an estimate based on the price you enter. - Session conversation transcripts and tool details are not stored in the metadata records used by this dashboard.
- Cached values can lag behind the live Devin service; the page displays the local data timestamp.