Skip to content

feat(sql): add the traceroot sql command (api client, CSV/table/JSON/file output)#30

Draft
dark-sorceror wants to merge 4 commits into
feat/sql-command-epicfrom
feat/sql-command
Draft

feat(sql): add the traceroot sql command (api client, CSV/table/JSON/file output)#30
dark-sorceror wants to merge 4 commits into
feat/sql-command-epicfrom
feat/sql-command

Conversation

@dark-sorceror

@dark-sorceror dark-sorceror commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #27

Adds the primary CLI surface for the SQL Query Gateway: traceroot sql "<query>". Users send read-only SQL against the curated analytical export (spans / traces) and get results back as an aligned table (default), one-line JSON, or RFC-4180 CSV — optionally written straight to a file.

It delivers the API-client methods, the CSV renderer, and the sql query command. A follow-up PR (feat/sql-schema-command) adds traceroot sql schema and the README docs.

How it works

traceroot sql "<query>"  [-f/--file <path>] [--csv] [--output <file>] [--max-rows <n>] [--json]
  → resolve query (positional XOR --file) + output mode (table | --json | --csv)
    → POST /api/v1/public/sql   { query, max_rows? }   (Bearer auth, key-scrubbed on failure)
      → render columns/rows as table | JSON line | CSV
        → write to stdout, or to --output <file>
           (truncated=true → stderr warning in table/csv; self-describing in --json)

The backend resolves project_id server-side from the API key — the CLI never sends scope. The CLI only knows the two endpoint paths and the documented request/response shape; nothing else about the backend is assumed.

What's included

API client (src/api/client.ts)

  • Generalized the private request<T> helper to take an optional { method, body } while leaving every existing GET caller (whoami, listTraces, getTrace, exportTrace) byte-for-byte unchanged.
  • sqlQuery(body)POST /api/v1/public/sql; sqlSchema()GET /api/v1/public/sql/schema (consumed by the follow-up schema PR). Both reuse the existing Bearer-auth headers, the per-request timeout signal, the {"detail": "..."} error-envelope parsing, and the api-key redaction on network failures.
  • Hand-written types (SqlColumn, SqlQueryRequest, SqlQueryResponse, SqlSchemaTable, SqlSchemaResponse) — the SQL endpoints are not in openapi.json yet, so these stand in until codegen can derive them.

CSV renderer (src/render/csv.ts)

  • renderCsv(headers, rows) — RFC-4180-ish, dependency-free: quotes a cell only when it contains , " \r \n; doubles inner quotes; null/undefined → empty cell; objects/arrays → JSON.stringify; numbers/booleans stringified; always a trailing newline.

sql query command (src/commands/sql.ts, src/commands/sql/query.ts)

  • New sql command group with the query as its default action, registered in src/commands/index.ts.
  • Options: -f/--file <path>, --csv, --output <file>, --max-rows <n>, plus the existing global --json.
  • Rules: exactly one of positional query / --file; --json and --csv are mutually exclusive; --max-rows must be a positive integer. All argument validation runs before auth resolution, so misuse fails fast without credentials.
  • Output: aligned table (default, bold header) / one-line JSON / CSV; --output writes the selected format to a file. truncated=true prints a stderr warning in table/CSV mode and is self-describing in JSON mode.
  • Errors follow the existing CLI convention (CliError → central reportError, exit 1); a backend 400 surfaces its detail verbatim. No new exit-code taxonomy.
  • --help carries the canonical examples (count spans 24h, p95 latency by model, cost by model, export to CSV, find error spans) using the canonical time column span_start_time.

Test plan

  • sqlQuery POSTs to the right URL with Bearer auth + content-type, serializing query/parameters/max_rows into the body; sqlSchema GETs with auth
  • API-key scrubbing preserved on POST network failures; {"detail"}CliError
  • CSV quoting for comma / quote / CR / LF; null & undefined → empty; object → JSON; trailing newline; header-only when no rows
  • Command table / JSON / CSV output; --output writes the selected format to a file
  • -f/--file reads the query; positional + --file rejects; neither rejects; empty/whitespace query rejects; unreadable file rejects
  • --json + --csv rejects; --max-rows forwarded to the request body; non-positive --max-rows rejects
  • Truncation warning in table/CSV mode; no warning in JSON mode
  • Unknown flag rejected via the CLI surface (runCli)
  • npm run build, npm run lint, npm run format:check clean; full Vitest suite green
  • Manual smoke against a live backend (traceroot sql "SELECT count() FROM spans", ... --csv) — not run here; no live SQL Gateway available

Summary by cubic

Adds the traceroot sql "<query>" command to run read-only SQL against the analytical export and output results as a table (default), one-line JSON, or RFC‑4180 CSV. Includes new API client methods, a CSV renderer, and file output support.

  • New Features

    • traceroot sql "<query>" with positional query or -f/--file; --json or --csv output; --output to file; --max-rows limit; validation runs before auth.
    • API client: sqlQuery POST to /api/v1/public/sql; sqlSchema GET /api/v1/public/sql/schema; Bearer auth, per-request timeout, {"detail"} error parsing, API key scrubbing on failures.
    • CSV: renderCsv(headers, rows) with RFC‑4180 quoting (commas, quotes, CR/LF), JSON-stringify for objects, empty for null/undefined, trailing newline.
    • Output: aligned table with bold header (default) | single-line JSON | CSV; --output writes bytes; warns on truncation in table/CSV; JSON is self-describing; backend 400 detail surfaces as-is.
  • Refactors

    • Internal request<T> supports { method, body }; existing GET callers (whoami, listTraces, getTrace, exportTrace) unchanged.

Written for commit b6d7e18. Summary will update on new commits.

Review in cubic

@dark-sorceror dark-sorceror changed the base branch from main to feat/sql-command-epic June 29, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the traceroot sql CLI command (API client, CSV/table/JSON/file output)

1 participant