Skip to main content

Async-first Python SDK for CMDOP — spawns the baked-in cmdop-core Go binary and speaks protobuf over stdio.

Project description

cmdop (Python)

cmdop — Python SDK for CMDOP

Async-first Python SDK for CMDOP — manage your machines, fleets, tunnels, and schedules, stream a machine's AI agent, and drive the skills marketplace, all from typed Python.

📚 Docs: docs.cmdop.com · SDK · Bots · Connect

How the CMDOP SDK connects your app to your machines

  • One install, zero dependenciespip install cmdop is everything. No native build step, no extra runtime, nothing fetched on first run.
  • Works anywhere, offline-ready — a single self-contained package runs the same on macOS, Linux, and Windows, including air-gapped hosts.
  • Typed end to end — every resource and response is fully typed, with one clean async streaming API for live agent output.

Install

pip install cmdop        # or: uv add cmdop

Quick start

from cmdop import Client

async with Client(token="...") as c:        # or Client.from_env()
    page = await c.machines.list(presence="online")
    for m in page.items:
        print(m.hostname, m.presence)

    text = await c.machines.ask(machine_id, "uptime").collect()
    print(text)

Client is an async context manager — use async with so resources are released on exit (or call await c.aclose()).

Namespaces

The same surface as the relay, mirrored exactly by the Node SDK (snake_case here, camelCase there):

Namespace Methods
machines list · get · update · disable · info · spend · ask · messages · clear_messages · active_session
fleets list · get · create · update · disable
fleets.members list · add · set_role · remove
fleets.machines list · attach · detach
tunnels open · close · list · get · sessions
schedules list · get · create · update · delete · trigger · runs
keys list · issue · revoke
skills list · get · my · install · star · versions · reviews · create · update · delete · publish · publish_status · categories · tags

List endpoints also expose iter(...) (yield every item, following cursors) and pages(...) (yield each page).

Two planes, one client. machines / fleets / tunnels / schedules / keys use your relay token (CMDOP_TOKEN); the skills marketplace uses your platform API key (CMDOP_API_KEY). Set whichever you need — the client routes each call to the right plane for you.

Streaming: machines.ask()

ask() returns a FrameStream (aliased AskStream) — an async iterator of typed frames with pin() / confirm() / collect():

stream = c.machines.ask(machine_id, "df -h", session_id="s1")
async for frame in stream:
    if frame.type == "event":
        print(frame.payload.get("delta", ""), end="")
    elif frame.type == "pin_required":          # machine asked for its connect PIN
        await stream.pin(frame.challenge_id, "1234")
    elif frame.type == "confirm_required":       # a dangerous plan awaits approval
        await stream.confirm(frame.token, accept=True)
    elif frame.type == "pin_denied":
        print("PIN rejected:", frame.reason)
    elif frame.type == "done":
        print("\n->", frame.text)

# one-shot: drain to the final text
text = await c.machines.ask(machine_id, "uptime").collect()

Frame types: event · done · error · confirm_required · pin_required · pin_denied. An error outcome raises AgentStreamError.

Environment variables

Var Meaning Default
CMDOP_TOKEN relay Bearer token (machines/fleets/…) — (required for relay ops)
CMDOP_BASE_URL relay REST root https://cloud.cmdop.com
CMDOP_API_KEY platform UserAPIKey (for skills) — (required for skills)
CMDOP_API_BASE_URL platform REST root https://api.cmdop.com
CMDOP_FLEET_ID default fleet for fleet-scoped ops none
CMDOP_TIMEOUT_MS per-call timeout 30000

Precedence is always explicit arg > env var > default.

Error handling

Every failure surfaces as a typed exception carrying a stable code (all subclass CmdopError):

from cmdop import (
    AuthError, PermissionError, NotFoundError, ConflictError,
    ValidationError, RateLimitError, ServerError, ConnectionError,
    TimeoutError, UnavailableError, AgentStreamError, CmdopError,
)

try:
    await c.machines.get("missing-id")
except NotFoundError:
    ...
except CmdopError as e:
    if e.retryable:           # True on TimeoutError — safe to retry
        ...
    print(e.code, e.message)

ConnectionError covers a lost connection mid-call (pending calls reject); TimeoutError (a retryable subclass) is a deadline/handshake timeout; UnavailableError means the relay is up but the target agent/machine is offline. AgentStreamError is the streaming-ask error outcome.

Links

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

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

cmdop-1.0.2-py3-none-any.whl (24.9 MB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for cmdop-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 28bcf34905799cbf58574c9a72e256d9d4a00b5098f535cfd4ea96276d310675
MD5 92432120c44a64506445760468c2dfe7
BLAKE2b-256 fd67723ffe535d4ece84c7cda034d20cfc9da697b1455fe83fc0eec35733c709

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