A robust, extensible TypeScript command routing system designed for the OpenCode-DGM integration. This system provides intelligent command parsing, flexible routing, async task management, and comprehensive error handling.
- Intent Recognition: Automatically identifies user intent from natural language commands
- Parameter Extraction: Intelligently extracts parameters, flags, and options from commands
- Flexible Routing: Pattern-based routing with support for wildcards and regex
- Handler Management: Registry system for command handlers with dependency validation
- Async Support: Built-in support for long-running tasks with progress tracking
- Middleware System: Extensible middleware for cross-cutting concerns
- Error Recovery: Comprehensive error handling with recovery strategies
- Event-Driven: Full event system for monitoring command execution
- Metrics & Monitoring: Built-in metrics collection for performance tracking
npm install @opencode-dgm/command-routerimport { createCommandSystem } from '@opencode-dgm/command-router';
// Create the command system
const system = createCommandSystem();
// Register a handler
system.registerHandler({
name: 'fileCreateHandler',
execute: async (command, context) => {
const { filename, content } = command.parameters;
context.logger.info(`Creating file: ${filename}`);
// Your file creation logic here
return {
commandId: command.id,
success: true,
data: { filename, created: true },
executionTime: Date.now() - command.timestamp.getTime(),
timestamp: new Date(),
};
},
});
// Register a route
system.registerRoute('file.create', 'fileCreateHandler');
// Execute a command
const result = await system.execute('create file test.txt with content "Hello World"');
console.log(result);Commands are structured representations of user input:
interface Command {
id: string;
intent: CommandIntent;
rawInput: string;
parameters: Record<string, any>;
options: CommandOptions;
metadata: CommandMetadata;
timestamp: Date;
}The system automatically recognizes intent from natural language:
// These all map to 'file.create' intent
"create file test.txt"
"new file test.txt"
"touch test.txt"Routes map intents to handlers using patterns:
// Exact match
system.registerRoute('file.create', 'fileHandler');
// Wildcard match
system.registerRoute('file.*', 'fileHandler');
// Regex match
system.registerRoute(/^file\.(create|update)$/, 'fileWriteHandler');
// Parameterized routes
system.registerRoute('api.:resource.:action', 'apiHandler');Handlers process commands and return results:
const handler: CommandHandler = {
name: 'myHandler',
description: 'Handles specific commands',
// Optional validation
validate: async (params) => {
return params.requiredField !== undefined;
},
// Main execution
execute: async (command, context) => {
// Access services
const db = context.services.get('database');
// Log information
context.logger.info('Processing command');
// Emit events
context.eventBus.emit('custom.event', { data: 'value' });
// Check for cancellation
if (context.abortSignal?.aborted) {
throw new Error('Command cancelled');
}
return {
commandId: command.id,
success: true,
data: { /* your data */ },
executionTime: 100,
timestamp: new Date(),
};
},
// Optional rollback
rollback: async (command, context) => {
// Cleanup logic
},
};Handle long-running tasks with progress updates:
// Execute async command
const result = await system.execute('process large dataset --async');
const taskId = result.data.taskId;
// Monitor progress
system.asyncResponseManager.on('task.progress', ({ task, progress }) => {
console.log(`Task ${task.id}: ${progress}% complete`);
});
// Wait for completion
const finalResult = await system.waitForTask(taskId);Add cross-cutting concerns with middleware:
system.registerMiddleware({
name: 'authentication',
execute: async (command, next) => {
// Check authentication
if (!isAuthenticated(command)) {
return {
commandId: command.id,
success: false,
error: {
code: 'UNAUTHORIZED',
message: 'Authentication required',
recoverable: false,
},
executionTime: 0,
timestamp: new Date(),
};
}
// Continue to next middleware/handler
return next();
},
});
// Apply middleware to routes
system.registerRoute('protected.*', 'protectedHandler', {
middleware: ['authentication'],
});The system includes comprehensive error handling:
// Register custom recovery strategies
system.errorHandler.registerStrategy({
name: 'retry-on-timeout',
applicable: (error) => error.category === 'timeout',
execute: async (error) => {
// Implement retry logic
return true; // Return true if recovery possible
},
});
// Access error statistics
const errorStats = system.getMetrics().errors;
console.log(`Total errors: ${errorStats.total}`);
console.log(`By severity:`, errorStats.bySeverity);Register custom intent patterns:
system.commandParser.registerIntent('deploy',
[/^deploy\s+(.+)\s+to\s+(.+)$/i],
['deploy', 'deployment'],
15 // priority
);
system.commandParser.registerParameterPatterns('deploy', [
{
name: 'application',
pattern: /deploy\s+([^\s]+)/i,
transform: (value) => value.toLowerCase(),
},
{
name: 'environment',
pattern: /to\s+([^\s]+)$/i,
transform: (value) => value.toLowerCase(),
},
]);Monitor system events:
// Command events
system.commandDispatcher.on('command.start', ({ command }) => {
console.log(`Command started: ${command.id}`);
});
system.commandDispatcher.on('command.complete', ({ command, result }) => {
console.log(`Command completed: ${command.id}, success: ${result.success}`);
});
// Router events
system.commandRouter.on('route.notfound', ({ command }) => {
console.log(`No route found for: ${command.intent.primary}`);
});
// Task events
system.asyncResponseManager.on('task.progress', ({ task, progress }) => {
console.log(`Task ${task.id}: ${progress}%`);
});┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Parser │────▶│ Router │────▶│ Dispatcher │
└─────────────┘ └──────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Intent │ │Route Matcher │ │ Registry │
│ Recognizer │ └──────────────┘ └─────────────┘
└─────────────┘ │
│ ▼
▼ ┌─────────────┐
┌─────────────┐ │ Handlers │
│ Parameter │ └─────────────┘
│ Extractor │
└─────────────┘
Main facade class for the command routing system.
execute(input: string, metadata?: Partial<CommandMetadata>): Promise<CommandResult>registerHandler(handler: CommandHandler, metadata?: Partial<HandlerMetadata>): thisregisterRoute(pattern: string | RegExp, handler: string, options?: Partial<Route>): thisregisterMiddleware(middleware: Middleware): thisgetTaskStatus(taskId: string): AsyncTask | undefinedcancelTask(taskId: string): booleanwaitForTask(taskId: string, timeoutMs?: number): Promise<CommandResult>getMetrics(): SystemMetrics
Parses user input into structured commands.
parse(input: string, metadata?: Partial<CommandMetadata>): CommandparseBatch(inputs: string[], metadata?: Partial<CommandMetadata>): Command[]tryParse(input: string, metadata?: Partial<CommandMetadata>): { command?: Command; error?: Error }registerIntent(intent: string, patterns: RegExp[], keywords: string[], priority?: number): void
Routes commands to appropriate handlers.
route(pattern: string | RegExp, handler: string, options?: Partial<Route>): thisuse(middleware: Middleware): thisresolve(command: Command): Promise<RouteMatch | null>createSubRouter(prefix: string): SubRouter
Manages command handlers.
register(handler: CommandHandler, metadata?: Partial<HandlerMetadata>): voidget(name: string): CommandHandler | undefinedsearch(criteria: SearchCriteria): HandlerMetadata[]validateDependencies(name: string): ValidationResult
The system includes comprehensive unit and integration tests:
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Run in watch mode
npm run test:watch- Handler registry uses Map for O(1) lookups
- Route matching is optimized with compiled regex patterns
- Async tasks are automatically cleaned up after completion
- Metrics collection can be disabled for performance
- Event emitters use efficient EventEmitter3 implementation
- Intent Design: Keep intents hierarchical (category.action)
- Handler Granularity: One handler per specific action
- Error Handling: Always provide recoverable flag in errors
- Async Operations: Use async commands for operations > 1s
- Middleware Order: Authentication → Validation → Logging
- Parameter Validation: Validate in handler, not parser
- Event Usage: Use events for monitoring, not control flow
MIT
