Handlers
Handlers in @cmdop/bot process commands from all channels in a channel-agnostic way. Four built-in handlers are included: TerminalHandler (/shell), AgentHandler (/agent), FilesHandler (/ls, /cat), and SkillsHandler (/skills). Create custom handlers by extending the BaseHandler class or use the hub.command() shorthand for simple commands. Each handler receives a CommandContext with access to terminal, files, agent, and skills services.
Handlers process commands from all channels. Four built-in handlers are included, and you can create custom handlers by extending BaseHandler.
What built-in handlers are available?
| Handler | Commands | Description |
|---|---|---|
TerminalHandler | /shell, /exec | Execute shell commands |
AgentHandler | /agent | Run AI agent tasks |
FilesHandler | /ls, /cat, /files | File operations |
SkillsHandler | /skills list|show|run | List, show, and run skills |
Built-in handlers are registered automatically when you create an IntegrationHub.
How do I create a custom handler?
// Import BaseHandler and related types for building custom handlers
import { BaseHandler, CommandContext, HandlerResult } from '@cmdop/bot/handlers';
// Extend BaseHandler and implement the handle() method
class DeployHandler extends BaseHandler {
name = 'deploy';
description = 'Deploy the application';
// handle() receives a CommandContext and must return a HandlerResult
async handle(ctx: CommandContext): Promise<HandlerResult> {
// Execute a multi-step deploy command on the remote machine
const { output } = await ctx.terminal.execute(
'cd /app && git pull && npm run build && pm2 restart app'
);
// Return the command output with success status
return { text: output, success: true };
}
}How do I register a custom handler?
import { IntegrationHub } from '@cmdop/bot';
// Create a hub and add your custom handler
const hub = new IntegrationHub({ apiKey: 'cmdop_xxx' });
// Register the custom handler so it responds to /deploy commands
hub.addHandler(new DeployHandler());What is the CommandContext interface?
Every handler receives a CommandContext with access to CMDOP services:
interface CommandContext {
// CMDOP services for interacting with the remote machine
terminal: TerminalService; // Execute shell commands
files: FilesService; // Read, list, and manage files
agent: AgentService; // Run AI agent tasks
// Parsed command information
command: string; // The command name (e.g., 'shell', 'deploy')
args: string[]; // Arguments passed after the command
rawText: string; // The full raw text of the message
// Information about the user who sent the command
userId: string; // User ID in channel-specific format
channel: 'telegram' | 'discord' | 'slack'; // Which channel the command came from
// Send a reply back to the user
reply(text: string): Promise<void>;
// Current target machine and setter
machine: string;
setMachine(machine: string): Promise<void>;
}What does the HandlerResult interface look like?
interface HandlerResult {
text: string; // Response text to send back to the user
success: boolean; // Whether the command succeeded
metadata?: Record<string, unknown>; // Optional extra data for logging or processing
}How do I use hub.command() as a shorthand?
For simple commands, use hub.command() instead of creating a full handler class:
// Register a 'deploy' command that runs a deploy script
hub.command('deploy', async (ctx) => {
const { output } = await ctx.terminal.execute('cd /app && ./deploy.sh');
await ctx.reply(output);
});
// Register a 'status' command that checks the systemd service
hub.command('status', async (ctx) => {
const { output } = await ctx.terminal.execute('systemctl status app');
await ctx.reply(output);
});
// Register a 'logs' command that tails the app log (default 50 lines)
hub.command('logs', async (ctx) => {
const lines = ctx.args[0] || '50';
const { output } = await ctx.terminal.execute(`tail -n ${lines} /var/log/app.log`);
await ctx.reply(output);
});How does the TerminalHandler work?
Execute shell commands on remote machines.
import { TerminalHandler } from '@cmdop/bot/handlers';
// Handles: /shell <cmd>, /exec <cmd> — runs shell commands on the target machine
const handler = new TerminalHandler();How does the AgentHandler work?
Run AI agent tasks.
import { AgentHandler } from '@cmdop/bot/handlers';
// Handles: /agent <task> — delegates tasks to the CMDOP AI agent
const handler = new AgentHandler();How does the FilesHandler work?
File operations — list, read, info.
import { FilesHandler } from '@cmdop/bot/handlers';
// Handles: /ls [path], /cat <path>, /files ls|cat|info <path>
const handler = new FilesHandler();How does the SkillsHandler work?
List, inspect, and run AI skills on remote machines.
import { SkillsHandler } from '@cmdop/bot/handlers';
// Handles: /skills list, /skills show <name>, /skills run <name> <prompt>
const handler = new SkillsHandler();Commands:
| Subcommand | Example | Description |
|---|---|---|
list | /skills list | List all installed skills |
show <name> | /skills show ssl-cert-checker | Show skill details and system prompt |
run <name> <prompt> | /skills run ssl-cert-checker Check github.com | Execute a skill with a prompt |
How do I build a complete bot with custom handlers?
// Full example: hub with custom HealthCheck and Restart handlers on Telegram
import { IntegrationHub } from '@cmdop/bot';
import { TelegramChannel } from '@cmdop/bot/telegram';
import { BaseHandler, CommandContext, HandlerResult } from '@cmdop/bot/handlers';
// Custom handler that checks server uptime, memory, and disk usage
class HealthCheckHandler extends BaseHandler {
name = 'health';
description = 'Check server health';
async handle(ctx: CommandContext): Promise<HandlerResult> {
// Run multiple system info commands in one call
const { output } = await ctx.terminal.execute('uptime && free -h && df -h');
return { text: output, success: true };
}
}
// Custom handler that restarts a systemd service (defaults to 'app')
class RestartHandler extends BaseHandler {
name = 'restart';
description = 'Restart application';
async handle(ctx: CommandContext): Promise<HandlerResult> {
// Use the first argument as the service name, default to 'app'
const service = ctx.args[0] || 'app';
const { output, exitCode } = await ctx.terminal.execute(
`systemctl restart ${service}`
);
// Return success or failure based on the exit code
return {
text: exitCode === 0 ? `Restarted ${service}` : output,
success: exitCode === 0,
};
}
}
// Create hub targeting the production server
const hub = new IntegrationHub({
apiKey: process.env.CMDOP_API_KEY!,
machine: 'prod-server',
});
// Register both custom handlers
hub.addHandler(new HealthCheckHandler());
hub.addHandler(new RestartHandler());
// Add Telegram as the communication channel
hub.addChannel(new TelegramChannel({
token: process.env.TELEGRAM_BOT_TOKEN!,
}));
// Start listening for commands
hub.start();