Skip to main content

Python SDK for CMDOP agent interaction

Project description

cmdop

Turn any machine into an API endpoint.

PyPI Python License

from cmdop import CMDOPClient

# Server in another country. Behind NAT. Behind firewall. Don't care.
with CMDOPClient.remote(api_key="cmd_xxx") as server:
    server.terminal.execute("docker restart app")
    server.files.write("/etc/nginx/sites/new.conf", config)
    logs = server.files.read("/var/log/app/error.log")

No SSH. No VPN. No open ports. No bullshit.


The Problem

You need to run a command on a remote server. What do you do?

Option A: SSH

→ Generate keys
→ Copy public key to server
→ Configure sshd
→ Open port 22
→ Hope firewall allows it
→ Pray NAT doesn't break it
→ paramiko/fabric in Python
→ Debug why it doesn't work

Option B: Build an API

→ Write FastAPI endpoints
→ Implement authentication
→ Get SSL certificate
→ Open ports
→ Deploy to every server
→ Maintain all of this
→ Write client code
→ Repeat for each new operation

Option C: CMDOP

server.terminal.execute("your command")

That's it.


How It Works

┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│  Your Code  │ ──── │ Cloud Relay │ ──── │   Agent     │
│   (SDK)     │      │grpc.cmdop.com│     │  (on server)│
└─────────────┘      └─────────────┘      └─────────────┘
                            │
        Outbound only ──────┘
        No open ports
        Works through any NAT/firewall
  1. Install agent on target machine
  2. Agent connects outbound to cloud relay
  3. Your code connects to same relay with API key
  4. Commands flow through, results come back
  5. Zero network configuration required

What Can You Do?

Terminal — full shell access:

session = server.terminal.create(shell="/bin/bash")
server.terminal.send_input(session.session_id, "kubectl get pods\n")
output = server.terminal.get_history(session.session_id)

Files — complete filesystem control:

server.files.list("/home/user")
server.files.read("/etc/nginx/nginx.conf")
server.files.write("/tmp/config.json", b'{"key": "value"}')
server.files.delete("/tmp/garbage", recursive=True)
server.files.copy("/backup/db.sql", "/restore/db.sql")

Browser — direct programmatic control (no LLM):

with server.browser.create_session() as browser:
    browser.navigate("https://example.com/login")
    browser.type("input[name='email']", "user@example.com")
    browser.type("input[name='password']", "secret")
    browser.click("button[type='submit']")

    # Extract data with CSS selectors
    prices = browser.extract(".product-price")

    # Or regex
    emails = browser.extract_regex(r"[\w.-]+@[\w.-]+\.\w+")

    # Or run any JavaScript
    data = browser.execute_script("return window.__APP_STATE__")

AI Agents — run LLM agents with type-safe responses:

from pydantic import BaseModel

class ServerHealth(BaseModel):
    status: str
    cpu_percent: float
    memory_used_gb: float
    disk_free_gb: float
    issues: list[str]

# Run agent, get typed response
result = server.agent.run(
    prompt="Check server health and report issues",
    output_schema=ServerHealth
)
health: ServerHealth = result.output  # Fully typed!
if health.issues:
    alert(health.issues)

Real World

Give AI Agents Hands

from pydantic import BaseModel

class DeployResult(BaseModel):
    success: bool
    version: str
    containers_running: int
    errors: list[str]

# Agent executes commands, parses output, returns structured data
with CMDOPClient.remote(api_key=KEY) as server:
    result = server.agent.run(
        prompt="Deploy myapp:v2.1 and verify all containers are healthy",
        agent_type="terminal",
        output_schema=DeployResult
    )
    if not result.output.success:
        rollback(result.output.errors)

Deploy Without The Circus

# No Ansible. No Terraform. No YAML hell.
def deploy(version: str):
    with CMDOPClient.remote(api_key=PROD_KEY) as prod:
        prod.terminal.execute(f"docker pull myapp:{version}")
        prod.terminal.execute("docker-compose up -d")

        # Verify
        health = prod.files.read("/var/log/myapp/health.log")
        assert b"healthy" in health

Fleet Management

# Update 1000 edge devices
async def update_fleet(device_keys: list[str], new_config: bytes):
    async with asyncio.TaskGroup() as tg:
        for key in device_keys:
            tg.create_task(update_device(key, new_config))

async def update_device(key: str, config: bytes):
    async with AsyncCMDOPClient.remote(api_key=key) as device:
        await device.files.write("/etc/app/config.yml", config)
        await device.terminal.execute("systemctl restart app")

Debug Customer Issues

# Support engineer gets temporary access
def diagnose(customer_key: str):
    with CMDOPClient.remote(api_key=customer_key) as machine:
        # See what's running
        session = machine.terminal.create()
        machine.terminal.send_input(session.session_id, "ps aux\n")

        # Read their logs
        logs = machine.files.read("~/Library/Logs/MyApp/error.log")

        # Check disk space
        machine.terminal.send_input(session.session_id, "df -h\n")

