Skip to content

[HDX-4003] [HDX-4004] feat(cli): dashboard list/export/create/import and saved-searches commands - #2914

Open
wrn14897 wants to merge 3 commits into
mainfrom
warren/cli-post-dashboard
Open

[HDX-4003] [HDX-4004] feat(cli): dashboard list/export/create/import and saved-searches commands#2914
wrn14897 wants to merge 3 commits into
mainfrom
warren/cli-post-dashboard

Conversation

@wrn14897

@wrn14897 wrn14897 commented Aug 14, 2026

Copy link
Copy Markdown
Member

Why

The hdx CLI could list dashboards and render tiles, but had no way to create, export, or import dashboards or saved searches — authoring them required the web UI or raw API calls. This implements the dashboard read/write command surface from HDX-4003 and HDX-4004, making the CLI a complete authoring surface for scripted/agent-driven workflows (author a dashboard, create it, then immediately validate every tile with hdx chart -d).

What

Dashboards (hdx dashboards, alias hdx dashboard)

  • list (default subcommand; bare hdx dashboards unchanged) — adds --query <substr> name filtering and --format table|json|csv (--json kept as back-compat alias). CSV emits id,name,tags,createdAt,updatedAt,tileCount. (HDX-4003)
  • export --id <id-or-name> — prints the full dashboard definition (charts, layout, filters) as JSON with server-managed fields stripped, re-validated against DashboardWithoutIdSchema so export | import is guaranteed round-trip compatible. Legacy dashboards that no longer validate are still exported with a stderr warning. (HDX-4003)
  • create — two modes: (HDX-4004)
    • --file <json> (or - for stdin): full DashboardWithoutId definition, validated locally with readable per-issue zod errors; missing tile ids auto-generated with the web's makeId() pattern
    • --name <name> + repeatable --chart '<json>' / --chart-file <path>: each chart is a full tile ({x,y,w,h,config}) or a bare chart config — charts without layout auto-flow onto the 24-column grid (12×4, two per row)
    • --if-not-exists skips creation when the name already exists (prints the existing id/URL, exit 0)
  • import --file <json> — recreates an exported dashboard, with --if-not-exists and --name-override. (HDX-4004)

Saved searches (hdx saved-searches)

  • list (default) — human-readable or --json
  • create --name --source <name-or-id> --where [--where-language lucene|sql] [--select] [--order-by] [--tags] — resolves sources by name or id

Bug fix

ApiClient.getSavedSearches() requested /saved-searches, but the API mounts the router at the singular /saved-search (packages/api/src/api-app.ts). The call always 404'd and the failure was silently swallowed in the TUI. Now fixed — saved searches load in the TUI source picker again.

Notes

  • Tickets sketched hdx dashboard (singular) and -s <url>; this follows the CLI's established conventions (hdx dashboards, -a/--app-url-s already means --source on chart) and adds a dashboard alias for the group.
  • Updated the command table in packages/cli/AGENTS.md; changeset included (@hyperdx/cli minor).

Validation

Verified end-to-end against a live deployment with OTel Demo data:

  • Created 4 dashboards (24 builder tiles: line/stacked_bar/number/table, trace + log + histogram-metric sources, dashboard filter) via hdx dashboards create --file — all accepted by the server-side schema; every tile rendered status: ok via hdx chart -d <dashboard> --json.
  • list --query flow --format csv / --format json / bare --json back-compat all verified.
  • Round trip: export --id "Shop Fleet Overview"import --name-override "… (copy)" → all 9 tiles of the copy rendered ok → re-running import --if-not-exists skipped with the existing id.
  • Inline-chart mode: create --name "CLI Smoke Test" --chart '…' --chart '…' auto-laid-out both tiles and both rendered ok; --if-not-exists on the same name skipped. (Test dashboards deleted afterwards.)
  • Created 5 saved searches via hdx saved-searches create (lucene + sql filters, tags) and confirmed with hdx saved-searches list.
  • yarn tsc --noEmit, yarn ci:lint, and prettier pass.

- hdx dashboards create --file <json>: validates against
  DashboardWithoutIdSchema locally, auto-generates missing tile ids,
  and POSTs to /dashboards
- hdx saved-searches list/create: list saved searches and create new
  ones (--name/--source/--where/--where-language/--select/--order-by/--tags),
  resolving sources by name or id
