import { b as getLogger } from './paths-2M_UxD_H.js';
import { b as openDualScopeDb } from './dual-scope-db-ATrwq0LH.js';
import { f as findLatestCacheFile, g as getCatalogDir, a as readCacheFile } from './catalog-model-resolver2-Cexk2TN0.js';
import { m as modelsCatalog } from './models-catalog-DG3aPok8.js';
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

//#region ../core/dist/llm/catalog-resolver.js
/**
* Table-first catalog read chokepoint — `resolveCatalogEntry()` (E8 · T11737).
*
* ## The single read chokepoint
*
* The provider+model catalog now has THREE physical surfaces, in strict priority:
*
*   1. **`models_catalog` DB table** (cleo-global · T11733) — the SSoT. Seeded
*      from the shipped offline `curated-catalog.json` (T11734).
*   2. **disk JSON cache** (`llm-catalog/` · T9314 · `catalog-cache.ts`) — the
*      offline/degraded fallback MIRROR (the most-recent models.dev snapshot).
*   3. **shipped seed** (`curated-catalog.json`) — the last-resort floor that
*      always ships in the package, so a fresh/offline install always resolves.
*
* Every catalog reader (the resolver default — T11944; future capability lookups)
* funnels through {@link resolveCatalogEntry} so the table and the cache can NEVER
* silently diverge: one chokepoint, table-or-fallback, in that order.
*
* ## Offline-first (degrade) — NO network at read time
*
* This module NEVER fetches. The network refresh op (`cleo llm refresh-catalog`)
* is a SEPARATE leaf. `resolveCatalogEntry()` reads the DB table first; when the
* table is empty/unseeded it falls back to the disk-cache snapshot, then to the
* shipped seed. A fresh install with no DB rows and no disk cache still resolves
* from the bundled `curated-catalog.json`.
*
* @module llm/catalog-resolver
* @task T11737
* @epic T11694 (E8-CATALOG-CURATION)
*/
var logger = getLogger("llm-catalog-resolver");
/** In-memory shipped-seed cache for the process lifetime. */
var _seed;
/**
* Load the shipped seed (`curated-catalog.json`) from disk — no network.
*
* Read once and cached for the process lifetime. Returns `null` only if the
* bundled file is missing/unparseable (never expected — it ships in the package).
*
* @internal
*/
function loadShippedSeed() {
	if (_seed !== void 0) return _seed;
	try {
		const jsonPath = join(dirname(fileURLToPath(import.meta.url)), "curated-catalog.json");
		_seed = JSON.parse(readFileSync(jsonPath, "utf-8"));
	} catch (err) {
		logger.warn({ err: err instanceof Error ? err.message : String(err) }, "catalog-resolver: shipped seed unreadable");
		_seed = null;
	}
	return _seed;
}
/** Resolve the global Drizzle handle — injected, else canonical open. */
async function resolveDb(deps) {
	if (deps?.db !== void 0) return deps.db;
	return (await openDualScopeDb("global")).db;
}
/**
* Read the latest-released entry for a provider from the `models_catalog` table.
*
* Returns the single newest row (ordered by `release_date` DESC, `id` DESC as a
* stable tie-break) for the provider, or `null` when the provider has no rows
* (table empty/unseeded for that provider). LIMIT 1 — never scans the whole table.
*
* @internal
*/
async function readLatestFromTable(db, providerId) {
	const { desc, eq } = await import('./index-DIWrtHjY.js');
	const row = (await db.select({
		id: modelsCatalog.id,
		providerId: modelsCatalog.providerId,
		releaseDate: modelsCatalog.releaseDate,
		contextLimit: modelsCatalog.contextLimit
	}).from(modelsCatalog).where(eq(modelsCatalog.providerId, providerId)).orderBy(desc(modelsCatalog.releaseDate), desc(modelsCatalog.id)).limit(1))[0];
	if (!row) return null;
	return {
		id: row.id,
		providerId: row.providerId,
		releaseDate: row.releaseDate,
		contextLimit: row.contextLimit ?? null,
		source: "table"
	};
}
/**
* Resolve the latest-released catalog entry for a provider from a catalog snapshot
* (disk-cache file or shipped seed), sorted by `release_date` DESC.
*
* The two fallback surfaces carry different shapes — the disk cache is the raw
* models.dev `{ [provider]: { models: { [id]: { release_date, limit } } } }`, the
* shipped seed is the curated `{ models: { [provider]: { [id]: entry } } }`. This
* helper accepts the already-extracted per-provider model map.
*
* @internal
*/
function latestFromModelMap(models, providerId, source) {
	let bestId = null;
	let bestDate = "";
	let bestCtx = null;
	for (const [id, entry] of Object.entries(models)) {
		const rd = entry.release_date;
		if (!rd) continue;
		if (rd > bestDate || rd === bestDate && bestId !== null && id > bestId) {
			bestDate = rd;
			bestId = id;
			bestCtx = entry.limit?.context ?? null;
		}
	}
	if (bestId === null) return null;
	return {
		id: bestId,
		providerId,
		releaseDate: bestDate,
		contextLimit: bestCtx,
		source
	};
}
/**
* Resolve the latest-released catalog entry for a provider — the SINGLE table-first
* read chokepoint (T11737).
*
* Resolution order (offline-first, no network):
*   1. `models_catalog` DB table (SSoT) — newest `release_date` row for the provider.
*   2. disk JSON cache (`llm-catalog/`) — newest entry when the table has no rows.
*   3. shipped `curated-catalog.json` seed — last-resort floor (always present).
*
* Returns `null` only when NONE of the three surfaces carry a dated entry for the
* provider — callers then degrade to their own static literal floor (the resolver
* default keeps `IMPLICIT_FALLBACK_MODEL` for exactly this case — T11944).
*
* @param providerCatalogKey - The models.dev provider key (e.g. `anthropic`).
* @param deps - Optional injected DB handle / cache dir (tests).
* @returns The newest-released entry for the provider, or `null` when unresolved.
*
* @task T11737
*/
async function resolveCatalogEntry(providerCatalogKey, deps) {
	const providerId = providerCatalogKey.toLowerCase();
	try {
		const fromTable = await readLatestFromTable(await resolveDb(deps), providerId);
		if (fromTable) return fromTable;
	} catch (err) {
		logger.warn({
			err: err instanceof Error ? err.message : String(err),
			providerId
		}, "catalog-resolver: models_catalog read failed; falling back to disk cache");
	}
	const latest = findLatestCacheFile(deps?.cacheDir ?? getCatalogDir());
	if (latest) {
		const provider = readCacheFile(latest)?.[providerId];
		if (provider?.models) {
			const fromCache = latestFromModelMap(provider.models, providerId, "disk-cache");
			if (fromCache) return fromCache;
		}
	}
	const seedModels = loadShippedSeed()?.models?.[providerId];
	if (seedModels) {
		const fromSeed = latestFromModelMap(seedModels, providerId, "shipped-seed");
		if (fromSeed) return fromSeed;
	}
	return null;
}

export { resolveCatalogEntry as r };
//# sourceMappingURL=catalog-resolver2-c0qfF34B.js.map
