use-computer-cli 0.1.1__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/PKG-INFO +56 -13
  2. use_computer_cli-0.2.1/README.md +137 -0
  3. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/pyproject.toml +28 -2
  4. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/__init__.py +51 -0
  5. use_computer_cli-0.2.1/src/use_computer/accessibility/__init__.py +51 -0
  6. use_computer_cli-0.2.1/src/use_computer/accessibility/atspi.py +383 -0
  7. use_computer_cli-0.2.1/src/use_computer/accessibility/ax.py +227 -0
  8. use_computer_cli-0.2.1/src/use_computer/accessibility/base.py +77 -0
  9. use_computer_cli-0.2.1/src/use_computer/accessibility/roles.py +248 -0
  10. use_computer_cli-0.2.1/src/use_computer/accessibility/uia.py +215 -0
  11. use_computer_cli-0.2.1/src/use_computer/actions.py +365 -0
  12. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/cli.py +467 -32
  13. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/compare.py +33 -0
  14. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/config.py +14 -0
  15. use_computer_cli-0.2.1/src/use_computer/errors.py +147 -0
  16. use_computer_cli-0.2.1/src/use_computer/render.py +132 -0
  17. use_computer_cli-0.2.1/src/use_computer/runner.py +690 -0
  18. use_computer_cli-0.2.1/src/use_computer/selectors.py +338 -0
  19. use_computer_cli-0.2.1/src/use_computer/skill/SKILL.md +324 -0
  20. use_computer_cli-0.2.1/src/use_computer/tree.py +295 -0
  21. use_computer_cli-0.2.1/tests/fake_provider.py +141 -0
  22. use_computer_cli-0.2.1/tests/test_atspi_hint.py +70 -0
  23. use_computer_cli-0.2.1/tests/test_cli.py +473 -0
  24. use_computer_cli-0.2.1/tests/test_elements.py +471 -0
  25. use_computer_cli-0.2.1/tests/test_render.py +67 -0
  26. use_computer_cli-0.2.1/tests/test_selectors.py +195 -0
  27. use_computer_cli-0.1.1/README.md +0 -97
  28. use_computer_cli-0.1.1/src/use_computer/actions.py +0 -185
  29. use_computer_cli-0.1.1/src/use_computer/errors.py +0 -53
  30. use_computer_cli-0.1.1/src/use_computer/runner.py +0 -298
  31. use_computer_cli-0.1.1/src/use_computer/skill/SKILL.md +0 -135
  32. use_computer_cli-0.1.1/tests/test_cli.py +0 -243
  33. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/.gitignore +0 -0
  34. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/LICENSE +0 -0
  35. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/backends/__init__.py +0 -0
  36. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/backends/base.py +0 -0
  37. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/backends/local.py +0 -0
  38. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/backends/vnc.py +0 -0
  39. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/coordinates.py +0 -0
  40. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/keys.py +0 -0
  41. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/src/use_computer/skill/__init__.py +0 -0
  42. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/__init__.py +0 -0
  43. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/conftest.py +0 -0
  44. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/fake_backend.py +0 -0
  45. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/test_backends.py +0 -0
  46. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/test_compare.py +0 -0
  47. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/test_config.py +0 -0
  48. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/test_config_init.py +0 -0
  49. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/test_coordinates.py +0 -0
  50. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/test_keys.py +0 -0
  51. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/test_runner.py +0 -0
  52. {use_computer_cli-0.1.1 → use_computer_cli-0.2.1}/tests/test_skill.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: use-computer-cli
3
- Version: 0.1.1
3
+ Version: 0.2.1
4
4
  Summary: Execute input on a screen for computer-use agents: move, click, drag, scroll, type, key, screenshot.
5
5
  Project-URL: Homepage, https://github.com/applica-software-guru/use-computer
6
6
  Author: Bruno Fortunato
@@ -50,19 +50,32 @@ Requires-Dist: ruff>=0.6; extra == 'dev'
50
50
  Provides-Extra: local
51
51
  Requires-Dist: mss>=10; extra == 'local'
52
52
  Requires-Dist: pynput>=1.8; extra == 'local'
53
+ Provides-Extra: tree
54
+ Requires-Dist: pyobjc-framework-applicationservices>=10; (sys_platform == 'darwin') and extra == 'tree'
55
+ Requires-Dist: uiautomation>=2.0; (sys_platform == 'win32') and extra == 'tree'
53
56
  Provides-Extra: vnc
