import { type TurnRow, type UsageFilter, type UsageRepository } from '../db/repositories/usage-repository.js'; /** * Getting the data back out, one row per stored turn. * * "Local-first, your data is yours" was the promise, and every output was a * nested aggregate: no command emitted a record-level row, and no format any * spreadsheet or CSV loader consumes. The only route to the rows was * `sqlite3 usage.db '.mode csv' 'SELECT * FROM usage_records'`, which bypasses * the product entirely and depends on a schema the docs explicitly call internal * and unversioned. This is that route, supported. * * The column set is a STABLE, documented contract -- deliberately not * `SELECT *`. The table may grow a column without every downstream spreadsheet * breaking, and a column here may not silently change meaning. */ export type ExportFormat = 'csv' | 'jsonl'; export declare const EXPORT_FORMATS: ExportFormat[]; /** * The exported columns, in order. * * `cost` and `estimated_cost` are separate columns carrying `cost_basis` * alongside, for the same reason every report keeps them apart: a single `cost` * column would force a choice between blending two incomparable figures and * dropping one. */ export declare const EXPORT_COLUMNS: readonly ["id", "timestamp", "client", "provider", "model", "session_id", "project_path", "turn_kind", "speed", "input_tokens", "output_tokens", "cache_read_tokens", "cache_write_tokens", "cache_write_5m_tokens", "cache_write_1h_tokens", "reasoning_tokens", "total_tokens", "cost_basis", "cost", "estimated_cost", "currency"]; export type ExportColumn = (typeof EXPORT_COLUMNS)[number]; /** * RFC 4180 quoting. * * A project path is the field most likely to contain a comma or a quote, and an * unquoted one silently shifts every later column in the row -- a corruption * that looks like data. */ export declare function csvCell(value: string | number | undefined): string; export declare function csvRow(turn: TurnRow): string; export declare function csvHeader(): string; export declare function jsonlRow(turn: TurnRow): string; export interface ExportOptions { format?: ExportFormat; /** Stop after this many rows. Omit to export everything the filter matches. */ limit?: number; } export interface ExportResult { rows: number; format: ExportFormat; /** Total rows the filter matches, so a truncated export is visibly truncated. */ total: number; } /** * Streams rows to a writer rather than building one string. * * A record-level export of a busy database is hundreds of thousands of rows; * materialising that as a single string would hold the whole export in memory to * hand it to a pipe one line at a time anyway. */ export declare class ExportService { private readonly repo; constructor(repo: UsageRepository); export(filter: UsageFilter, write: (line: string) => void, options?: ExportOptions): ExportResult; }