Desktop (native) control for dsh as a stdio JSON-RPC bridge over a Python core: capture the screen, inject pointer/keyboard input, drive accessibility-tree actions, and gate high-risk operations behind required confirmations.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:JohnXu22786/computer-control
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time. Only install sources you trust, and pin a commit (github:owner/repo#sha).
README
computer-control — Desktop Control Plugin (for dsh)
Lets an agent operate the computer desktop directly: screenshot observation, mouse/keyboard injection, semantic actions via the accessibility tree (semantics first, pixel-coordinate fallback), with built-in safety guards — emergency stop, allow/deny rules, confirmation flow and idle standby.
The plugin is self-contained and loadable by dsh directly: it declares tools and events via manifest.json, speaks line-delimited JSON-RPC 2.0 (stdio) as its transport protocol, and python -m computer_control serve is the entry point.
Feature Overview
| Capability | Description |
|---|---|
Screenshot screen.capture |
Full-screen or region capture, PNG/JPEG, scaling, grayscale — token costs under control |
Mouse pointer.* |
Move, left/middle/right click (double/triple), drag, scroll (horizontal/vertical) |
Keyboard keyboard.* |
Single keys, chords (scancode injection, layout-independent), arbitrary Unicode text input |
Wait wait.pause |
Pause between actions so the UI can settle before the next screenshot |
Semantic actions a11y.* |
Hierarchical accessibility-tree summaries (skeleton/standard/full), semantic activate/input — UIA mode first, automatic fallback to bounding-box pixel clicks |
Batch batch.execute |
Run several actions in one call to reduce model round trips; confirmable as a whole, can continue on error |
| Capability broadcast | tools.list / system.status report per-backend availability; semantic tools are only exposed when UIA is available |
Safety guards (see the "Safety" section):
- Emergency stop: triple trigger — global hotkey (default
Ctrl+Alt+F12, configurable), protocol command, panic file; a STOP banner shows in the screen corner - Allow/deny rules: matched by tool name and arguments, deny always wins; whitelist mode switchable
- Confirmation flow: high-risk actions (including chords with
winorctrl+alt) wait for human approval, auto-denied on timeout - Idle standby: no activity past a threshold puts the session into standby, rejecting all actions until resumed
- Dry-run mode:
platform: "dry-run"logs actions without executing them, for safe rehearsals
Directory Structure
computer-control/
├── manifest.json # dsh plugin manifest (tools/events/entry/transport)
├── pyproject.toml # packaging metadata and dependency declarations
├── requirements.txt # core dependencies (Pillow only)
├── requirements-optional.txt# optional capability deps (UIA/mss/hotkey)
├── README.md
├── docs/ # integration / protocol / actions / configuration
├── examples/ # config examples, session examples, demo scripts
├── computer_control/ # plugin implementation (Python package)
│ ├── cli.py __main__.py # entry: serve / check / list
│ ├── session.py # session lifecycle and serial action execution
│ ├── engine.py # action execution engine (coordinate mapping, events)
│ ├── policy.py # safety gate: rules/confirmation/panic/watchdog
│ ├── actions.py # action registry and argument validation
│ ├── geometry.py # model canvas <-> physical pixel mapping
│ ├── protocol.py server.py# JSON-RPC routing and stdio/HTTP transports
│ ├── client.py # client for harness/scripts
│ ├── overlay.py # emergency-stop visual banner (optional, tkinter)
│ ├── drivers/ # execution-layer abstraction + windows impl + dry-run driver
│ └── a11y/ # accessibility-tree summaries + Windows UIA bridge
└── tests/ # pure-logic and protocol tests (no real hardware)
Installing in DSH
dsh plugin --profile demo add github:JohnXu22786/computer-control
Remove with:
dsh plugin --profile demo remove computer-control
dsh bundle
The repo also ships a Cordis bundle so anything that consumes dsh.bundle
manifests can install it the same way: package.json declares
dsh.bundle.patch pointing at cordis.patch.yml, and index.js is the bridge
a dsh profile loads. It spawns python -m computer_control serve as a child
process and re-exposes every tool from manifest.json to the harness over the
stdio protocol — the Python core is left untouched. Profile-level settings can
be pinned through the patch row's config (e.g. platform.name: dry-run for a
rehearsal-only install).
Requirements for the bridge: node >= 18 and a Python 3.9+ interpreter with
this package installed (pip install -r requirements.txt is enough for the
core tools).
Installation
Requirements: Python 3.9+, Windows 10/11 (full functionality); other platforms see "Platform Support".
# Core (screenshot + input injection)
pip install -r requirements.txt
# Optional capabilities (recommended):
# comtypes -> accessibility-tree (UIA) semantic actions
# mss -> faster multi-monitor screenshot backend
pip install -r requirements-optional.txt
Environment self-check:
python -m computer_control check
Prints diagnostics for platform, DPI mode, virtual-desktop geometry, screenshot backend, UIA availability, hotkeys, etc.
Quick Start
# List declared tools and events
python -m computer_control list
# Start the plugin service (stdio, for dsh to load)
python -m computer_control serve
Run a rehearsal session with examples/demo.py (defaults to dry-run, touches nothing on the real desktop):
python examples/demo.py
dsh Integration (Summary)
Full integration notes: docs/integration.md.
- Loading: dsh reads
manifest.json, starts the process viaentry.command, and establishes a stdio pipe (UTF-8, one JSON object per line). - Lifecycle: send
session.start(optionally with config) → wait for thesession.startedevent, then call tools; sendsession.stopat the end. - Calling actions:
tools.call(single) ortools.call_batch(batch). Responses use a unified{ok, result, error, meta}envelope. - Events: the server pushes
action.started/finished,safety.confirmation_requested, etc. aseventnotifications. - Confirmation flow: high-risk actions return
awaiting_confirmationand emit a confirmation event; the harness should surface a human approval prompt, then reply viasession.confirm; timed out confirmations are auto-denied.
The recommended model loop: screen.capture to observe → execute pointer.* / a11y.* on canvas coordinates → wait.pause (if needed) → a fresh screenshot to verify the result.
Action Reference (Summary)
All actions, parameters and examples: docs/actions.md.
| Action | Purpose | Risk |
|---|---|---|
screen.capture |
Screenshot (region/format/scale/grayscale) | none |
pointer.move |
Move pointer | moderate |
pointer.click |
Single/double/triple click, optional position | moderate |
pointer.drag |
Press and drag | moderate |
pointer.scroll |
Wheel (horizontal/vertical) | moderate |
keyboard.press |
Single key | moderate |
keyboard.combo |
Chord (upgrades to high when it includes win or ctrl+alt) | moderate/high |
keyboard.type |
Text input (Unicode) | moderate |
wait.pause |
Pause | none |
a11y.snapshot |
Hierarchical accessibility-tree summary | none |
a11y.activate |
Semantic activation (pattern first, pixel fallback) | moderate |
a11y.input |
Semantic text input (value pattern first) | moderate |
batch.execute |
Batch execution | max of items |
Coordinate Contract
The model sees a canvas, not the raw screen: screenshots are scaled to an aspect-preserving canvas whose width is display_width_px (default 1920), and coordinates returned by the model live on that canvas; the plugin maps them back to physical pixels uniformly via scale = physical width / canvas width. The screen.capture result carries a canvas field along with display_width_px/display_height_px as the source of truth. Multi-monitor setups (including negative-coordinate regions left/above the primary display) and per-monitor DPI are handled in the execution layer.
Safety
See docs/configuration.md and the points below:
- Emergency stop (triple): default global hotkey
Ctrl+Alt+F12(configurable viasafety.emergency_hotkey, set empty to disable); protocol methodcontrol.panic; panic file (safety.panic_file, presence stops everything). After a panic all actions returnsafety_stopped;session.resumeor pressing the hotkey again restores operation. A red STOP banner shows in the screen corner while engaged (safety.visual_indicator). - Allow/deny rules (
safety.rules):{match: {tool: "keyboard.*", argument: {name, matcher, value}}, effect: "deny"}; deny rules always win over allow rules.safety.default_rule: "deny"switches to whitelist mode (anything not explicitly allowed is denied). Rules can be adjusted at runtime viasession.configure. - Confirmation flow:
safety.confirm_threshold(defaulthigh) decides which risk levels need human approval;safety.confirm_timeout_s(default 30s) auto-denies on timeout. Approved actions execute normally and emitaction.finished. - Idle standby: enabled when
safety.idle_timeout_s> 0; after no activity for the timeout the session enters standby (emitssession.idle),session.resumerestores it;idle_action: "none"only emits the event without pausing. - Dry-run mode: with
platform: "dry-run"every action is logged but never executed — handy for integration debugging and safety rehearsals.
Platform Support
| Platform | Driver | Notes |
|---|---|---|
| Windows | drivers/windows.py |
Full implementation: SendInput scancode injection, per-monitor DPI awareness, virtual-desktop coordinates, mss/Pillow screenshots, UIA semantic layer |
| macOS / Linux | interface abstracted | drivers/base.py defines the complete driver contract (capture/pointer/keys/a11y/hotkey); implement it per platform to plug in (drivers/windows.py is a complete example). Until implemented, platform: "auto" fails with a clear error |
| Any platform | drivers/dummy.py |
Dry-run driver: records every action, touches no hardware |
Dependencies and degradation:
Pillow(required): screenshots and encoding. The plugin refuses to start without it.mss(optional): faster and more reliable on multi-monitor Windows; falls back to Pillow ImageGrab when missing.comtypes(optional): UIA semantic actions. When missing,a11y.*tools are marked unavailable intools.listand calls returnbackend_unavailable— the pixel-coordinate path (screenshot + click) is unaffected.keyboard(optional, reserved): global hotkeys on macOS/Linux drivers will depend on it; only the Windows and dry-run drivers ship today, and Windows uses built-inGetAsyncKeyStatepolling, so the package is not needed.
Known limitations:
- Secure attention sequences (e.g.
Ctrl+Alt+Del) cannot be triggered via input injection — an OS-level protection that the plugin cannot bypass either. - UIA depends on the target program exposing an accessibility interface; programs that do not (some games, self-drawn UIs) can only use the pixel path.
- Keyboard scancode injection works better with DirectInput/raw-input programs, but some anti-cheat programs may still reject it (normal protective behavior).
Testing
python -m unittest discover -s tests -v
Tests use only pure logic and the dry-run driver — no real input is injected, no real hardware is touched; the real Windows chain is verified manually via python -m computer_control check and examples/demo.py --live.
License
MIT — see LICENSE. Copyright (c) 2026 JohnXu22786.
Links
More in this category
superdesigndev/treg★ 428
Tool catalog for agents: search ~2,600 external endpoints (SEO and SERP, backlinks, social, people and company enrichment, ad libraries, scraping) by the task you want done, read each one's parameters and per-call price, then call it with the credential injected server-side. Ships the skill plus an MCP row that stays disabled until TREG_TOKEN is set.
Lum1104/dsh-browser★ 216
Chrome sidebar extension that lets DSH operate your browser directly, no vision capabilities required.
zhaoolee/notes★ 142
Export DSH conversations as Smartisan Notes-style PNGs, or create and update Markdown notes in a configured account-scoped workspace.
liustack/modsearch★ 115
Web search bridge for text-only agents: ask the web or X, get structured JSON evidence (search, fetch, citations).
taxueseek/argo★ 94
Search built for agents: multilingual coverage across web, academic, code, shopping, finance, news, and encyclopedias.
Vladimir-Human/ru-marketplace-mcp#dsh★ 64
Skills and optional MCP rows for ten Russian marketplaces: price comparison across Wildberries, Detsky Mir and Yandex Market, plus per-source search, product cards and reviews. The 13 skills load on install; both MCP rows stay disabled until RU_MARKETPLACE_MCP_DIR points at a local clone, which needs Python 3.12+ and uv.