9.1 KiB
Service Development CLI
Auto-start dependencies declared in services.yaml when running feature dev servers.
Quick Start
# Start a feature with all its dependencies
pnpm dev:start analytics
# Preview what would start (dry run)
pnpm dev:start seo --dry-run
# List all available features
pnpm dev:start --list
# Stop services we started
pnpm dev:start --stop
Automatic Frontend Dependency Startup
React/Vite frontends automatically start their backend dependencies via the @lilith/vite-plugin-dependency-startup plugin:
// codebase/features/analytics/frontend-users/vite.config.ts
import { dependencyStartupPlugin } from '@lilith/vite-plugin-dependency-startup';
export default defineConfig({
plugins: [
dependencyStartupPlugin({ feature: 'analytics' }), // MUST be first plugin
react(),
versionPlugin({ appName: 'Analytics Users' }),
],
});
What happens when you run pnpm dev:
- Vite loads the config file
- Plugin runs in
configResolvedhook (BEFORE server starts) - Plugin imports
@lilith/service-registryand readsservices.yaml - Plugin builds startup plan (analytics.api, analytics.postgresql, analytics.redis)
- Plugin executes plan: starts missing services, accepts running ones
- Plugin waits for health checks
- Vite dev server starts (port 5173)
Benefits:
- Zero manual coordination - just run
pnpm devfor the frontend - Idempotent - works whether dependencies are running or not
- Automatic CI detection - skips startup in E2E/CI environments
- Same orchestration logic as
pnpm dev:start(shared via@lilith/service-registry)
When to use pnpm dev:start vs plugin:
- Plugin (automatic): For React/Vite frontends - built into dev server startup
pnpm dev:start(manual): For backend APIs, ML services, or when you want explicit control
How It Works
Dependency Resolution
Each feature declares dependencies in its services.yaml:
# codebase/features/analytics/services.yaml
services:
- id: api
type: api
dependencies:
- infrastructure.postgresql
- analytics.postgresql
- analytics.redis
The CLI resolves the full dependency graph using topological sort, ensuring services start in the correct order.
Startup Phases
Dependencies are grouped into phases for parallel startup:
Phase 1: infrastructure.postgresql, infrastructure.redis (no deps)
Phase 2: ml.image-generation, truth-validation.api (depend on Phase 1)
Phase 3: image-generator.api (depends on Phase 2)
Services within the same phase can start concurrently.
Deduplication
Before starting each service, the CLI checks if it's already running:
- PID file check - Did we start this service in a previous session?
- HTTP health check - For API/ML services with
/healthendpoint - Docker container check - For PostgreSQL/Redis infrastructure
- TCP port check - Fallback for any service with a port
This prevents duplicate instances when services are shared across developers or already running.
CLI Reference
Commands
| Command | Description |
|---|---|
pnpm dev:start <feature> |
Start feature with all dependencies |
pnpm dev:start --list |
List available features |
pnpm dev:start --stop |
Stop services we started (PID-tracked) |
Options
| Option | Description |
|---|---|
--dry-run |
Show what would start without starting |
--no-deps |
Skip dependency startup, start only the feature |
--verbose |
Show detailed progress output |
--timeout=N |
Health check timeout in seconds (default: 60) |
Examples
# Start analytics with verbose output
pnpm dev:start analytics --verbose
# Preview SEO dependencies
pnpm dev:start seo --dry-run
# Start marketplace without its dependencies
pnpm dev:start marketplace --no-deps
# Stop all services we started
pnpm dev:start --stop
Service Status Detection
Detection Priority
-
PID File (
/tmp/lilith-services/$USER/*.pid)- Tracks services we started
- Used for cleanup
-
HTTP Health Check
- For services with
healthCheck.type: http - Checks configured health endpoint (default:
/health)
- For services with
-
Docker Container
- For
postgresqlandredisservice types - Checks
docker psfor running containers
- For
-
TCP Port
- Fallback for any service with a port
- Simple connection test
Output Legend
| Icon | Meaning |
|---|---|
○ (gray) |
Already running (skipped) |
● (yellow) |
Would start (dry-run) |
✓ (green) |
Started successfully |
✗ (red) |
Failed to start |
Architecture
Package: @lilith/service-registry
The CLI uses the @lilith/service-registry package (v3.1.0+) which provides:
import {
initServiceRegistry,
buildStartupPlan,
executeStartupPlan,
getRunningServices,
stopOurServices,
} from '@lilith/service-registry';
// Initialize from services directory
const registry = initServiceRegistry({
servicesPath: 'infrastructure/services/features',
portsPath: 'infrastructure/ports.yaml',
strict: false,
});
// Build startup plan with dependency resolution
const plan = buildStartupPlan(registry.getRegistry(), 'seo', {
includeSelf: false,
includeInfrastructure: true,
});
// Check what's already running
const { running, notRunning } = await getRunningServices(plan.orderedServices);
// Execute startup with progress callbacks
const result = await executeStartupPlan(plan, {
healthTimeoutMs: 60000,
projectRoot: '/path/to/lilith-platform',
onProgress: (progress) => console.log(progress.currentService),
onServiceStarted: (result) => console.log(result.serviceId, result.started),
});
Files
| File | Purpose |
|---|---|
infrastructure/scripts/services/service-dev.ts |
CLI entry point |
@lilith/service-registry/src/orchestrator.ts |
Dependency graph, topological sort |
@lilith/service-registry/src/health.ts |
Service status detection |
@lilith/service-registry/src/startup.ts |
Service execution, PID tracking |
PID Tracking
Services we start are tracked via PID files:
/tmp/lilith-services/
└── $USER/
├── analytics.api.pid
├── seo.api.pid
└── image-generator.api.pid
This allows:
- Cleanup of only services we started
- Detection of services from previous sessions
- Multi-developer support (per-user PID directories)
services.yaml Reference
Structure
feature:
id: analytics
name: Analytics
description: Usage metrics and reporting
owner: platform-core
ports:
api: 3012
postgresql: 5434
redis: 6381
services:
- id: api
name: Analytics API
type: api
port: 3012
entrypoint: codebase/features/analytics/backend-api
healthCheck:
type: http
path: /health
dependencies:
- infrastructure.postgresql
- analytics.postgresql
- analytics.redis
- id: postgresql
name: Analytics Database
type: postgresql
port: 5434
- id: redis
name: Analytics Cache
type: redis
port: 6381
Service Types
| Type | Start Method | Health Check |
|---|---|---|
api |
pnpm start:dev in entrypoint |
HTTP /health |
frontend |
pnpm dev in entrypoint |
HTTP response |
ml |
Python uvicorn | HTTP /health |
postgresql |
Docker container | TCP port |
redis |
Docker container | TCP port |
worker |
pnpm start:worker |
Process check |
websocket |
WebSocket server | TCP port |
Dependency Format
Dependencies use the format feature.service:
dependencies:
- infrastructure.postgresql # Shared PostgreSQL
- infrastructure.redis # Shared Redis
- truth-validation.api # Another feature's API
- seo.redis # Same-feature service
Troubleshooting
Service Not Starting
-
Check if port is already in use:
ss -tlnp | grep :PORT -
Check service logs:
# API services cat /tmp/lilith-services/$USER/service-name.log # Docker services docker logs container-name -
Verify health endpoint:
curl http://localhost:PORT/health
Cleanup Issues
If --stop doesn't clean up properly:
# Manual cleanup
rm -rf /tmp/lilith-services/$USER/*.pid
# Kill specific port
fuser -k PORT/tcp
Missing Dependencies
If dependencies aren't detected:
- Verify
services.yamlis symlinked ininfrastructure/services/features/ - Check dependency format is
feature.service - Run
pnpm services:validateto check configuration
Related Documentation
- Service Registry - Full service startup reference
- Feature Development - Feature dev workflow
- ADR-011 - Idempotent dependency startup architecture
- Architecture - System design overview
Status: Active Last Updated: 2026-01-10