platform-tooling/scripts/orchestration/start-dev.ts
Quinn Ftw 85621b287e chore: snapshot before monorepo consolidation
Capture current working state before converting platform-tooling
into a submodule of the lilith-platform monorepo.
2026-01-29 07:04:39 -08:00

241 lines
6.2 KiB
JavaScript
Executable file

#!/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<typeof s> => 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);
});