fix(barrel): delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) (#12055)

Resynced onto the release tip after #12051/#12052/#12053 landed. Same LKGP-clear conflict as #12053 (kept the current clearStaleLKGP() helper at both call sites). One additional issue this final phase's combined-worktree validation surfaced: clearStaleLKGP() itself (added by #12013, which none of the 4 phase PRs could have seen since it landed after they were authored) still had a dynamic `await import("@/lib/localDb")` — a real break once this PR deletes the barrel. Fixed to `await import("@/lib/db/settings")`, matching the direct-import pattern used at every other call site. typecheck:core, check-db-rules, check:cycles, and the eslint-import-boundaries regression test (3/3, including "G14 rejects localDb barrel imports") all green after resync — zero barrel-importing production files remain. Nice clean 5-phase migration, and thanks for taking on the full #11795 cleanup.
This commit is contained in:
Webman
2026-08-30 00:36:26 -05:00
committed by GitHub
parent 4e11887085
commit 50bc8ab8aa
89 changed files with 538 additions and 1986 deletions

View File

@@ -289,8 +289,7 @@ When creating _any_ validation tests or one-off logic scripts, default to `scrip
### Database
- **Always** go through `src/lib/db/` domain modules — **never** write raw SQL in routes or handlers
- **Never** add logic to `src/lib/localDb.ts` (re-export layer only)
- **Never** barrel-import from `localDb.ts` — import specific `db/` modules instead
- **Never** barrel-import from `localDb.ts` — import specific `src/lib/db/*` modules
- DB singleton: `getDbInstance()` from `src/lib/db/core.ts` (WAL journaling)
- Migrations: `src/lib/db/migrations/` — versioned SQL files, idempotent, run in transactions
@@ -355,8 +354,7 @@ Documentation must describe verified behavior, not plausible behavior.
1. Create `src/lib/db/yourModule.ts` — import `getDbInstance` from `./core.ts`
2. Export CRUD functions for your domain table(s)
3. Add migration in `src/lib/db/migrations/` if new tables needed
4. Re-export from `src/lib/localDb.ts` (add to the re-export list only)
5. Write tests
4. Write tests
### Adding a New MCP Tool
@@ -668,7 +666,7 @@ the stale-enforcement added in Fase 6A.3.
## Hard Rules
1. Never commit secrets or credentials
2. Never add logic to `localDb.ts`
2. Never barrel-import from `localDb.ts` — import specific `src/lib/db/*` modules
3. Never use `eval()` / `new Function()` / implied eval
4. Never commit directly to `main`
5. Never write raw SQL in routes — use `src/lib/db/` modules

View File

@@ -1588,11 +1588,6 @@
"count": 2
}
},
"src/lib/db/compressionCombos.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
}
},
"src/lib/db/core.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 3
@@ -1618,11 +1613,6 @@
"count": 2
}
},
"src/lib/db/middleware.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 2
}
},
"src/lib/db/migrationRunner.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 2
@@ -2436,11 +2426,6 @@
"count": 2
}
},
"tests/integration/files-api.test.ts": {
"no-restricted-imports": {
"count": 1
}
},
"tests/integration/fingerprint-expansion.test.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
@@ -2588,11 +2573,6 @@
"count": 1
}
},
"tests/integration/traffic-inspector-hosts.test.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
}
},
"tests/integration/upstream-cli-smoke.int.test.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
@@ -2870,11 +2850,6 @@
"count": 1
}
},
"tests/unit/batch-deletion.test.ts": {
"no-restricted-imports": {
"count": 1
}
},
"tests/unit/batch-page-static.test.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
@@ -4101,9 +4076,6 @@
},
"@typescript-eslint/no-unused-vars": {
"count": 1
},
"no-restricted-imports": {
"count": 1
}
},
"tests/unit/fix-tool-adjacency.test.ts": {

View File

@@ -514,7 +514,7 @@ For the full stealth playbook and operational guidance, see
Primary state DB (SQLite):
- Core infra: `src/lib/db/core.ts` (better-sqlite3, migrations, WAL)
- Re-export facade: `src/lib/localDb.ts` (thin compatibility layer for callers)
- DB access: import specific `src/lib/db/*` modules directly (the old `localDb.ts` barrel was removed)
- file: `${DATA_DIR}/storage.sqlite` (or `$XDG_CONFIG_HOME/omniroute/storage.sqlite` when set, else `~/.omniroute/storage.sqlite`)
- entities (tables + KV namespaces): providerConnections, providerNodes, modelAliases, combos, apiKeys, settings, pricing, **customModels**, **proxyConfig**, **ipFilter**, **thinkingBudget**, **systemPrompt**
@@ -888,7 +888,7 @@ flowchart LR
### Persistence
- `src/lib/db/*`: persistent config/state and domain persistence on SQLite
- `src/lib/localDb.ts`: compatibility re-export for DB modules
- `src/lib/db/*`: import specific modules directly — no barrel (the old `localDb.ts` re-export layer was removed)
- `src/lib/usageDb.ts`: usage history/call logs facade on top of SQLite tables
## Provider Executor Coverage (Strategy Pattern)

View File

@@ -313,7 +313,7 @@ table groups the actual directories and notable top-level files.
Top-level files in `src/lib/`:
- `localDb.ts` — re-export layer only. **Never** add logic here.
- The old `localDb.ts` barrel was removed — consumers import specific `src/lib/db/*` modules directly.
- `proxyHealth.ts`, `proxyLogger.ts`, `tokenHealthCheck.ts`, `localHealthCheck.ts`
- `oneproxyRotator.ts`, `oneproxySync.ts`
- `apiBridgeServer.ts`, `cacheLayer.ts`, `semanticCache.ts`, `settingsCache.ts`
@@ -759,7 +759,7 @@ See [RESILIENCE_GUIDE.md](./RESILIENCE_GUIDE.md) and the dedicated section in
2. Export CRUD functions for your domain.
3. If new tables: add a migration under `src/lib/db/migrations/`, numbered
sequentially, idempotent, transactional.
4. Re-export from `src/lib/localDb.ts` (re-export only — **no logic**).
4. Importers use direct imports from `@/lib/db/yourModule` (no barrel — the old `localDb.ts` re-export layer was removed).
5. Add tests under `tests/unit/`.
### Add a new MCP tool
@@ -790,7 +790,7 @@ See [A2A-SERVER.md § Adding a New Skill](../frameworks/A2A-SERVER.md). Skills l
- **TypeScript**: `strict: false` (legacy posture). Prefer explicit types over
inference for cross-module boundaries.
- **Database**: never write raw SQL in routes or handlers — always go through
`src/lib/db/` modules. Never add logic to `src/lib/localDb.ts`.
`src/lib/db/` modules. Never barrel-import — use specific `src/lib/db/*` modules directly.
- **DB-entity typing (#3512)**: a function that writes or reads a DB table's
row shape should take/return a named TS interface mirroring that table's
columns 1:1, not `any` or an inline anonymous type at the call site. Land
@@ -824,7 +824,7 @@ See [A2A-SERVER.md § Adding a New Skill](../frameworks/A2A-SERVER.md). Skills l
## 12. Hard Rules (from CLAUDE.md)
1. Never commit secrets or credentials.
2. Never add logic to `src/lib/localDb.ts`.
2. Never barrel-import — use specific `src/lib/db/*` modules directly.
3. Never use `eval()` / `new Function()` / implied eval.
4. Never commit directly to `main`.
5. Never write raw SQL in routes — always go through `src/lib/db/` modules.

View File

@@ -158,7 +158,7 @@ status. CI performs the broader package artifact and ecosystem checks.
**Contracts**
- Domain modules under `src/lib/db/`; `src/lib/localDb.ts` remains a re-export layer only.
- Domain modules under `src/lib/db/`; import specific modules directly (the old `localDb.ts` re-export layer was removed).
- Numbered, idempotent SQL migrations under `src/lib/db/migrations/`, transaction safety, upgrade
behavior, indexes, and every caller affected by the schema.
- Routes and handlers never issue raw SQL directly.

View File

@@ -370,7 +370,7 @@ export function clearStaleLKGP(
): void {
void (async () => {
try {
const { clearLKGP } = await import("@/lib/localDb");
const { clearLKGP } = await import("@/lib/db/settings");
const promises: Promise<void>[] = [clearLKGP(comboName, comboId || comboName)];
if (executionKey) {
promises.push(clearLKGP(comboName, executionKey));

View File

@@ -1,89 +1,24 @@
#!/usr/bin/env node
// scripts/check/check-db-rules.mjs
// Gate de convenções de banco (CLAUDE.md Hard Rules #2 e #5). Três verificações:
// (a) Todo módulo de domínio em src/lib/db/*.ts deve ser re-exportado por
// src/lib/localDb.ts (camada de compat). Um módulo db NOVO que não é
// re-exportado (e não está congelado) falha — força a decisão consciente
// de expor ou justificar (Hard Rule #2).
// (b) src/lib/localDb.ts é APENAS camada de re-export: nada de lógica
// (function/class/arrow de negócio). Mata o anti-padrão de "só uma
// funçãozinha aqui" que vira regra de negócio fora dos módulos db/.
// Gate de convenções de banco (CLAUDE.md Hard Rules #2 e #5). Uma verificação:
// (c) Nenhum SQL cru em src/app/api/**/route.ts ou open-sse/handlers/*.ts.
// SQL deve viver em src/lib/db/ (Hard Rule #5). Ofensores pré-existentes
// são congelados; QUALQUER novo SQL cru em rota/handler falha.
// Stale-enforcement (6A.3): entradas em INTENTIONALLY_INTERNAL / EXTERNAL_DB_ALLOWED
// que não suprimem nenhuma violação real → gate falha com instrução de remoção.
// As antigas verificações (a) re-export completo via src/lib/localDb.ts e
// (b) localDb.ts sem lógica foram REMOVIDAS: o barrel src/lib/localDb.ts foi
// deletado (#11795) — consumidores importam módulos src/lib/db/* diretamente
// (regra "never barrel-import", aplicada por eslint no-restricted-imports).
// Stale-enforcement (6A.3): entradas em EXTERNAL_DB_ALLOWED que não suprimem
// nenhuma violação real → gate falha com instrução de remoção.
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { assertNoStale } from "./lib/allowlist.mjs";
const cwd = process.cwd();
const DB_DIR = path.join(cwd, "src/lib/db");
const LOCAL_DB = path.join(cwd, "src/lib/localDb.ts");
const API_DIR = path.join(cwd, "src/app/api");
const HANDLERS_DIR = path.join(cwd, "open-sse/handlers");
// (a) Módulos db/ que NÃO são re-exportados por localDb.ts por DESIGN (Hard Rule #2:
// "Never barrel-import from localDb.ts — import specific db/ modules instead").
// Cada entrada aqui foi auditada e é consumida via import direto de "@/lib/db/X"
// (estático ou dinâmico) pelos seus consumidores — exatamente o padrão correto.
// Re-exportar esses módulos via localDb.ts INCENTIVARIA o anti-padrão proibido.
// O gate ainda bloqueia QUALQUER módulo db/ NOVO que não seja re-exportado E não
// esteja nessa lista — mantendo a decisão consciente obrigatória (Hard Rule #2).
// Legenda de classificação:
// type-only = exporta apenas tipos (sem runtime API), não há o que re-exportar
// db-internal = importado apenas dentro de src/lib/db/ (coordenação interna)
// intentionally-internal = consumido por import direto fora de db/ (correto per Rule #2)
// DEAD? = zero importers encontrados na auditoria de 2026-06-11; não deletar
// sem investigação — pode ser reserva de schema ou F2 pendente
export const INTENTIONALLY_INTERNAL = new Set([
"_rowTypes", // type-only: 5 importers internos em db/ (AgentBridge/Inspector row types)
"accessTokens", // intentionally-internal: 4 rotas /api/cli/* (connect, whoami, tokens, tokens/[id]) + server/authz/accessTokenAuth.ts via import direto "@/lib/db/accessTokens" (Rule #2)
"apiKeyColumnFallbacks", // db-internal: importado só por db/apiKeys.ts (API_KEY_COLUMN_FALLBACKS — fallbacks de coluna split do apiKeys.ts)
"apiKeyUsageLimitFields", // db-internal: importado só por db/apiKeys.ts (helpers de campo de limite de uso split do apiKeys.ts; mig 101)
"backupRetention", // db-internal: importado só por db/backup.ts e db/migrationRunner.ts (política de retenção compartilhada; mora fora de backup.ts porque core.ts importa migrationRunner.ts — importar backup.ts de lá fecharia um ciclo, #10421)
"caseMapping", // db-internal: importado só por db/core.ts (toSnakeCase/toCamelCase/objToSnake — column-mapping snake↔camel split do core.ts, #4947)
"cleanup", // intentionally-internal: 3 API routes (purge-quota-snapshots, purge-call-logs, purge-detailed-logs)
"cliToolState", // intentionally-internal: 14+ API routes em /api/cli-tools/*-settings
"comboForecast", // intentionally-internal: src/lib/usage/comboForecast.ts
"commandCodeAuth", // intentionally-internal: 5 API routes em /api/providers/command-code/auth/*
"compression", // intentionally-internal: 2 API routes (settings/compression, context/rtk/config)
"compressionDetailNormalizers", // db-internal: importado só por db/compression.ts (normalizeSessionDedupConfig/normalizeCcrConfig/buildDetailConfigDefaults/applyDetailConfigUpdate — normalizadores do detail-config split do compression.ts, #8404)
"connectionRuntimeState", // intentionally-internal: warmupScheduler sqlite/redis stores importam diretamente de @/lib/db/connectionRuntimeState (Rule #2)
"vacuumScheduler", // intentionally-internal: src/instrumentation-node.ts (dynamic import, lifecycle wiring per Rule #2)
"detailedLogs", // intentionally-internal: 3 callers (callLogs.ts, logs/detail route, embeddings handler)
"discovery", // DEAD?: 0 importers na auditoria de 2026-06-11; lib/discovery/index.ts não usa db/discovery
"domainState", // intentionally-internal: 5 callers (batchWriter, circuitBreaker, costRules, fallbackPolicy, lockoutPolicy)
"encryption", // intentionally-internal: 8+ callers (container, webhookDispatcher, cloudAgent/credentials, services/apiKey, 4+ routes, open-sse)
"healthCheck", // db-internal: importado por db/core.ts (runDbHealthCheck)
"jsonMigration", // intentionally-internal: src/app/api/settings/import-json/route.ts
"migrationRunner", // db-internal: importado por db/core.ts (runMigrations ao inicializar o DB)
"modelCapabilityOverrides", // intentionally-internal: src/app/api/model-capability-overrides/route.ts via import direto "@/lib/db/modelCapabilityOverrides" (#6727 — evita empurrar localDb.ts para o cap de 800 linhas)
"notion", // intentionally-internal: settings/notion API route + open-sse/mcp-server/tools/notionTools.ts
"obsidian", // intentionally-internal: src/lib/obsidianSync.ts + settings/obsidian route + MCP obsidianTools.ts
"optimizationSettings", // db-internal: imported by db/core.ts for SQLite PRAGMA application helpers that require the live adapter
"pluginMetrics", // DEAD? (production): write path não foi conectado ainda (documentado no cabeçalho do módulo); testado por tests/unit/plugins-metrics.test.ts
"prompts", // DEAD? (production): zero callers de produção encontrados; domínio domain/prompts.ts é independente; testado por tests/integration/proxy-pipeline.test.ts
"probeUtils", // db-internal: importado so por db/core.ts (retryProbeIfTransient no caminho da corruption-probe, #9541); testado por tests/unit/probe-9541-repro.test.ts
"providerNodeSelect", // db-internal: importado só por db/providers.ts (selectProviderNodeForConnection — lógica pura de seleção de provider node split do providers.ts, #4421)
"providerStats", // intentionally-internal: src/app/api/provider-stats/route.ts
"proxyLatency", // intentionally-internal: imported directly by src/lib/db/proxies.ts (anti-barrel, #6798)
"proxySubscriptions", // db-internal: importado só por db/proxies.ts (addProxiesToScopePool — split do proxies.ts para ficar sob o cap de tamanho congelado, #7299); a função já é re-exportada por proxies.ts (que localDb.ts re-exporta)
"recovery", // intentionally-internal: bin/cli/runtime.mjs (import() dinâmico) + tests
"schemaColumns", // db-internal: importado só por db/core.ts (ensureProviderConnections/UsageHistory/CallLogsColumns + hasColumn/hasTable/getTableColumns — schema-column reconciliation split do core.ts, #4948)
"secrets", // intentionally-internal: src/instrumentation-node.ts (import() dinâmico na inicialização)
"serviceModels", // intentionally-internal: 3 callers (services/modelSync, services/bootstrap, /api/services/9router/models)
"stateReset", // db-internal: 3 callers dentro de src/lib/db/ (core, backup, apiKeys) para coordenação de reset
"stats", // intentionally-internal: src/app/api/settings/database/refresh-stats/route.ts
"tierConfig", // intentionally-internal: open-sse/services/tierResolver.ts (require() dinâmico)
"webSessionDedup", // db-internal: importado só por db/providers.ts (webSessionCredentialKey/parseProviderSpecificData — helpers puros de dedup de credencial web-session split do providers.ts, #3368 PR6)
]);
// Alias para retrocompatibilidade com os testes existentes que importam KNOWN_UNEXPORTED.
// O comportamento do gate é idêntico — só o nome e os comentários mudaram (#3499).
export const KNOWN_UNEXPORTED = INTENTIONALLY_INTERNAL;
// (c) Leituras de SQL contra bancos EXTERNOS, permitidas por design (#3500).
// Esta rota NÃO consulta o DB do OmniRoute (getDbInstance) — ela abre o
// SQLite de OUTRO aplicativo (Kiro) para auto-importar credenciais.
@@ -97,16 +32,13 @@ export const KNOWN_UNEXPORTED = INTENTIONALLY_INTERNAL;
// precisa de entrada aqui: o SQL contra o state.vscdb externo do Cursor vive
// em src/lib/cursor/tokenExtractor.ts, fora do escopo desta checagem (que só
// varre src/app/api/**/route.ts e open-sse/handlers/*.ts).
const EXTERNAL_DB_ALLOWED = new Set([
export const EXTERNAL_DB_ALLOWED = new Set([
"src/app/api/oauth/kiro/auto-import/route.ts", // read-only no SQLite do Kiro (DB externo)
]);
// Alias de retrocompatibilidade (testes/consumidores que importam KNOWN_RAW_SQL).
// Comportamento do gate idêntico — só o nome e o enquadramento mudaram (#3500).
const KNOWN_RAW_SQL = EXTERNAL_DB_ALLOWED;
// Módulos sempre excluídos da checagem (a): não são domínio re-exportável.
const DB_MODULE_EXCLUDE = new Set(["core", "localDb", "index"]);
export const KNOWN_RAW_SQL = EXTERNAL_DB_ALLOWED;
function walk(dir, acc = []) {
if (!fs.existsSync(dir)) return acc;
@@ -118,59 +50,6 @@ function walk(dir, acc = []) {
return acc;
}
// Lista os módulos de domínio em src/lib/db (top-level *.ts), excluindo
// core/localDb/index, *.d.ts e qualquer subdiretório (migrations/, adapters/, __tests__/).
export function collectDbModules(dbDir = DB_DIR) {
if (!fs.existsSync(dbDir)) return [];
return fs
.readdirSync(dbDir, { withFileTypes: true })
.filter((e) => e.isFile() && /\.ts$/.test(e.name) && !/\.d\.ts$/.test(e.name))
.map((e) => e.name.replace(/\.ts$/, ""))
.filter((name) => !DB_MODULE_EXCLUDE.has(name))
.sort();
}
// Extrai os nomes de módulo re-exportados de localDb.ts a partir de
// `... from "./db/X"` (cobre export {…}, export * e export type {…}).
export function extractReexportedModules(localDbSource) {
const re = /from\s+["']\.\/db\/([A-Za-z0-9_]+)["']/g;
const out = new Set();
let m;
while ((m = re.exec(localDbSource))) out.add(m[1]);
return out;
}
// (a) Módulos db/ que não são re-exportados e não estão na lista de
// intencionalmente-internos (INTENTIONALLY_INTERNAL). O gate falha para
// qualquer módulo NOVO que não seja re-exportado nem justificado.
export function findMissingReexports(dbModules, reexported, allowlist = INTENTIONALLY_INTERNAL) {
return dbModules.filter((mod) => !reexported.has(mod) && !allowlist.has(mod));
}
// (b) localDb.ts deve conter SOMENTE import/export + comentários (sem lógica).
// Remove comentários e strings, depois procura declarações de runtime.
export function hasLogic(localDbSource) {
const stripped = localDbSource
// comentários de bloco
.replace(/\/\*[\s\S]*?\*\//g, "")
// comentários de linha
.replace(/\/\/[^\n]*/g, "")
// template strings
.replace(/`(?:\\[\s\S]|[^\\`])*`/g, '""')
// strings simples/duplas (paths de import etc.)
.replace(/"(?:\\.|[^"\\])*"/g, '""')
.replace(/'(?:\\.|[^'\\])*'/g, '""');
// function/class declaradas, ou atribuição a função (const X = (…) =>, const X = function).
const logicPatterns = [
/(^|[^.\w])function\s+[A-Za-z_$]/, // function decl (não method .foo())
/(^|[^.\w])class\s+[A-Za-z_$]/, // class decl
/(?:const|let|var)\s+[A-Za-z_$][\w$]*\s*=\s*(?:async\s*)?\(/, // const X = (…) ... (arrow/call)
/(?:const|let|var)\s+[A-Za-z_$][\w$]*\s*=\s*(?:async\s+)?function\b/, // const X = function
];
return logicPatterns.some((rx) => rx.test(stripped));
}
// SQL cru é sempre uma STRING passada a db.prepare()/exec(): casamos os padrões
// SÓ dentro de literais de string (não em código JS — `import … from`, `.set(`,
// `new Set(`, `delete x` etc. são falsos positivos se varrermos o código todo).
@@ -198,7 +77,7 @@ export function extractStringLiterals(code) {
// tira as aspas/crases delimitadoras
out.push(m[0].slice(1, -1));
}
return out.join("\n\n"); // separador que nenhum padrão SQL atravessa
return out.join("\n\u0000\n"); // separador que nenhum padrão SQL atravessa
}
// (c) Arquivos com SQL cru dentro de literais de string (linhas não-comentário),
@@ -217,7 +96,7 @@ export function findRawSql(files, allowlist = KNOWN_RAW_SQL) {
// Match each literal independently. Joining literals before scanning would
// turn harmless code such as `update(...)` plus a later `"set"` string into
// a false UPDATE ... SET SQL match.
const literals = extractStringLiterals(stripComments(src)).split("\n\0\n");
const literals = extractStringLiterals(stripComments(src)).split("\n\u0000\n");
if (literals.some((literal) => SQL_PATTERNS.some((rx) => rx.test(literal)))) {
offenders.push(rel);
}
@@ -239,33 +118,6 @@ export function collectSqlScanFiles(apiDir = API_DIR, handlersDir = HANDLERS_DIR
function main() {
const failures = [];
const localDbSource = fs.readFileSync(LOCAL_DB, "utf8");
// (a) re-export completeness
const dbModules = collectDbModules();
const reexported = extractReexportedModules(localDbSource);
// Live unexported modules BEFORE allowlist filtering (needed for stale-enforcement).
const liveUnexported = dbModules.filter((mod) => !reexported.has(mod));
assertNoStale(INTENTIONALLY_INTERNAL, liveUnexported, "check-db-rules:unexported");
const missing = findMissingReexports(dbModules, reexported);
if (missing.length) {
failures.push(
`[#2 re-export] ${missing.length} módulo(s) db/ não re-exportado(s) por src/lib/localDb.ts:\n` +
missing.map((m) => ` ✗ src/lib/db/${m}.ts`).join("\n") +
`\n → re-exporte de src/lib/localDb.ts (apenas a lista de re-export, nada de lógica)` +
` ou adicione a INTENTIONALLY_INTERNAL com justificativa (import direto de "@/lib/db/${missing[0]}").`
);
}
// (b) localDb sem lógica
if (hasLogic(localDbSource)) {
failures.push(
`[#2 sem-lógica] src/lib/localDb.ts contém lógica (function/class/arrow). É camada de` +
` re-export apenas — mova a lógica para um módulo src/lib/db/.`
);
}
// (c) SQL cru fora de db/
// Live raw-SQL offenders BEFORE allowlist filtering (needed for stale-enforcement).
@@ -289,8 +141,8 @@ function main() {
}
if (!process.exitCode) {
console.log(
`[check-db-rules] OK (${dbModules.length} módulos db/, ${reexported.size} re-exportados, ` +
`${INTENTIONALLY_INTERNAL.size} intencionalmente-internos (Rule #2); ${EXTERNAL_DB_ALLOWED.size} leituras de DB externo permitidas (#3500))`
`[check-db-rules] OK (${scanFiles.length} arquivos varridos; ` +
`${EXTERNAL_DB_ALLOWED.size} leituras de DB externo permitidas (#3500))`
);
}
}

