Skip to Content

Handlers

TL;DR

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?

HandlerCommandsDescription
TerminalHandler/shell, /execExecute shell commands
AgentHandler/agentRun AI agent tasks
FilesHandler/ls, /cat, /filesFile operations
SkillsHandler/skills list|show|runList, 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:

SubcommandExampleDescription
list/skills listList all installed skills
show <name>/skills show ssl-cert-checkerShow skill details and system prompt
run <name> <prompt>/skills run ssl-cert-checker Check github.comExecute 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();
Last updated on