/** * Catalog seeder — populates `models_catalog` from the shipped offline seed (T11734). * * ## What it does * * {@link seedModelsCatalog} reads the bundled `curated-catalog.json`, validates it * against the contract zod schema ({@link curatedCatalogSchema}), flattens every * provider→model entry into a `models_catalog` row, and UPSERTS each via * {@link openDualScopeDb}`('global')`. Seeding is the offline bootstrap of the * catalog SSoT — NO network is touched (the network refresh op is a separate leaf). * * ## Idempotent (AC2) + version-skip (AC4) * * The seed carries a semver `version`. The seeder records the seeded version in the * global key-value `__catalog_meta` row and SKIPS a re-seed when the persisted * version equals the shipped version (logged, not re-written). On a NEWER shipped * version it re-seeds (upsert on the `(provider_id, models_dev_id)` natural key, so * a re-seed never duplicates rows — running twice yields the same row count). * * @module llm/catalog-seeder * @task T11734 * @epic T11694 (E8-CATALOG-CURATION) */ import { type CuratedCatalog } from '@cleocode/contracts'; import { type CleoGlobalDb } from '../store/dual-scope-db.js'; import { type NewModelsCatalogRow } from '../store/schema/cleo-global/models-catalog.js'; /** Result of a {@link seedModelsCatalog} run. */ export interface SeedCatalogResult { /** Whether rows were written this run (`false` on a version-skip). */ readonly seeded: boolean; /** The seed version applied (or already persisted on a skip). */ readonly version: string; /** Number of rows upserted (0 on a skip). */ readonly rowCount: number; /** Why the run behaved as it did. */ readonly reason: 'seeded' | 'version-skip' | 'reseeded-newer'; } /** Injectable seam for {@link seedModelsCatalog} (tests pass a temp-DB handle + fixture). */ export interface SeedCatalogDeps { /** * An already-open global Drizzle handle. When omitted the seeder opens via * {@link openDualScopeDb}`('global')`. Tests pass a temp-DB handle (opened via * {@link openSeederAtPath}) to stay off `.cleo/*.db`. */ readonly db?: CleoGlobalDb; /** * A catalog object to seed from, bypassing the bundled `curated-catalog.json` * (tests pass a fixture). When omitted the shipped seed is read + validated. */ readonly catalog?: CuratedCatalog; } /** * Open the global seeder handle at an EXPLICIT path (test seam). * * @param dbPath - Absolute path to the temp `cleo.db`. * @task T11734 */ export declare function openSeederAtPath(dbPath: string): Promise; /** * Read + validate the shipped seed (`curated-catalog.json`) — no network. * * Throws a descriptive error when the bundled file is missing or fails schema * validation, so a malformed seed fails the build/seed loudly rather than silently * persisting garbage. * * @task T11734 */ export declare function loadAndValidateSeed(): CuratedCatalog; /** * Flatten a {@link CuratedCatalog} into `models_catalog` INSERT rows. * * Each provider→model entry becomes one row; nested `modalities` / `cost` are * JSON-serialized into their TEXT columns. `id` and `models_dev_id` both take the * entry id (the latter anchors the `(provider_id, models_dev_id)` upsert key). * * @task T11734 */ export declare function flattenCatalogToRows(catalog: CuratedCatalog): NewModelsCatalogRow[]; /** * Seed `models_catalog` from the shipped offline seed — idempotent, offline-first. * * Behaviour: * - First run (no persisted version): upsert every row, persist the version. (AC1) * - Re-run with the SAME version: SKIP (logged, not re-written). (AC4) * - Re-run with a NEWER shipped version: re-seed (upsert, no duplicates). (AC3) * * The upsert is on the `(provider_id, models_dev_id)` natural key, so running twice * yields the same row count and never duplicates (AC2). NO network is touched. * * @param deps - Optional injected DB handle / fixture catalog (tests). * @returns A {@link SeedCatalogResult} describing what happened. * * @task T11734 */ export declare function seedModelsCatalog(deps?: SeedCatalogDeps): Promise; //# sourceMappingURL=catalog-seeder.d.ts.map