use-computer-cli 0.3.1__tar.gz → 0.3.2__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.3.1 → use_computer_cli-0.3.2}/PKG-INFO +1 -1
  2. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/pyproject.toml +1 -1
  3. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/runner.py +1 -1
  4. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/selectors.py +11 -6
  5. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_blind_spots.py +15 -0
  6. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/.gitignore +0 -0
  7. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/LICENSE +0 -0
  8. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/README.md +0 -0
  9. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/__init__.py +0 -0
  10. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/accessibility/__init__.py +0 -0
  11. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/accessibility/atspi.py +0 -0
  12. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/accessibility/ax.py +0 -0
  13. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/accessibility/base.py +0 -0
  14. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/accessibility/roles.py +0 -0
  15. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/accessibility/uia.py +0 -0
  16. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/actions.py +0 -0
  17. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/backends/__init__.py +0 -0
  18. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/backends/base.py +0 -0
  19. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/backends/local.py +0 -0
  20. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/backends/vnc.py +0 -0
  21. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/cli.py +0 -0
  22. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/compare.py +0 -0
  23. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/config.py +0 -0
  24. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/coordinates.py +0 -0
  25. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/errors.py +0 -0
  26. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/keys.py +0 -0
  27. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/prune.py +0 -0
  28. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/render.py +0 -0
  29. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/skill/SKILL.md +0 -0
  30. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/skill/__init__.py +0 -0
  31. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/src/use_computer/tree.py +0 -0
  32. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/__init__.py +0 -0
  33. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/conftest.py +0 -0
  34. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/fake_backend.py +0 -0
  35. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/fake_provider.py +0 -0
  36. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_atspi_hint.py +0 -0
  37. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_backends.py +0 -0
  38. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_cli.py +0 -0
  39. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_compare.py +0 -0
  40. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_config.py +0 -0
  41. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_config_init.py +0 -0
  42. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_coordinates.py +0 -0
  43. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_elements.py +0 -0
  44. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_keys.py +0 -0
  45. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_prune.py +0 -0
  46. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_render.py +0 -0
  47. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_runner.py +0 -0
  48. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_selectors.py +0 -0
  49. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_skill.py +0 -0
  50. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_skill_is_true.py +0 -0
  51. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_trusting_a_result.py +0 -0
  52. {use_computer_cli-0.3.1 → use_computer_cli-0.3.2}/tests/test_which_window.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: use-computer-cli
3
- Version: 0.3.1
3
+ Version: 0.3.2
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "use-computer-cli"
7
- version = "0.3.1"
7
+ version = "0.3.2"
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"
@@ -681,7 +681,7 @@ class Session:
681
681
 
682
682
  # On the raw snapshot, before anything of ours removes a child: a region hidden by our
683
683
  # own pruning is not a region the platform failed to describe.
684
- root = mark_unexposed(root)
684
+ root = mark_unexposed(root, depth)
685
685
  exposed_children = bool(root.children)
686
686
 
687
687
  if action.of is not None:
@@ -182,7 +182,7 @@ def active_window(entries: Sequence[WindowInfo]) -> WindowInfo:
182
182
  )
183
183
 
184
184
 
185
- def mark_unexposed(node: UINode) -> UINode:
185
+ def mark_unexposed(node: UINode, depth_limit: int | None = None) -> UINode:
186
186
  """Say where a node's children do not account for the node's own area.
187
187
 
188
188
  A tree can be rich, correct, and silent about the only region that matters. Measured in GNOME
@@ -195,13 +195,15 @@ def mark_unexposed(node: UINode) -> UINode:
195
195
  served worst, because an agent could not tell it from a window that exposes everything.
196
196
 
197
197
  This runs on the **raw** snapshot, before pruning: a region hidden by our own pruning is not a
198
- region the platform failed to describe.
198
+ region the platform failed to describe. For the same reason ``depth_limit`` is honoured -- at
199
+ the depth the snapshot stopped at, every node looks childless, and marking there would report
200
+ the caller's own limit as a property of the application.
199
201
  """
200
- marked, _ = _mark(node)
202
+ marked, _ = _mark(node, depth_limit)
201
203
  return marked
202
204
 
203
205
 
204
- def _mark(node: UINode) -> tuple[UINode, bool]:
206
+ def _mark(node: UINode, depth_limit: int | None, depth: int = 0) -> tuple[UINode, bool]:
205
207
  """Mark this subtree, and say whether anything in it carries a mark.
206
208
 
207
209
  Only the **innermost** node is marked. A canvas nested three panels deep would otherwise be
@@ -211,11 +213,14 @@ def _mark(node: UINode) -> tuple[UINode, bool]:
211
213
  children = []
212
214
  deeper = False
213
215
  for child in node.children:
214
- marked, found = _mark(child)
216
+ marked, found = _mark(child, depth_limit, depth + 1)
215
217
  children.append(marked)
216
218
  deeper = deeper or found
217
219
  kids = tuple(children)
218
- region = None if deeper else _blind_spot(node, kids)
220
+ # A node whose children were cut by the caller's own `--depth` has no evidence either way:
221
+ # saying the platform describes nothing there would be BUG-011 in a second place.
222
+ truncated_here = depth_limit is not None and depth >= depth_limit - 1
223
+ region = None if deeper or truncated_here else _blind_spot(node, kids)
219
224
  return (
220
225
  node.model_copy(update={"children": kids, "unexposed": region}),
221
226
  deeper or region is not None,
@@ -7,6 +7,8 @@ because the provider worked and returned plenty.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ from collections.abc import Iterator
11
+
10
12
  from tests.fake_provider import node
11
13
  from use_computer.render import TREE_LEGEND
12
14
  from use_computer.render import tree as render_tree
@@ -155,3 +157,16 @@ def test_the_marker_is_rendered_and_the_legend_explains_it() -> None:
155
157
  text = render_tree(mark_unexposed(drawing_window()))
156
158
  assert "?unexposed" in TREE_LEGEND
157
159
  assert "?unexposed 163,106 1757x885" in text
160
+
161
+
162
+ def test_a_depth_limit_does_not_manufacture_a_blind_spot() -> None:
163
+ # At the depth the snapshot stopped at every node looks childless. Marking there would report
164
+ # the caller's own limit as a property of the application -- the same mistake as BUG-011.
165
+ marked = mark_unexposed(drawing_window(), depth_limit=2)
166
+ assert all(found.unexposed is None for found in _every(marked))
167
+
168
+
169
+ def _every(node: UINode) -> Iterator[UINode]:
170
+ yield node
171
+ for child in node.children:
172
+ yield from _every(child)