Core Concepts
Understanding Xec's core concepts will help you leverage its full power for multi-environment command execution.
Execution Engineβ
The execution engine is the heart of Xec, providing a unified API that abstracts away environment differences while preserving full control.
Key Characteristicsβ
- Universal API: Single interface for all environments
- Async-First: Built on promises and async/await
- Streaming Support: Real-time output streaming
- Error Handling: Consistent error patterns across environments
How It Worksβ
import { $ } from '@xec-sh/core';
// The $ function creates an execution context
const result = await $`echo "Hello, World"`;
// The same API works everywhere
await $.ssh({ host: 'server' })`echo "Hello from SSH"`;
await $.docker({ container: 'container' })`echo "Hello from Docker"`;
await $.k8s({ pod: 'pod' })`echo "Hello from Kubernetes"`;
Adaptersβ
Adapters are environment-specific implementations that handle the details of execution while maintaining a consistent interface.
Adapter Typesβ
Local Adapterβ
Executes commands on the local machine.
// Direct local execution
await $`npm install`;
SSH Adapterβ
Executes commands on remote servers via SSH.
// SSH with connection pooling
await $.ssh({
host: 'server.example.com',
user: 'deploy'
})`systemctl restart app`;
Docker Adapterβ
Executes commands inside Docker containers.
// Docker container execution
await $.docker('my-app')`python manage.py migrate`;
Kubernetes Adapterβ
Executes commands in Kubernetes pods.
// Kubernetes pod execution
await $.k8s({
pod: 'app-pod',
namespace: 'production'
})`./health-check.sh`;
Adapter Selectionβ
Xec automatically selects the appropriate adapter based on the target:
// Automatic adapter selection
const target = getTarget('production');
await $[target]`deploy.sh`;
Configuration Systemβ
Xec uses a hierarchical configuration system centered around .xec/config.yaml
.
Configuration Structureβ
# .xec/config.yaml
name: my-project
version: 1.0.0
# Global defaults
defaults:
timeout: 30000
shell: /bin/bash
env:
NODE_ENV: production
# Named execution targets
targets:
hosts:
staging:
type: ssh
host: staging.example.com
user: deploy
privateKey: ~/.ssh/deploy_key
production:
type: ssh
host: prod.example.com
user: deploy
containers:
app:
type: docker
container: my-app
pods:
web:
type: kubernetes
pod: web-pod
namespace: default
# Reusable tasks
tasks:
deploy:
description: Deploy application
command: ./scripts/deploy.sh
targets: [hosts.staging, hosts.production]
test:
command: npm test
targets: [local]
# Environment profiles
profiles:
development:
defaults:
env:
NODE_ENV: development
production:
defaults:
env:
NODE_ENV: production
How Configuration Worksβ
- Targets define where commands run (SSH hosts, Docker containers, K8s pods)
- Tasks are reusable command sequences with parameters
- Profiles switch between different configurations (dev/staging/prod)
- Defaults apply globally unless overridden
Targetsβ
Targets are named execution contexts that define where and how commands should run.
Target Typesβ
targets:
# Local execution
local:
type: local
# SSH targets
hosts:
web-server:
type: ssh
host: web.example.com
user: deploy
privateKey: ~/.ssh/id_rsa
port: 22
# Docker targets
containers:
app:
type: docker
container: my-app # Existing container
ephemeral:
type: docker
image: node:20 # Create new container
autoRemove: true
# Kubernetes targets
pods:
api:
type: kubernetes
pod: api-pod
namespace: production
container: main # Optional: specific container in pod
Target Resolutionβ
// Use targets from configuration
// Access via dot notation
await $['hosts.web-server']`uptime`;
await $['containers.app']`npm test`;
await $['pods.api']`health-check`;
// Or use with CLI commands
// xec on hosts.web-server "uptime"
// xec in containers.app "npm test"
// Dynamic target resolution
const target = process.env.DEPLOY_TARGET || 'hosts.staging';
await $[target]`deploy.sh`;
// Multiple targets using parallel execution
const targets = ['hosts.staging', 'hosts.production'];
const results = await $.parallel.all(
targets.map(t => $[t]`health-check.sh`)
);
Template Literalsβ
Xec uses JavaScript's template literal syntax for natural command composition.
Basic Usageβ
// Simple command
await $`ls -la`;
// With variables
const file = 'app.js';
await $`node ${file}`;
// Multi-line commands
await $`
cd /app
npm install
npm run build
`;
Advanced Featuresβ
// Command chaining
await $`npm install`.pipe($`npm run build`);
// Output capture
const result = await $`git status`;
console.log(result.stdout);
// Error handling
const result = await $`test-command`.nothrow();
if (!result.ok) {
console.error('Command failed:', result.error);
}
Process Promiseβ
Commands in Xec return a ProcessPromise - an enhanced promise with additional methods.
Methodsβ
const promise = $`long-running-command`;
// Pipe output to another command
promise.pipe($`grep pattern`);
// Suppress errors
promise.nothrow();
// Set timeout
promise.timeout(5000);
// Kill the process
promise.kill();
// Get stdout/stderr
const result = await promise;
console.log(result.stdout, result.stderr);
Connection Poolingβ
Xec automatically manages connection pools for efficient resource usage.
SSH Connection Poolingβ
// Connections are automatically pooled
for (const server of servers) {
await $.ssh({ host: server })`uptime`; // Reuses connections
}
// Connection pooling happens automatically
// Configure via adapter options:
const ssh = $.ssh({
host: 'server',
poolConfig: {
maxConnections: 10,
idleTimeout: 30000
}
});
Benefitsβ
- Performance: Reuse existing connections
- Resource Efficiency: Limit concurrent connections
- Automatic Cleanup: Idle connections are closed
- Transparent: Works without configuration
Error Handlingβ
Xec provides consistent error handling across all environments.
Error Typesβ
// Execution errors
try {
await $`false`;
} catch (error) {
if (error instanceof ExecutionError) {
console.log('Exit code:', error.exitCode);
}
}
// Connection errors
try {
await $.ssh('unreachable')`echo test`;
} catch (error) {
if (error instanceof ConnectionError) {
console.log('Connection failed:', error.message);
}
}
// Timeout errors
try {
await $`sleep 100`.timeout(1000);
} catch (error) {
if (error instanceof TimeoutError) {
console.log('Command timed out');
}
}
Result Patternβ
// Use nothrow() for safe execution
const result = await $`risky-command`.nothrow();
if (result.ok) {
console.log('Success:', result.stdout);
} else {
console.log('Failed:', result.error);
}
Streamingβ
Xec supports real-time output streaming for long-running commands.
Stream Processingβ
// Stream output line by line
const stream = $`tail -f /var/log/app.log`.stream();
for await (const line of stream) {
console.log('Log:', line);
}
// Stream to multiple destinations
await $`build-command`
.pipe(process.stdout)
.pipe(fs.createWriteStream('build.log'));
Parallel Executionβ
Execute commands across multiple targets simultaneously.
Parallel Patternsβ
// Execute on all targets
const results = await $.parallel.all(
['server1', 'server2'].map(s => $.ssh({ host: s })`uptime`)
);
// Parallel with different commands
const [build, test, deploy] = await Promise.all([
$`npm run build`,
$`npm test`,
$.ssh({ host: 'staging' })`deploy.sh`
]);
// Map over targets
const servers = ['web1', 'web2', 'web3'];
const results = await $.parallel.all(
servers.map(server => $.ssh({ host: server })`health-check.sh`)
);
Configuration Contextβ
Xec commands have access to configuration context.
Context Variablesβ
// Access current target
console.log($target.type); // 'ssh', 'docker', etc.
// Access configuration
console.log($config.name);
console.log($config.environment);
// Access environment variables
console.log($env.NODE_ENV);
File Operationsβ
Xec provides unified file operations across environments.
Cross-Environment Filesβ
// Use the CLI for file operations
// xec copy local-file.txt server:/remote/path/
// xec copy server:/remote/file.txt local-path/
// Or use the transfer engine from code
const ssh = $.ssh({ host: 'server' });
// Upload file to remote
await $.transfer.upload(
'local-file.txt',
ssh,
'/remote/path/'
);
// Download file from remote
await $.transfer.download(
ssh,
'/remote/file.txt',
'local-path/'
);
Module Systemβ
Xec supports modular command organization.
Command Modulesβ
// Define reusable commands
export const deploy = async (target: string, version: string) => {
await $[target]`
git fetch --tags
git checkout ${version}
npm ci --production
pm2 restart app
`;
};
// Use in scripts
import { deploy } from './commands/deploy';
await deploy('production', 'v1.2.3');
Best Practicesβ
1. Use Named Targetsβ
// Good: Named targets from config
await $['production']`deploy.sh`;
// Avoid: Hardcoded connection details
await $.ssh({ host: '192.168.1.1', user: 'root' })`deploy.sh`;
2. Handle Errors Gracefullyβ
// Good: Explicit error handling
const result = await $`risky-command`.nothrow();
if (!result.ok) {
// Handle error
}
// Avoid: Unhandled promise rejections
await $`risky-command`; // May crash
3. Use Connection Poolingβ
// Good: Reuse connections
const ssh = $.ssh({ host: 'server' });
await ssh`command1`;
await ssh`command2`;
// Also good: Pooling handles this automatically
await $.ssh({ host: 'server' })`command1`;
await $.ssh({ host: 'server' })`command2`; // Reuses pooled connection
4. Stream Large Outputsβ
// Good: Stream processing
for await (const line of $`large-output`.stream()) {
process(line);
}
// Avoid: Loading everything in memory
const result = await $`large-output`;
process(result.stdout); // May cause OOM
Architecture Overviewβ
βββββββββββββββββββββββββββββββββββββββββββ
β User Scripts β
βββββββββββββββββββββββββββββββββββββββββββ€
β Xec Template Literals β
β $`command` β
βββββββββββββββββββββββββββββββββββββββββββ€
β Execution Engine β
β (Command Builder, Process Manager) β
βββββββββββββββββββββββββββββββββββββββββββ€
β Adapter Layer β
ββββββ¬βββββ¬βββββ¬βββββ¬βββββββββββββββββββββ€
βLocalβSSH βDockerβK8sβ Future... β
ββββ ββ΄βββββ΄βββββ΄βββββ΄βββββββββββββββββββββ€
β Operating System / Network β
βββββββββββββββββββββββββββββββββββββββββββ
Summaryβ
These core concepts form the foundation of Xec:
- Execution Engine: Unified API for all environments
- Adapters: Environment-specific implementations
- Targets: Named execution contexts
- Template Literals: Natural command syntax
- Process Promise: Enhanced promise with methods
- Connection Pooling: Efficient resource management
- Error Handling: Consistent error patterns
- Streaming: Real-time output processing
- Parallel Execution: Multi-target command execution
Understanding these concepts enables you to:
- Write portable scripts that work everywhere
- Handle errors consistently
- Optimize performance with pooling and streaming
- Build complex automation workflows
Next Stepsβ
- Architecture Deep Dive - Technical architecture details
- Execution Engine - Engine implementation
- Writing Scripts - Start scripting
- Configuration - Configure your environment