- fix getSavedSearches() to hit /saved-search (the API mounts the
  singular path; the old /saved-searches always 404'd and was silently
  swallowed)
@changeset-bot

changeset-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2712850

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hyperdx/cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
hyperdx-oss Ignored Ignored Preview Aug 14, 2026 10:02pm
hyperdx-storybook Ignored Ignored Preview Aug 14, 2026 10:02pm

Request Review

@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Aug 14, 2026
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 811 production lines changed (Tier 2 max: < 250)

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 2
  • Production lines changed: 811
  • Branch: warren/cli-post-dashboard
  • Author: wrn14897

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@greptile-apps

greptile-apps Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR expands the CLI’s dashboard authoring surface and adds saved-search listing and creation while correcting the saved-search API route.

  • Adds dashboard list filtering and formats, schema-validated create/import, export, generated tile IDs, and idempotent creation support.
  • Adds saved-search list/create commands with source resolution and structured output.
  • Corrects the saved-search endpoint and adds API client creation methods with detailed error responses.
  • Updates CLI documentation and adds a minor changeset.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/cli/src/cli.tsx Adds dashboard list/export/create/import and saved-search list/create command flows; the previously misleading dashboard input description now points to the full shared schema and a valid export shape.
packages/cli/src/api/client.ts Corrects the saved-search route and adds typed dashboard and saved-search creation requests with bounded response-error details.
packages/cli/AGENTS.md Updates the documented CLI command surface for the new dashboard and saved-search operations.
.changeset/cli-create-dashboards-saved-searches.md Records the user-facing CLI additions and endpoint correction as a minor release.

Sequence Diagram

sequenceDiagram
  participant User
  participant CLI as hdx CLI
  participant Schema as DashboardWithoutIdSchema
  participant API as HyperDX API
  User->>CLI: dashboards create/import
  CLI->>CLI: Read JSON and generate missing tile IDs
  CLI->>Schema: Validate dashboard definition
  alt Valid definition
    Schema-->>CLI: Parsed dashboard
    CLI->>API: POST /dashboards
    API-->>CLI: Created dashboard
    CLI-->>User: Dashboard details
  else Invalid definition
    Schema-->>CLI: Validation issues
    CLI-->>User: Per-field errors
  end
Loading

Reviews (3): Last reviewed commit: "feat(cli): dashboard list/export/import ..." | Re-trigger Greptile

Comment thread packages/cli/src/cli.tsx Outdated
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Deep Review

No critical issues found. The change is additive CLI surface (new dashboards create/export/import and saved-searches list/create commands) plus a genuine bug fix (getSavedSearches now targets the singular /saved-search mount). Request/response contracts were verified against the server: POST /saved-search and POST /dashboards bodies align with the CLI payloads, and created.id ?? created._id correctly handles the dashboard response that omits the id virtual. Auto-layout math is sound (grid is 24 columns, so x=(i%2)*12, w=12 places two tiles per row without overflow), zod strips unknown keys so crafted JSON keys cannot pollute prototypes, and tile-id backfill is correct.