View File

@@ -64,7 +64,7 @@ Client → API Route (/v1/chat/completions)
- \`src/lib/db/\`: 45+ domain-specific modules
- \`core.ts\`: Singleton better-sqlite3 with WAL journaling
- \`migrationRunner.ts\`: Versioned SQL migrations (55+ files)
- \`localDb.ts\`: Re-export layer only — no logic
- \`src/lib/db/*\`: domain modules only — no barrel layer
### Key Modules
- **open-sse/**: Core streaming engine (handlers, executors, translator)

View File

@@ -11,7 +11,7 @@ Live count: `ls src/lib/db/*.ts | wc -l` (currently 117). Migrations: `ls src/li
- **`core.ts`** — `getDbInstance()` returns singleton `better-sqlite3` with WAL journaling. Exports `rowToCamel()` (snake_case → camelCase), `encryptConnectionFields()` for provider credentials at rest. `SCHEMA_SQL` defines **17 base tables** (verify: `grep -c "CREATE TABLE" src/lib/db/core.ts` minus 1 for `_omniroute_migrations`).
- **`migrationRunner.ts`** — Applies versioned SQL files from `db/migrations/` inside transactions. Tracks applied migrations in `_omniroute_migrations`. Each migration is idempotent.
- **`db/migrations/`** — 148 SQL files (`001_initial_schema.sql``153_radar_local_model_state.sql`; numbering has intentional gaps). Each runs in a transaction, never fails partially.
- **`localDb.ts`** — Re-export layer only. Never add logic here.
- The old `localDb.ts` barrel has been removed — consumers must import from the owning named module below.
## Key Domain Modules
@@ -56,15 +56,13 @@ Full list: `ls src/lib/db/*.ts | wc -l` (115 files). Drift detection: `npm run c
## Adding a New Domain Module
1. Create `src/lib/db/[module].ts` with CRUD functions
2. Export from `src/lib/localDb.ts` (add re-export)
3. If new tables: create migration in `db/migrations/NNN_[description].sql`
4. Migration runs automatically at startup via `migrationRunner.ts`
5. Add unit tests in `tests/unit/db/`
2. If new tables: create migration in `db/migrations/NNN_[description].sql`
3. Migration runs automatically at startup via `migrationRunner.ts`
4. Add unit tests in `tests/unit/db/`
## Anti-Patterns
- Raw SQL in routes — always use domain module functions
- Direct `prepare()` statements outside `db/` — breaks modularity
- Adding logic to `localDb.ts` — re-export layer only
- Barrel-importing from `localDb.ts` — import specific modules instead
- Skipping migrations for schema changes — all changes go through `db/migrations/`

View File

@@ -72,7 +72,7 @@ export function createKeyGroup(name: string, description = ""): KeyGroup {
const now = new Date().toISOString();
db.prepare(
"INSERT INTO key_groups (id, name, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
"INSERT INTO key_groups (id, name, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?)"
).run(id, name, description, now, now);
return getKeyGroup(id)!;
@@ -80,7 +80,7 @@ export function createKeyGroup(name: string, description = ""): KeyGroup {
export function updateKeyGroup(
id: string,
updates: { name?: string; description?: string; isActive?: boolean },
updates: { name?: string; description?: string; isActive?: boolean }
): KeyGroup | undefined {
const existing = getKeyGroup(id);
if (!existing) return undefined;
@@ -132,7 +132,7 @@ export function getGroupPermissions(groupId: string): GroupModelPermission[] {
const db = getDbInstance() as any;
const rows = db
.prepare(
"SELECT * FROM group_model_permissions WHERE group_id = ? ORDER BY access_type ASC, model_pattern ASC",
"SELECT * FROM group_model_permissions WHERE group_id = ? ORDER BY access_type ASC, model_pattern ASC"
)
.all(groupId) as any[];
return rows.map(rowToPermission);
@@ -142,7 +142,7 @@ export function addGroupPermission(
groupId: string,
modelPattern: string,
accessType: "allow" | "deny",
provider?: string,
provider?: string
): GroupModelPermission {
const db = getDbInstance() as any;
const id = randomUUID();
@@ -150,7 +150,7 @@ export function addGroupPermission(
const result = db
.prepare(
"INSERT INTO group_model_permissions (id, group_id, model_pattern, provider, access_type, created_at) VALUES (?, ?, ?, ?, ?, ?)",
"INSERT INTO group_model_permissions (id, group_id, model_pattern, provider, access_type, created_at) VALUES (?, ?, ?, ?, ?, ?)"
)
.run(id, groupId, modelPattern, provider || null, accessType, now);
@@ -169,17 +169,6 @@ export function removeGroupPermission(permissionId: string): boolean {
}
return result.changes > 0;
}
export function clearGroupPermissions(groupId: string): void {
const db = getDbInstance() as any;
const result = db.prepare("DELETE FROM group_model_permissions WHERE group_id = ?").run(groupId);
if (result.changes > 0) {
invalidateModelCatalogCache();
}
}
// ── Key Group Members ────────────────────────────────────────────────────
export function getGroupMembers(groupId: string): KeyGroupMember[] {
const db = getDbInstance() as any;
const rows = db
@@ -197,7 +186,7 @@ export function getKeyGroupsForApiKey(keyId: string): KeyGroup[] {
INNER JOIN key_group_members m ON g.id = m.group_id
WHERE m.key_id = ? AND g.is_active = 1
ORDER BY g.name ASC
`,
`
)
.all(keyId) as any[];
return rows.map(rowToGroup);
@@ -252,7 +241,7 @@ export interface ModelAccessCheck {
export function checkKeyModelAccess(
keyId: string,
model: string,
provider?: string,
provider?: string
): ModelAccessCheck {
const groups = getKeyGroupsForApiKey(keyId);
if (groups.length === 0) {
@@ -270,7 +259,7 @@ export function checkKeyModelAccess(
SELECT * FROM group_model_permissions
WHERE group_id IN (${placeholders})
ORDER BY access_type ASC
`,
`
)
.all(...groupIds) as any[];
@@ -281,7 +270,7 @@ export function checkKeyModelAccess(
(p) =>
p.accessType === "deny" &&
matchesModelPattern(p.modelPattern, model) &&
(!p.provider || p.provider === provider),
(!p.provider || p.provider === provider)
);
if (denyRules.length > 0) {
@@ -293,7 +282,7 @@ export function checkKeyModelAccess(
(p) =>
p.accessType === "allow" &&
matchesModelPattern(p.modelPattern, model) &&
(!p.provider || p.provider === provider),
(!p.provider || p.provider === provider)
);
if (allowRules.length > 0) {

View File

@@ -136,8 +136,3 @@ export function countCcrBlocks(principalId?: string): number {
) as { n: number } | undefined;
return row?.n ?? 0;
}
/** Test seam: the write throttle is module state and has to be resettable between tests. */
export function resetCcrBlockPruneCounter(): void {
writesSincePrune = 0;
}

View File

