Handlers
Handlers are standalone, channel-agnostic command processors. ShellHandler executes
shell commands, AgentHandler runs AI tasks, FilesHandler manages files, SkillsHandler
manages skills (list, show, run), and HelpHandler generates help text. Each handler
manages its own CMDOP client. Use SendCallback to deliver responses. Create custom
handlers by extending MessageHandler.
Standalone, channel-agnostic command handlers. Each manages its own CMDOP client internally.
What handlers are available?
| Handler | Commands | Description |
|---|---|---|
ShellHandler | /shell, /exec | Execute shell commands |
AgentHandler | /agent | Run AI agent tasks |
FilesHandler | /files ls|cat|info | File operations |
LsHandler | /ls | List directory (shortcut) |
CatHandler | /cat | Read file (shortcut) |
SkillsHandler | /skills list|show|run, /skill | Skills management |
HelpHandler | /help | Show available commands |
How does ShellHandler work?
Execute shell commands on remote machines.
from cmdop_bot.handlers import ShellHandler
from cmdop_bot.models import Command, Message
# Create a ShellHandler with API key, target machine, and timeout
handler = ShellHandler(
cmdop_api_key="cmdop_xxx",
machine="my-server",
timeout=30.0, # Max seconds per command
)
async def on_shell_command(cmd_text: str):
results = []
# Build a Command object representing the shell invocation
command = Command(
name="shell",
args=[cmd_text],
raw_text=f"/shell {cmd_text}",
)
# Execute the command; results.append collects each response line
await handler.handle(command, results.append)
return "\n".join(results)
# Clean up the internal CMDOP client when done
await handler.close()How does AgentHandler work?
Run AI agent tasks.
from cmdop_bot.handlers import AgentHandler
# Create an AgentHandler for running AI agent tasks on a remote machine
handler = AgentHandler(
cmdop_api_key="cmdop_xxx",
machine="my-server",
)
async def run_agent(task: str):
results = []
# Build a Command for the agent task
command = Command(
name="agent",
args=[task],
raw_text=f"/agent {task}",
)
# Execute the agent task; responses are collected via results.append
await handler.handle(command, results.append)
return "\n".join(results)
# Clean up the internal CMDOP client when done
await handler.close()How does FilesHandler work?
List directories, read files, get file info.
from cmdop_bot.handlers import FilesHandler
# Create a FilesHandler for listing, reading, and inspecting files
handler = FilesHandler(
cmdop_api_key="cmdop_xxx",
machine="my-server",
)
# List directory — subcommand "ls" lists up to 50 entries
command = Command(name="files", args=["ls", "/var/log"], raw_text="/files ls /var/log")
await handler.handle(command, print)
# Read file — subcommand "cat" reads content (truncated at 3000 chars)
command = Command(name="files", args=["cat", "/etc/hostname"], raw_text="/files cat /etc/hostname")
await handler.handle(command, print)
# File info — subcommand "info" returns size, type, modified date, permissions
command = Command(name="files", args=["info", "/etc/hostname"], raw_text="/files info /etc/hostname")
await handler.handle(command, print)
# Clean up the internal CMDOP client when done
await handler.close()Subcommands
| Subcommand | Description |
|---|---|
ls / list | List directory (up to 50 entries) |
cat / read | Read file (truncated at 3000 chars) |
info | File metadata (size, type, modified, permissions) |
How does SkillsHandler work?
List, inspect, and run skills on remote machines.
from cmdop_bot.handlers import SkillsHandler
# Create a SkillsHandler with API key, target machine, and timeout
handler = SkillsHandler(
cmdop_api_key="cmdop_xxx",
machine="my-server",
timeout=300.0, # Max seconds for skill runs
)
# List skills — subcommand "list"
command = Command(name="skills", args=["list"], raw_text="/skills list")
await handler.handle(command, print)
# Show skill details — subcommand "show"
command = Command(name="skills", args=["show", "code-review"], raw_text="/skills show code-review")
await handler.handle(command, print)
# Run a skill — subcommand "run"
command = Command(name="skills", args=["run", "code-review", "Review this PR"], raw_text="/skills run code-review Review this PR")
await handler.handle(command, print)
# Clean up the internal CMDOP client when done
await handler.close()Subcommands
| Subcommand | Description |
|---|---|
list | List available skills on the machine |
show <name> | Show skill details (content truncated at 500 chars) |
run <name> <prompt> | Run a skill with a prompt |
The /skill <name> <prompt> shorthand is equivalent to /skills run <name> <prompt>.
How do LsHandler and CatHandler work?
Shortcuts that wrap FilesHandler.
from cmdop_bot.handlers import LsHandler, CatHandler
# LsHandler is a shortcut for FilesHandler with the "ls" subcommand
ls = LsHandler(cmdop_api_key="cmdop_xxx")
# CatHandler is a shortcut for FilesHandler with the "cat" subcommand
cat = CatHandler(cmdop_api_key="cmdop_xxx")
# Both wrap FilesHandler internally — use them for simpler /ls and /cat commandsHow does HelpHandler work?
Generates help text from all registered handlers.
from cmdop_bot.handlers import HelpHandler, ShellHandler, AgentHandler, SkillsHandler
# HelpHandler aggregates all registered handlers to generate help text
help_handler = HelpHandler()
# Register each handler so /help knows about its commands
help_handler.add_handler(ShellHandler(cmdop_api_key="cmdop_xxx"))
help_handler.add_handler(AgentHandler(cmdop_api_key="cmdop_xxx"))
help_handler.add_handler(SkillsHandler(cmdop_api_key="cmdop_xxx"))
# /help will list: /shell, /agent, /skills commandsWhat is SendCallback?
All handlers use SendCallback — a callable that receives response strings:
from typing import Callable, Awaitable
# SendCallback type: an async function that receives a string response
SendCallback = Callable[[str], Awaitable[None]]
# Example: define a custom callback that prints each response
async def my_send(text: str) -> None:
print(f"Bot says: {text}")
# Pass your callback to handler.handle() to receive command output
await handler.handle(command, my_send)How do I create a custom handler?
Create custom handlers by implementing MessageHandler:
from cmdop_bot.core import MessageHandler
from cmdop_bot.models import Command
# Extend MessageHandler to create a custom command handler
class DeployHandler(MessageHandler):
@property
def name(self) -> str:
return "deploy" # Command name used as /deploy
@property
def description(self) -> str:
return "Deploy the application" # Shown in /help output
async def handle(self, command: Command, send) -> None:
# Implement your custom logic here; use send() to return responses
await send("Deployment started...")