JohnXu22786/computer-control
dsh plugin: desktop control - screen capture, pointer/keyboard injection, accessibility-tree semantic actions, with emergency stop, allow/deny rules, confirmation flow and idle standby
Listed
0
Tools
Bundle verified
What it does
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.
Best for
- Windows desktop workflows requiring screenshots, pointer and keyboard input, or accessibility-based actions from DSH.
- Sensitive desktop automation that benefits from confirmations, allow/deny rules, emergency stop, and dry-run rehearsal.
Not ideal for
- macOS or Linux live desktop control; only Windows and dry-run drivers are provided.
- Applications without accessible UI metadata must use coordinate-based control, and protected inputs such as Ctrl+Alt+Del cannot be injected.
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.
Frequently Asked QuestionsFAQ
Use the verified command dsh plugin --profile default add github:JohnXu22786/computer-control in a DSH-enabled shell. The command resolves the public package metadata and keeps the plugin attached to the catalog identity shown on this page.
Compatibility follows the bundle and profile status shown above. If a profile is not detected, keep the plugin disabled there and check the repository documentation before enabling it in production.
The GitHub link and activity metadata are the source of truth for releases and maintenance. Revisit this page after a new release to confirm the catalog has observed the latest version.