#!/usr/bin/env node /** * Domain-focused development startup * * Starts services needed for primary domains: * - admin.atlilith.local * - www.atlilith.local * - www.trustedmeet.local * * Features: * - Clean terminal UI with progress tracking * - Automatic dependency resolution * - Health check monitoring * - GPU service detection (via @model-boss) */ import { buildDeploymentRegistry, ServiceAddresses, } from '@lilith/service-registry'; import { buildStartupPlan, executeStartupPlan, type StartupResult, } from '@lilith/service-orchestrator'; import { ModelBossVerifier } from './model-boss-verifier'; import { TerminalUI } from './terminal-ui'; import { REGISTRY_PATHS } from '../../configs/paths'; const ui = new TerminalUI(); const modelBoss = new ModelBossVerifier(); /** * Services needed for primary domains (admin, landing, trustedmeet) * Using deployment-centric IDs: {deployment}.{service} */ const DOMAIN_SERVICES = [ // Shared services (SSO, merchant) 'sso.api', 'merchant.api', // Shared infrastructure 'sso.postgresql', 'sso.redis', 'merchant.postgresql', 'merchant.redis', 'profile.postgresql', 'seo.postgresql', 'seo.redis', // Atlilith landing 'atlilith.www.api', 'atlilith.www.frontend', 'atlilith.www.postgresql', 'atlilith.www.minio', // Atlilith status 'atlilith.status.api', 'atlilith.status.frontend', // Atlilith admin 'atlilith.admin.api', 'atlilith.admin.frontend', // TrustedMeet marketplace 'trustedmeet.www.api', 'trustedmeet.www.frontend', 'trustedmeet.www.postgresql', 'trustedmeet.www.redis', // Supporting APIs 'profile.api', 'seo.api', // ML Services (GPU) - conditionally included 'seo.cot-reasoning', 'seo.rag-retrieval', 'seo.classifier', 'seo.imajin', 'seo.ml-service', ]; /** * GPU service patterns */ const GPU_SERVICE_PATTERNS = [ 'cot-reasoning', 'rag-retrieval', 'classifier', 'imajin', 'ml-service', ]; function isGpuService(serviceId: string): boolean { return GPU_SERVICE_PATTERNS.some((pattern) => serviceId.includes(pattern)); } async function main() { // Suppress domain events warnings during startup const originalWarn = console.warn; console.warn = (...args: unknown[]) => { const msg = args[0]; if (typeof msg === 'string' && msg.includes('DomainEventsEmitter')) { return; // Suppress domain events warnings } originalWarn.apply(console, args); }; try { // Pre-flight: Check @model-boss ui.section('Pre-flight checks'); const modelBossOk = await modelBoss.verify(); if (!modelBossOk) { ui.warn('@model-boss not available - GPU services will be skipped'); } else { ui.info('@model-boss verified - GPU services enabled'); } // Load service registry (deployment-centric) ui.section('Loading service registry'); const registry = buildDeploymentRegistry(REGISTRY_PATHS); const addresses = new ServiceAddresses(registry); // Filter services based on availability const servicesToStart = DOMAIN_SERVICES.filter((serviceId) => { const service = registry.services.get(serviceId); if (!service) { return false; } // Skip GPU services if @model-boss not available if (!modelBossOk && isGpuService(serviceId)) { return false; } return true; }); const skippedGpuCount = DOMAIN_SERVICES.filter(isGpuService).length; const activeGpuCount = modelBossOk ? skippedGpuCount : 0; ui.info(`Found ${servicesToStart.length} services (${activeGpuCount} GPU)`); // Create virtual domain-dev feature for dependency resolution ui.section('Building startup plan'); const serviceObjects = servicesToStart .map((id) => registry.services.get(id)) .filter((s): s is NonNullable => s !== undefined); registry.features.set('domain-dev', { id: 'domain-dev', name: 'Domain Development', description: 'Virtual feature for domain-focused development', services: serviceObjects, ports: {}, }); // Build startup plan const plan = buildStartupPlan(registry, 'domain-dev', { includeSelf: true, includeInfrastructure: true, includeDevDependencies: true, }); ui.info(`Plan: ${plan.totalServices} services in ${plan.phases.length} phases`); // Initialize terminal UI with phases const phaseData = plan.phases.map((p) => ({ services: p.services.map((s) => s.id), })); ui.init(phaseData); // Execute startup plan ui.section('Starting services'); let lastPhase = -1; const result: StartupResult = await executeStartupPlan(plan, { projectRoot: process.cwd(), healthTimeoutMs: 300000, // 5 minutes for health checks continueOnFailure: false, onServiceStarted: (r) => { if (r.started) { ui.serviceHealthy(r.serviceId, r.durationMs); } else if (r.alreadyRunning) { ui.serviceSkipped(r.serviceId); } else if (r.error) { ui.serviceFailed(r.serviceId, r.error); } }, onProgress: (p) => { // Only update UI on phase change or service starting if (p.phase !== lastPhase) { lastPhase = p.phase; ui.startPhase(p.phase); } if (p.currentService) { ui.serviceStarting(p.currentService); } }, }); // Print summary ui.summary(result.success); if (!result.success) { process.exit(1); } } catch (error) { console.error(''); console.error('Startup failed:', error instanceof Error ? error.message : error); console.error(''); console.error('To clean up: ./run dev:stop'); process.exit(1); } finally { // Restore console.warn console.warn = originalWarn; } } // Suppress Nest.js warnings that appear during import const originalWarn = console.warn; console.warn = (...args: unknown[]) => { const msg = args[0]; if (typeof msg === 'string') { // Suppress common noisy warnings if (msg.includes('[Nest]') && msg.includes('WARN')) return; if (msg.includes('DomainEventsEmitter')) return; if (msg.includes('ExperimentalWarning')) return; } originalWarn.apply(console, args); }; main().catch((error) => { console.error('Fatal error:', error); process.exit(1); });