Deep-dive reference for Workbench. The README is the showcase; everything technical lives here. Also available in 简体中文 and 日本語.
| Module | What it does |
|---|---|
| 🖥 Overview | CPU / memory trend charts, disk usage donut, process group stats |
| ⚙ Processes | Full snapshot, CPU / memory sorting, virtual scrolling (viewport-only rows), kill with confirmation, AI agent detection |
| 🌐 Servers & ports | netstat listener detection, dev-framework tags (Vite / Next / Django…) |
| 🤖 AI sessions | opencode online sessions, local Claude / Codex session preview |
| 💰 Cost metering | Token usage, daily trends, budget alerts, Anthropic cache pricing |
| 📁 Projects | Local project registry, automatic Git branch detection |
| ✅ Tasks | Kanban board (drag to change status) |
| ⚙ Settings | Theme editor, shell / layout / widget arrangement, refresh interval, budget thresholds, JSON import & export, backup & restore, start at login |
| 📋 Logs | Built-in viewer, auto refresh, tail reading |
| ⌨ CLI | workbench ps / ports / diag, --json, read-only, no window |
- Three-level confidence: measured (API-reported usage) → estimated (character heuristic) → zero_pricing
- 40+ model prices built in; the Cost ledger ships a real price-override editor (edit a unit price, add a model, restore built-in prices; invalid prices reject the whole table)
- Anthropic cache semantics:
input_tokensexcludes cache;cache_read_input_tokensbilled at 0.1×,cache_creation_input_tokensat 1.25× - Day attribution: measured and estimated rows are bucketed per JSONL-line
timestamp(falls back to the previous day / file mtime when missing), so appending to a session never rewrites historical days - Unknown models are never priced: unrecognized models are flagged
unknownand recorded aszero_pricing— no invented unit prices - Idempotent: each session file keeps only its latest snapshot (replaced
whole-group by
file_path), so re-scans never stack stale rows - Estimates can be disabled: the "include estimated rows" toggle live-updates overview / trend / model breakdowns
- Budget alerts: system notifications, de-duplicated to avoid nagging
- Scan scope: the cost scan covers every session file under
~/.claude/projectsand~/.codex/sessions(the UI preview shows the 10 most recent)
- Closing the window hides to tray; tray left-click toggles visibility, the tray menu really exits the process
- Global hotkey
Ctrl+Shift+Wtoggles window visibility (degrades to a warning if the hotkey is taken; startup is never blocked) - Logs roll per launch (> 5 MB archives to
workbench.log.1), the Logs page reads on demand from the tail - Killing PID 0/4 and critical system processes (csrss / lsass / services …) is rejected outright
| Item | Requirement |
|---|---|
| OS | Windows, Linux and macOS have CI jobs; platform-specific verification varies |
| Runtime | Windows needs WebView2 (preinstalled on Windows 11); Linux needs WebKitGTK 4.1; macOS uses the system webview |
| Disk | Depends on the platform and bundle |
| Privileges | No admin required; killing a process needs same-or-higher privileges. If a normal kill fails, it can be retried with one click through UAC elevation (Windows only) |
| Network | None required. The only optional probe is the local opencode service when you open the AI sessions page |
Check the latest release for available assets. Availability and signing status have not been verified here.
Requires Node.js ^22.22.2 || ^24.15.0 || >=26.0.0 for the locked frontend dependencies,
Rust ≥ 1.90, and the platform webview runtime.
npm installLocal CI mirror (same steps as .github/workflows/ci.yml):
node scripts/ci-local.mjs # icon contract → typecheck → lint → format →
# unit → build → Playwright → cargo fmt/test/clippy → IPC drift
node scripts/ci-local.mjs --fast # skip the visual-regression pass
node scripts/ci-local.mjs --linux # also try the Linux backend job in DockerPackaged-app E2E (real Tauri window, real IPC). Start the exe with
WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--remote-debugging-port=22300, then:
node scripts/e2e-desktop.mjs 22300 # data, backup/restore, pricing, start-at-login
node scripts/e2e-io.mjs 22300 # import / exportTypes crossing the IPC boundary are not hand-written in src/types.ts; they
are generated from the Rust structs:
cd src-tauri
cargo test --test export_bindings # regenerates src/generated/*.tssrc/types.ts only re-exports; src/generated/ is the committed contract
snapshot. After changing a Rust struct you must re-run the command above,
otherwise the CI drift gate will reject the PR.
The desktop binary doubles as a read-only CLI — useful in scripts, CI and SSH sessions where opening a 1440×900 window is absurd:
workbench ps --json # [{pid, name, cpu, memory, parent_pid, status, cmd_line, …}]
workbench ports # PROTO PORT ADDRESS STATE PID
workbench diag --json # the same report the Settings panel rendersExit codes: 0 success, 2 unknown command. It never mutates state.
npm run tauri dev # desktop dev mode (hot reload)
npm run dev # frontend only, browser demo mode, no Rust needednpm run tauri buildArtifacts land in src-tauri/target/release/bundle/.
| Layer | Choice |
|---|---|
| Runtime | Tauri 2 (Rust + OS webview) |
| Frontend | React 19 + TypeScript (strict) + Vite 8 |
| State | Zustand 5 (persist) |
| Styling | Tailwind CSS 4 + dual-theme CSS variables |
| Charts | ECharts 6 (tree-shaken, dark/light themes registered) |
| Virtualization | @tanstack/react-virtual 3 (variable-height expanded rows) |
| Subscriptions | Zustand 5 useShallow selectors + memo page components |
| IPC | Tauri IPC + spawn_blocking |
| Contracts | ts-rs 12 generates src/generated/*.ts from Rust structs, CI drift-gated |
| Process data | sysinfo |
| Ports | netstat parser (POSIX / Windows dialects) |
| i18n | 简体中文 default, switchable English / 日本語 |
| Quality gates | ESLint 10 + Prettier 3 + vitest 5 + clippy -D warnings |
workbench/
├── src/ # frontend
│ ├── pages/ # 8 feature pages (lazy loaded)
│ ├── lib/ # store / api / echarts / chartTheme / i18n / util
│ ├── generated/ # ts-rs generated IPC contract types (do not edit)
│ ├── ui.tsx # shared component library
│ ├── types.ts # type re-exports + frontend-only types
│ ├── App.tsx # app shell
│ └── styles/global.css # global styles & theme variables
├── src-tauri/ # Rust backend
│ ├── capabilities/ # Tauri permission set (minimal)
│ ├── icons/ # app icons (generated by scripts/make_icons.py)
│ ├── src/
│ │ ├── commands.rs # IPC command layer
│ │ ├── procs.rs # process snapshot + kill
│ │ ├── ports.rs # netstat parsing
│ │ ├── devserver.rs # dev-framework detection
│ │ ├── ai.rs # AI session fetching
│ │ ├── pricing.rs # model price matching
│ │ ├── session_meter.rs# JSONL token cost parsing
│ │ ├── cost_store.rs # cost data persistence
│ │ ├── ipc_guard.rs # IPC argument whitelist
│ │ └── lib.rs # Tauri bootstrap
│ └── tests/ # IPC contract export test
├── docs/ # GitHub Pages landing page + screenshots
├── scripts/ # icon & screenshot helper scripts
└── .github/workflows/ # CI / Release / Pages
npm run format:check # Prettier
npm run lint # ESLint (zero warnings)
npm run test # frontend unit tests (vitest, 370 cases)
npm run coverage # coverage
npm run dup # duplication (jscpd, 3% threshold)
npm run build # tsc + vite build
cd src-tauri
cargo test # backend tests + IPC contract export
cargo clippy --all-targets -- -D warnings # zero-warning gatePure Rust logic (netstat parsing, framework detection, price matching, token
extraction, JSON storage, IPC guard) carries #[cfg(test)] unit tests; the
frontend is covered by vitest (util / store / shared components / error
boundary). CI runs formatting, lint, type check, build, both test suites,
clippy with zero warnings, and verifies src/generated/ matches the Rust
structs.
How is the cost metering computed? Is it accurate?
It parses the usage fields in local Claude Code / Codex session JSONL files
and prices them against 40+ built-in model prices, with three confidence
levels:
- measured — real usage reported by the API, most accurate
- estimated — no usage, heuristic by character count (can be disabled)
- zero_pricing — model unrecognized, recorded as free rather than priced with a made-up unit price
Anthropic cache tokens follow the official semantics (read 0.1×, write 1.25×).
Does it upload my data anywhere?
No. Projects, tasks, the cost ledger and logs all live in the local app data
directory. The app only does two local things: read netstat for ports, and
read local session files for costs. The only optional network call is probing
the local opencode service (default port 4096); on failure it just shows "not
detected".
Can killing a process break my system?
No. PID 0/4 and critical system processes (csrss, lsass, services…) are rejected by the backend; the frontend asks for confirmation first; and the target identity is re-verified after refresh to avoid PID-reuse mistakes.
Where did the window go after I closed it? How do I quit?
Hiding to the tray is intentional (the app is meant to keep monitoring). Right
click the tray icon and choose "Quit"; the hotkey Ctrl+Shift+W
toggles visibility anytime.
Windows says "Unknown publisher" — what do I do?
Check the publisher and signing status of the downloaded file before deciding whether to run it. Release asset availability and signing have not been verified here.
The authoritative plan — including what is explicitly not planned and how a release is cut — lives in ROADMAP.md. Summary:
- Real-time process / port / dev-server monitoring
- AI session cost metering with budget alerts
- Projects & task kanban
- Data-driven themes (8 built-ins + custom editor + import/export), log viewer
- Three shells (rail / top dock / zen) and seven overview layouts
- Draggable, hideable overview widgets
- Multilingual UI (English / 简体中文 / 日本語)
- Cross-platform CI jobs defined for Windows, Linux and macOS
- Installer code signing (needs a paid certificate)
- Model pricing editor (edit / add / reset overrides from the UI)
- Data backup and restore (timestamped snapshots, one-click rollback)
- SQLite cost ledger (100k records aggregate in 246 ms; sharding not needed)
- Page-level E2E (Playwright 10 pages × 8 themes plus packaged-app suites: data/backup and import/export)
- Editable widget widths (4/6/8/12 columns)
- Theme editor: status colours, live contrast readout, UI scale
- Theme share links (theme packed into a URL fragment — opens straight into the importer, no backend needed)
- Elevated process termination (UAC prompt after a normal kill fails; protected processes are rejected by the backend up front)
- Process tree view (parent/child from
parent_pid, cycle-safe, virtualised) - Command palette (Ctrl/Cmd+K over pages, processes, ports, projects, tasks)
- Port → process jump and reverse port lookup in the process detail
- CSV export for processes and listening ports
- Environment self-check with per-item ok / warn / fail and a copyable report
- Auto-update (needs a signed release channel)
- Port collection runs
netstat -anoon Windows andss -tulpnon Linux (falling back tonetstat). CI defines Linux backend checks and adebbuild; platform and release-asset verification must be assessed separately. - Interaction paths are covered by Playwright (structure and WCAG AA across 10 pages × 8 themes), a structural feature suite for the command palette / process tree / port jump / CSV export, plus a packaged-exe desktop E2E over real IPC
- The cost ledger is a SQLite database (
cost.db,rusqlitebundled) with indexes on day / model / file. Aggregation runs inside SQL, so 100k records aggregate in ~250 ms instead of re-parsing a whole JSON file per query. The oldcost_data.jsonis imported once and renamed.migrated(never deleted) - Signing status and availability of release installers have not been verified here
- The tray menu text is bilingual Chinese/English and does not follow the UI language switch (it is built once at startup)
- No plugin API, no HTTP API and no remote monitoring — by design, see COMPARISON.md for what that costs us against Glances and System Informer
| File | Purpose | Location |
|---|---|---|
data.json |
Projects & tasks | app data dir |
cost.db |
Cost records + pricing settings (SQLite) | app data dir |
pricing_overrides.json |
User price overrides (editable on the Cost ledger page) | app data dir |
workbench.log |
Runtime logs | app data dir |
*.corrupt-<timestamp> |
Untouched backup of a data file that failed to parse (never overwritten by the next save) | app data dir |
On Windows the app data dir is %APPDATA%\com.workbench.desktop.
All screenshots are captured in browser demo mode (npm run preview):
outside Tauri the app injects static demo data from src/lib/demo.ts; all
paths and host names are placeholders, no real machine data.
| Overview | Processes |
|---|---|
![]() |
![]() |
| CPU / memory trends, disks, process groups | Full snapshot, virtual scrolling, kill confirmation |
| Servers & ports | AI sessions |
|---|---|
![]() |
![]() |
| netstat parsing, dev-framework tags | Session files, live sessions, tool mix |
| Cost ledger | Projects |
|---|---|
![]() |
![]() |
| Budget progress, trend, model split, price editor | Local projects, Git branch detection |
| Tasks | Settings |
|---|---|
![]() |
![]() |
| Drag-and-drop kanban | Theme editor, shell/layout, widgets, backup |
| Logs | Themes |
|---|---|
![]() |
![]() |
| Built-in log viewer, tail reading | Five styles, one token set |
| Brand mark (sidebar) | Brand mark (masthead) |
|---|---|
![]() |
![]() |
The mark is four vector strokes with currentColor and no plate — it sits
directly on the panel, on any background.
| Icon at every shipped size | Icon embedded in the exe |
|---|---|
| Collapsed sidebar | Narrow screen (drawer) |
|---|---|
![]() |
![]() |
Theme is data, not CSS: six colours plus six shape knobs expand into 26 CSS variables and a matching chart palette. Everything below is reachable from Settings → Appearance, and any built-in theme can be duplicated and edited.
| Orbit layout (Aurora) | Ledger layout (Ink) |
|---|---|
![]() |
![]() |
| Zen layout (Void) | Dense layout (Terminal) |
|---|---|
![]() |
![]() |
| Dock shell | Zen shell |
|---|---|
![]() |
![]() |
- Shell moves the navigation: left rail, top dock, or a collapsed icon strip.
- Layout re-arranges the overview: bento, ledger, orbit, dense, zen.
- Widgets can be dragged, reordered and hidden independently of both.
Five styles share one token set (surface, hairline, radius, type scale); they differ in surface colour, radius, density and type family, not just hue.
See docs/plan-audit.md for the capability audit and docs/audit-2026-10-22.md for the full correctness / security / reliability / performance / test-quality review, including the competitor matrix (Raycast, Datadog, Warp, Linear, Zed) and the measured coverage / complexity numbers.
Regenerate screenshots yourself: npm run build, then npm run preview,
then run a headless Chrome with
--remote-debugging-port=9222, then node scripts/shoot_docs.mjs.
The script asserts demo mode is active and refuses to shoot otherwise, so real
data can never leak into the repo.



