54
57
  Requires-Dist: vncdotool>=1.3; extra == 'vnc'
55
58
  Description-Content-Type: text/markdown
56
59
 
57
60
  # use-computer
58
61
 
59
- Executes input on a screen for computer-use agents: move, click, double-click, right-click,
60
- drag, scroll, type text, press key combinations, and capture a screenshot.
62
+ Reads and acts on a screen for computer-use agents. It reads the **accessibility tree** the
63
+ operating system already maintains -- roles, names, states and clickable boxes -- and it moves the
64
+ mouse, clicks, drags, scrolls, types text, presses key combinations, and captures screenshots.
61
65
 
62
- `use-computer` is the **acting** half of a pair. [ui-locator](https://github.com/applica-software-guru/ui-locator)
63
- answers *where* the Invia button is and returns pixel coordinates; `use-computer` performs the
64
- click there. Both are driven by another AI agent through a CLI that emits JSON on stdout and
65
- diagnostics on stderr, with a Python API underneath.
66
+ Every interaction takes the highest rung it can reach:
67
+
68
+ 1. **Element, through the platform API** -- the OS presses the button itself. No coordinates, so
69
+ nothing to aim and no scale to get wrong.
70
+ 2. **Element, by coordinate** -- the tree can see the control but exposes no way to operate it, so
71
+ `use-computer` clicks its centre and tells you it did.
72
+ 3. **Pixel, from vision** -- the tree cannot see it. Screenshot,
73
+ [ui-locator](https://github.com/applica-software-guru/ui-locator), click those pixels.
74
+
75
+ Each rung is cheaper, faster and more accurate than the one below. Rung three is the floor the
76
+ whole ladder stands on and is not going anywhere; it is simply no longer the only rung. Everything
77
+ is driven by another AI agent through a CLI that emits JSON on stdout and diagnostics on stderr,
78
+ with a Python API underneath.
66
79
 
67
80
  ## Install
68
81
 
@@ -70,9 +83,26 @@ diagnostics on stderr, with a Python API underneath.
70
83
  pip install use-computer-cli # no backend
71
84
  pip install "use-computer-cli[local]" # drive this machine's display (pynput + mss)
72
85
  pip install "use-computer-cli[vnc]" # drive a remote framebuffer over RFB (vncdotool)
86
+ pip install "use-computer-cli[tree]" # read the accessibility tree (Windows, macOS)
87
+ ```
88
+
89
+ Backends and the accessibility bindings are optional extras, imported lazily, so the package
90
+ installs without them.
91
+
92
+ **On Linux, do not use the `tree` extra.** PyGObject has no Linux wheel, so pip would build it from
93
+ source and fail. The bindings are already on almost every desktop; the virtualenv just has to see
94
+ them:
95
+
96
+ ```bash
97
+ sudo apt install python3-gi gir1.2-atspi-2.0
98
+ python3 -m venv --system-site-packages .venv
99
+ .venv/bin/pip install "use-computer-cli[local]"
73
100
  ```
74
101
 
75
- Backends are optional extras, imported lazily, so the package installs without them.
102
+ `gi` is a compiled extension built for one Python minor version -- Ubuntu 22.04 ships it for 3.10 --
103
+ so the virtualenv has to be the distro's `python3`, not another minor version. Both packages report
104
+ as installed either way, which is why the error message checks and says which case you are in.
105
+ With uv: `uv venv --python /usr/bin/python3 --system-site-packages`.
76
106
 
77
107
  The distribution is `use-computer-cli` because `use-computer` is taken on PyPI by an unrelated
78
108
  project. The command it installs is `use-computer`, and the package it imports is `use_computer`.
@@ -80,16 +110,28 @@ project. The command it installs is `use-computer`, and the package it imports i
80
110
  ## Use
81
111
 
82
112
  ```bash
83
- use-computer click --x 120 --y 340 --use staging
113
+ use-computer tree --use laptop # what is on screen, structurally
114
+ use-computer click --role button --name "Invia" # act on it by name
115
+ use-computer set-value --role text --name "Email" --value "mario@example.com"
116
+ use-computer click --x 120 --y 340 --use staging # or by pixel, when the tree cannot see it
84
117
  use-computer type --text "hello" --use staging
85
118
  use-computer key ctrl+s --use staging
86
- use-computer screenshot --use laptop # writes a file, returns its path
119
+ use-computer screenshot --use laptop # writes a file, returns its path
87
120
 
88
121
  # a batch runs over one connection -- the default command, so `batch` may be omitted
89
- echo '[{"action":"click","x":120,"y":340},{"action":"key","combo":"enter"}]' \
90
- | use-computer - --use staging --verify
122
+ echo '[
123
+ {"action":"focus","role":"text","name":"Destinatario"},
124
+ {"action":"type","text":"mario@example.com"},
125
+ {"action":"click","role":"button","name":"Invia"},
126
+ {"action":"tree"}
127
+ ]' | use-computer - --use laptop
91
128
  ```
92
129
 
130
+ An action names its target by coordinate **or** by element, never both. A selector that matches
131
+ nothing comes back with a screenshot -- the signal to switch to vision. A selector that matches
132
+ several comes back with the candidates, because two buttons named "OK" in two dialogs is the
133
+ ordinary case and picking one silently fails a hundred runs later.
134
+
93
135
  stdout is one JSON object per run; every diagnostic goes to stderr. Exit codes: `0` success,
94
136
  `1` failure, `2` bad usage.
95
137
 
@@ -104,7 +146,8 @@ action and reports the path, so a verified action does not need a `screenshot` c
104
146
  - **Setup cost.** Opening a VNC connection dominates a single action, so one run performs a
105
147
  batch of actions over one connection.
106
148
  - **Blind actuation.** A click that lands on nothing looks exactly like a click that worked, so
107
- `--verify` compares the screen before and after and reports whether it changed.
149
+ `--verify` compares the screen before and after and reports whether it changed -- and an action
150
+ that went through the accessibility API reports the element it actually operated.
108
151
 
109
152
  ## Configure
110
153
 
@@ -0,0 +1,137 @@
1
+ # use-computer
2
+
3
+ Reads and acts on a screen for computer-use agents. It reads the **accessibility tree** the
4
+ operating system already maintains -- roles, names, states and clickable boxes -- and it moves the
5
+ mouse, clicks, drags, scrolls, types text, presses key combinations, and captures screenshots.
6
+
7
+ Every interaction takes the highest rung it can reach:
8
+
9
+ 1. **Element, through the platform API** -- the OS presses the button itself. No coordinates, so
10
+ nothing to aim and no scale to get wrong.
11
+ 2. **Element, by coordinate** -- the tree can see the control but exposes no way to operate it, so
12
+ `use-computer` clicks its centre and tells you it did.
13
+ 3. **Pixel, from vision** -- the tree cannot see it. Screenshot,
14
+ [ui-locator](https://github.com/applica-software-guru/ui-locator), click those pixels.
15
+
16
+ Each rung is cheaper, faster and more accurate than the one below. Rung three is the floor the
17
+ whole ladder stands on and is not going anywhere; it is simply no longer the only rung. Everything
18
+ is driven by another AI agent through a CLI that emits JSON on stdout and diagnostics on stderr,
19
+ with a Python API underneath.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pip install use-computer-cli # no backend
25
+ pip install "use-computer-cli[local]" # drive this machine's display (pynput + mss)
26
+ pip install "use-computer-cli[vnc]" # drive a remote framebuffer over RFB (vncdotool)
27
+ pip install "use-computer-cli[tree]" # read the accessibility tree (Windows, macOS)
28
+ ```
29
+
30
+ Backends and the accessibility bindings are optional extras, imported lazily, so the package
31
+ installs without them.
32
+
33
+ **On Linux, do not use the `tree` extra.** PyGObject has no Linux wheel, so pip would build it from
34
+ source and fail. The bindings are already on almost every desktop; the virtualenv just has to see
35
+ them:
36
+
37
+ ```bash
38
+ sudo apt install python3-gi gir1.2-atspi-2.0
39
+ python3 -m venv --system-site-packages .venv
40
+ .venv/bin/pip install "use-computer-cli[local]"
41
+ ```
42
+
43
+ `gi` is a compiled extension built for one Python minor version -- Ubuntu 22.04 ships it for 3.10 --
44
+ so the virtualenv has to be the distro's `python3`, not another minor version. Both packages report
45
+ as installed either way, which is why the error message checks and says which case you are in.
46
+ With uv: `uv venv --python /usr/bin/python3 --system-site-packages`.
47
+
48
+ The distribution is `use-computer-cli` because `use-computer` is taken on PyPI by an unrelated
49
+ project. The command it installs is `use-computer`, and the package it imports is `use_computer`.
50
+
51
+ ## Use
52
+
53
+ ```bash
54
+ use-computer tree --use laptop # what is on screen, structurally
55
+ use-computer click --role button --name "Invia" # act on it by name
56
+ use-computer set-value --role text --name "Email" --value "mario@example.com"
57
+ use-computer click --x 120 --y 340 --use staging # or by pixel, when the tree cannot see it
58
+ use-computer type --text "hello" --use staging
59
+ use-computer key ctrl+s --use staging
60
+ use-computer screenshot --use laptop # writes a file, returns its path
61
+
62
+ # a batch runs over one connection -- the default command, so `batch` may be omitted
63
+ echo '[
64
+ {"action":"focus","role":"text","name":"Destinatario"},
65
+ {"action":"type","text":"mario@example.com"},
66
+ {"action":"click","role":"button","name":"Invia"},
67
+ {"action":"tree"}
68
+ ]' | use-computer - --use laptop
69
+ ```
70
+
71
+ An action names its target by coordinate **or** by element, never both. A selector that matches
72
+ nothing comes back with a screenshot -- the signal to switch to vision. A selector that matches
73
+ several comes back with the candidates, because two buttons named "OK" in two dialogs is the
74
+ ordinary case and picking one silently fails a hundred runs later.
75
+
76
+ stdout is one JSON object per run; every diagnostic goes to stderr. Exit codes: `0` success,
77
+ `1` failure, `2` bad usage.
78
+
79
+ Screenshots are files, never bytes in the JSON. `--verify` writes the screen it captured after the
80
+ action and reports the path, so a verified action does not need a `screenshot` call after it.
81
+
82
+ ## Three problems it solves
83
+
84
+ - **Coordinate spaces.** A screenshot on a HiDPI display is larger than the space the OS clicks
85
+ in. Every coordinate carries its space, `use-computer` scales between them, and it refuses to
86
+ guess when the ratio is unknown.
87
+ - **Setup cost.** Opening a VNC connection dominates a single action, so one run performs a
88
+ batch of actions over one connection.
89
+ - **Blind actuation.** A click that lands on nothing looks exactly like a click that worked, so
90
+ `--verify` compares the screen before and after and reports whether it changed -- and an action
91
+ that went through the accessibility API reports the element it actually operated.
92
+
93
+ ## Configure
94
+
95
+ ```bash
96
+ use-computer config init # asks, then proves it works
97
+ use-computer config init --backend vnc --host 10.0.0.5 # doesn't ask
98
+ use-computer config init --backend local --allow-local
99
+ ```
100
+
101
+ `config init` writes the file below, then opens the backend it just configured and reports the
102
+ screen geometry and scale — so a coordinate space whose ratio cannot be derived surfaces at setup
103
+ rather than at the first click that lands in the wrong place.
104
+
105
+ It writes `.use-computer/config.toml` at the project root (found by walking up, the way git finds
106
+ its own):
107
+
108
+ ```toml
109
+ default-profile = "laptop"
110
+ delay = 0.1
111
+
112
+ [profiles.laptop]
113
+ backend = "local"
114
+ allow-local = true
115
+
116
+ [profiles.staging]
117
+ backend = "vnc"
118
+ host = "10.0.0.5"
119
+ port = 5900
120
+ ```
121
+
122
+ Secrets go in `.use-computer/.env`, which is not committed. `use-computer config show` prints
123
+ every resolved value, the layer it came from and the variable that would override it.
124
+
125
+ ## The agent skill
126
+
127
+ Instructions for the calling agent ship inside the package and are installed from it, so they
128
+ always match the installed version:
129
+
130
+ ```bash
131
+ use-computer skill install --scope project
132
+ ```
133
+
134
+ ## Documentation
135
+
136
+ This package is developed with [SDD](https://github.com/applica-software-guru/sdd). The specs
137
+ it implements live in `product/` and `system/` at the repository root.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "use-computer-cli"
7
- version = "0.1.1"
7
+ version = "0.2.1"
8
8
  description = "Execute input on a screen for computer-use agents: move, click, drag, scroll, type, key, screenshot."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -38,6 +38,23 @@ dependencies = [
38
38
  # pyautogui is deliberately not used: its last release (0.9.54) dates from 2023.
39
39
  local = ["pynput>=1.8", "mss>=10"]
40
40
  vnc = ["vncdotool>=1.3"]
41
+ # Reading and operating the UI tree. One binding per platform, behind a marker.
42
+ #
43
+ # Linux needs a system package as well: there is no pyatspi on PyPI, so the AT-SPI bindings come
44
+ # from gir1.2-atspi-2.0 (plus python3-pyatspi on Debian/Ubuntu) and PyGObject reaches them through
45
+ # GObject Introspection. UITreeUnavailableError names both halves.
46
+ #
47
+ # Not dogtail (GPLv2 -- fine to run, wrong to import from a permissively licensed library) and not
48
+ # atomacos (last release 3.3.0, May 2021 -- the same staleness argument that ruled out pyautogui).
49
+ # Linux is deliberately absent. PyGObject has no Linux wheel, so listing it here made
50
+ # `pip install "use-computer-cli[tree]"` build from source, need pycairo and system headers, and
51
+ # fail -- leaving the user with no CLI at all rather than a CLI missing one capability. The AT-SPI
52
+ # bindings come from the distro (python3-gi, gir1.2-atspi-2.0) and are reached with a
53
+ # --system-site-packages virtualenv; the error message says so when they are absent.
54
+ tree = [
55
+ "uiautomation>=2.0; sys_platform == 'win32'",
56
+ "pyobjc-framework-ApplicationServices>=10; sys_platform == 'darwin'",
57
+ ]
41
58
  dev = [
42
59
  "pytest>=8",
43
60
  "pytest-mock>=3.12",
@@ -78,7 +95,16 @@ strict = true
78
95
  files = ["src", "tests"]
79
96
 
80
97
  [[tool.mypy.overrides]]
81
- module = ["pynput.*", "mss.*", "vncdotool.*", "ApplicationServices.*", "Quartz.*", "tomli"]
98
+ module = [
99
+ "pynput.*",
100
+ "mss.*",
101
+ "vncdotool.*",
102
+ "ApplicationServices.*",
103
+ "Quartz.*",
104
+ "gi.*",
105
+ "uiautomation.*",
106
+ "tomli",
107
+ ]
82
108
  ignore_missing_imports = true
83
109
 
84
110
  [tool.pytest.ini_options]
@@ -7,11 +7,15 @@ from __future__ import annotations
7
7
 
8
8
  from importlib.metadata import PackageNotFoundError, version
9
9
 
10
+ from use_computer.accessibility import AccessibilityProvider, create_provider
10
11
  from use_computer.actions import (
11
12
  Action,
12
13
  ClickAction,
14
+ CollapseAction,
13
15
  DoubleClickAction,
14
16
  DragAction,
17
+ ExpandAction,
18
+ FocusAction,
15
19
  KeyAction,
16
20
  MouseButton,
17
21
  MoveAction,
@@ -19,7 +23,13 @@ from use_computer.actions import (
19
23
  ScreenshotAction,
20
24
  ScrollAction,
21
25
  ScrollDirection,
26
+ SelectAction,
27
+ SetValueAction,
28
+ ShowMenuAction,
29
+ ToggleAction,
30
+ TreeAction,
22
31
  TypeAction,
32
+ WindowsAction,
23
33
  )
24
34
  from use_computer.backends import Backend, create_backend
25
35
  from use_computer.compare import ChangeReport, Screenshot, compare
@@ -28,15 +38,31 @@ from use_computer.config import load as load_config
28
38
  from use_computer.coordinates import Coordinate, CoordinateSpace, ScreenInfo, convert
29
39
  from use_computer.errors import (
30
40
  ActionFailedError,
41
+ ActionNotSupportedError,
42
+ AmbiguousNodeError,
31
43
  BackendNotAvailableError,
32
44
  ConfigError,
33
45
  CoordinateSpaceError,
34
46
  KeySyntaxError,
47
+ NodeNotFoundError,
35
48
  PermissionDeniedError,
49
+ UITreeUnavailableError,
36
50
  UseComputerError,
37
51
  )
38
52
  from use_computer.keys import KeyCombo, parse_combo
39
53
  from use_computer.runner import ActionResult, ErrorInfo, RunResult, Session, run_actions
54
+ from use_computer.tree import (
55
+ Box,
56
+ NodeSelector,
57
+ OutputFormat,
58
+ TreeReason,
59
+ TreeResult,
60
+ TreeScope,
61
+ UINode,
62
+ Via,
63
+ WindowInfo,
64
+ WindowsResult,
65
+ )
40
66
 
41
67
  #: The distribution name on PyPI, which differs from the import package: `use-computer` was
42
68
  #: already taken there. importlib.metadata is keyed by the distribution, so this is the name
@@ -50,14 +76,19 @@ except PackageNotFoundError: # pragma: no cover - source checkout without an in
50
76
 
51
77
  __all__ = [
52
78
  "DISTRIBUTION",
79
+ "AccessibilityProvider",
53
80
  "Action",
54
81
  "ActionFailedError",
82
+ "ActionNotSupportedError",
55
83
  "ActionResult",
84
+ "AmbiguousNodeError",
56
85
  "Backend",
57
86
  "BackendNotAvailableError",
58
87
  "BackendProfile",
88
+ "Box",
59
89
  "ChangeReport",
60
90
  "ClickAction",
91
+ "CollapseAction",
61
92
  "ConfigError",
62
93
  "Coordinate",
63
94
  "CoordinateSpace",
@@ -65,11 +96,16 @@ __all__ = [
65
96
  "DoubleClickAction",
66
97
  "DragAction",
67
98
  "ErrorInfo",
99
+ "ExpandAction",
100
+ "FocusAction",
68
101
  "KeyAction",
69
102
  "KeyCombo",
70
103
  "KeySyntaxError",
71
104
  "MouseButton",
72
105
  "MoveAction",
106
+ "NodeNotFoundError",
107
+ "NodeSelector",
108
+ "OutputFormat",
73
109
  "PermissionDeniedError",
74
110
  "ResolvedConfig",
75
111
  "RightClickAction",
@@ -79,14 +115,29 @@ __all__ = [
79
115
  "ScreenshotAction",
80
116
  "ScrollAction",
81
117
  "ScrollDirection",
118
+ "SelectAction",
82
119
  "Session",
120
+ "SetValueAction",
83
121
  "Settings",
122
+ "ShowMenuAction",
123
+ "ToggleAction",
124
+ "TreeAction",
125
+ "TreeReason",
126
+ "TreeResult",
127
+ "TreeScope",
84
128
  "TypeAction",
129
+ "UINode",
130
+ "UITreeUnavailableError",
85
131
  "UseComputerError",
132
+ "Via",
133
+ "WindowInfo",
134
+ "WindowsAction",
135
+ "WindowsResult",
86
136
  "__version__",
87
137
  "compare",
88
138
  "convert",
89
139
  "create_backend",
140
+ "create_provider",
90
141
  "load_config",
91
142
  "parse_combo",
92
143
  "run_actions",
@@ -0,0 +1,51 @@
1
+ """Choosing an accessibility provider.
2
+
3
+ By the *running platform*, never by configuration: a profile does not get to claim macOS
4
+ accessibility on Linux. And only for a backend that can have one at all -- RFB carries pixels,
5
+ so a vnc profile gets a clear error and the calling agent uses coordinates.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+
12
+ from use_computer.accessibility.base import AccessibilityProvider, require
13
+ from use_computer.errors import UITreeUnavailableError
14
+
15
+ #: Backends that drive a display this machine can also inspect. Everything else is remote
16
+ #: pixels, and no accessibility API reaches across RFB.
17
+ LOCAL_BACKENDS = frozenset({"local"})
18
+
19
+
20
+ def create_provider(backend: str) -> AccessibilityProvider:
21
+ """Build the provider for this platform, or say why there cannot be one.
22
+
23
+ Raises:
24
+ UITreeUnavailableError: wrong backend, unsupported platform, or a missing binding.
25
+ PermissionDeniedError: the OS refused the accessibility permission.
26
+ """
27
+ if backend not in LOCAL_BACKENDS:
28
+ raise UITreeUnavailableError(
29
+ f"the {backend!r} backend drives a remote framebuffer, which carries pixels and no "
30
+ "accessibility information. Use a screenshot and coordinates instead."
31
+ )
32
+
33
+ if sys.platform.startswith("linux"):
34
+ from use_computer.accessibility.atspi import AtspiProvider # noqa: PLC0415
35
+
36
+ return AtspiProvider()
37
+ if sys.platform == "win32":
38
+ from use_computer.accessibility.uia import UiaProvider # noqa: PLC0415
39
+
40
+ return UiaProvider()
41
+ if sys.platform == "darwin":
42
+ from use_computer.accessibility.ax import AxProvider # noqa: PLC0415
43
+
44
+ return AxProvider()
45
+
46
+ raise UITreeUnavailableError(
47
+ f"no accessibility provider for platform {sys.platform!r}."
48
+ )
49
+
50
+
51
+ __all__ = ["AccessibilityProvider", "UITreeUnavailableError", "create_provider", "require"]