@@ -41,10 +41,6 @@ function defaultCompressionComboPipeline(): CompressionPipelineStep[] {
];
}
function toRecord(value: unknown): JsonRecord {
return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : {};
}
function parseJsonArray<T>(value: unknown, fallback: T[]): T[] {
if (Array.isArray(value)) return value as T[];
if (typeof value !== "string") return fallback;
@@ -389,18 +385,6 @@ export function assignRoutingCombo(compressionComboId: string, routingComboId: s
backupDbFile("pre-write");
return true;
}
export function unassignRoutingCombo(compressionComboId: string, routingComboId: string): boolean {
ensureCompressionComboTables();
const result = getDbInstance()
.prepare(
"DELETE FROM compression_combo_assignments WHERE compression_combo_id = ? AND routing_combo_id = ?"
)
.run(compressionComboId, routingComboId);
if (result.changes > 0) backupDbFile("pre-write");
return result.changes > 0;
}
// Static stackPriority map — mirrors the values defined in each engine file.
// Using a static map avoids cross-workspace imports (open-sse → src/lib/db) that
// would introduce a circular dependency detected by check:cycles.

View File

@@ -46,16 +46,6 @@ export interface UserBadge {
badgeCategory?: string | null;
badgeRarity?: string;
}
export interface XpAuditLogEntry {
id: number;
apiKeyId: string;
action: string;
xpEarned: number;
metadata: string | null;
createdAt: string;
}
export interface TokenLedgerEntry {
id: number;
fromApiKeyId: string;

View File

@@ -7,8 +7,7 @@
*/
import { getDbInstance } from "@/lib/db/core";
import { rowToCamel } from "@/lib/db/core";
import type { HookConfig, HookConfigRow, HookLogEntry, HookScope } from "@/lib/middleware/types";
import type { HookConfig, HookConfigRow, HookLogEntry } from "@/lib/middleware/types";
// ── Helpers ───────────────────────────────────────────────────────────────
@@ -70,27 +69,13 @@ export function getEnabledMiddlewareHooks(): HookConfig[] {
return rows.map(rowToHookConfig);
}
/**
* Get scoped hooks for a given combo ID.
*/
export function getComboMiddlewareHooks(comboId: string): HookConfig[] {
const db = getDbInstance() as any;
const rows = db
.prepare(
"SELECT * FROM middleware_hooks WHERE enabled = 1 AND (scope_type = 'global' OR (scope_type = 'combo' AND combo_id = ?)) ORDER BY priority ASC"
)
.all(comboId) as HookConfigRow[];
return rows.map(rowToHookConfig);
}
/**
* Get a single hook by name.
*/
export function getMiddlewareHook(name: string): HookConfig | undefined {
const db = getDbInstance() as any;
const row = db.prepare("SELECT * FROM middleware_hooks WHERE name = ?").get(name) as
| HookConfigRow
| undefined;
HookConfigRow | undefined;
return row ? rowToHookConfig(row) : undefined;
}
@@ -170,34 +155,6 @@ export function recordHookExecution(name: string, error?: string): void {
).run(name);
}
}
// ── Log Operations ────────────────────────────────────────────────────────
/**
* Insert a hook execution log entry.
*/
export function insertHookLog(entry: HookLogEntry): void {
const db = getDbInstance() as any;
db.prepare(
`
INSERT INTO middleware_logs (id, hook_name, request_id, duration_ms, mutated, skipped, error, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`
).run(
entry.id,
entry.hookName,
entry.requestId,
entry.durationMs,
entry.mutated ? 1 : 0,
entry.skipped ? 1 : 0,
entry.error || null,
entry.timestamp
);
}
/**
* Get hook execution logs, optionally filtered by hook name.
*/
export function getHookLogs(hookName?: string, limit = 50): HookLogEntry[] {
const db = getDbInstance() as any;
let rows: any[];
@@ -219,21 +176,3 @@ export function getHookLogs(hookName?: string, limit = 50): HookLogEntry[] {
timestamp: r.timestamp,
}));
}
/**
* Clean up old hook logs (keep last N entries).
*/
export function cleanupHookLogs(maxEntries = 10000): number {
const db = getDbInstance() as any;
// Delete logs beyond the max, keeping the most recent
const result = db
.prepare(
`
DELETE FROM middleware_logs WHERE id NOT IN (
SELECT id FROM middleware_logs ORDER BY timestamp DESC LIMIT ?
)
`
)
.run(maxEntries);
return result.changes;
}

View File

@@ -82,25 +82,6 @@ export function getModelCap(poolId: string, apiKeyId: string, model: string): Mo
.get(poolId, apiKeyId, model);
return row ? rowToModelCap(row) : null;
}
/**
* List all model caps for a given (pool, key) pair.
*/
export function listModelCaps(poolId: string, apiKeyId: string): ModelCap[] {
const rows = getDb()
.prepare<ModelCapRow>(
`SELECT pool_id, api_key_id, model, cap_value, cap_unit
FROM quota_allocation_model_caps
WHERE pool_id = ? AND api_key_id = ?`
)
.all(poolId, apiKeyId);
return rows.map(rowToModelCap);
}
/**
* Insert or replace a model cap.
* cap_value must be > 0 (enforced by DB CHECK constraint).
*/
export function setModelCap(cap: ModelCap): void {
getDb()
.prepare(
@@ -113,16 +94,3 @@ export function setModelCap(cap: ModelCap): void {
)
.run(cap.poolId, cap.apiKeyId, cap.model, cap.capValue, cap.capUnit);
}
/**
* Remove the cap for a specific (pool, key, model) triple.
* No-op if it does not exist.
*/
export function deleteModelCap(poolId: string, apiKeyId: string, model: string): void {
getDb()
.prepare(
`DELETE FROM quota_allocation_model_caps
WHERE pool_id = ? AND api_key_id = ? AND model = ?`
)
.run(poolId, apiKeyId, model);
}

View File

@@ -204,9 +204,3 @@ export function startSessionAccountAffinityCleanup(): void {
}, CLEANUP_INTERVAL_MS);
if (typeof cleanupTimer === "object" && "unref" in cleanupTimer) cleanupTimer.unref?.();
}
export function stopSessionAccountAffinityCleanupForTests(): void {
if (!cleanupTimer) return;
clearInterval(cleanupTimer);
cleanupTimer = null;
}

View File

