用途
make-dashboard 用于根据本地数据文件(CSV、Parquet、JSON、DuckDB、SQLite)或 Postgres/MySQL 数据库,构建自包含的仪表盘、KPI 页面或图表。用户编写一份 JSON 规格(dashboard.json),脚本校验后生成单个离线 index.html。每个图表卡片会显示其 SQL、行数和刷新时间。构建过程无网络、无 LLM 参与。
主要能力
- 通过一份 JSON 规格定义数据源、SQL 查询、KPI 和 Vega-Lite 图表。
- 支持的数据源类型:csv、parquet、json(以键名作为表名)、duckdb、sqlite(附加,查询 name.table、path)、postgres、mysql(附加,通过 "env" 指定存放连接字符串的环境变量名,连接字符串不得写入规格)。
- KPI 格式支持 number、currency、percent、compact(percent 期望小数)。
- 图表宽度支持 full(默认)或 half;vegaLite 可接受任意不含 data 的 Vega-Lite 规格。
- 页面自动应用调色板和明暗主题,不应在 vegaLite 中设置颜色、config、width 或 autosize。
- 日期使用 UTC 字符串,配合 "type": "temporal" 和 utc 时间单位(如 utcyearmonth)。
- 每个查询限制 30 秒(build 时可用 --timeout <seconds> 调整)。
- 构建输出为单个 dist/index.html,可直接打开或通过 serve 命令查看。
使用方式
- 首次使用:若 <skill-dir>/node_modules 缺失,运行 npm ci --prefix <skill-dir> 进行一次性本地安装。若请求为空,询问要构建什么以及使用哪些数据文件。
- 先查看数据,不要猜测列名:node <skill-dir>/scripts/inspect.mjs data/sales.csv(显示列名、类型、5 行样本;支持 csv、parquet 或 json)。对于规格中附加的数据源(duckdb、sqlite、postgres、mysql),先列出其表,再查看其中一张:node <skill-dir>/scripts/inspect.mjs --spec dashboard.json --source shop 以及 --source shop --table items。
- 在数据旁编写 dashboard.json。源路径相对于规格文件,且必须位于规格文件所在文件夹内。实际创建文件,描述或宣布补丁不够;若文件工具失败,可用 shell 写入(cat > dashboard.json <<'EOF' … EOF),然后 ls dashboard.json 确认存在。
- 校验并修复直到输出 valid(它也会运行每个查询,valid 意味着可构建):node <skill-dir>/scripts/validate.mjs dashboard.json。
- 构建:node <skill-dir>/scripts/build.mjs dashboard.json(写入 dist/index.html,--out <dir> 可更改输出目录)。错误信息会指出确切路径和修复方法,例如 charts[0].vegaLite.encoding.y.field: "revnue" is not a column of query ...。
- 展示:运行 build 打印的 serve: 行(已包含正确路径),或直接打开 dist/index.html(它是单个文件)。
- 修改时只编辑 dashboard.json,然后重新运行步骤 4 和 5。切勿编辑 dist/。刷新即重新构建;定时重建参见 references/refresh.md。
规格形状示例:
{ "title": "Sales", "description": "optional", "maxRows": 5000, "sources": { "sales": { "type": "parquet", "path": "data/sales.parquet" } }, "queries": { "total": "SELECT SUM(amount) AS revenue FROM sales", "by_region": "SELECT region, SUM(amount) AS revenue FROM sales GROUP BY 1 ORDER BY 2 DESC" }, "kpis": [ { "id": "k1", "title": "Revenue", "query": "total", "field": "revenue", "format": "currency" } ], "charts": [ { "id": "c1", "title": "Revenue by region", "query": "by_region", "width": "half", "vegaLite": { "mark": "bar", "encoding": { "x": { "field": "region", "type": "nominal", "sort": null }, "y": { "field": "revenue", "type": "quantitative" } } } } ] }
queries 每个只能是一个 SELECT,名称需匹配 [a-z][a-z0-9_]*。每个聚合都要用别名(AS revenue);图表字段就是这些别名。sqlite/postgres/mysql 需要一次性运行 node <skill-dir>/scripts/setup.mjs dashboard.json(在线;构建保持离线)。
适用场景
- 从本地数据文件快速生成离线仪表盘或 KPI 页面。
- 需要图表卡片展示其 SQL、行数和刷新时间的场景。
- 数据源为 CSV、Parquet、JSON、DuckDB、SQLite 或 Postgres/MySQL 的分析展示。
- 希望构建过程无网络、无 LLM 参与,输出单个可分发 HTML 文件的场景。
限制
- 规格文件夹之外的数据会被拒绝。不要绕过:将数据移动或复制到规格文件夹下。只有运行命令的人可以添加 --allow-outside 到 validate/build。
- 查询有 30 秒限制(build 时可用 --timeout <seconds>)。
- 不应在 vegaLite 中设置颜色、config、width 或 autosize。
- 图表不能加载 URL(vegaLite 中任何位置的 url、data、usermeta 都会被拒绝),页面带有严格 CSP。
- 附加的 DuckDB 文件若定义视图或宏会被拒绝。
- 对于 postgres/mysql,附加时的 READ_ONLY 仅限制 DuckDB:真正的防护是只读数据库角色。
- 页面嵌入查询结果:若数据私密,不要发布。
- 若无法运行 shell 命令,需编写 dashboard.json 并提供步骤 4、5、6 的确切命令(含真实 <skill-dir> 路径)。
- 安全方面:SQL 由模型编写,视为不可信。每个查询必须解析为单个 SELECT,然后对其语法树运行允许列表(嵌套、CTE、子查询和字符串常量无法隐藏任何内容):只能读取声明的源(name、source.table、source.schema.table)及其自身的 CTE,唯一允许的表函数是 range、generate_series 和 unnest。系统目录(pg_catalog、information_schema、duckdb_* 视图)以及读取设置、环境或文件的函数会被拒绝。然后在锁定到声明文件的 DuckDB 中运行。标题、SQL 和数据在页面中会被转义。凭据不要放入规格和数据路径。不要将 path 指向用户未提及的文件。serve.mjs 绑定 127.0.0.1 并拒绝非本地 Host 头(403)。