- ADR-0001: Multi-Agent Coworking Architecture with LLM Gateway Orchestrator - ADR-0002: Tier Assignment Strategy for Model Selection (cost-first escalation) - ADR-0003: Confidence Gate Thresholds & Learning Cycle Intervals (6h/12h/24h cycles) - ADR-0004: External Provider Fallback Chain Ordering (Cerebras → Groq → Mistral) - Enhanced client SDK: Offline Ollama fallback, health checks, exponential backoff retry - Integration tests: claude-code-integration.test.ts (14 test cases) - PHASE_2F_DEPLOYMENT.md: Pre-deployment checklist, automated deploy, rollback plan - Post-deployment verification procedures for health, client fallback, metrics
81 lines
2.3 KiB
TypeScript
81 lines
2.3 KiB
TypeScript
import { readFileSync } from 'fs';
|
|
import { resolve } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { getPool } from './client.js';
|
|
import { logger } from '../observability/logger.js';
|
|
|
|
const __dirname = resolve(fileURLToPath(import.meta.url), '..');
|
|
|
|
interface MigrationRecord {
|
|
name: string;
|
|
executed_at: string;
|
|
}
|
|
|
|
async function ensureMigrationsTable(): Promise<void> {
|
|
const pool = getPool();
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL UNIQUE,
|
|
executed_at TIMESTAMP NOT NULL DEFAULT NOW()
|
|
);
|
|
`);
|
|
}
|
|
|
|
async function getMigratedFiles(): Promise<Set<string>> {
|
|
const pool = getPool();
|
|
try {
|
|
const result = await pool.query('SELECT name FROM schema_migrations;');
|
|
return new Set(result.rows.map((row: MigrationRecord) => row.name));
|
|
} catch {
|
|
return new Set();
|
|
}
|
|
}
|
|
|
|
async function runMigration(name: string, sql: string): Promise<void> {
|
|
const pool = getPool();
|
|
const client = await pool.connect();
|
|
|
|
try {
|
|
await client.query('BEGIN');
|
|
await client.query(sql);
|
|
await client.query(
|
|
'INSERT INTO schema_migrations (name) VALUES ($1) ON CONFLICT (name) DO NOTHING;',
|
|
[name],
|
|
);
|
|
await client.query('COMMIT');
|
|
logger.info({ migration: name }, 'Migration executed successfully');
|
|
} catch (err) {
|
|
await client.query('ROLLBACK');
|
|
logger.error({ err, migration: name }, 'Migration failed');
|
|
throw err;
|
|
} finally {
|
|
client.release();
|
|
}
|
|
}
|
|
|
|
export async function runMigrations(): Promise<void> {
|
|
try {
|
|
await ensureMigrationsTable();
|
|
const migrated = await getMigratedFiles();
|
|
|
|
const migrations = [
|
|
{ name: '001_initial.sql', path: './migrations/001_initial.sql' },
|
|
{ name: '002-tokenvault-cost-tracking.sql', path: './migrations/002-tokenvault-cost-tracking.sql' },
|
|
];
|
|
|
|
for (const { name, path } of migrations) {
|
|
if (!migrated.has(name)) {
|
|
logger.info({ migration: name }, 'Running migration');
|
|
const sql = readFileSync(resolve(__dirname, path), 'utf-8');
|
|
await runMigration(name, sql);
|
|
}
|
|
}
|
|
|
|
logger.info({ count: migrations.length }, 'All migrations completed');
|
|
} catch (err) {
|
|
logger.error({ err }, 'Migration runner failed');
|
|
throw err;
|
|
}
|
|
}
|