Skip to Content

Handlers

TL;DR

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?

HandlerCommandsDescription
ShellHandler/shell, /execExecute shell commands
AgentHandler/agentRun AI agent tasks
FilesHandler/files ls|cat|infoFile operations
LsHandler/lsList directory (shortcut)
CatHandler/catRead file (shortcut)
SkillsHandler/skills list|show|run, /skillSkills management
HelpHandler/helpShow 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

SubcommandDescription
ls / listList directory (up to 50 entries)
cat / readRead file (truncated at 3000 chars)
infoFile 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

SubcommandDescription
listList 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 commands

How 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 commands

What 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...")
Last updated on