Files
nntrivi2001 4aae61297f feat: analytics parity — tool cost, date ranges, budgets, query explorer, replay, snapshots (v2.4.0)
Analytics parity phases 2–7 (docs/plans/analytics-parity/), built on
138e706 and ported onto v2.3.1:
- Cost by AI tool (?tool= on cost/analytics/stats/workflows, by_tool,
  Codex fast/priority and flex tiers, agenthub_cost_usd_total{tool,model})
- Date ranges (from/to) on cost/analytics/workflows; Analytics range select
  with Today / This week (Monday) / This month (1st) / 7d / 30d / 90d / All /
  Custom; workflow filters carried into workflow-stats.js (read worker)
- Budgets and cost alerts as alert rule types (alert_rules rebuilt without
  its rule_type CHECK), GET /api/budgets/status, rule preview, spend pace,
  status-bar budget chip; rules stay editable in place
- Query Explorer (/query, allowlisted sort, CSV/JSON export) and scheduled
  reports (report_definitions/report_runs, Settings → Reports)
- Session Replay tab and immutable snapshots (session_snapshots, /snapshot/:token)
- Remote Codex sync, push ingest with a hashed token, privacy redaction and
  ignored folders (Settings → Data), Cursor sessions (no cost)
- Read-only MCP server, statusline, Grafana/Prometheus files

Also: resizable sidebar, pet run/wave animations, the workspace home keeps a
half-typed prompt, and the pre-commit hook runs only the related tests for a
small commit.
2026-10-06 17:12:28 +07:00

85 lines
3.4 KiB
JavaScript

/**
* @file Static checks for the Prometheus/Grafana bundle (`monitoring/`,
* `docker-compose.monitoring.yml`): every YAML and dashboard JSON parses, the
* compose file mounts paths that exist, and every `agenthub_*` metric a board
* or recording rule queries is one `server/routes/metrics.js` exports — except
* `agenthub_cost_usd_total`, which those panels mark as required from another
* phase. Docker is never run.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { it } = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const yaml = require("js-yaml");
const ROOT = path.resolve(__dirname, "..", "..");
const MON = path.join(ROOT, "monitoring");
const readYaml = (p) => yaml.load(fs.readFileSync(path.join(ROOT, p), "utf8"));
const BOARDS = fs
.readdirSync(path.join(MON, "grafana", "dashboards"))
.map((f) => path.join(MON, "grafana", "dashboards", f));
const exported = new Set(
fs
.readFileSync(path.join(ROOT, "server", "routes", "metrics.js"), "utf8")
.match(/"agenthub_[a-z_]+"/g)
.map((s) => s.slice(1, -1))
);
const OPTIONAL = new Set(["agenthub_cost_usd_total"]);
const metricsIn = (expr) => expr.match(/\bagenthub_[a-z_]+\b/g) || [];
it("compose, Prometheus and provisioning YAML parse and point at real files", () => {
const compose = readYaml("docker-compose.monitoring.yml");
assert.deepEqual(Object.keys(compose.services).sort(), ["grafana", "prometheus"]);
for (const svc of Object.values(compose.services)) {
for (const v of svc.volumes) {
const src = v.split(":")[0];
if (src.startsWith("./")) assert.ok(fs.existsSync(path.join(ROOT, src)), src);
}
}
const prom = readYaml("monitoring/prometheus/prometheus.yml");
assert.equal(prom.scrape_configs[0].metrics_path, "/api/metrics");
for (const f of prom.rule_files) assert.ok(fs.existsSync(path.join(MON, "prometheus", f)), f);
readYaml("monitoring/grafana/provisioning/datasources/datasource.yml");
readYaml("monitoring/grafana/provisioning/dashboards/provider.yml");
});
it("recording rules query only exported metrics", () => {
const rules = readYaml("monitoring/prometheus/agenthub-rules.yml");
const all = rules.groups.flatMap((g) => g.rules);
assert.ok(all.length > 0);
for (const r of all) {
assert.match(r.record, /^agenthub:/);
for (const m of metricsIn(r.expr))
assert.ok(exported.has(m) || OPTIONAL.has(m), `${r.record}: ${m}`);
}
});
it("three boards: valid JSON, unique uid and panel ids, exported metrics only", () => {
assert.equal(BOARDS.length, 3);
const uids = new Set();
let costPanels = 0;
for (const file of BOARDS) {
const b = JSON.parse(fs.readFileSync(file, "utf8"));
assert.ok(b.uid && !uids.has(b.uid), file);
uids.add(b.uid);
const ids = b.panels.map((p) => p.id);
assert.equal(new Set(ids).size, ids.length, `${file}: duplicate panel id`);
for (const p of b.panels) {
assert.equal(p.datasource.uid, "${ds}");
for (const t of p.targets) {
for (const m of metricsIn(t.expr)) {
assert.ok(exported.has(m) || OPTIONAL.has(m), `${b.uid} / ${p.title}: ${m}`);
if (OPTIONAL.has(m)) {
assert.match(p.description || "", /Requires agenthub_cost_usd_total/, p.title);
costPanels++;
}
}
}
}
}
assert.ok(costPanels >= 4, "cost panels present");
});