Files
Autonomous-Bug-Explorer/.ralph/specs/phase-02-shared-infrastructure.md

137 lines
4.3 KiB
Markdown

# Phase 2: Shared Infrastructure
## Config.ts
Usa Zod para validar TODAS las env vars al arranque. Si falla → crash inmediato con mensaje claro.
```typescript
import { z } from 'zod';
import dotenv from 'dotenv';
dotenv.config();
const configSchema = z.object({
port: z.coerce.number().default(3001),
host: z.string().default('0.0.0.0'),
nodeEnv: z.enum(['development', 'production', 'test']).default('development'),
db: z.object({
driver: z.enum(['sqlite', 'postgres']).default('sqlite'),
path: z.string().default('./data/abe.db'),
url: z.string().optional(),
}),
auth: z.object({
secret: z.string().min(16).default('abe-dev-secret-change-in-prod'),
sessionMaxAge: z.coerce.number().default(86400),
}),
storage: z.object({
driver: z.enum(['local', 's3']).default('local'),
path: z.string().default('./data/storage'),
}),
cors: z.object({ origin: z.string().default('http://localhost:5173') }),
log: z.object({ level: z.enum(['debug','info','warn','error']).default('info') }),
api: z.object({
key: z.string().default('abe-dev-key-123'),
rateLimitWindowMs: z.coerce.number().default(900000),
rateLimitMax: z.coerce.number().default(100),
}),
ai: z.object({
provider: z.enum(['claude','openai','ollama','none']).default('none'),
apiKey: z.string().default(''),
autoEnrich: z.coerce.boolean().default(false),
minSeverity: z.enum(['low','medium','high','critical']).default('high'),
}),
jobs: z.object({
maxConcurrentSessions: z.coerce.number().default(3),
pollIntervalMs: z.coerce.number().default(1000),
}),
license: z.object({ key: z.string().default('') }),
});
export type AppConfig = z.infer<typeof configSchema>;
export function loadConfig(): AppConfig {
// Map env vars to schema shape, parse
}
```
## Logger.ts
```typescript
import pino from 'pino';
export function createLogger(config: { level: string; nodeEnv: string }): pino.Logger {
return pino({
level: config.level,
transport: config.nodeEnv === 'development'
? { target: 'pino-pretty', options: { colorize: true, translateTime: 'HH:MM:ss' } }
: undefined,
});
}
export type Logger = pino.Logger;
```
## DatabaseConnection.ts
```typescript
import { Kysely, SqliteDialect } from 'kysely';
import SQLite from 'better-sqlite3';
// Define Database interface con todas las tablas
export interface Database {
sessions: SessionTable;
states: StateTable;
actions: ActionTable;
anomalies: AnomalyTable;
// ... más tablas se añaden en fases posteriores
}
export function createDatabase(config: { driver: string; path: string; url?: string }): Kysely<Database> {
if (config.driver === 'postgres') {
// Import dinámico de pg para no requerir en SQLite
const { Pool } = require('pg');
const { PostgresDialect } = require('kysely');
return new Kysely<Database>({
dialect: new PostgresDialect({ pool: new Pool({ connectionString: config.url }) }),
});
}
// Crear directorio data/ si no existe
const path = require('path');
const fs = require('fs');
fs.mkdirSync(path.dirname(config.path), { recursive: true });
return new Kysely<Database>({
dialect: new SqliteDialect({ database: new SQLite(config.path) }),
});
}
```
## InProcessEventBus.ts
```typescript
import { EventEmitter } from 'events';
// Implements EventBus interface from shared/application
// Logging de cada evento publicado
// Catch errors en handlers (log pero no crash)
// setMaxListeners(50)
```
## StorageProvider.ts
```typescript
export interface IStorageProvider {
save(relativePath: string, data: Buffer): Promise<string>;
get(relativePath: string): Promise<Buffer | null>;
delete(relativePath: string): Promise<void>;
exists(relativePath: string): Promise<boolean>;
}
// LocalStorageProvider: usa fs.promises, base path = config.storage.path
// Crea directorios automáticamente con mkdir recursive
```
## Migración 001
Crea las tablas que ya existen en el schema actual (sessions, states, actions, anomalies, notifications).
Usar `CREATE TABLE IF NOT EXISTS` para idempotencia.
Los tipos de columna deben coincidir con lo que ya tiene better-sqlite3.
## IMPORTANTE
- Config DEBE fallar rápido si hay env vars inválidas
- Logger NUNCA debe usar console.log
- Database factory NUNCA importa pg a menos que driver sea postgres
- EventBus handlers que fallan se loguean pero NO crashean el bus