🟡 P2 -- recommended

  • packages/cli/src/cli.tsx:967 -- The new pure helpers (csvField RFC4180 quoting, chart-mode 24-column auto-layout, validateDashboardOrExit tile-id backfill, tags parsing) and the /saved-search path fix ship with no tests, even though the package has a jest harness and a convention of importing testable helpers from @/shared/*; the helpers are defined inline in cli.tsx (no exports, top-level program.parse()), so they cannot be imported by a test without extraction.
    • Fix: Extract the pure helpers into a @/shared module and add unit tests, including a client test asserting getSavedSearches/createSavedSearch hit /saved-search (singular) so the path fix cannot silently regress.
    • testing, kieran-typescript, project-standards, reliability
  • packages/cli/src/cli.tsx:1319 -- The CLI validates dashboard definitions locally with plain DashboardWithoutIdSchema, but the server runs migrateLegacyDashboardTileColors plus addFilterIssues; a legacy dashboard exported here is emitted as a raw doc, then import re-validates it strictly and rejects it locally even though the server would heal and accept it.
    • Fix: Treat local validation as a best-effort pre-check (skip strict rejection on the import path, or run the same color-healing before validating) and rely on formatErrorBody to surface authoritative server rejections.
    • api-contract
🔵 P3 nitpicks (6)
  • packages/cli/src/cli.tsx:1097 -- The new list/export/saved-searches-list actions call getDashboards/getSavedSearches/getSources without try/catch, so a network/500 failure surfaces as an unhandled rejection with a raw stack trace, unlike the write commands in the same diff that emit a clean one-line error and exit(1) (matches the pre-existing connections/sources convention, so not a regression).
    • Fix: Wrap the top-level client.get* calls in the read paths to print a chalk.red one-liner and process.exit(1), or add a global unhandledRejection handler with program.parseAsync().
  • packages/cli/src/cli.tsx:977 -- readJsonFileOrExit('-') on an interactive TTY (no piped stdin) blocks waiting for EOF or throws EAGAIN, whose caught message (Failed to read -: ...EAGAIN) does not tell the user stdin was expected.
    • Fix: When path === '-' and process.stdin.isTTY, print a clear "pipe JSON on stdin or pass a file path" message and exit 1 before reading.
  • packages/cli/src/cli.tsx:1000 -- The dashboard type is spelled ReturnType<typeof DashboardWithoutIdSchema.parse> at two signatures, an obscure synonym for the exported DashboardWithoutId alias already imported in client.ts.
    • Fix: Import and use DashboardWithoutId in both signatures for cross-file consistency.
  • packages/cli/src/cli.tsx:1199 -- The --app-url/--json/--format/--query options are declared verbatim on both the bare dashboards command and its list subcommand, both dispatching to runDashboardsList; a future option or default change in one place silently diverges.
    • Fix: Make list the { isDefault: true } subcommand (as done for saved-searches list) or extract a shared option-registration helper.
  • packages/cli/src/cli.tsx:1150 -- The getSources + Object.fromEntries(...flatMap) source-name resolution block (with its non-fatal try/catch) and the comma-separated tags-parsing block are each duplicated verbatim across the dashboards and saved-searches actions.
    • Fix: Extract resolveSourceNames(client) and parseTags(value) helpers and reuse them.
  • packages/cli/src/cli.tsx:1027 -- Source and dashboard lookup by name (saved-searches create --source, dashboards export --id) silently picks the first case-insensitive match, so two sources/dashboards sharing a name resolve ambiguously with no warning.
    • Fix: Detect multiple name matches and error out listing the candidate ids, requiring an id to disambiguate.

Reviewers (6): api-contract, kieran-typescript, reliability, maintainability, testing, project-standards.

Testing gaps:

  • No unit tests for csvField quoting, chart-mode auto-layout grid math, validateDashboardOrExit tile-id backfill, or tags parsing (all inline in cli.tsx, currently unimportable).
  • No client test asserting the /saved-search path or formatErrorBody error surfacing; a path regression would reintroduce the original 404 undetected.
  • No coverage of readJsonFileOrExit stdin/empty/TTY branches, the --file vs --name mode-exclusion guard, or the --if-not-exists skip path.
  • Coverage note: correctness, adversarial, and agent-native reviewers did not return in time; the orchestrator independently verified the correctness/adversarial surface (grid bounds, zod key-stripping, prototype-pollution safety, id/_id fallbacks, TOCTOU on --if-not-exists, tile-id collision probability) and found no additional defects.

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 295 passed • 1 skipped • 1157s

Status Count
✅ Passed 295
❌ Failed 0
⚠️ Flaky 1
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@vercel
vercel Bot temporarily deployed to Preview – hyperdx-storybook August 14, 2026 21:23 Inactive
…3, HDX-4004)

- hdx dashboards list (default subcommand): --query name filter and
  --format table|json|csv; --json kept as a back-compat alias
- hdx dashboards export --id <id-or-name>: emits a round-trip-safe
  definition (server-managed fields stripped, re-validated against
  DashboardWithoutIdSchema so export | import always round-trips)
- hdx dashboards create: new inline-chart mode (--name + repeatable
  --chart / --chart-file with auto grid layout) alongside --file, plus
  --if-not-exists for idempotent creation
- hdx dashboards import --file: recreates exported dashboards with
  --if-not-exists and --name-override
- add 'dashboard' alias for the command group
@wrn14897 wrn14897 changed the title feat(cli): add dashboards create and saved-searches commands [HDX-4003] [HDX-4004] feat(cli): dashboard list/export/create/import and saved-searches commands Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant