Ship analytics dashboards without leaving the terminal.
OpenBoardCLI turns a single CSV, Excel, or JSON file into one authenticated
React analytics app — then builds it, pushes it to GitHub, and deploys it
to Vercel. Every new dataset becomes another tab in the same app, and a
master Overview tab tracks spend and insights across all of them.
Privacy-first: pick Local only (Ollama / LM Studio, nothing leaves
your machine), Hybrid, or All remote before anything else.
Pick your privacy mode first — Local only, Hybrid, or All remote — then an LLM provider, and (All remote only) GitHub + Vercel tokens. A first run opens onboarding automatically; everything is stored encrypted in ~/.openboard.
02point at data
Hand it a CSV, Excel (.xlsx), or JSON path. OpenBoardCLI parses it, infers types, and summarizes the columns — with 10 category presets (travel, food, subscriptions, invoices, …) that tune the first generation.
03describe the board
An internal chat drives the model. Ask for the metrics, charts, and tables you actually want.
04deploy
Build, commit, push, and ship to Vercel — one step, with a live progress bar. Each deploy is tagged for rollback and health-checked before OpenBoardCLI calls it done.
# what's in the box
Three privacy modes
Local only — local LLM (Ollama / LM Studio) + local preview, nothing leaves your machine. Hybrid — cloud LLM, local preview. All remote — cloud LLM + GitHub + live Vercel app. The mode is chosen first and shapes everything: wizard steps, chat commands, and the agent pipeline.
Bring your own model
OpenAI, Anthropic, OpenAI Codex, Google Gemini, Moonshot, xAI, Mistral, OpenRouter — or a local Ollama / LM Studio server — with a pick-a-model list and an execution-effort dial (low → max) that maps to each provider's reasoning knob. Switch any time with /model. Keys are encrypted with AES-256-GCM.
One app, many tabs
The first dataset scaffolds a shared authenticated React app. Each later dataset is added as a tab — same shell, same login, same deploy target. Tab composition is deterministic and product-owned: the model writes dashboard components, never your app shell, so one generation can't break another dashboard's tab.
Master Overview tab
A landing tab generated across all your dashboards: total spend, past 7 days, cross-app trend, per-app breakdown, and top spending + saving insights. Refreshed automatically as dashboards come and go.
10 category presets
Health, finance, grocery, travel & rides, food delivery, shopping, subscriptions, utilities & bills, invoice ledgers, or custom — each ships a tuned prompt so the first generation already knows your columns.
Invoice inbox → dashboards
Point OpenBoardCLI at Gmail — under the top-level Integrations menu — and it turns receipts into per-biller spending dashboards on a schedule. Ready-made fetchers ship for Amazon, Amazon Pay, Rapido, Swiggy Food & Instamart, Uber, Urban Company and Zomato. Bring an existing CSV and its dashboard is built from the data already on disk — no waiting for new mail. Your App Password is encrypted and handed to each fetcher through its environment at run time — never written to disk.
Biller Studio — it writes the fetcher
No shipped list covers your billers, so OpenBoardCLI builds one. Give it a sender address and a subject fragment; it samples one real email — forwarded receipts count too — shows you the exact text before anything reaches your model, proposes the fields it can extract, then writes, compiles and dry-runs a fetcher against your mailbox before saving. PDF receipts included. The model writes only the biller-specific regions and OpenBoardCLI splices them into its own skeleton — ~5× less output, so even a slow local model finishes without truncating. Generated code is scanned first — a fetcher reads mail and writes a CSV, nothing else.
Fetches you can watch — and stop
Scheduled runs write to a persistent fetch log you can scroll through later, and the screen says fetching now while one is in flight. Stop fetch aborts a run mid-way — even one the scheduler started — and the schedule anchors when a run starts, so quitting part-way never triggers a full re-fetch on the next launch.
Server-side auth by default
Login runs through a real /api/auth endpoint backed by an httpOnly cookie. No credentials and no auth decisions are shipped to the client.
One shell, every screen
The main menu reads Onboarding · Integrations · Dashboards · Settings, and every screen shares the same frame: a breadcrumb title, one line of facts, the options, a one-line description of whatever is highlighted, and key hints worded identically everywhere. Data sources live under Integrations; deploy targets stay in Settings.
Headless & scriptable
The same pipeline runs non-interactively from an agent — step-labelled progress ([3/8] + generation heartbeats), NDJSON events, stable error codes, dry runs, idempotency keys. Flip to the view for the command contract.
Prompt history
Every change is recorded per dashboard, so openboard update can regenerate from fresh data without you re-describing intent.
Deploys itself
Build → GitHub push → Vercel deploy is part of the flow. If CLI auth is missing, it falls back to Vercel's Git integration on the pushed commit.
Self-healing builds
When generated code fails to build, the error is fed back to the model for an automatic repair pass — most broken generations fix themselves before you ever see them.
Verified, reversible deploys
After every deploy the live URL is health-checked (app shell + auth API), and each deploy is git-tagged — openboard rollback restores the previous one in a single command.
Resumable runs
Every run persists its state under ~/.openboard/runs. If a deploy dies after generation, agent resume picks up at the build step — no second LLM bill.
# quickstart
Node 18+.
# install from npm
npm install -g openboard-cli
# launch the interactive TUI
openboard
# …or run it headless (csv, xlsx, or json)
openboard agent create --data ./rides.csv --name "Rides" --type travel
openboard agent create --data ./invoices.xlsx --name "Invoices" --type invoices
openboard agent update --dashboard rides --prompt "add a weekday vs weekend chart"
# from source instead
git clone https://github.com/syedateebulislam/openboard
cd openboard && npm install && npm run build
Built with TypeScript, Ink, and a pluggable LLM layer.
Generated apps use React, Vite, Recharts, and Tailwind.
AGENT INTERFACE · NON-INTERACTIVE · JSON IN/OUT
Drive OpenBoardCLI from your agent.
OpenBoardCLI ships a stable, non-interactive command contract. Point
OpenClaw — or any agent runner, cron job, or CI step — at a data
file and it runs the whole pipeline: parse, generate, build, push,
deploy. No TUI, no prompts, machine-readable output.
openclaw / cron / ci / llm agent└─ openboard reads the csv / json
└─ analyzes columns + rows
└─ configured llm writes react files
└─ build (auto-repairs on failure) · commit · push to github
└─ deploy to vercel (or git-integration fallback)└─ verify the live url · tag deploy-N for rollback
Configure OpenBoardCLI once (provider key, GitHub token, Vercel token, dashboard login). After that the contract below is fully hands-off. Each run streams NDJSON phase events on stderr and persists resumable state.
# command contract
setup — configure the LLM headlessly
openboard agent setup mode --mode local|hybrid|remote
openboard agent setup llm --provider openai|openai-codex|anthropic|gemini|moonshot|xai|mistral|openrouter|ollama|lmstudio \
[--model "<model>"] [--effort low|medium|high|max] [--api-key "sk-..."] [--json]
openboard agent setup status --json
# --effort maps to the provider's reasoning knob (reasoning_effort / extended thinking)# secrets can come from OPENBOARD_* env vars instead of flags
create — onboard a new dataset as a tab
openboard agent create --data "<csv|xlsx|json>" --name "<title>" \
[--type health|finance|grocery|travel|food|shopping|subscriptions|utilities|invoices|custom] \
[--prompt "<intent>"] [--dry-run] [--idempotency-key "<key>"] [--json]
# alias: openboard agent onboard · returns a reusable dashboard selector# --type picks a tuned category prompt; the Overview master tab updates automatically# --dry-run returns the plan (rows, columns, selector) without an LLM call# --idempotency-key makes orchestrator retries safe — same key, same result
update — change an existing dashboard
openboard agent update --dashboard "<selector>" --prompt "<request>" \
[--data "<csv|xlsx|json>"] [--dry-run] [--json]
# e.g. --prompt "add a weekday vs weekend chart and flag pricey rides"
billers — turn a Gmail inbox into per-biller dashboards
openboard agent setup billers --scripts-dir "<folder of fetch_<biller>.py>" \
--biller-email "you@gmail.com" --biller-app-password "<16-char App Password>" [--json]
openboard agent billers status --json # discovered fetchers + which are enabled
openboard agent billers sync [--biller zomato] --json
# credentials reach each fetcher via OPENBOARD_GMAIL_* in its environment, never on disk# results[]: key, displayName, ok, changed, dashboardUpdated, dashboardExists, error# fetches are lock-serialised — a cron sync overlapping an open TUI is safe# creating a NEW fetcher (Biller Studio) is TUI-only: it shows you the email first# in the TUI all of this lives under Integrations → Gmail
refresh — regenerate from saved prompt history
openboard update --dashboard "<selector>" # one board, latest data
openboard update --all # every registered board
query — discover state before acting
openboard agent list --json # all dashboards + deploy urls
openboard agent status --dashboard "<selector>" --json
# incl. dataStale: data changed since last gen?
openboard agent runs --json # recent runs + failure summary
recover — resume a failed run, or roll back a bad deploy
openboard agent resume "<run-id>" --json # re-runs build→deploy only if generation succeeded
openboard agent rollback --dashboard "<selector>" --json
# restore + redeploy the previous deploy-N tag
Selectors come back from create and are reused by update. Run ids come back from every run and feed resume. Quote Windows paths. From source, swap openboard for node dist/index.js.
Exit code 0 on success, non-zero on failure. With --json, the final result is the only thing on stdout; stderr streams NDJSON progress events ({"event":"phase","phase":"build","pct":52,"step":5,"totalSteps":8}) — track pct or step/totalSteps for progress; every provider emits ~10s generation heartbeats, so a phase silent for minutes is wedged. Without --json, stdout lines carry the same [step/8] labels. verified is the post-deploy health check. Branch on errorCode (E_BUILD_FAILED, E_DEPLOY_AUTH, E_LOCKED, E_NO_LLM, …), never on the prose error — the full table is in Agent.md.
# wire it into OpenClaw
OpenClaw (or any runner) just shells out to the contract and reads the JSON back.
# an OpenClaw task / any agent step — idempotency key makes retries safe
result=$(openboard agent create --data "$DATA" --name "$NAME" --idempotency-key "$TASK_ID" --json)
selector=$(echo "$result" | jq -r '.dashboardSelector')
# later, on a user request
openboard agent update --dashboard "$selector" --prompt "$REQUEST" --json
# on failure: resume from the failed phase (no second LLM bill after generation)
runid=$(echo "$result" | jq -r '.runId')
openboard agent resume "$runid" --json
# bad deploy? restore the previous tagged deploy
openboard agent rollback --dashboard "$selector" --json
# nightly, after data files refresh — skip boards whose data didn't change
stale=$(openboard agent status --dashboard "$selector" --json | jq -r '.dataStale')
[ "$stale" = "true" ] && openboard update --dashboard "$selector"
Full contract, flag tables, and decision rules live in Agent.md. A plain-text summary for LLMs is at /llms.txt.