Bring your own model
OpenAI, Anthropic, OpenAI Codex, Moonshot, or a local Ollama host. API keys are encrypted with AES-256-GCM and never reach the browser bundle.
CLI · TUI · MIT licensed
OpenBoard turns a single CSV 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, not a new project.
npm install -g openboard-cli
node dist/index.jsPick an LLM provider and add your GitHub + Vercel tokens once. Everything is stored encrypted in ~/.openboard.
Hand it a CSV or JSON path. OpenBoard parses it, infers types, and summarizes the columns.
An internal chat drives the model. Ask for the metrics, charts, and tables you actually want.
Build, commit, push, and ship to Vercel — one step, with a live progress bar. Each deploy is tagged for rollback and health-checked before OpenBoard calls it done.
OpenAI, Anthropic, OpenAI Codex, Moonshot, or a local Ollama host. API keys are encrypted with AES-256-GCM and never reach the browser bundle.
The first dataset scaffolds a shared authenticated React app. Each later dataset is added as a tab — same shell, same login, same deploy target.
Login runs through a real /api/auth endpoint backed by an httpOnly cookie. No credentials and no auth decisions are shipped to the client.
The same pipeline runs non-interactively from an agent — NDJSON progress events, stable error codes, dry runs, idempotency keys. Flip to the view for the command contract.
Every change is recorded per dashboard, so openboard update can regenerate from fresh data without you re-describing intent.
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.
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.
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.
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.
Node 18+.
# install from npm
npm install -g openboard-cli
# launch the interactive TUI
openboard
# …or run it headless
openboard agent create --data ./rides.csv --name "Rides"
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
OpenBoard 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.
openboard agent create --data ./rides.csv --name "Rides" --json
$ openboard agent create --data ./rides.csv --name "Rides" --json {"event":"phase","phase":"generate","pct":8} · {"event":"phase","phase":"build","pct":52} … { "success": true, "action": "create", "dashboardSelector": "rides", "deployUrl": "https://rides-board.vercel.app", "deployTag": "deploy-1", "verified": true, "runId": "run-2026-06-10-ab12cd34", "writtenFiles": ["App.tsx", "components/RidesDashboard.tsx"] } $ ▌
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 OpenBoard 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.
openboard agent create --data "<csv|json>" --name "<title>" \
[--type health|finance|grocery|custom] [--prompt "<intent>"] \
[--dry-run] [--idempotency-key "<key>"] [--json]
# alias: openboard agent onboard · returns a reusable dashboard selector
# --dry-run returns the plan (rows, columns, selector) without an LLM call
# --idempotency-key makes orchestrator retries safe — same key, same result
openboard agent update --dashboard "<selector>" --prompt "<request>" \
[--data "<csv|json>"] [--dry-run] [--json]
# e.g. --prompt "add a weekday vs weekend chart and flag pricey rides"
openboard update --dashboard "<selector>" # one board, latest data
openboard update --all # every registered board
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
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.
{
"success": true,
"action": "update",
"dashboard": "Uber Rides",
"dashboardSelector": "uber-rides",
"deployUrl": "https://…vercel.app",
"deployTag": "deploy-4",
"verified": true,
"runId": "run-2026-06-10-ab12cd34",
"tokenUsage": { "promptTokens": 5200, … },
"writtenFiles": ["App.tsx", "…"]
}
{
"success": false,
"action": "update",
"dashboardSelector": "uber-rides",
"runId": "run-2026-06-10-ab12cd34",
"error": "Dashboard not found: …",
"errorCode": "E_DASHBOARD_NOT_FOUND",
"writtenFiles": []
}
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}) — track pct for progress, treat a silent phase as wedged. 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.
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.