@@ -129,15 +129,6 @@ function rowToConfig(record: Record<string, unknown>): UpstreamProxyConfig {
updatedAt: record.updated_at as string,
};
}
export async function getUpstreamProxyConfigs() {
const db = getDbInstance();
const rows = db
.prepare("SELECT * FROM upstream_proxy_config ORDER BY provider_id")
.all() as UpstreamProxyRow[];
return rows.map((row) => rowToConfig(toRecord(row)));
}
export async function getUpstreamProxyConfig(providerId: string) {
const db = getDbInstance();
const row = db

View File

@@ -1,850 +0,0 @@
/**
* localDb.js — Re-export layer for backward compatibility.
*
* All 27+ consumer files import from "@/lib/localDb".
* This thin layer re-exports everything from the domain-specific DB modules,
* so zero consumer changes are needed.
*/
export {
// Provider Connections
getProviderConnections,
getProviderConnectionsCount,
getProviderConnectionById,
createProviderConnection,
updateProviderConnection,
resetConnectionBackoff,
clearConnectionErrorIfUnchanged,
touchConnectionLastUsed,
deleteProviderConnection,
deleteProviderConnections,
deleteProviderConnectionsByProvider,
reorderProviderConnections,
cleanupProviderConnections,
getProviderNodes,
getProviderNodesCount,
getProviderNodeById,
resolveProviderNodeForConnection,
createProviderNode,
updateProviderNode,
deleteProviderNode,
// T05: Rate-limit DB persistence (survives token refresh)
setConnectionRateLimitUntil,
isConnectionRateLimited,
getRateLimitedConnections,
clearStaleCrashCooldowns,
// T13: Stale quota display fix (zero out usage after window resets)
getEffectiveQuotaUsage,
formatResetCountdown,
} from "./db/providers";
export {
// Model Aliases
getModelAliases,
setModelAlias,
deleteModelAlias,
deleteModelAliasesForProvider,
// MITM Alias
getMitmAlias,
setMitmAliasAll,
// Custom Models
getCustomModels,
getAllCustomModels,
addCustomModel,
replaceCustomModels,
removeCustomModel,
updateCustomModel,
getModelCompatOverrides,
mergeModelCompatOverride,
removeModelCompatOverride,
getModelNormalizeToolCallId,
getModelPreserveOpenAIDeveloperRole,
getModelUpstreamExtraHeaders,
getModelIsHidden,
setModelIsHidden,
getHiddenModelsByProvider,
// Synced Available Models
getSyncedAvailableModels,
getAllSyncedAvailableModels,
getActiveProvidersWithSyncedModel,
replaceSyncedAvailableModelsForConnection,
deleteSyncedAvailableModelsForConnection,
deleteSyncedAvailableModelsForProvider,
removeSyncedAvailableModel,
} from "./db/models";
export type { ModelCompatPerProtocol, ModelCompatPatch, SyncedAvailableModel } from "./db/models";
export {
// Combos
getCombos,
getCombosCount,
getComboById,
getComboByName,
getComboByNameInsensitive,
createCombo,
updateCombo,
reorderCombos,
deleteCombo,
} from "./db/combos";
export * from "./db/ccrBlocks";
export * from "./db/compressionCacheStats";
export * from "./db/compressionCombos";
export * from "./db/compressionContextBudget";
export * from "./db/compressionRunTelemetry";
export * from "./db/jobRegistryDb";
export * from "./db/modelContextOverrides";
export * from "./db/responsesContinuationStore";
export {
getApiKeys,
getApiKeysCount,
getApiKeyById,
createApiKey,
deleteApiKey,
validateApiKey,
getApiKeyMetadata,
updateApiKeyPermissions,
regenerateApiKey,
isModelAllowedForKey,
pickApiKeyForInternalUse,
clearApiKeyCaches,
resetApiKeyState,
ApiKeyPolicyInvariantError,
} from "./db/apiKeys";
export {
// Evals
saveEvalRun,
listEvalRuns,
getEvalScorecard,
listCustomEvalSuites,
getCustomEvalSuite,
saveCustomEvalSuite,
deleteCustomEvalSuite,
serializeEvalTargetKey,
} from "./db/evals";
export type {
EvalCaseRecord,
EvalSuiteRecord,
EvalTargetType,
EvalTargetDescriptor,
EvalRunSummary,
PersistedEvalRun,
} from "./db/evals";
export {
// Settings
getSettings,
getSettingsRevision,
updateSettings,
isCloudEnabled,
// LKGP (Last Known Good Provider) (#919)
getLKGP,
setLKGP,
clearLKGP,
// Pricing
getPricing,
getPricingWithSources,
getPricingForModel,
updatePricing,
resetPricing,
resetAllPricing,
// Proxy Config
getProxyConfig,
getProxyForLevel,
setProxyForLevel,
deleteProxyForLevel,
resolveProxyForConnection,
setProxyConfig,
} from "./db/settings";
export type { PricingSource, PricingSourceMap } from "./db/settings";
export {
getDatabaseSettings,
getUserDatabaseSettings,
updateDatabaseSettings,
} from "./db/databaseSettings";
export type { UserDatabaseSettings } from "./db/databaseSettings";
export * from "./db/exclusiveConnectionLeases";
export {
// Proxy Registry
listProxies,
getProxyById,
createProxy,
createProxyAndAssign,
updateProxy,
updateProxyAndAssign,
upsertProxy,
deleteProxyById,
getProxyAssignments,
getProxyWhereUsed,
assignProxyToScope,
addProxyToScopePool,
removeProxyFromScopePool,
getScopeProxyPool,
setScopeRotationStrategy,
getScopeRotationStrategy,
resolveProxyForConnectionFromRegistry,
resolveProxyForProvider,
resolveProxyForScopeFromRegistry,
migrateLegacyProxyConfigToRegistry,
getProxyHealthStats,
bulkAssignProxyToScope,
} from "./db/proxies";
export {
// Pricing Sync
getSyncedPricing,
saveSyncedPricing,
clearSyncedPricing,
syncPricingFromSources,
getSyncStatus,
initPricingSync,
startPeriodicSync,
stopPeriodicSync,
} from "./pricingSync";
export {
// Backup Management
backupDbFile,
cleanupDbBackups,
getDbBackupMaxFiles,
setDbBackupMaxFiles,
getDbBackupRetentionDays,
setDbBackupRetentionDays,
listDbBackups,
restoreDbBackup,
// Export-All / Import helpers (#3500 slice 5)
exportAllSummaryRows,
getTableNamesFromAdapter,
countImportedRows,
} from "./db/backup";
export type { ExportAllRows } from "./db/backup";
export {
// Skills DB operations (#3500 slice 5)
updateSkill,
} from "./db/skills";
export type { SkillPatch } from "./db/skills";
export {
// Read Cache (cached wrappers for hot-read paths)
getCachedSettings,
getCachedPricing,
getCachedProviderConnections,
getCachedRawProviderConnections,
getCachedProviderConnectionById,
getCachedProviderNodes,
getCachedLKGP,
setCachedLKGP,
invalidateDbCache,
getCombosCacheVersion,
} from "./db/readCache";
export {
// Registered Keys Provisioning (#464)
issueRegisteredKey,
getRegisteredKey,
listRegisteredKeys,
revokeRegisteredKey,
validateRegisteredKey,
incrementRegisteredKeyUsage,
checkQuota,
setProviderKeyLimit,
setAccountKeyLimit,
getProviderKeyLimit,
getAccountKeyLimit,
} from "./db/registeredKeys";
export type {
RegisteredKey,
RegisteredKeyWithSecret,
ProviderKeyLimit,
AccountKeyLimit,
QuotaCheckResult,
IssueKeyParams,
} from "./db/registeredKeys";
export {
// Model-Combo Mappings (#563)
getModelComboMappings,
getModelComboMappingById,
createModelComboMapping,
updateModelComboMapping,
deleteModelComboMapping,
resolveComboForModel,
} from "./db/modelComboMappings";
export {
// Files
createFile,
getFile,
getFileContent,
listFiles,
countFiles,
formatFileResponse,
deleteFile,
} from "./db/files";
export {
// Batches
createBatch,
getBatch,
updateBatch,
listBatches,
countBatches,
getPendingBatches,
getTerminalBatches,
ensureBatchItemCheckpoints,
countBatchItemCheckpoints,
listBatchItemCheckpoints,
markBatchItemProcessing,
markBatchItemResult,
markBatchItemError,
deleteBatch,
deleteCompletedBatches,
} from "./db/batches";
export type { FileRecord } from "./db/files";
export type { BatchItemCheckpoint, BatchRecord } from "./db/batches";
export type { ModelComboMapping } from "./db/modelComboMappings";
export * from "./db/reasoningRoutingRules";
export * from "./db/autoCandidateOverrides";
export {
// Webhooks
getWebhooks,
getWebhook,
getEnabledWebhooks,
createWebhook,
updateWebhook as updateWebhookRecord,
deleteWebhook,
recordWebhookDelivery,
disableWebhooksWithHighFailures,
} from "./db/webhooks";
export type { Webhook, WebhookKind } from "./db/webhooks";
export { insertDelivery, getDeliveries } from "./db/webhookDeliveries";
export {
upsertDiscoveryResult,
getDiscoveryResults,
getDiscoveryResultById,
markVerified,
deleteDiscoveryResult,
} from "./db/discoveryResults";
export type {
DiscoveryResult,
DiscoveryMethod,
DiscoveryAuthType,
DiscoveryRiskLevel,
DiscoveryStatus,
} from "./db/discoveryResults";
export type { WebhookDelivery } from "./db/webhookDeliveries";
export {
saveQuotaSnapshot,
getQuotaSnapshots,
getAggregatedSnapshots,
cleanupOldSnapshots,
} from "./db/quotaSnapshots";
export * from "./db/sessionAccountAffinity";
export * from "./db/quotaResetEvents";
export type { QuotaSnapshotRow, ProviderUtilizationPoint } from "@/shared/types/utilization";
export {
getVersionManagerStatus,
getVersionManagerTool,
upsertVersionManagerTool,
updateVersionManagerTool,
deleteVersionManagerTool,
updateToolHealth,
updateToolVersion,
setToolStatus,
getServiceRow,
updateServiceField,
} from "./db/versionManager";
export {
listSyncTokens,
getSyncTokenById,
getSyncTokenByHash,
createSyncTokenRecord,
revokeSyncToken,
touchSyncTokenLastUsed,
} from "./db/syncTokens";
export {
getUpstreamProxyConfigs,
getUpstreamProxyConfig,
upsertUpstreamProxyConfig,
updateUpstreamProxyConfig,
deleteUpstreamProxyConfig,
getProvidersByMode,
getFallbackChainForProvider,
validateProxyUrl,
} from "./db/upstreamProxy";
export {
getProviderLimitsCache,
getAllProviderLimitsCache,
setProviderLimitsCache,
setProviderLimitsCacheBatch,
deleteProviderLimitsCache,
} from "./db/providerLimits";
export type { ProviderLimitsCacheEntry } from "./db/providerLimits";
export {
getPersistedCreditBalance,
getAllPersistedCreditBalances,
persistCreditBalance,
} from "./db/creditBalance";
export {
insertCompressionAnalyticsRow,
getCompressionAnalyticsSummary,
} from "./db/compressionAnalytics";
export type {
CompressionAnalyticsRow,
CompressionAnalyticsSummary,
} from "./db/compressionAnalytics";
export {
// Reasoning Replay Cache (#1628)
setReasoningCache,
getReasoningCache,
deleteReasoningCache,
clearAllReasoningCache,
} from "./db/reasoningCache";
export type { ReasoningCacheEntry, ReasoningCacheStats } from "./db/reasoningCache";
export {
// 1proxy Integration (#1788)
listOneproxyProxies,
getOneproxyStats,
upsertOneproxyProxy,
getOneproxyProxyById,
deleteOneproxyProxy,
clearAllOneproxyProxies,
getOneproxyProxyForRotation,
markOneproxyProxyFailed,
} from "./db/oneproxy";
export type { OneproxyProxyRecord, OneproxyStats } from "./db/oneproxy";
export {
getSessionAccountAffinity,
upsertSessionAccountAffinity,
touchSessionAccountAffinity,
deleteSessionAccountAffinity,
evictSessionAccountAffinityForConnection,
cleanupStaleSessionAccountAffinities,
startSessionAccountAffinityCleanup,
stopSessionAccountAffinityCleanupForTests,
} from "./db/sessionAccountAffinity";
export {
// Gamification & Leaderboard
updateScore,
getRank,
getTopN,
addXp,
getXp,
updateLevel,
unlockBadge,
hasBadge,
getBadges,
getBadgeDefinitions,
transferTokens,
getBalance,
getHistory,
createInviteToken,
getInviteByCode,
redeemInvite,
revokeInvite,
connectServer,
disconnectServer,
listServers,
getConnectedServerByKeyHash,
} from "./db/gamification";
export type {
LeaderboardRow,
UserLevelRow,
BadgeDefinition,
UserBadge,
XpAuditLogEntry,
TokenLedgerEntry,
InviteToken,
CommunityServer,
} from "./db/gamification";
export * from "./db/featureFlags";
export {
upsertHandoff,
getHandoff,
deleteHandoff,
cleanupExpiredHandoffs,
hasActiveHandoff,
recordSessionModelUsage,
getLastSessionModel,
} from "./db/contextHandoffs";
export type { HandoffPayload } from "./db/contextHandoffs";
export {
getAllMiddlewareHooks,
getEnabledMiddlewareHooks,
getComboMiddlewareHooks,
getMiddlewareHook,
createMiddlewareHook,
updateMiddlewareHook,
deleteMiddlewareHook,
recordHookExecution,
insertHookLog,
getHookLogs,
cleanupHookLogs,
} from "./db/middleware";
export {
getAllKeyGroups,
getKeyGroup,
getKeyGroupWithPermissions,
createKeyGroup,
updateKeyGroup,
deleteKeyGroup,
getGroupPermissions,
addGroupPermission,
removeGroupPermission,
clearGroupPermissions,
getGroupMembers,
getKeyGroupsForApiKey,
addKeyToGroup,
removeKeyFromGroup,
checkKeyModelAccess,
} from "./db/apiKeyGroups";
export {
createRelayToken,
getRelayTokens,
getRelayToken,
getRelayTokenByHash,
updateRelayToken,
deleteRelayToken,
toggleRelayToken,
checkRateLimit,
recordRelayUsage,
getRelayUsage,
getRelayLogs,
} from "./db/relayProxies";
export type {
RelayToken,
RelayTokenRow,
RelayLogRow,
CreateRelayTokenInput,
RelayTokenWithSecret,
} from "./db/relayProxies";
export {
upsertFreeProxy,
listFreeProxies,
countFreeProxies,
listFreeProxiesBySource,
getFreeProxyById,
markFreeProxyInPool,
promoteFreeProxyToPool,
deleteFreeProxy,
clearFreeProxiesBySource,
pruneStaleFreeProxies,
getFreeProxyStats,
recordFreeProxySync,
recordFreeProxySyncErrors,
clearFreeProxySyncErrors,
getFreeProxySyncErrors,
} from "./db/freeProxies";
export type { FreeProxyRecord, FreeProxyStats, FreeProxySyncErrors } from "./db/freeProxies";
export {
listPlaygroundPresets,
getPlaygroundPreset,
createPlaygroundPreset,
updatePlaygroundPreset,
deletePlaygroundPreset,
} from "./db/playgroundPresets";
export type { PlaygroundPresetListItem } from "./db/playgroundPresets";
// Plan 21 — Memory Engine Redesign
export {
getMemoryVecMeta,
setMemoryVecMeta,
markMemoryNeedsReindex,
markAllMemoriesNeedReindex,
getMemoryReindexQueue,
countMemoryReindexPending,
type MemoryVecMeta,
} from "./db/memoryVec";
// T-A-F2: AgentBridge state/mappings/bypass + Inspector custom hosts/sessions
export * from "./db/agentBridgeState";
export * from "./db/agentBridgeMappings";
export * from "./db/agentBridgeBypass";
export * from "./db/inspectorCustomHosts";
export * from "./db/inspectorSessions";
export * from "./db/omp";
// Quota Sharing — Group B (planos 16+22)
export {
listPools,
getPool,
getPoolsByGroup,
ensurePool,
createPool,
updatePool,
deletePool,
upsertAllocations,
listAllocationsForApiKey,
} from "./db/quotaPools";
// Quota per-(key, model) caps — Group B Fase 3 #7
export { getModelCap, listModelCaps, setModelCap, deleteModelCap } from "./db/quotaModelCaps";
export {
// Quota Groups (B2)
createGroup,
getGroup,
getGroupName,
listGroups,
renameGroup,
deleteGroup,
} from "./db/quotaGroups";
export type { QuotaGroup } from "./db/quotaGroups";
export {
getBucket,
incrementBucket,
getPair,
sumPoolDimension,
gcOlderThan as gcQuotaConsumption,
} from "./db/quotaConsumption";
export {
getPlan as getProviderPlan,
listPlans as listProviderPlans,
upsertPlan as upsertProviderPlan,
deletePlan as deleteProviderPlan,
} from "./db/providerPlans";
export {
// Per-API-Key Token Limits (migration 073)
upsertTokenLimit,
listTokenLimits,
getTokenLimitsForRequest,
deleteTokenLimit,
getWindowUsage,
incrementWindowTokens,
resetWindowIfElapsed,
logTokenLimitReset,
} from "./db/tokenLimits";
export type {
TokenLimit,
TokenLimitScopeType,
UpsertTokenLimitInput,
TokenWindowState,
} from "./db/tokenLimits";
export {
insertPlugin,
getPluginById,
getPluginByName,
listPlugins,
updatePluginStatus,
updatePluginConfig,
deletePlugin,
pluginExists,
} from "./db/plugins";
export type { PluginRow, PluginCreateInput } from "./db/plugins";
export {
getApiKeyContextSource,
setApiKeyContextSource,
deleteApiKeyContextSource,
listApiKeyContextSources,
} from "./db/apiKeyContextSources";
export type { ApiKeyContextSource } from "./db/apiKeyContextSources";
export * from "./db/localCorpus";
export { sumUsageTokensThisMonth } from "./db/usageSummary";
export {
// Model Intelligence (task-fitness scores)
getModelIntelligence,
getModelIntelligenceBySource,
upsertModelIntelligence,
deleteModelIntelligence,
deleteExpiredIntelligence,
deleteModelIntelligenceBySource,
listModelIntelligence,
bulkUpsertModelIntelligence,
getResolvedTaskFitness,
setUserFitnessOverrideEntry,
deleteUserFitnessOverrideEntry,
} from "./db/modelIntelligence";
export type { ModelIntelligenceEntry } from "./db/modelIntelligence";
export {
getProviderMetrics,
getSearchProviderStats,
getRecentSearchLogs,
getSearchAggregateStats,
getSearchProviderCounts,
getFallbackStats,
} from "./db/callLogStats";
export type {
ProviderMetricRow,
SearchProviderStatRow,
SearchRecentRow,
SearchAggregateStats,
SearchProviderCountRow,
FallbackStatsRow,
} from "./db/callLogStats";
export {
buildUnifiedSource,
buildPresetUnifiedSource,
getUsageSummary,
getDailyUsage,
getDailyCostRows,
getHeatmapRows,
getModelUsageRows,
getProviderCostRows,
getProviderUsageRows,
getAccountCostRows,
getAccountUsageRows,
getApiKeyUsageRows,
getServiceTierUsageRows,
getApiKeyMetadataRows,
getWeeklyPatternRows,
getPresetCostModelRows,
getAllUsageHistory,
getAllDomainCostHistory,
getAllDomainBudgets,
} from "./db/usageAnalytics";
export type {
AnalyticsParams,
BuildUnifiedSourceOptions,
UnifiedSourceResult,
UsageSummaryRow,
DailyUsageRow,
DailyCostRow,
HeatmapRow,
ModelUsageRow,
ProviderCostRow,
ProviderUsageRow,
AccountCostRow,
AccountUsageRow,
ApiKeyUsageRow,
ServiceTierUsageRow,
ApiKeyMetadataRow,
WeeklyPatternRow,
PresetCostModelRow,
} from "./db/usageAnalytics";
// ---------------------------------------------------------------------------
// call_logs auto-routing analytics (#3500 slice 4)
// ---------------------------------------------------------------------------
export {
getAutoRoutingTotalCount,
getAutoRoutingVariantBreakdown,
getAutoRoutingTopProviders,
} from "./db/usageLogs";
export type {
AutoRoutingTotalResult,
AutoRoutingVariantRow,
AutoRoutingTopProviderRow,
} from "./db/usageLogs";
// ---------------------------------------------------------------------------
// semantic_cache — cache entries CRUD (#3500 slice 4)
// ---------------------------------------------------------------------------
export {
listSemanticCacheEntries,
deleteSemanticCacheBySignature,
deleteSemanticCacheByModel,
} from "./db/semanticCache";
export type {
SemanticCacheEntry,
SemanticCacheListOptions,
SemanticCacheListResult,
DeleteSemanticCacheBySignatureResult,
DeleteSemanticCacheByModelResult,
} from "./db/semanticCache";
// ---------------------------------------------------------------------------
// proxy_logs — export query (#3500 slice 4)
// ---------------------------------------------------------------------------
export { exportProxyLogsSince } from "./db/proxyLogs";
// ---------------------------------------------------------------------------
// Per-connection 429 cooldown wrappers (#5957 / #5958 — Issue 1 follow-ups)
// Logic lives in db/providers/rateLimit.ts (Hard Rule #2 — localDb is re-export
// only); re-exported here for the historical localDb import contract.
// ---------------------------------------------------------------------------
export { markConnectionRateLimitedUntil, clearConnectionRateLimit } from "./db/providers";
// Provider param filters — denylist/allowlist config per provider/model (#6625)
export * from "./db/paramFilters";
export * from "./db/interceptionRules"; // Per-model web-search/web-fetch interception rules (#3384)
export * from "./db/relayProbeStats"; // Relay probe latency/health stats (#6909)
export * from "./db/ccDiscoveryAliases"; // Claude Code discovery-alias gate (flag + per-provider/model overrides)
export * from "./db/ccDiscoveryMetrics"; // Claude Code discovery-alias usage counters (alias requests + discovery hits)
export * from "./db/functionalGatewayMirrors"; // Functional-gateway mirror gate (flag + per-provider/model overrides)
// Radar client — local feed cache + settings (opt-in, encrypted supporter key)
export {
getRadarCache,
setRadarCache,
getRadarSettings,
setRadarOptIn,
setRadarKey,
getRadarReferralsCache,
setRadarReferralsCache,
getRadarOffersCache,
setRadarOffersCache,
getRadarIntelCache,
setRadarIntelCache,
listRadarLocalModelState,
setRadarLocalModelOverride,
clearRadarLocalModelOverride,
setRadarModelTombstone,
getRadarLocalMergeState,
} from "./db/radar";
export type {
RadarCache,
RadarSettings,
RadarReferralsCache,
RadarOffersCache,
RadarIntelCache,
RadarLocalModelState,
RadarLocalModelOverridePatch,
RadarLocalMergeState,
} from "./db/radar";
export * from "./db/conductorBridge"; // OmniConductor hub mirror — SSE cursor (PRD Conductor RF1)
export * from "./db/agenticConversations"; // Multi-turn conversation id tracking (X-ConversationId)

View File

@@ -8,7 +8,7 @@
* import technique — tokenHealthCheck.ts-private helpers passed as params
* rather than imported) but greenfield: type-checked normally, no
* @ts-nocheck, and imports updateProviderConnection directly from its
* owning module rather than the localDb barrel (Hard Rule #2).
* owning db/ module (Hard Rule #2).
*/
import { updateProviderConnection } from "@/lib/db/providers";

View File

@@ -20,7 +20,8 @@ process.env.API_KEY_SECRET = "test-all-statuses-secret";
// Import DB modules after setting DATA_DIR
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
// Import cliTools modules (batchStatusCache for cache tests)

View File

@@ -12,7 +12,8 @@ process.env.CLOUD_URL = "http://cloud.example";
const core = await import("../../src/lib/db/core.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const compliance = await import("../../src/lib/compliance/index.ts");
const listRoute = await import("../../src/app/api/keys/route.ts");
const keyRoute = await import("../../src/app/api/keys/[id]/route.ts");

View File

@@ -11,7 +11,10 @@ process.env.API_KEY_SECRET = "test-api-key-secret";
const core = await import("../../src/lib/db/core.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const { assignProxyToScope, createProxy } = await import("@/lib/db/proxies");
const { createProviderConnection } = await import("@/lib/db/providers");
const localDb = { updateSettings, assignProxyToScope, createProviderConnection, createProxy };
const proxiesRoute = await import("../../src/app/api/v1/management/proxies/route.ts");
const settingsProxyRoute = await import("../../src/app/api/settings/proxy/route.ts");
const settingsMitmRoute = await import("../../src/app/api/settings/mitm/route.ts");

View File

@@ -20,7 +20,8 @@ process.env.API_KEY_SECRET = "test-api-key-secret-codewhale";
process.env.JWT_SECRET = "test-jwt-secret-codewhale";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const { GET, POST, DELETE } =
await import("../../src/app/api/cli-tools/codewhale-settings/route.ts");

View File

@@ -14,7 +14,8 @@ process.env.API_KEY_SECRET = "test-api-key-secret-deepseek-tui";
process.env.JWT_SECRET = "test-jwt-secret-deepseek-tui";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const { GET, POST, DELETE } =
await import("../../src/app/api/cli-tools/deepseek-tui-settings/route.ts");

View File

@@ -16,7 +16,8 @@ process.env.JWT_SECRET = "test-jwt-secret-forge";
// Import DB reset helpers (must be before route import)
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
// Import route handlers
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/forge-settings/route.ts");

View File

@@ -29,7 +29,8 @@ process.env.OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE = "1";
// Import DB reset helpers (must be before route import)
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
// Import route handlers

View File

@@ -14,7 +14,8 @@ process.env.API_KEY_SECRET = "test-api-key-secret-jcode";
process.env.JWT_SECRET = "test-jwt-secret-jcode";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/jcode-settings/route.ts");

View File

@@ -20,7 +20,8 @@ process.env.API_KEY_SECRET = "test-api-key-secret-letta";
process.env.JWT_SECRET = "test-jwt-secret-letta";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/letta-settings/route.ts");

View File

@@ -22,7 +22,8 @@ process.env.API_KEY_SECRET = "test-api-key-secret-omp";
process.env.JWT_SECRET = "test-jwt-secret-omp";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/omp-settings/route.ts");

View File

@@ -14,7 +14,8 @@ process.env.API_KEY_SECRET = "test-api-key-secret-pi";
process.env.JWT_SECRET = "test-jwt-secret-pi";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/pi-settings/route.ts");

View File

@@ -14,7 +14,8 @@ process.env.API_KEY_SECRET = "test-api-key-secret-smelt";
process.env.JWT_SECRET = "test-jwt-secret-smelt";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/smelt-settings/route.ts");

View File

@@ -61,10 +61,7 @@ const PROVIDER_DEFAULT_MODELS: Record<string, string> = {
async function isHealthy(conn: LiveConnection): Promise<boolean> {
if (!h.LIVE_ENABLED) return false;
const model =
conn.model ??
PROVIDER_DEFAULT_MODELS[conn.provider] ??
`${conn.provider}/default`;
const model = conn.model ?? PROVIDER_DEFAULT_MODELS[conn.provider] ?? `${conn.provider}/default`;
const directModel = `${conn.provider}/${model}`;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), WARMUP_TIMEOUT_MS);
@@ -90,14 +87,18 @@ async function isHealthy(conn: LiveConnection): Promise<boolean> {
/**
* Pick up to `n` confirmed-healthy connections, preferring fast/cheap providers.
*/
async function pickConfirmedHealthy(
n: number,
preferred?: string[]
): Promise<LiveConnection[]> {
async function pickConfirmedHealthy(n: number, preferred?: string[]): Promise<LiveConnection[]> {
if (!h.LIVE_ENABLED) return [];
const conns = await (h as any).listLiveConnections();
const PREFERRED_ORDER =
preferred ?? ["groq", "cerebras", "opencode-go", "deepseek", "gemini", "together", "openrouter"];
const PREFERRED_ORDER = preferred ?? [
"groq",
"cerebras",
"opencode-go",
"deepseek",
"gemini",
"together",
"openrouter",
];
const sorted = [...conns].sort((a: LiveConnection, b: LiveConnection) => {
const ai = PREFERRED_ORDER.indexOf(a.provider);
const bi = PREFERRED_ORDER.indexOf(b.provider);
@@ -118,7 +119,7 @@ async function pickConfirmedHealthy(
*/
async function resolveInputCost(provider: string, model: string): Promise<number> {
try {
const { getPricingForModel } = await import("../../../src/lib/localDb.ts");
const { getPricingForModel } = await import("@/lib/db/settings");
const pricing = await getPricingForModel(provider, model);
const cost = Number((pricing as any)?.input);
return Number.isFinite(cost) ? cost : Infinity;
@@ -164,317 +165,344 @@ after(async () => {
// Test 1: cost-optimized — cheaper real provider served first
// ---------------------------------------------------------------------------
test("live cost-optimized — cheaper real provider served first", {
skip: !h.LIVE_ENABLED && "RUN_COMBO_LIVE!=1",
}, async () => {
if (!h.LIVE_ENABLED) return;
test(
"live cost-optimized — cheaper real provider served first",
{
skip: !h.LIVE_ENABLED && "RUN_COMBO_LIVE!=1",
},
async () => {
if (!h.LIVE_ENABLED) return;
// We prefer groq+deepseek: distinct model names (easy to confirm served),
// and deepseek has a known non-zero price ($0.28/M) while groq free models
// land at $0 in the registry. The cost sorter puts $0 before $0.28, so if
// we list deepseek FIRST and groq SECOND, a correct sort gives us groq first.
//
// We fall back to cerebras if groq is unhealthy.
const candidates = await pickConfirmedHealthy(2, [
"groq", "cerebras", "deepseek", "opencode-go",
]);
// We need exactly 2 healthy connections with DISTINCT providers.
const seen = new Set<string>();
const uniqueCandidates = candidates.filter((c: LiveConnection) => {
if (seen.has(c.provider)) return false;
seen.add(c.provider);
return true;
});
if (uniqueCandidates.length < 2) {
console.log(
`[cost-optimized skip] Only ${uniqueCandidates.length} distinct healthy provider(s) — need ≥2. Skipping.`
);
return;
}
const [a, b] = uniqueCandidates;
const aModel = a.model ?? PROVIDER_DEFAULT_MODELS[a.provider] ?? `${a.provider}/default`;
const bModel = b.model ?? PROVIDER_DEFAULT_MODELS[b.provider] ?? `${b.provider}/default`;
// Resolve pricing from the live DB snapshot.
const aCost = await resolveInputCost(a.provider, aModel);
const bCost = await resolveInputCost(b.provider, bModel);
console.log(
`[cost-optimized] Candidates: ${a.provider}/${aModel} ($${aCost}/M) vs ${b.provider}/${bModel} ($${bCost}/M)`
);
// If both are Infinity (no pricing data for either), we can't prove cost ordering.
if (!Number.isFinite(aCost) && !Number.isFinite(bCost)) {
console.log(
`[cost-optimized skip] Neither ${a.provider} nor ${b.provider} has resolvable catalog ` +
`pricing in the live DB snapshot — cannot prove cost ordering. Skipping.`
);
return;
}
// If both are equal (including both $0), we can't prove reordering.
if (aCost === bCost) {
console.log(
`[cost-optimized skip] Both providers have equal pricing ($${aCost}/M each) — ` +
`cannot prove reordering. Skipping.`
);
return;
}
// Identify cheap vs pricey.
const [cheapConn, priceyConn] =
aCost <= bCost ? [a, b] : [b, a];
const [cheapCost, priceyCost] =
aCost <= bCost ? [aCost, bCost] : [bCost, aCost];
const cheapModel =
cheapConn.model ?? PROVIDER_DEFAULT_MODELS[cheapConn.provider] ?? `${cheapConn.provider}/default`;
const priceyModel =
priceyConn.model ?? PROVIDER_DEFAULT_MODELS[priceyConn.provider] ?? `${priceyConn.provider}/default`;
console.log(
`[cost-optimized] Cheap: ${cheapConn.provider}/${cheapModel} ($${cheapCost}/M), ` +
`Pricey: ${priceyConn.provider}/${priceyModel} ($${priceyCost}/M)`
);
// Create combo with PRICEY first — a correct cost sorter must reorder to CHEAP first.
const comboName = `__live-smoke-cost-opt-${Date.now()}__`;
const combo = await (h as any).combosDb.createCombo({
name: comboName,
strategy: "cost-optimized",
// Pricey listed FIRST: proves the sorter reorders, not just uses the given order.
models: [(h as any).comboModelFor(priceyConn), (h as any).comboModelFor(cheapConn)],
config: { maxRetries: 0, retryDelayMs: 0, stickyRoundRobinLimit: 1 },
});
try {
const response = await (h as any).handleChat(
(h as any).buildRequest({
body: (h as any).liveBody(comboName, {
messages: [{ role: "user", content: `ping ${uniqueNonce("cost-opt")}` }],
}),
})
);
assert.equal(response.status, 200, `Expected HTTP 200, got ${response.status}`);
const text = await (h as any).readCompletionText(response);
assert.ok(text.length > 0, "Expected non-empty completion text from cost-optimized combo");
// Collect served-provider signals.
const headerProvider = (h as any).servedProvider(response);
const bodyProvider = await (h as any).servedProviderFromBody(response);
const rawModel = await readResponseModel(response);
console.log(
`[cost-optimized] served: header=${headerProvider ?? "(absent)"}, ` +
`body=${bodyProvider ?? "(absent)"}, model="${rawModel ?? "(n/a)"}"`
);
// PRIMARY ASSERTION: served provider must be the cheap one, not the pricey one.
// The cost sorter should have put the cheap provider first despite it being listed second.
// We prefer groq+deepseek: distinct model names (easy to confirm served),
// and deepseek has a known non-zero price ($0.28/M) while groq free models
// land at $0 in the registry. The cost sorter puts $0 before $0.28, so if
// we list deepseek FIRST and groq SECOND, a correct sort gives us groq first.
//
// We use three signals in priority order:
// 1. X-OmniRoute-Selected-Connection-Id header (fallback paths only — may be absent on 200).
// 2. Body model field provider prefix (e.g. "groq/model" → "groq").
// 3. Raw model string comparison (model name matches cheap provider's known model).
// We fall back to cerebras if groq is unhealthy.
const candidates = await pickConfirmedHealthy(2, [
"groq",
"cerebras",
"deepseek",
"opencode-go",
]);
// Signal 1: header.
if (headerProvider !== undefined) {
assert.equal(
headerProvider,
cheapConn.provider,
`[header] Expected cheap provider "${cheapConn.provider}" to serve, got "${headerProvider}". ` +
`Cost sorter may not have reordered: cheap=$${cheapCost}/M, pricey=$${priceyCost}/M`
);
// We need exactly 2 healthy connections with DISTINCT providers.
const seen = new Set<string>();
const uniqueCandidates = candidates.filter((c: LiveConnection) => {
if (seen.has(c.provider)) return false;
seen.add(c.provider);
return true;
});
if (uniqueCandidates.length < 2) {
console.log(
`[cost-optimized PASS via header] ${cheapConn.provider} served (cost $${cheapCost}/M < $${priceyCost}/M)`
`[cost-optimized skip] Only ${uniqueCandidates.length} distinct healthy provider(s) — need ≥2. Skipping.`
);
return;
}
// Signal 2: body provider prefix.
if (bodyProvider !== undefined) {
assert.equal(
bodyProvider,
cheapConn.provider,
`[body prefix] Expected cheap provider "${cheapConn.provider}" to serve, got "${bodyProvider}". ` +
`Cost sorter may not have reordered: cheap=$${cheapCost}/M, pricey=$${priceyCost}/M`
);
const [a, b] = uniqueCandidates;
const aModel = a.model ?? PROVIDER_DEFAULT_MODELS[a.provider] ?? `${a.provider}/default`;
const bModel = b.model ?? PROVIDER_DEFAULT_MODELS[b.provider] ?? `${b.provider}/default`;
// Resolve pricing from the live DB snapshot.
const aCost = await resolveInputCost(a.provider, aModel);
const bCost = await resolveInputCost(b.provider, bModel);
console.log(
`[cost-optimized] Candidates: ${a.provider}/${aModel} ($${aCost}/M) vs ${b.provider}/${bModel} ($${bCost}/M)`
);
// If both are Infinity (no pricing data for either), we can't prove cost ordering.
if (!Number.isFinite(aCost) && !Number.isFinite(bCost)) {
console.log(
`[cost-optimized PASS via body prefix] ${cheapConn.provider} served (cost $${cheapCost}/M < $${priceyCost}/M)`
`[cost-optimized skip] Neither ${a.provider} nor ${b.provider} has resolvable catalog ` +
`pricing in the live DB snapshot — cannot prove cost ordering. Skipping.`
);
return;
}
// Signal 3: raw model string.
if (rawModel !== undefined) {
// If the response model matches the cheap provider's model name, the cheap provider served.
if (rawModel === cheapModel || rawModel.endsWith(`/${cheapModel}`)) {
// If both are equal (including both $0), we can't prove reordering.
if (aCost === bCost) {
console.log(
`[cost-optimized skip] Both providers have equal pricing ($${aCost}/M each) — ` +
`cannot prove reordering. Skipping.`
);
return;
}
// Identify cheap vs pricey.
const [cheapConn, priceyConn] = aCost <= bCost ? [a, b] : [b, a];
const [cheapCost, priceyCost] = aCost <= bCost ? [aCost, bCost] : [bCost, aCost];
const cheapModel =
cheapConn.model ??
PROVIDER_DEFAULT_MODELS[cheapConn.provider] ??
`${cheapConn.provider}/default`;
const priceyModel =
priceyConn.model ??
PROVIDER_DEFAULT_MODELS[priceyConn.provider] ??
`${priceyConn.provider}/default`;
console.log(
`[cost-optimized] Cheap: ${cheapConn.provider}/${cheapModel} ($${cheapCost}/M), ` +
`Pricey: ${priceyConn.provider}/${priceyModel} ($${priceyCost}/M)`
);
// Create combo with PRICEY first — a correct cost sorter must reorder to CHEAP first.
const comboName = `__live-smoke-cost-opt-${Date.now()}__`;
const combo = await (h as any).combosDb.createCombo({
name: comboName,
strategy: "cost-optimized",
// Pricey listed FIRST: proves the sorter reorders, not just uses the given order.
models: [(h as any).comboModelFor(priceyConn), (h as any).comboModelFor(cheapConn)],
config: { maxRetries: 0, retryDelayMs: 0, stickyRoundRobinLimit: 1 },
});
try {
const response = await (h as any).handleChat(
(h as any).buildRequest({
body: (h as any).liveBody(comboName, {
messages: [{ role: "user", content: `ping ${uniqueNonce("cost-opt")}` }],
}),
})
);
assert.equal(response.status, 200, `Expected HTTP 200, got ${response.status}`);
const text = await (h as any).readCompletionText(response);
assert.ok(text.length > 0, "Expected non-empty completion text from cost-optimized combo");
// Collect served-provider signals.
const headerProvider = (h as any).servedProvider(response);
const bodyProvider = await (h as any).servedProviderFromBody(response);
const rawModel = await readResponseModel(response);
console.log(
`[cost-optimized] served: header=${headerProvider ?? "(absent)"}, ` +
`body=${bodyProvider ?? "(absent)"}, model="${rawModel ?? "(n/a)"}"`
);
// PRIMARY ASSERTION: served provider must be the cheap one, not the pricey one.
// The cost sorter should have put the cheap provider first despite it being listed second.
//
// We use three signals in priority order:
// 1. X-OmniRoute-Selected-Connection-Id header (fallback paths only — may be absent on 200).
// 2. Body model field provider prefix (e.g. "groq/model" → "groq").
// 3. Raw model string comparison (model name matches cheap provider's known model).
// Signal 1: header.
if (headerProvider !== undefined) {
assert.equal(
headerProvider,
cheapConn.provider,
`[header] Expected cheap provider "${cheapConn.provider}" to serve, got "${headerProvider}". ` +
`Cost sorter may not have reordered: cheap=$${cheapCost}/M, pricey=$${priceyCost}/M`
);
console.log(
`[cost-optimized PASS via model field] model="${rawModel}" matches cheap provider ` +
`${cheapConn.provider}/${cheapModel} (cost $${cheapCost}/M < $${priceyCost}/M)`
`[cost-optimized PASS via header] ${cheapConn.provider} served (cost $${cheapCost}/M < $${priceyCost}/M)`
);
return;
}
// If the response model matches the pricey provider's model name, the cost sort failed.
if (rawModel === priceyModel || rawModel.endsWith(`/${priceyModel}`)) {
assert.fail(
`[model field] Pricey provider "${priceyConn.provider}" (model="${priceyModel}", ` +
`$${priceyCost}/M) served BEFORE cheap "${cheapConn.provider}" (model="${cheapModel}", ` +
`$${cheapCost}/M) — cost sorter did not reorder correctly.`
// Signal 2: body provider prefix.
if (bodyProvider !== undefined) {
assert.equal(
bodyProvider,
cheapConn.provider,
`[body prefix] Expected cheap provider "${cheapConn.provider}" to serve, got "${bodyProvider}". ` +
`Cost sorter may not have reordered: cheap=$${cheapCost}/M, pricey=$${priceyCost}/M`
);
console.log(
`[cost-optimized PASS via body prefix] ${cheapConn.provider} served (cost $${cheapCost}/M < $${priceyCost}/M)`
);
return;
}
// Model field present but does not match either known model (provider echoes an
// aliased or prefixed name). We cannot distinguish which provider served.
console.warn(
`[cost-optimized] Signal ambiguous: rawModel="${rawModel}" does not match ` +
`"${cheapModel}" or "${priceyModel}". Got 200 + non-empty text; cannot confirm ` +
`cheap provider served. Recording as diagnostic-pass (cost gap confirmed: ` +
`$${cheapCost}/M vs $${priceyCost}/M).`
);
return;
}
// Signal 3: raw model string.
if (rawModel !== undefined) {
// If the response model matches the cheap provider's model name, the cheap provider served.
if (rawModel === cheapModel || rawModel.endsWith(`/${cheapModel}`)) {
console.log(
`[cost-optimized PASS via model field] model="${rawModel}" matches cheap provider ` +
`${cheapConn.provider}/${cheapModel} (cost $${cheapCost}/M < $${priceyCost}/M)`
);
return;
}
// No signal at all — 200 + non-empty but we cannot confirm which provider served.
console.warn(
`[cost-optimized] All provider signals absent (header=absent, body=absent, model=absent). ` +
`Got HTTP 200 + non-empty text. Cost gap confirmed ($${cheapCost}/M vs $${priceyCost}/M) ` +
`but serving provider not identifiable. Recording as diagnostic-pass.`
);
} finally {
if (typeof combo?.id === "string") {
await (h as any).combosDb.deleteCombo(combo.id as string);
// If the response model matches the pricey provider's model name, the cost sort failed.
if (rawModel === priceyModel || rawModel.endsWith(`/${priceyModel}`)) {
assert.fail(
`[model field] Pricey provider "${priceyConn.provider}" (model="${priceyModel}", ` +
`$${priceyCost}/M) served BEFORE cheap "${cheapConn.provider}" (model="${cheapModel}", ` +
`$${cheapCost}/M) — cost sorter did not reorder correctly.`
);
}
// Model field present but does not match either known model (provider echoes an
// aliased or prefixed name). We cannot distinguish which provider served.
console.warn(
`[cost-optimized] Signal ambiguous: rawModel="${rawModel}" does not match ` +
`"${cheapModel}" or "${priceyModel}". Got 200 + non-empty text; cannot confirm ` +
`cheap provider served. Recording as diagnostic-pass (cost gap confirmed: ` +
`$${cheapCost}/M vs $${priceyCost}/M).`
);
return;
}
// No signal at all — 200 + non-empty but we cannot confirm which provider served.
console.warn(
`[cost-optimized] All provider signals absent (header=absent, body=absent, model=absent). ` +
`Got HTTP 200 + non-empty text. Cost gap confirmed ($${cheapCost}/M vs $${priceyCost}/M) ` +
`but serving provider not identifiable. Recording as diagnostic-pass.`
);
} finally {
if (typeof combo?.id === "string") {
await (h as any).combosDb.deleteCombo(combo.id as string);
}
}
}
});
);
// ---------------------------------------------------------------------------
// Test 2: fusion — panel fans out + judge synthesizes one answer
// ---------------------------------------------------------------------------
test("live fusion — panel fans out and judge synthesizes one answer", {
skip: !h.LIVE_ENABLED && "RUN_COMBO_LIVE!=1",
}, async () => {
if (!h.LIVE_ENABLED) return;
test(
"live fusion — panel fans out and judge synthesizes one answer",
{
skip: !h.LIVE_ENABLED && "RUN_COMBO_LIVE!=1",
},
async () => {
if (!h.LIVE_ENABLED) return;
// Cost guard: pick ≤3 panel providers. Use cheapest/most reliable.
const candidates = await pickConfirmedHealthy(3, [
"groq", "cerebras", "opencode-go", "deepseek", "gemini", "together",
]);
// Cost guard: pick ≤3 panel providers. Use cheapest/most reliable.
const candidates = await pickConfirmedHealthy(3, [
"groq",
"cerebras",
"opencode-go",
"deepseek",
"gemini",
"together",
]);
// Need at least 2 distinct healthy providers to run a real fusion.
const seen = new Set<string>();
const panelConns = candidates
.filter((c: LiveConnection) => {
if (seen.has(c.provider)) return false;
seen.add(c.provider);
return true;
})
.slice(0, 3); // cap at 3 to limit cost
if (panelConns.length < 2) {
console.log(
`[fusion skip] Only ${panelConns.length} distinct healthy provider(s) confirmed — ` +
`need ≥2 for a real panel. Skipping.`
);
return;
}
// Judge = first (cheapest) panel provider — cheap enough for a 16-token synthesis call.
const judgeConn = panelConns[0];
const judgeModel =
judgeConn.model ??
PROVIDER_DEFAULT_MODELS[judgeConn.provider] ??
`${judgeConn.provider}/default`;
const judgeModelStr = `${judgeConn.provider}/${judgeModel}`;
const panelModels = panelConns.map((c: LiveConnection) => (h as any).comboModelFor(c));
console.log(
`[fusion] Panel (${panelConns.length}): ${panelConns.map((c: LiveConnection) => c.provider).join(", ")} | ` +
`judge: ${judgeModelStr}`
);
const comboName = `__live-smoke-fusion-${Date.now()}__`;
const combo = await (h as any).combosDb.createCombo({
name: comboName,
strategy: "fusion",
models: panelModels,
config: {
maxRetries: 0,
retryDelayMs: 0,
judgeModel: judgeModelStr,
fusionTuning: {
minPanel: 2,
panelHardTimeoutMs: 90_000,
},
},
});
try {
const response = await (h as any).handleChat(
(h as any).buildRequest({
body: (h as any).liveBody(comboName, {
messages: [{ role: "user", content: `ping ${uniqueNonce("fusion")} — reply in one short sentence` }],
// max_tokens:16 is the harness default via liveBody(); the judge call will
// also be bounded, keeping the panel + judge calls cheap.
}),
// Need at least 2 distinct healthy providers to run a real fusion.
const seen = new Set<string>();
const panelConns = candidates
.filter((c: LiveConnection) => {
if (seen.has(c.provider)) return false;
seen.add(c.provider);
return true;
})
);
.slice(0, 3); // cap at 3 to limit cost
assert.equal(response.status, 200, `Expected HTTP 200 from fusion combo, got ${response.status}`);
if (panelConns.length < 2) {
console.log(
`[fusion skip] Only ${panelConns.length} distinct healthy provider(s) confirmed — ` +
`need ≥2 for a real panel. Skipping.`
);
return;
}
const text = await (h as any).readCompletionText(response);
assert.ok(
text.length > 0,
"Expected a non-empty synthesized completion from the fusion judge"
);
// The fusion response comes from the JUDGE call.
// The body's `model` field should reflect the judge model, confirming the judge ran.
const rawModel = await readResponseModel(response);
const bodyProvider = await (h as any).servedProviderFromBody(response);
// Judge = first (cheapest) panel provider — cheap enough for a 16-token synthesis call.
const judgeConn = panelConns[0];
const judgeModel =
judgeConn.model ??
PROVIDER_DEFAULT_MODELS[judgeConn.provider] ??
`${judgeConn.provider}/default`;
const judgeModelStr = `${judgeConn.provider}/${judgeModel}`;
const panelModels = panelConns.map((c: LiveConnection) => (h as any).comboModelFor(c));
console.log(
`[fusion] synthesized text (first 80 chars): "${text.slice(0, 80)}" | ` +
`model="${rawModel ?? "(n/a)"}" | body provider=${bodyProvider ?? "(absent)"}`
`[fusion] Panel (${panelConns.length}): ${panelConns.map((c: LiveConnection) => c.provider).join(", ")} | ` +
`judge: ${judgeModelStr}`
);
// SIGNAL ANALYSIS — panel/judge evidence.
// The judge model string is judgeModelStr (e.g. "groq/llama-3.1-8b-instant").
// If rawModel matches the judge's model name, the judge ran.
if (rawModel !== undefined) {
const judgeRan =
rawModel === judgeModel ||
rawModel === judgeModelStr ||
rawModel.endsWith(`/${judgeModel}`);
if (judgeRan) {
console.log(
`[fusion EVIDENCE] Judge confirmed: response model="${rawModel}" matches judge ${judgeModelStr}`
);
} else {
// Model field doesn't match judge — may be aliased or provider returns own name.
console.warn(
`[fusion] response model="${rawModel}" does not exactly match judge "${judgeModelStr}". ` +
`Panel synthesis still assumed from HTTP 200 + non-empty text.`
);
const comboName = `__live-smoke-fusion-${Date.now()}__`;
const combo = await (h as any).combosDb.createCombo({
name: comboName,
strategy: "fusion",
models: panelModels,
config: {
maxRetries: 0,
retryDelayMs: 0,
judgeModel: judgeModelStr,
fusionTuning: {
minPanel: 2,
panelHardTimeoutMs: 90_000,
},
},
});
try {
const response = await (h as any).handleChat(
(h as any).buildRequest({
body: (h as any).liveBody(comboName, {
messages: [
{
role: "user",
content: `ping ${uniqueNonce("fusion")} — reply in one short sentence`,
},
],
// max_tokens:16 is the harness default via liveBody(); the judge call will
// also be bounded, keeping the panel + judge calls cheap.
}),
})
);
assert.equal(
response.status,
200,
`Expected HTTP 200 from fusion combo, got ${response.status}`
);
const text = await (h as any).readCompletionText(response);
assert.ok(
text.length > 0,
"Expected a non-empty synthesized completion from the fusion judge"
);
// The fusion response comes from the JUDGE call.
// The body's `model` field should reflect the judge model, confirming the judge ran.
const rawModel = await readResponseModel(response);
const bodyProvider = await (h as any).servedProviderFromBody(response);
console.log(
`[fusion] synthesized text (first 80 chars): "${text.slice(0, 80)}" | ` +
`model="${rawModel ?? "(n/a)"}" | body provider=${bodyProvider ?? "(absent)"}`
);
// SIGNAL ANALYSIS — panel/judge evidence.
// The judge model string is judgeModelStr (e.g. "groq/llama-3.1-8b-instant").
// If rawModel matches the judge's model name, the judge ran.
if (rawModel !== undefined) {
const judgeRan =
rawModel === judgeModel ||
rawModel === judgeModelStr ||
rawModel.endsWith(`/${judgeModel}`);
if (judgeRan) {
console.log(
`[fusion EVIDENCE] Judge confirmed: response model="${rawModel}" matches judge ${judgeModelStr}`
);
} else {
// Model field doesn't match judge — may be aliased or provider returns own name.
console.warn(
`[fusion] response model="${rawModel}" does not exactly match judge "${judgeModelStr}". ` +
`Panel synthesis still assumed from HTTP 200 + non-empty text.`
);
}
}
// The fusion panel had ≥2 members — at least 2 distinct upstream calls happened
// before the judge synthesized. We can't easily introspect individual panel calls
// from the test layer (they're internal to fusion.ts / combo.ts). The 200 + non-empty
// text from the judge is the authoritative proof of fusion completing.
console.log(
`[fusion PASS] Panel(${panelConns.length}) → judge(${judgeModelStr}) synthesis: HTTP 200, ` +
`${text.length} chars. Provider signal from body: ${bodyProvider ?? "absent (model name has no slash prefix)"}.`
);
} finally {
if (typeof combo?.id === "string") {
await (h as any).combosDb.deleteCombo(combo.id as string);
}
}
// The fusion panel had ≥2 members — at least 2 distinct upstream calls happened
// before the judge synthesized. We can't easily introspect individual panel calls
// from the test layer (they're internal to fusion.ts / combo.ts). The 200 + non-empty
// text from the judge is the authoritative proof of fusion completing.
console.log(
`[fusion PASS] Panel(${panelConns.length}) → judge(${judgeModelStr}) synthesis: HTTP 200, ` +
`${text.length} chars. Provider signal from body: ${bodyProvider ?? "absent (model name has no slash prefix)"}.`
);
} finally {
if (typeof combo?.id === "string") {
await (h as any).combosDb.deleteCombo(combo.id as string);
}
}
});
);

View File

@@ -1,6 +1,6 @@
import { describe, it, afterEach } from "node:test";
import assert from "node:assert";
import { createFile, listFiles, deleteFile, getFile } from "@/lib/localDb";
import { createFile, listFiles, deleteFile, getFile } from "@/lib/db/files";
describe("Files API - Integration Tests", () => {
afterEach(() => {

View File

@@ -19,7 +19,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-secret-embedding-providers";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
// Import route AFTER setting DATA_DIR
const embeddingProvidersRoute =

View File

@@ -19,7 +19,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-secret-engine-status";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
// Import route AFTER setting DATA_DIR
const engineStatusRoute = await import("../../src/app/api/memory/engine-status/route.ts");

View File

@@ -20,7 +20,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-secret-reindex";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const memoryStore = await import("../../src/lib/memory/store.ts");
const reindexRoute = await import("../../src/app/api/memory/reindex/route.ts");

View File

@@ -19,7 +19,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-secret-retrieve-preview";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
// Import route AFTER setting DATA_DIR
const retrieveRoute = await import("../../src/app/api/memory/retrieve-preview/route.ts");

View File

@@ -19,7 +19,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-secret-for-memory-put";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
// ── Dynamic import of route module (after DATA_DIR set) ──
const memoryIdRoute = await import("../../src/app/api/memory/[id]/route.ts");

View File

@@ -19,7 +19,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-secret-summarize";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const memoryStore = await import("../../src/lib/memory/store.ts");
const summarizeRoute = await import("../../src/app/api/memory/summarize/route.ts");

View File

@@ -56,7 +56,10 @@ process.env.DISABLE_SQLITE_AUTO_BACKUP = "true";
process.env.INITIAL_PASSWORD = "provider-journey-bootstrap";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const { updateProviderConnection } = await import("@/lib/db/providers");
const { getCachedProviderNodes } = await import("@/lib/db/readCache");
const localDb = { updateSettings, updateProviderConnection, getCachedProviderNodes };
const modelsDb = await import("../../src/lib/db/models.ts");
const providerNodesRoute = await import("../../src/app/api/provider-nodes/route.ts");
const providersRoute = await import("../../src/app/api/providers/route.ts");

View File

@@ -26,7 +26,9 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-secret-qdrant-routes";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings, getSettings } = await import("@/lib/db/settings");
const { createProviderConnection } = await import("@/lib/db/providers");
const localDb = { updateSettings, getSettings, createProviderConnection };
const memorySettings = await import("../../src/lib/memory/settings.ts");
// ── Route imports ──

View File

@@ -23,7 +23,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-quota-plans-secret";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const compliance = await import("../../src/lib/compliance/index.ts");
const plansRoute = await import("../../src/app/api/quota/plans/route.ts");
const planIdRoute = await import("../../src/app/api/quota/plans/[connectionId]/route.ts");

View File

@@ -27,8 +27,8 @@ process.env.API_KEY_SECRET = "test-quota-usage-provider-secret";
process.env.QUOTA_STORE_DRIVER = "sqlite";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { createPool, upsertAllocations, createProviderConnection } = localDb;
const { createPool, upsertAllocations } = await import("@/lib/db/quotaPools");
const { createProviderConnection } = await import("@/lib/db/providers");
const { resetQuotaStoreSingleton } = await import("../../src/lib/quota/QuotaStore.ts");
const usageRoute = await import("../../src/app/api/quota/pools/[id]/usage/route.ts");

View File

@@ -26,7 +26,8 @@ process.env.API_KEY_SECRET = "test-quota-pools-secret";
// Import in dependency order to ensure migrations run before routes
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const compliance = await import("../../src/lib/compliance/index.ts");
const poolsRoute = await import("../../src/app/api/quota/pools/route.ts");
const poolIdRoute = await import("../../src/app/api/quota/pools/[id]/route.ts");

View File

@@ -26,9 +26,10 @@ process.env.API_KEY_SECRET = "test-quota-usage-secret";
process.env.QUOTA_STORE_DRIVER = "sqlite";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const { createPool, upsertAllocations } = await import("@/lib/db/quotaPools");
const localDb = { updateSettings };
const compliance = await import("../../src/lib/compliance/index.ts");
const { createPool, upsertAllocations } = localDb;
const { getSqliteQuotaStore } = await import("../../src/lib/quota/sqliteQuotaStore.ts");
const { resetQuotaStoreSingleton } = await import("../../src/lib/quota/QuotaStore.ts");
const poolsRoute = await import("../../src/app/api/quota/pools/route.ts");

View File

@@ -25,9 +25,10 @@ process.env.API_KEY_SECRET = "test-quota-preview-secret";
process.env.QUOTA_STORE_DRIVER = "sqlite";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const { createPool, upsertAllocations } = await import("@/lib/db/quotaPools");
const localDb = { updateSettings };
const compliance = await import("../../src/lib/compliance/index.ts");
const { createPool, upsertAllocations } = localDb;
const { getSqliteQuotaStore } = await import("../../src/lib/quota/sqliteQuotaStore.ts");
const { resetQuotaStoreSingleton } = await import("../../src/lib/quota/QuotaStore.ts");
const previewRoute = await import("../../src/app/api/quota/preview/route.ts");

View File

@@ -26,7 +26,8 @@ delete process.env.QUOTA_STORE_REDIS_URL;
process.env.QUOTA_STORE_DRIVER = "sqlite";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const compliance = await import("../../src/lib/compliance/index.ts");
const { resetQuotaStoreSingleton } = await import("../../src/lib/quota/QuotaStore.ts");
const settingsRoute = await import("../../src/app/api/settings/quota-store/route.ts");

View File

@@ -16,7 +16,6 @@ process.env.DATA_DIR = TEST_DATA_DIR;
// Boot DB so migrations run
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const hostsRoute = await import("../../src/app/api/tools/traffic-inspector/hosts/route.ts");
const hostDetailRoute =

View File

@@ -10,7 +10,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "acp-agents-route-api-key-secret";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const routeModule = await import("../../src/app/api/acp/agents/route.ts");
const ORIGINAL_INITIAL_PASSWORD = process.env.INITIAL_PASSWORD;

View File

@@ -10,7 +10,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-api-key-secret";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const apiAuth = await import("../../src/shared/utils/apiAuth.ts");
const { requireManagementAuth } = await import("../../src/lib/api/requireManagementAuth.ts");

View File

@@ -9,7 +9,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "usage-limit-test-secret";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updatePricing } = await import("@/lib/db/settings");
const localDb = { updatePricing };
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const usageHistory = await import("../../src/lib/usage/usageHistory.ts");
const usageLimits = await import("../../src/lib/usage/apiKeyUsageLimits.ts");

View File

@@ -27,7 +27,8 @@ process.env.CLOUD_URL = "http://cloud.example";
const core = await import("../../src/lib/db/core.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings, getSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings, getSettings };
const listRoute = await import("../../src/app/api/keys/route.ts");
async function resetStorage() {

View File

@@ -9,7 +9,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { replaceSyncedAvailableModelsForConnection } = await import("@/lib/db/models");
const localDb = { replaceSyncedAvailableModelsForConnection };
const modelsRoute = await import("../../src/app/api/models/route.ts");
const v1ModelsCatalog = await import("../../src/app/api/v1/models/catalog.ts");

View File

@@ -33,7 +33,7 @@ const {
getModelIsHidden,
mergeModelCompatOverride,
updateCustomModel,
} = await import("../../src/lib/localDb.ts");
} = await import("@/lib/db/models");
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
before(() => {

View File

@@ -1,14 +1,7 @@
import { describe, it } from "node:test";
import assert from "node:assert";
import {
createFile,
createBatch,
getBatch,
deleteBatch,
deleteCompletedBatches,
getFile,
deleteFile,
} from "@/lib/localDb";
import { createFile, getFile, deleteFile } from "@/lib/db/files";
import { createBatch, getBatch, deleteBatch, deleteCompletedBatches } from "@/lib/db/batches";
describe("deleteBatch", () => {
it("should delete a single batch and its associated files", () => {

View File

@@ -18,7 +18,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-secret-file-dl";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { createFile, deleteFile } = await import("@/lib/db/files");
const localDb = { createFile, deleteFile };
const fileContentRoute = await import("../../src/app/api/files/[id]/content/route.ts");
async function resetStorage() {

View File

@@ -12,7 +12,25 @@ process.env.API_KEY_SECRET = "test-secret";
// We import these as modules to allow mocking
const core = await import("@/lib/db/core.ts");
const localDb = await import("@/lib/localDb");
const { createFile, getFileContent } = await import("@/lib/db/files");
const {
createBatch,
ensureBatchItemCheckpoints,
getBatch,
markBatchItemProcessing,
markBatchItemResult,
updateBatch,
} = await import("@/lib/db/batches");
const localDb = {
createFile,
getFileContent,
createBatch,
ensureBatchItemCheckpoints,
getBatch,
markBatchItemProcessing,
markBatchItemResult,
updateBatch,
};
const { dispatch } = await import("@/lib/batches/dispatch");
const batchProcessor = await import("../../open-sse/services/batchProcessor.ts");
const { waitForAllBatches, getCachedHeaders, resetCachedHeaders } = batchProcessor;

View File

@@ -8,22 +8,18 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-batch-api
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "test-secret-123";
const { createFile, getFileContent, getFile, listFiles, formatFileResponse, deleteFile } =
await import("@/lib/db/files");
const {
createFile,
createBatch,
getBatch,
getFileContent,
updateBatch,
createProviderConnection,
createApiKey,
getFile,
listFiles,
formatFileResponse,
deleteFile,
getTerminalBatches,
ensureBatchItemCheckpoints,
markBatchItemResult,
} = await import("../../src/lib/localDb.ts");
} = await import("@/lib/db/batches");
const { createProviderConnection } = await import("@/lib/db/providers");
const { createApiKey } = await import("@/lib/db/apiKeys");
const { getDbInstance } = await import("../../src/lib/db/core.ts");
const {
initBatchProcessor,
@@ -580,7 +576,7 @@ test("List batches pagination and response format", async () => {
const batchIds = batchOrder.map((entry) => entry.id);
// 2. Test listBatches logic (direct DB call)
const { listBatches } = await import("../../src/lib/localDb");
const { listBatches } = await import("@/lib/db/batches");
const allBatches = listBatches(apiKey.id, 10);
assert.strictEqual(allBatches.length, 5);
assert.strictEqual(allBatches[0].id, batchIds[0]);

View File

@@ -8,14 +8,10 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-batch-res
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "test-secret-123";
const {
createFile,
createBatch,
getBatch,
getFileContent,
createProviderConnection,
createApiKey,
} = await import("../../src/lib/localDb.ts");
const { createFile, getFileContent } = await import("@/lib/db/files");
const { createBatch, getBatch } = await import("@/lib/db/batches");
const { createProviderConnection } = await import("@/lib/db/providers");
const { createApiKey } = await import("@/lib/db/apiKeys");
const { initBatchProcessor, stopBatchProcessor, waitForAllBatches, processPendingBatches } =
await import("../../open-sse/services/batchProcessor.ts");
@@ -127,7 +123,7 @@ test("Batch processor produces output file for successful items", async () => {
assert.ok(obj.response && typeof obj.response.status_code === "number");
// Output file should have an expiration timestamp set (30 days default)
const { getFile } = await import("../../src/lib/localDb.ts");
const { getFile } = await import("@/lib/db/files");
const fileRow = getFile(currentBatch.outputFileId!);
assert.ok(fileRow?.expiresAt && typeof fileRow.expiresAt === "number");
}

View File

@@ -1,232 +0,0 @@
/**
* Regression guard for INTENTIONALLY_INTERNAL classification (#3499).
*
* Every module in INTENTIONALLY_INTERNAL must be genuinely consumed via a
* direct/dynamic import somewhere in src/, open-sse/, bin/, or within
* src/lib/db/ itself (for db-internal coordination modules like stateReset,
* healthCheck, migrationRunner), OR documented as type-only / DEAD?.
*
* Purpose: if a future cleanup removes the last consumer of a module that is
* still in INTENTIONALLY_INTERNAL, this test turns red and forces a conscious
* reclassification decision — the DEAD? category must be explicitly expanded,
* not silently accumulated.
*
* Hard Rule #18 compliance: this test was written BEFORE the fix was applied,
* confirmed to fail on the old framing, and now passes on the audited truth.
*/
import { test } from "node:test";
import assert from "node:assert";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { INTENTIONALLY_INTERNAL } from "../../scripts/check/check-db-rules.mjs";
const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), "../../..");
// ── Helpers ────────────────────────────────────────────────────────────────
/**
* Walk a directory tree, calling cb(absolutePath) for every file.
*/
function walkSync(dir: string, cb: (p: string) => void): void {
if (!fs.existsSync(dir)) return;
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
// Skip well-known non-source dirs to keep the walk fast
if (["node_modules", ".git", ".next", "dist", "out"].includes(entry.name)) continue;
walkSync(full, cb);
} else {
cb(full);
}
}
}
/**
* Returns true if any file in the search roots imports db/<mod> (static or dynamic).
* Patterns matched:
* from "@/lib/db/<mod>"
* from "../db/<mod>" (any relative path ending /db/<mod>)
* import("@/lib/db/<mod>")
* require(".../db/<mod>")
* import(`${...}/db/<mod>.ts`) — dynamic template (bin/cli/runtime.mjs pattern)
*/
function hasImporter(mod: string, roots: string[]): boolean {
// Escape special regex chars in mod name (underscore-prefixed names like _rowTypes)
const escaped = mod.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const patterns = [
// static: from "…/db/<mod>"
new RegExp(`from\\s+['""][^'"]+/db/${escaped}['"]`),
// dynamic: import("…/db/<mod>") or require("…/db/<mod>")
new RegExp(`(?:import|require)\\s*\\(\\s*['""][^'"]+/db/${escaped}['"]`),
// dynamic template: import(`…/db/<mod>.ts`) — bin/cli/runtime.mjs uses template literals
new RegExp(`import\\s*\\(\`[^'"\`]+/db/${escaped}\\.ts\`\\)`),
// dynamic via file:// URL helper: import(projectFileUrl("…/db/<mod>.ts")) —
// bin/cli/runtime.mjs since #11238 (Windows-safe file:// dynamic imports).
new RegExp(
`import\\s*\\(\\s*projectFileUrl\\(\\s*['""][^'"]+/db/${escaped}\\.ts['"]\\s*\\)\\s*\\)`
),
// relative import within db/: from "./<mod>" or from "./<mod>"
new RegExp(`from\\s+['"]\\.\\.?/${escaped}['"]`),
];
// The canonical db module path — we must skip ONLY this exact file, not all files
// that happen to share the same basename (e.g. src/lib/usage/comboForecast.ts must
// NOT be skipped when searching for importers of src/lib/db/comboForecast.ts).
const dbModulePath = path.join(REPO_ROOT, "src", "lib", "db", `${mod}.ts`);
let found = false;
for (const root of roots) {
if (found) break;
walkSync(root, (filePath) => {
if (found) return;
// Only scan TS/JS/MJS source files
if (!/\.(ts|tsx|mjs|js|cjs)$/.test(filePath)) return;
// Skip only the db module itself (not same-name files in other dirs)
if (filePath === dbModulePath) return;
let src: string;
try {
src = fs.readFileSync(filePath, "utf8");
} catch {
return;
}
if (patterns.some((rx) => rx.test(src))) found = true;
});
}
return found;
}
// ── Audit roots ────────────────────────────────────────────────────────────
// Modules marked "type-only": exports only TypeScript types; no runtime import
// needed — the ts compiler erases them. These are genuinely correct as-is.
const TYPE_ONLY = new Set(["_rowTypes"]);
// Modules explicitly documented as DEAD? in the classification comments.
// They remain in INTENTIONALLY_INTERNAL for schema-reservation reasons.
// Flag them but do NOT fail — a separate decision is needed to remove them.
const DOCUMENTED_DEAD = new Set([
"discovery", // DEAD?: 0 importers; lib/discovery/index.ts is independent
"pluginMetrics", // DEAD? (production): write path not yet wired (self-documented)
"prompts", // DEAD? (production): zero production callers; integration test only verifies interface shape
]);
const SEARCH_ROOTS = [
path.join(REPO_ROOT, "src"),
path.join(REPO_ROOT, "open-sse"),
path.join(REPO_ROOT, "bin"),
path.join(REPO_ROOT, "tests"),
];
// ── Tests ──────────────────────────────────────────────────────────────────
test("INTENTIONALLY_INTERNAL is exported from check-db-rules.mjs", () => {
assert.ok(
INTENTIONALLY_INTERNAL instanceof Set,
"INTENTIONALLY_INTERNAL must be a Set exported from the gate script"
);
assert.ok(INTENTIONALLY_INTERNAL.size > 0, "INTENTIONALLY_INTERNAL must not be empty");
});
test("INTENTIONALLY_INTERNAL contains the expected 40 audited modules", () => {
const expected = [
"_rowTypes",
"accessTokens",
"apiKeyColumnFallbacks",
"apiKeyUsageLimitFields",
"backupRetention",
"caseMapping",
"cleanup",
"cliToolState",
"comboForecast",
"commandCodeAuth",
"compression",
"compressionDetailNormalizers",
"connectionRuntimeState",
"detailedLogs",
"discovery",
"domainState",
"encryption",
"healthCheck",
"jsonMigration",
"migrationRunner",
"modelCapabilityOverrides",
"notion",
"obsidian",
"optimizationSettings",
"pluginMetrics",
"prompts",
"probeUtils",
"providerNodeSelect",
"providerStats",
"proxyLatency",
"proxySubscriptions",
"recovery",
"schemaColumns",
"secrets",
"serviceModels",
"stateReset",
"stats",
"tierConfig",
"vacuumScheduler",
"webSessionDedup",
];
for (const mod of expected) {
assert.ok(
INTENTIONALLY_INTERNAL.has(mod),
`Expected ${mod} to be in INTENTIONALLY_INTERNAL after audit`
);
}
assert.equal(
INTENTIONALLY_INTERNAL.size,
expected.length,
`INTENTIONALLY_INTERNAL has ${INTENTIONALLY_INTERNAL.size} entries; expected ${expected.length}`
);
});
test("every non-type-only, non-dead module in INTENTIONALLY_INTERNAL has ≥1 real importer", () => {
const failures: string[] = [];
for (const mod of INTENTIONALLY_INTERNAL) {
if (TYPE_ONLY.has(mod)) continue; // type-only: no runtime import expected
if (DOCUMENTED_DEAD.has(mod)) continue; // dead modules exempted with explicit flag
if (!hasImporter(mod, SEARCH_ROOTS)) {
failures.push(mod);
}
}
assert.deepEqual(
failures,
[],
`These INTENTIONALLY_INTERNAL modules have ZERO importers — either they became dead ` +
`(add to DOCUMENTED_DEAD with a DEAD? comment) or they were misclassified:\n ${failures.join(", ")}`
);
});
test("type-only module _rowTypes is imported within src/lib/db/ by its consumers", () => {
// _rowTypes exports only TypeScript interfaces, so the import is always `import type`.
// It should be consumed within db/ itself (AgentBridge, Inspector CRUD modules).
const dbDir = path.join(REPO_ROOT, "src/lib/db");
let found = false;
if (fs.existsSync(dbDir)) {
for (const entry of fs.readdirSync(dbDir, { withFileTypes: true })) {
if (!entry.isFile() || !/\.ts$/.test(entry.name)) continue;
if (entry.name === "_rowTypes.ts") continue;
const src = fs.readFileSync(path.join(dbDir, entry.name), "utf8");
if (/from\s+['"]\.\/_rowTypes['"]/.test(src)) {
found = true;
break;
}
}
}
assert.ok(
found,
"_rowTypes must be imported (as type) by at least one sibling module in src/lib/db/"
);
});
test("DOCUMENTED_DEAD modules are still in INTENTIONALLY_INTERNAL (dead list must stay honest)", () => {
for (const mod of DOCUMENTED_DEAD) {
assert.ok(
INTENTIONALLY_INTERNAL.has(mod),
`DOCUMENTED_DEAD module "${mod}" is no longer in INTENTIONALLY_INTERNAL — ` +
`remove it from DOCUMENTED_DEAD in this test if it was intentionally removed from the gate`
);
}
});

View File

@@ -4,135 +4,20 @@ import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
collectDbModules,
extractReexportedModules,
findMissingReexports,
hasLogic,
extractStringLiterals,
findRawSql,
collectSqlScanFiles,
INTENTIONALLY_INTERNAL,
KNOWN_UNEXPORTED,
EXTERNAL_DB_ALLOWED,
KNOWN_RAW_SQL,
} from "../../scripts/check/check-db-rules.mjs";
import { reportStaleEntries } from "../../scripts/check/lib/allowlist.mjs";
const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), "../../..");
const LOCAL_DB = path.join(REPO_ROOT, "src/lib/localDb.ts");
// ---------- (a) re-export completeness ----------
test("findMissingReexports: flags a NEW db module that is not re-exported", () => {
const dbModules = ["providers", "brandNewModule"];
const reexported = new Set(["providers"]) as Set<string>;
const allowlist = new Set<string>();
const missing = findMissingReexports(dbModules, reexported, allowlist) as string[];
assert.deepEqual(missing, ["brandNewModule"]);
});
test("findMissingReexports: a re-exported module passes", () => {
const dbModules = ["providers"];
const reexported = new Set(["providers"]) as Set<string>;
const missing = findMissingReexports(dbModules, reexported, new Set<string>()) as string[];
assert.deepEqual(missing, []);
});
test("findMissingReexports: an allowlisted (frozen) module passes even if not re-exported", () => {
const dbModules = ["notion"];
const reexported = new Set<string>();
const allowlist = new Set(["notion"]) as Set<string>;
const missing = findMissingReexports(dbModules, reexported, allowlist) as string[];
assert.deepEqual(missing, []);
});
test("extractReexportedModules: parses ./db/X from export forms", () => {
const src = [
'export { getCombos } from "./db/combos";',
'export * from "./db/featureFlags";',
'export type { Webhook } from "./db/webhooks";',
'export { sumUsageTokensThisMonth } from "./db/usageSummary";',
// not a db module — must be ignored
'export { initPricingSync } from "./pricingSync";',
].join("\n");
const mods = extractReexportedModules(src) as Set<string>;
assert.equal(mods.has("combos"), true);
assert.equal(mods.has("featureFlags"), true);
assert.equal(mods.has("webhooks"), true);
assert.equal(mods.has("usageSummary"), true);
assert.equal(mods.has("pricingSync"), false);
});
test("collectDbModules: returns real modules and excludes core/localDb/index", () => {
const mods = collectDbModules() as string[];
assert.ok(mods.includes("providers"), "expected providers module");
assert.ok(mods.includes("combos"), "expected combos module");
assert.equal(mods.includes("core"), false, "core must be excluded");
assert.equal(mods.includes("localDb"), false, "localDb must be excluded");
assert.equal(mods.includes("index"), false, "index must be excluded");
});
// FREEZE GUARD: the live repo state must be green under the shipped allowlist.
test("live repo: no NEW unexported db modules beyond the frozen allowlist", async () => {
// Re-import the gate's frozen allowlist indirectly by running its default behavior:
// findMissingReexports with the gate default allowlist must be empty for the repo.
const dbModules = collectDbModules() as string[];
const reexported = extractReexportedModules(fs.readFileSync(LOCAL_DB, "utf8")) as Set<string>;
// Default allowlist (KNOWN_UNEXPORTED) is applied inside findMissingReexports.
const missing = findMissingReexports(dbModules, reexported) as string[];
assert.deepEqual(
missing,
[],
`Unexported db module(s) not in KNOWN_UNEXPORTED: ${missing.join(", ")}`
);
});
// ---------- (b) localDb has no logic ----------
test("hasLogic: false for a pure re-export layer", () => {
const src = [
"// re-export layer",
'export { a, b } from "./db/foo";',
'export * from "./db/bar";',
'export type { T } from "./db/baz";',
].join("\n");
assert.equal(hasLogic(src) as boolean, false);
});
test("hasLogic: true for a function declaration", () => {
const src = 'export { a } from "./db/foo";\nfunction doThing() { return 1; }';
assert.equal(hasLogic(src) as boolean, true);
});
test("hasLogic: true for an arrow-function const", () => {
const src = 'export { a } from "./db/foo";\nconst helper = (x) => x + 1;';
assert.equal(hasLogic(src) as boolean, true);
});
test("hasLogic: true for a class declaration", () => {
const src = 'export { a } from "./db/foo";\nclass Thing {}';
assert.equal(hasLogic(src) as boolean, true);
});
test("hasLogic: SQL/logic-looking text inside comments or strings does not trip", () => {
const src = [
"/* function notReal() {} */",
"// const fake = () => 1;",
'export const SOURCE = "./db/foo";', // string only, no function on rhs
'export { a } from "./db/foo";',
].join("\n");
// export const X = "string" is a value (not logic): the rhs is a string literal,
// so the arrow/call pattern must NOT match.
assert.equal(hasLogic(src) as boolean, false);
});
test("live repo: src/lib/localDb.ts contains no logic", () => {
const src = fs.readFileSync(LOCAL_DB, "utf8");
assert.equal(hasLogic(src) as boolean, false);
});
// ---------- (c) no raw SQL outside db/ ----------
test("extractStringLiterals: returns only string bodies, ignoring code", () => {
const code = 'import { x } from "y";\nconst q = `SELECT * FROM t`;\nobj.set(1);';
const code = 'import { x } from "y";\nconst q = `SELECT * FROM t`; obj.set(1);';
const literals = extractStringLiterals(code) as string;
assert.ok(literals.includes("SELECT * FROM t"), "captures the template body");
assert.ok(literals.includes("y"), "captures the import path string");
@@ -143,7 +28,7 @@ test("findRawSql: flags a NEW route with raw SQL in a string literal", () => {
const tmp = path.join(REPO_ROOT, ".tmp-check-db-rules-raw-sql.route.ts");
fs.writeFileSync(
tmp,
'const rows = db.prepare(`SELECT id FROM users WHERE x = ?`).all();\n',
"const rows = db.prepare(`SELECT id FROM users WHERE x = ?`).all();\n",
"utf8"
);
try {
@@ -156,7 +41,11 @@ test("findRawSql: flags a NEW route with raw SQL in a string literal", () => {
test("findRawSql: does NOT flag SQL that only appears in a comment", () => {
const tmp = path.join(REPO_ROOT, ".tmp-check-db-rules-comment.route.ts");
fs.writeFileSync(tmp, "// SELECT id FROM users -- documentation only\nexport const x = 1;\n", "utf8");
fs.writeFileSync(
tmp,
"// SELECT id FROM users -- documentation only\nexport const x = 1;\n",
"utf8"
);
try {
const offenders = findRawSql([tmp], new Set<string>()) as string[];
assert.deepEqual(offenders, []);
@@ -193,6 +82,10 @@ test("findRawSql: an allowlisted (frozen) offender passes", () => {
assert.deepEqual(offenders, []);
});
test("KNOWN_RAW_SQL is an alias for EXTERNAL_DB_ALLOWED (retrocompat)", () => {
assert.equal(EXTERNAL_DB_ALLOWED, KNOWN_RAW_SQL);
});
test("live repo: no NEW raw-SQL offenders beyond the frozen allowlist", () => {
// findRawSql uses the gate default allowlist (KNOWN_RAW_SQL) when none is passed.
const files = collectSqlScanFiles() as string[];
@@ -202,17 +95,6 @@ test("live repo: no NEW raw-SQL offenders beyond the frozen allowlist", () => {
// --- stale-allowlist enforcement (6A.3) ---
test("stale-enforcement: INTENTIONALLY_INTERNAL entry no longer unexported is reported as stale", () => {
// Simulate a module that has now been re-exported (no longer unexported).
const liveUnexported: string[] = []; // module was re-exported
const stale = (reportStaleEntries as (a: Set<string>, l: string[], g: string) => string[])(
new Set(["oldModule"]),
liveUnexported,
"check-db-rules:unexported"
);
assert.deepEqual(stale, ["oldModule"]);
});
test("stale-enforcement: EXTERNAL_DB_ALLOWED entry no longer has raw SQL is reported as stale", () => {
// Simulate a file that no longer contains raw SQL (route was refactored).
const liveRawSql: string[] = [];
@@ -223,23 +105,3 @@ test("stale-enforcement: EXTERNAL_DB_ALLOWED entry no longer has raw SQL is repo
);
assert.deepEqual(stale, ["src/app/api/oauth/cursor/auto-import/route.ts"]);
});
test("stale-enforcement: live repo INTENTIONALLY_INTERNAL entries are all still unexported", () => {
// Every entry in INTENTIONALLY_INTERNAL must still be an unexported module.
// If it was re-exported (moved to localDb.ts), it must be removed from the allowlist.
const dbModules = collectDbModules() as string[];
const reexported = extractReexportedModules(
fs.readFileSync(path.resolve(fileURLToPath(import.meta.url), "../../../src/lib/localDb.ts"), "utf8")
) as Set<string>;
const liveUnexported = dbModules.filter((mod) => !reexported.has(mod));
const stale = (reportStaleEntries as (a: Set<string>, l: string[], g: string) => string[])(
INTENTIONALLY_INTERNAL as Set<string>,
liveUnexported,
"check-db-rules:unexported"
);
assert.deepEqual(stale, [], `INTENTIONALLY_INTERNAL has stale entries: ${stale.join(", ")}`);
});
test("KNOWN_UNEXPORTED is an alias for INTENTIONALLY_INTERNAL (retrocompat)", () => {
assert.equal(INTENTIONALLY_INTERNAL, KNOWN_UNEXPORTED);
});

View File

@@ -56,7 +56,8 @@ process.env.API_KEY_SECRET = "test-api-key-secret-crush";
process.env.JWT_SECRET = "test-jwt-secret-crush";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/crush-settings/route.ts");

View File

@@ -20,7 +20,19 @@ type ProviderConnectionRecord = {
};
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { resetApiKeyState, createApiKey } = await import("@/lib/db/apiKeys");
const { updateSettings } = await import("@/lib/db/settings");
const { createProviderConnection, getProviderConnections } = await import("@/lib/db/providers");
const { setModelAlias, getModelAliases } = await import("@/lib/db/models");
const localDb = {
resetApiKeyState,
updateSettings,
createApiKey,
createProviderConnection,
getProviderConnections,
setModelAlias,
getModelAliases,
};
const credentialsRoute = await import("../../src/app/api/cloud/credentials/update/route.ts");
const aliasRoute = await import("../../src/app/api/cloud/models/alias/route.ts");

View File

@@ -17,7 +17,9 @@ interface EvalsRoutePayload {
}
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { resetApiKeyState, createApiKey } = await import("@/lib/db/apiKeys");
const { saveCustomEvalSuite, saveEvalRun } = await import("@/lib/db/evals");
const localDb = { resetApiKeyState, createApiKey, saveCustomEvalSuite, saveEvalRun };
const evalsRoute = await import("../../src/app/api/evals/route.ts");
const evalSuitesRoute = await import("../../src/app/api/evals/suites/route.ts");
const evalSuiteByIdRoute = await import("../../src/app/api/evals/suites/[suiteId]/route.ts");

View File

@@ -8,8 +8,8 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-file-dele
const ORIGINAL_DATA_DIR = process.env.DATA_DIR;
process.env.DATA_DIR = TEST_DATA_DIR;
const { createFile, deleteFile, listFiles, createBatch, getBatch, updateBatch } =
await import("@/lib/localDb");
const { createFile, deleteFile, listFiles } = await import("@/lib/db/files");
const { createBatch, getBatch, updateBatch } = await import("@/lib/db/batches");
const { getDbInstance, resetDbInstance } = await import("@/lib/db/core");
after(() => {

View File

@@ -1,6 +1,6 @@
import { describe, it, before, afterEach } from "node:test";
import assert from "node:assert";
import { createFile, getFile, listFiles, deleteFile } from "@/lib/localDb";
import { createFile, getFile, listFiles, deleteFile } from "@/lib/db/files";
import { getDbInstance } from "@/lib/db/core.ts";
describe("File Expiration Policy", () => {

View File

@@ -24,7 +24,8 @@ const backupDb = await import("../../src/lib/db/backup.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const combosDb = await import("../../src/lib/db/combos.ts");
const settingsDb = await import("../../src/lib/db/settings.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { createProxy } = await import("@/lib/db/proxies");
const localDb = { createProxy };
const tokenRefresh = await import("../../open-sse/services/tokenRefresh.ts");
const proxyFetch = await import("../../open-sse/utils/proxyFetch.ts");
const proxyDispatcher = await import("../../open-sse/utils/proxyDispatcher.ts");

View File

@@ -31,7 +31,7 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const modelsDb = await import("../../src/lib/db/models.ts");
const { mergeModelCompatOverride } = await import("../../src/lib/localDb.ts");
const { mergeModelCompatOverride } = await import("@/lib/db/models");
const v1ModelsCatalog = await import("../../src/app/api/v1/models/catalog.ts");
async function resetStorage() {

View File

@@ -14,7 +14,8 @@ process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "test-live-ws-public-
const core = await import("../../src/lib/db/core.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const wsRoute = await import("../../src/app/api/v1/ws/route.ts");
function resetStorage() {

View File

@@ -9,7 +9,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const modelsDb = await import("../../src/lib/db/models.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { getModelAliases, setModelAlias } = await import("@/lib/db/models");
const localDb = { getModelAliases, setModelAlias };
const { compatibleProviderSupportsModelImport, getCompatibleFallbackModels } =
await import("../../src/lib/providers/managedAvailableModels.ts");
const {

View File

@@ -9,7 +9,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const modelsDb = await import("../../src/lib/db/models.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { getModelAliases, setModelAlias } = await import("@/lib/db/models");
const localDb = { getModelAliases, setModelAlias };
const { importManagedModels } = await import("../../src/lib/providerModels/managedModelImport.ts");
const { mergeProviderModelListing } =
await import("../../src/lib/providers/mergeProviderModelListing.ts");

View File

@@ -12,7 +12,8 @@ process.env.JWT_SECRET = process.env.JWT_SECRET || "model-alias-route-jwt";
const core = await import("../../src/lib/db/core.ts");
const modelsDb = await import("../../src/lib/db/models.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const route = await import("../../src/app/api/models/alias/route.ts");
const catalogRoute = await import("../../src/app/api/models/catalog/route.ts");
const v1Catalog = await import("../../src/app/api/v1/models/catalog.ts");

View File

@@ -22,7 +22,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.JWT_SECRET = process.env.JWT_SECRET || "model-aliases-selfheal-jwt";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const modelDeprecation = await import("../../open-sse/services/modelDeprecation.ts");
const route = await import("../../src/app/api/settings/model-aliases/route.ts");

View File

@@ -12,7 +12,9 @@ if (!process.env.API_KEY_SECRET) {
}
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const { setModelAlias, getModelAliases } = await import("@/lib/db/models");
const localDb = { updateSettings, setModelAlias, getModelAliases };
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const modelsDb = await import("../../src/lib/db/models.ts");

View File

@@ -15,7 +15,8 @@ process.env.JWT_SECRET = "test-jwt-secret-for-oidc-callback";
// @ts-ignore - intentional for test harness timing (see note at top)
const core = await import("../../src/lib/db/core.ts");
// @ts-ignore - intentional for test harness timing
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
// @ts-ignore - intentional for test harness timing
const callbackRoute = await import("../../src/app/api/auth/oidc/callback/route.ts");

View File

@@ -14,7 +14,8 @@ process.env.JWT_SECRET = "test-jwt-secret-for-oidc-login";
// @ts-ignore - intentional for test harness timing (see note at top)
const core = await import("../../src/lib/db/core.ts");
// @ts-ignore - intentional for test harness timing
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
// @ts-ignore - intentional for test harness timing
const loginRoute = await import("../../src/app/api/auth/oidc/login/route.ts");

View File

@@ -34,7 +34,8 @@ const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
import { applyErrorState, resetAccountState } from "../../open-sse/services/accountFallback.ts";
import { markConnectionRateLimitedUntil, clearConnectionRateLimit } from "../../src/lib/localDb.ts";
const { markConnectionRateLimitedUntil, clearConnectionRateLimit } =
await import("@/lib/db/providers");
test.after(() => {
core.resetDbInstance();

View File

@@ -11,7 +11,8 @@ process.env.API_KEY_SECRET = "provider-window-costs-test-secret";
const core = await import("../../src/lib/db/core.ts");
const apiKeys = await import("../../src/lib/db/apiKeys.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updatePricing } = await import("@/lib/db/settings");
const localDb = { updatePricing };
const providerLimits = await import("../../src/lib/db/providerLimits.ts");
const usageHistory = await import("../../src/lib/usage/usageHistory.ts");
const costRules = await import("../../src/domain/costRules.ts");

View File

@@ -17,7 +17,8 @@ process.env.API_KEY_SECRET = "test-api-key-secret";
const core = await import("../../src/lib/db/core.ts");
const proxyLogger = await import("../../src/lib/proxyLogger.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const route = await import("../../src/app/api/settings/proxies/egress/route.ts");

View File

@@ -26,7 +26,7 @@ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-test-replace-cus
process.env.DATA_DIR = tmpDir;
const { replaceCustomModels, mergeModelCompatOverride, getModelIsHidden, getModelCompatOverrides } =
await import("../../src/lib/localDb.ts");
await import("@/lib/db/models");
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
before(() => {

View File

@@ -15,7 +15,10 @@ const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const compliance = await import("../../src/lib/compliance/index.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const modelsDb = await import("../../src/lib/db/models.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const { createProxy, assignProxyToScope } = await import("@/lib/db/proxies");
const { invalidateDbCache } = await import("@/lib/db/readCache");
const localDb = { updateSettings, createProxy, assignProxyToScope, invalidateDbCache };
const listKeysRoute = await import("../../src/app/api/keys/route.ts");
const settingsProxyRoute = await import("../../src/app/api/settings/proxy/route.ts");
const managementProxiesRoute = await import("../../src/app/api/v1/management/proxies/route.ts");

View File

@@ -26,7 +26,7 @@ process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "9293-test-secret";
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const { mergeModelCompatOverride, getModelIsHidden } = await import("../../src/lib/localDb.ts");
const { mergeModelCompatOverride, getModelIsHidden } = await import("@/lib/db/models");
const v1ModelsCatalog = await import("../../src/app/api/v1/models/catalog.ts");
async function resetStorage() {

View File

@@ -20,7 +20,8 @@ const compliance = await import("../../src/lib/compliance/index.ts");
const syncTokensRoute = await import("../../src/app/api/sync/tokens/route.ts");
const syncTokenByIdRoute = await import("../../src/app/api/sync/tokens/[id]/route.ts");
const syncBundleRoute = await import("../../src/app/api/sync/bundle/route.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
function resetStorage() {
apiKeysDb.resetApiKeyState();

View File

@@ -14,7 +14,7 @@ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-test-synced-del-
process.env.DATA_DIR = tmpDir;
const { replaceSyncedAvailableModelsForConnection, getSyncedAvailableModels } =
await import("../../src/lib/localDb.ts");
await import("@/lib/db/models");
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
before(() => {
@@ -38,7 +38,7 @@ test("a deleted synced model is restored when upstream advertises it again", asy
let synced = (await getSyncedAvailableModels(provider)).map((m) => m.id);
assert.ok(synced.includes("model-del"), "both models present after first sync");
const { removeSyncedAvailableModel } = await import("../../src/lib/localDb.ts");
const { removeSyncedAvailableModel } = await import("@/lib/db/models");
assert.equal(await removeSyncedAvailableModel(provider, "model-del"), true);
synced = (await getSyncedAvailableModels(provider)).map((m) => m.id);
assert.ok(!synced.includes("model-del"), "delete removes the current synced row");

View File

@@ -24,7 +24,7 @@ const {
getSyncedAvailableModels,
mergeModelCompatOverride,
getModelIsHidden,
} = await import("../../src/lib/localDb.ts");
} = await import("@/lib/db/models");
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
before(() => {

View File

@@ -9,7 +9,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-account-analytics-secret";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updatePricing } = await import("@/lib/db/settings");
const localDb = { updatePricing };
const providersDb = await import("../../src/lib/db/providers.ts");
const usageHistory = await import("../../src/lib/usage/usageHistory.ts");
const { resolveUsageAccountIdentity } = await import("../../src/lib/usage/accountIdentity.ts");

View File

@@ -10,7 +10,8 @@ const ORIGINAL_API_KEY_SECRET = process.env.API_KEY_SECRET;
process.env.API_KEY_SECRET = "test-usage-analytics-secret";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updatePricing } = await import("@/lib/db/settings");
const localDb = { updatePricing };
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const usageHistory = await import("../../src/lib/usage/usageHistory.ts");

View File

@@ -8,7 +8,8 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-usage-ana
process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updatePricing } = await import("@/lib/db/settings");
const localDb = { updatePricing };
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const usageHistory = await import("../../src/lib/usage/usageHistory.ts");

View File

@@ -13,7 +13,8 @@ process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "test-v1-ws-route-sec
const core = await import("../../src/lib/db/core.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { updateSettings } = await import("@/lib/db/settings");
const localDb = { updateSettings };
const wsRoute = await import("../../src/app/api/v1/ws/route.ts");
function resetStorage() {