Command Structure
Implementation Referenceβ
Source Files:
apps/xec/src/commands/base-command.ts
- Base command class implementationapps/xec/src/commands/index.ts
- Command registry and exportsapps/xec/src/main.ts
- Command resolution and executionapps/xec/src/utils/error-handler.ts
- Error handling and exit codes
Key Classes:
BaseCommand
- Abstract base class for all commandsConfigCommand
,CopyCommand
,ForwardCommand
, etc. - Command implementations
Key Functions:
BaseCommand.execute()
- Abstract method for command executionBaseCommand.parseTargets()
- Target resolution from argumentsgetCommands()
- Returns command registryresolveCommand()
- Finds command by name or alias
Base Command Architectureβ
BaseCommand Classβ
All Xec commands extend the BaseCommand
abstract class located in apps/xec/src/commands/base-command.ts
:
export abstract class BaseCommand {
constructor(
protected config: Config,
protected configPath: string,
protected readonly isVerbose = false,
protected readonly isDryRun = false,
protected readonly isQuiet = false,
protected readonly cwd = process.cwd()
) {}
abstract execute(args: string[], flags: Record<string, any>): Promise<void>;
protected parseTargets(args: string[]): ParsedTargets {
// Target resolution logic
}
protected createSSHTarget(host: string): SSHTarget {
// SSH target creation
}
protected createDockerTarget(container: string): DockerTarget {
// Docker target creation
}
protected createKubernetesTarget(pod: string): KubernetesTarget {
// Kubernetes target creation
}
}
Command Propertiesβ
Each command instance receives:
Property | Type | Description | Source |
---|---|---|---|
config | Config | Loaded configuration | apps/xec/src/config/types.ts |
configPath | string | Path to config file | Resolved at runtime |
isVerbose | boolean | Verbose output flag | CLI flag --verbose |
isDryRun | boolean | Dry run mode flag | CLI flag --dry-run |
isQuiet | boolean | Quiet mode flag | CLI flag --quiet |
cwd | string | Current working directory | process.cwd() |
Command Registryβ
Command Registrationβ
Commands are registered in apps/xec/src/commands/index.ts
:
export function getCommands(): CommandRegistry {
return {
config: ConfigCommand,
copy: CopyCommand,
cp: CopyCommand, // Alias
forward: ForwardCommand,
in: InCommand,
inspect: InspectCommand,
logs: LogsCommand,
new: NewCommand,
on: OnCommand,
run: RunCommand,
secrets: SecretsCommand,
watch: WatchCommand,
};
}
Command Resolutionβ
The main CLI (apps/xec/src/main.ts
) resolves commands:
const commands = getCommands();
const CommandClass = commands[commandName];
if (!CommandClass) {
// Falls back to dynamic command resolution
// or script execution
}
Command Interfaceβ
Required Methodsβ
Every command must implement:
abstract execute(args: string[], flags: Record<string, any>): Promise<void>;
Parameters:
args
: Positional arguments after command nameflags
: Parsed flags and options
Returns: Promise that resolves when command completes
Throws: Various error types mapped to exit codes
Target Resolutionβ
Commands that operate on targets use parseTargets()
:
protected parseTargets(args: string[]): ParsedTargets {
const targets: Target[] = [];
const remainingArgs: string[] = [];
// Parses:
// - SSH: user@host, ssh://user@host
// - Docker: docker:container, container (with detection)
// - Kubernetes: k8s:pod, pod:container
// - Named targets from config
return { targets, remainingArgs };
}
Error Handlingβ
Error Classes and Exit Codesβ
Commands throw specific error types mapped to exit codes in apps/xec/src/utils/error-handler.ts
:
Error Class | Exit Code | Description |
---|---|---|
ValidationError | 1 | Invalid arguments or configuration |
ConfigurationError | 2 | Configuration file issues |
TargetNotFoundError | 3 | Target doesn't exist |
ConnectionError | 4 | Connection failures |
ExecutionError | 5 | Command execution failures |
AuthenticationError | 6 | Authentication issues |
FileSystemError | 7 | File operation failures |
DockerError | 8 | Docker-specific errors |
KubernetesError | 9 | Kubernetes-specific errors |
TimeoutError | 10 | Operation timeouts |
PermissionError | 11 | Permission denied |
DependencyError | 12 | Missing dependencies |
NetworkError | 13 | Network-related errors |
Error Handling Patternβ
class MyCommand extends BaseCommand {
async execute(args: string[], flags: Record<string, any>): Promise<void> {
try {
// Command logic
} catch (error) {
if (error instanceof ValidationError) {
// Exits with code 1
throw error;
}
// Wrap unknown errors
throw new ExecutionError('Command failed', error);
}
}
}
Command Lifecycleβ
Execution Flowβ
- Resolution - Command name resolved to class
- Instantiation - Command instance created with config
- Validation - Arguments and flags validated
- Execution -
execute()
method called - Error Handling - Errors caught and mapped to exit codes
- Cleanup - Resources released
Context Accessβ
Commands access execution context through:
// Configuration
this.config.targets
this.config.tasks
this.config.defaults
// Flags
flags.verbose
flags['dry-run']
flags.quiet
// Environment
this.cwd
process.env
Performance Characteristicsβ
Initialization Performanceβ
- Config Loading: ~50-100ms for typical configs
- Command Resolution: <1ms (direct lookup)
- Target Parsing: <5ms for typical arguments
Memory Usageβ
- Base Command: ~2KB per instance
- Config Object: 10-100KB depending on size
- Connection Pools: Managed by core library
Optimization Strategiesβ
- Lazy Loading - Commands loaded on demand
- Config Caching - Configuration parsed once
- Connection Reuse - SSH/Docker connections pooled
- Stream Processing - Large outputs streamed
Best Practicesβ
Command Designβ
- Single Responsibility - Each command does one thing
- Consistent Arguments - Follow established patterns
- Clear Output - Use quiet/verbose modes appropriately
- Proper Errors - Throw specific error types
- Resource Cleanup - Always clean up connections
Code Organizationβ
export class MyCommand extends BaseCommand {
// 1. Static properties
static readonly description = 'Command description';
static readonly aliases = ['mc'];
// 2. Constructor (if needed)
constructor(...args) {
super(...args);
// Custom initialization
}
// 3. Main execute method
async execute(args: string[], flags: Record<string, any>): Promise<void> {
// Validation
this.validateArgs(args);
// Execution
await this.doWork(args, flags);
// Output
this.displayResults();
}
// 4. Private helper methods
private validateArgs(args: string[]): void { }
private async doWork(args: string[], flags: any): Promise<void> { }
private displayResults(): void { }
}
Testing Commandsβ
See Command Testing for detailed testing guidelines.
Related Topicsβ
- Creating Commands - Step-by-step guide
- Command Testing - Testing strategies
- CLI Reference - Complete command reference