Scrape Without The LLM Tax

# Direct browser control - fast, predictable, cheap
def scrape_products(url: str) -> list[dict]:
    with CMDOPClient.local() as client:
        with client.browser.create_session(headless=True) as browser:
            browser.navigate(url)

            # Wait for content to load
            browser.wait_for(".product-card", timeout_ms=10000)

            # Extract with CSS selectors
            names = browser.extract(".product-name")
            prices = browser.extract(".product-price")
            links = browser.extract(".product-link", attr="href")

            return [
                {"name": n, "price": p, "url": l}
                for n, p, l in zip(names, prices, links)
            ]

Installation

pip install cmdop

Quick Start

from cmdop import CMDOPClient

# Remote server via cloud relay
with CMDOPClient.remote(api_key="cmd_xxx") as client:
    result = client.files.list("/home")
    print(result.entries)

# Local agent on same machine
with CMDOPClient.local() as client:
    result = client.files.list("/home")

Async

from cmdop import AsyncCMDOPClient

async with AsyncCMDOPClient.remote(api_key="cmd_xxx") as client:
    await client.terminal.execute("echo hello")
    content = await client.files.read("/etc/hostname")

API

Terminal

Method What it does
create(shell, cols, rows) Start terminal session
send_input(id, data) Send commands/keystrokes
get_history(id) Get output
resize(id, cols, rows) Resize terminal
send_signal(id, signal) Send SIGINT/SIGTERM/etc
close(id) End session

Files

Method What it does
list(path) List directory
read(path) Read file
write(path, content) Write file
delete(path) Delete file/dir
copy(src, dst) Copy
move(src, dst) Move/rename
mkdir(path) Create directory
info(path) Get metadata

Agent

Method What it does
run(prompt, output_schema=None) Run AI agent, optionally with Pydantic model for structured output

Supported agent types: chat, terminal, command, router, planner

Structured output with Pydantic:

class MyResult(BaseModel):
    answer: str
    confidence: float

result = client.agent.run("...", output_schema=MyResult)
typed_data: MyResult = result.output  # Validated against schema

Browser

Direct browser control without LLM. Fast, predictable, cheap.

Method What it does
create_session(headless=True) Start browser session (returns context manager)
navigate(url) Go to URL, returns final URL
click(selector) Click element by CSS selector
type(selector, text) Type into input/textarea
wait_for(selector, timeout_ms) Wait for element to appear
extract(selector, attr=None) Get text or attribute from elements
extract_regex(pattern) Extract regex matches from page
get_html() Get page HTML
get_text() Get page text content
execute_script(js) Run JavaScript, return result
screenshot() Capture PNG screenshot
get_state() Get current URL and title
set_cookies(cookies) Set browser cookies
get_cookies() Get all cookies

Session as context manager:

with client.browser.create_session() as browser:
    browser.navigate("https://example.com")
    data = browser.extract("h1")
# Session auto-closes

Security

  • TLS everywhere — all traffic encrypted
  • Outbound only — agent never accepts incoming connections
  • API key scoping — keys bound to specific agents/teams
  • No credentials on wire — no SSH keys, no passwords
  • Audit logs — every action tracked

Requirements

  • Python 3.10+
  • CMDOP agent running on target machine

Links

cmdop.com

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cmdop-0.1.9.tar.gz (139.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cmdop-0.1.9-py3-none-any.whl (239.8 kB view details)

Uploaded Python 3

File details

Details for the file cmdop-0.1.9.tar.gz.

File metadata

  • Download URL: cmdop-0.1.9.tar.gz
  • Upload date:
  • Size: 139.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.18

File hashes

Hashes for cmdop-0.1.9.tar.gz
Algorithm Hash digest
SHA256 98014891be211bc4f292cad7930153ea01227a464554a3e125436442641669ed
MD5 59e4c285131b4f508df44139f49c4c0b
BLAKE2b-256 57fe1d1b96833bd535098ee9c68897acf06e3651bfc18944f7363d7f350bc2c6

See more details on using hashes here.

File details

Details for the file cmdop-0.1.9-py3-none-any.whl.

File metadata

  • Download URL: cmdop-0.1.9-py3-none-any.whl
  • Upload date:
  • Size: 239.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.18

File hashes

Hashes for cmdop-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 b0d21365c3424bd18ec707fbb26b6e0ddbb1bd1d4816404a451a918fd50c9d35
MD5 82eeb018268451eaf7fb27105115ebf0
BLAKE2b-256 a21b1e919cb5fe57d9f529d3d9e50989f7ad4aef1a17ecd2d7208c9e779ffe0f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page