use-computer-cli 0.1.1__tar.gz → 0.2.0__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.
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/PKG-INFO +47 -13
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/README.md +42 -12
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/pyproject.toml +24 -2
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/__init__.py +51 -0
- use_computer_cli-0.2.0/src/use_computer/accessibility/__init__.py +51 -0
- use_computer_cli-0.2.0/src/use_computer/accessibility/atspi.py +293 -0
- use_computer_cli-0.2.0/src/use_computer/accessibility/ax.py +225 -0
- use_computer_cli-0.2.0/src/use_computer/accessibility/base.py +76 -0
- use_computer_cli-0.2.0/src/use_computer/accessibility/roles.py +248 -0
- use_computer_cli-0.2.0/src/use_computer/accessibility/uia.py +210 -0
- use_computer_cli-0.2.0/src/use_computer/actions.py +345 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/cli.py +311 -14
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/config.py +14 -0
- use_computer_cli-0.2.0/src/use_computer/errors.py +124 -0
- use_computer_cli-0.2.0/src/use_computer/render.py +84 -0
- use_computer_cli-0.2.0/src/use_computer/runner.py +623 -0
- use_computer_cli-0.2.0/src/use_computer/selectors.py +304 -0
- use_computer_cli-0.2.0/src/use_computer/skill/SKILL.md +290 -0
- use_computer_cli-0.2.0/src/use_computer/tree.py +287 -0
- use_computer_cli-0.2.0/tests/fake_provider.py +140 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/test_cli.py +130 -0
- use_computer_cli-0.2.0/tests/test_elements.py +450 -0
- use_computer_cli-0.2.0/tests/test_render.py +54 -0
- use_computer_cli-0.2.0/tests/test_selectors.py +153 -0
- use_computer_cli-0.1.1/src/use_computer/actions.py +0 -185
- use_computer_cli-0.1.1/src/use_computer/errors.py +0 -53
- use_computer_cli-0.1.1/src/use_computer/runner.py +0 -298
- use_computer_cli-0.1.1/src/use_computer/skill/SKILL.md +0 -135
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/.gitignore +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/LICENSE +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/backends/__init__.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/backends/base.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/backends/local.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/backends/vnc.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/compare.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/coordinates.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/keys.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/src/use_computer/skill/__init__.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/__init__.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/conftest.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/fake_backend.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/test_backends.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/test_compare.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/test_config.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/test_config_init.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/test_coordinates.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/test_keys.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/tests/test_runner.py +0 -0
- {use_computer_cli-0.1.1 → use_computer_cli-0.2.0}/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.
|
|
3
|
+
Version: 0.2.0
|
|
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,33 @@ 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: pygobject>=3.46; (sys_platform == 'linux') and extra == 'tree'
|
|
55
|
+
Requires-Dist: pyobjc-framework-applicationservices>=10; (sys_platform == 'darwin') and extra == 'tree'
|
|
56
|
+
Requires-Dist: uiautomation>=2.0; (sys_platform == 'win32') and extra == 'tree'
|
|
53
57
|
Provides-Extra: vnc
|
|
54
58
|
Requires-Dist: vncdotool>=1.3; extra == 'vnc'
|
|
55
59
|
Description-Content-Type: text/markdown
|
|
56
60
|
|
|
57
61
|
# use-computer
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
Reads and acts on a screen for computer-use agents. It reads the **accessibility tree** the
|
|
64
|
+
operating system already maintains -- roles, names, states and clickable boxes -- and it moves the
|
|
65
|
+
mouse, clicks, drags, scrolls, types text, presses key combinations, and captures screenshots.
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
Every interaction takes the highest rung it can reach:
|
|
68
|
+
|
|
69
|
+
1. **Element, through the platform API** -- the OS presses the button itself. No coordinates, so
|
|
70
|
+
nothing to aim and no scale to get wrong.
|
|
71
|
+
2. **Element, by coordinate** -- the tree can see the control but exposes no way to operate it, so
|
|
72
|
+
`use-computer` clicks its centre and tells you it did.
|
|
73
|
+
3. **Pixel, from vision** -- the tree cannot see it. Screenshot,
|
|
74
|
+
[ui-locator](https://github.com/applica-software-guru/ui-locator), click those pixels.
|
|
75
|
+
|
|
76
|
+
Each rung is cheaper, faster and more accurate than the one below. Rung three is the floor the
|
|
77
|
+
whole ladder stands on and is not going anywhere; it is simply no longer the only rung. Everything
|
|
78
|
+
is driven by another AI agent through a CLI that emits JSON on stdout and diagnostics on stderr,
|
|
79
|
+
with a Python API underneath.
|
|
66
80
|
|
|
67
81
|
## Install
|
|
68
82
|
|
|
@@ -70,9 +84,16 @@ diagnostics on stderr, with a Python API underneath.
|
|
|
70
84
|
pip install use-computer-cli # no backend
|
|
71
85
|
pip install "use-computer-cli[local]" # drive this machine's display (pynput + mss)
|
|
72
86
|
pip install "use-computer-cli[vnc]" # drive a remote framebuffer over RFB (vncdotool)
|
|
87
|
+
pip install "use-computer-cli[tree]" # read the accessibility tree
|
|
73
88
|
```
|
|
74
89
|
|
|
75
|
-
Backends are optional extras, imported lazily, so the package
|
|
90
|
+
Backends and the accessibility bindings are optional extras, imported lazily, so the package
|
|
91
|
+
installs without them.
|
|
92
|
+
|
|
93
|
+
The `tree` extra installs one binding per platform: `uiautomation` on Windows,
|
|
94
|
+
`pyobjc-framework-ApplicationServices` on macOS, PyGObject on Linux. **Linux needs a system package
|
|
95
|
+
as well** -- there is no `pyatspi` on PyPI, so the AT-SPI bindings come from `gir1.2-atspi-2.0`
|
|
96
|
+
(plus `python3-pyatspi` on Debian and Ubuntu). The error names both halves when one is missing.
|
|
76
97
|
|
|
77
98
|
The distribution is `use-computer-cli` because `use-computer` is taken on PyPI by an unrelated
|
|
78
99
|
project. The command it installs is `use-computer`, and the package it imports is `use_computer`.
|
|
@@ -80,16 +101,28 @@ project. The command it installs is `use-computer`, and the package it imports i
|
|
|
80
101
|
## Use
|
|
81
102
|
|
|
82
103
|
```bash
|
|
83
|
-
use-computer
|
|
104
|
+
use-computer tree --use laptop # what is on screen, structurally
|
|
105
|
+
use-computer click --role button --name "Invia" # act on it by name
|
|
106
|
+
use-computer set-value --role text --name "Email" --value "mario@example.com"
|
|
107
|
+
use-computer click --x 120 --y 340 --use staging # or by pixel, when the tree cannot see it
|
|
84
108
|
use-computer type --text "hello" --use staging
|
|
85
109
|
use-computer key ctrl+s --use staging
|
|
86
|
-
use-computer screenshot --use laptop
|
|
110
|
+
use-computer screenshot --use laptop # writes a file, returns its path
|
|
87
111
|
|
|
88
112
|
# a batch runs over one connection -- the default command, so `batch` may be omitted
|
|
89
|
-
echo '[
|
|
90
|
-
|
|
113
|
+
echo '[
|
|
114
|
+
{"action":"focus","role":"text","name":"Destinatario"},
|
|
115
|
+
{"action":"type","text":"mario@example.com"},
|
|
116
|
+
{"action":"click","role":"button","name":"Invia"},
|
|
117
|
+
{"action":"tree"}
|
|
118
|
+
]' | use-computer - --use laptop
|
|
91
119
|
```
|
|
92
120
|
|
|
121
|
+
An action names its target by coordinate **or** by element, never both. A selector that matches
|
|
122
|
+
nothing comes back with a screenshot -- the signal to switch to vision. A selector that matches
|
|
123
|
+
several comes back with the candidates, because two buttons named "OK" in two dialogs is the
|
|
124
|
+
ordinary case and picking one silently fails a hundred runs later.
|
|
125
|
+
|
|
93
126
|
stdout is one JSON object per run; every diagnostic goes to stderr. Exit codes: `0` success,
|
|
94
127
|
`1` failure, `2` bad usage.
|
|
95
128
|
|
|
@@ -104,7 +137,8 @@ action and reports the path, so a verified action does not need a `screenshot` c
|
|
|
104
137
|
- **Setup cost.** Opening a VNC connection dominates a single action, so one run performs a
|
|
105
138
|
batch of actions over one connection.
|
|
106
139
|
- **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
|
|
140
|
+
`--verify` compares the screen before and after and reports whether it changed -- and an action
|
|
141
|
+
that went through the accessibility API reports the element it actually operated.
|
|
108
142
|
|
|
109
143
|
## Configure
|
|
110
144
|
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
# use-computer
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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.
|
|
10
20
|
|
|
11
21
|
## Install
|
|
12
22
|
|
|
@@ -14,9 +24,16 @@ diagnostics on stderr, with a Python API underneath.
|
|
|
14
24
|
pip install use-computer-cli # no backend
|
|
15
25
|
pip install "use-computer-cli[local]" # drive this machine's display (pynput + mss)
|
|
16
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
|
|
17
28
|
```
|
|
18
29
|
|
|
19
|
-
Backends are optional extras, imported lazily, so the package
|
|
30
|
+
Backends and the accessibility bindings are optional extras, imported lazily, so the package
|
|
31
|
+
installs without them.
|
|
32
|
+
|
|
33
|
+
The `tree` extra installs one binding per platform: `uiautomation` on Windows,
|
|
34
|
+
`pyobjc-framework-ApplicationServices` on macOS, PyGObject on Linux. **Linux needs a system package
|
|
35
|
+
as well** -- there is no `pyatspi` on PyPI, so the AT-SPI bindings come from `gir1.2-atspi-2.0`
|
|
36
|
+
(plus `python3-pyatspi` on Debian and Ubuntu). The error names both halves when one is missing.
|
|
20
37
|
|
|
21
38
|
The distribution is `use-computer-cli` because `use-computer` is taken on PyPI by an unrelated
|
|
22
39
|
project. The command it installs is `use-computer`, and the package it imports is `use_computer`.
|
|
@@ -24,16 +41,28 @@ project. The command it installs is `use-computer`, and the package it imports i
|
|
|
24
41
|
## Use
|
|
25
42
|
|
|
26
43
|
```bash
|
|
27
|
-
use-computer
|
|
44
|
+
use-computer tree --use laptop # what is on screen, structurally
|
|
45
|
+
use-computer click --role button --name "Invia" # act on it by name
|
|
46
|
+
use-computer set-value --role text --name "Email" --value "mario@example.com"
|
|
47
|
+
use-computer click --x 120 --y 340 --use staging # or by pixel, when the tree cannot see it
|
|
28
48
|
use-computer type --text "hello" --use staging
|
|
29
49
|
use-computer key ctrl+s --use staging
|
|
30
|
-
use-computer screenshot --use laptop
|
|
50
|
+
use-computer screenshot --use laptop # writes a file, returns its path
|
|
31
51
|
|
|
32
52
|
# a batch runs over one connection -- the default command, so `batch` may be omitted
|
|
33
|
-
echo '[
|
|
34
|
-
|
|
53
|
+
echo '[
|
|
54
|
+
{"action":"focus","role":"text","name":"Destinatario"},
|
|
55
|
+
{"action":"type","text":"mario@example.com"},
|
|
56
|
+
{"action":"click","role":"button","name":"Invia"},
|
|
57
|
+
{"action":"tree"}
|
|
58
|
+
]' | use-computer - --use laptop
|
|
35
59
|
```
|
|
36
60
|
|
|
61
|
+
An action names its target by coordinate **or** by element, never both. A selector that matches
|
|
62
|
+
nothing comes back with a screenshot -- the signal to switch to vision. A selector that matches
|
|
63
|
+
several comes back with the candidates, because two buttons named "OK" in two dialogs is the
|
|
64
|
+
ordinary case and picking one silently fails a hundred runs later.
|
|
65
|
+
|
|
37
66
|
stdout is one JSON object per run; every diagnostic goes to stderr. Exit codes: `0` success,
|
|
38
67
|
`1` failure, `2` bad usage.
|
|
39
68
|
|
|
@@ -48,7 +77,8 @@ action and reports the path, so a verified action does not need a `screenshot` c
|
|
|
48
77
|
- **Setup cost.** Opening a VNC connection dominates a single action, so one run performs a
|
|
49
78
|
batch of actions over one connection.
|
|
50
79
|
- **Blind actuation.** A click that lands on nothing looks exactly like a click that worked, so
|
|
51
|
-
`--verify` compares the screen before and after and reports whether it changed
|
|
80
|
+
`--verify` compares the screen before and after and reports whether it changed -- and an action
|
|
81
|
+
that went through the accessibility API reports the element it actually operated.
|
|
52
82
|
|
|
53
83
|
## Configure
|
|
54
84
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "use-computer-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
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,19 @@ 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
|
+
tree = [
|
|
50
|
+
"uiautomation>=2.0; sys_platform == 'win32'",
|
|
51
|
+
"pyobjc-framework-ApplicationServices>=10; sys_platform == 'darwin'",
|
|
52
|
+
"PyGObject>=3.46; sys_platform == 'linux'",
|
|
53
|
+
]
|
|
41
54
|
dev = [
|
|
42
55
|
"pytest>=8",
|
|
43
56
|
"pytest-mock>=3.12",
|
|
@@ -78,7 +91,16 @@ strict = true
|
|
|
78
91
|
files = ["src", "tests"]
|
|
79
92
|
|
|
80
93
|
[[tool.mypy.overrides]]
|
|
81
|
-
module = [
|
|
94
|
+
module = [
|
|
95
|
+
"pynput.*",
|
|
96
|
+
"mss.*",
|
|
97
|
+
"vncdotool.*",
|
|
98
|
+
"ApplicationServices.*",
|
|
99
|
+
"Quartz.*",
|
|
100
|
+
"gi.*",
|
|
101
|
+
"uiautomation.*",
|
|
102
|
+
"tomli",
|
|
103
|
+
]
|
|
82
104
|
ignore_missing_imports = true
|
|
83
105
|
|
|
84
106
|
[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"]
|