computeruseprotocol 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,162 @@
1
+ """Action executor — dispatches CUP actions to platform-specific handlers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ if TYPE_CHECKING:
9
+ from cup._base import PlatformAdapter
10
+ from cup.actions._handler import ActionHandler
11
+
12
+
13
+ VALID_ACTIONS = frozenset(
14
+ {
15
+ "click",
16
+ "collapse",
17
+ "decrement",
18
+ "dismiss",
19
+ "doubleclick",
20
+ "expand",
21
+ "focus",
22
+ "increment",
23
+ "longpress",
24
+ "press",
25
+ "rightclick",
26
+ "scroll",
27
+ "select",
28
+ "setvalue",
29
+ "toggle",
30
+ "type",
31
+ }
32
+ )
33
+
34
+
35
+ @dataclass
36
+ class ActionResult:
37
+ """Result of an action execution."""
38
+
39
+ success: bool
40
+ message: str
41
+ error: str | None = None
42
+
43
+
44
+ def _get_action_handler(platform_name: str) -> ActionHandler:
45
+ """Lazily import and instantiate the action handler for the given platform."""
46
+ if platform_name == "windows":
47
+ from cup.actions._windows import WindowsActionHandler
48
+
49
+ return WindowsActionHandler()
50
+ elif platform_name == "macos":
51
+ from cup.actions._macos import MacosActionHandler
52
+
53
+ return MacosActionHandler()
54
+ elif platform_name == "linux":
55
+ from cup.actions._linux import LinuxActionHandler
56
+
57
+ return LinuxActionHandler()
58
+ elif platform_name == "web":
59
+ from cup.actions._web import WebActionHandler
60
+
61
+ return WebActionHandler()
62
+ else:
63
+ raise RuntimeError(
64
+ f"No action handler for platform '{platform_name}'. "
65
+ f"Supported: windows, macos, linux, web"
66
+ )
67
+
68
+
69
+ class ActionExecutor:
70
+ """Cross-platform action executor using element references from tree capture.
71
+
72
+ Usage::
73
+
74
+ executor = ActionExecutor(adapter)
75
+ tree, stats, refs = adapter.capture_tree(windows)
76
+ executor.set_refs(refs)
77
+ result = executor.action("e14", "click")
78
+ """
79
+
80
+ def __init__(self, adapter: PlatformAdapter) -> None:
81
+ self._adapter = adapter
82
+ self._refs: dict[str, Any] = {}
83
+ self._handler: ActionHandler = _get_action_handler(adapter.platform_name)
84
+
85
+ def set_refs(self, refs: dict[str, Any]) -> None:
86
+ """Replace element references with a fresh set from capture_tree()."""
87
+ self._refs = refs
88
+
89
+ def action(
90
+ self,
91
+ element_id: str,
92
+ action: str,
93
+ params: dict[str, Any] | None = None,
94
+ ) -> ActionResult:
95
+ """Execute a CUP action on an element by its ID.
96
+
97
+ Args:
98
+ element_id: Element ID from the tree (e.g., "e14").
99
+ action: CUP canonical action name.
100
+ params: Optional parameters (value, direction, etc.).
101
+ """
102
+ if action not in VALID_ACTIONS:
103
+ return ActionResult(
104
+ success=False,
105
+ message="",
106
+ error=f"Unknown action '{action}'. Valid: {sorted(VALID_ACTIONS)}",
107
+ )
108
+
109
+ # press does not require an element reference
110
+ if action == "press":
111
+ keys = (params or {}).get("keys", "")
112
+ if not keys:
113
+ return ActionResult(
114
+ success=False,
115
+ message="",
116
+ error="Action 'press' requires a 'keys' parameter",
117
+ )
118
+ return self.press(keys)
119
+
120
+ if element_id not in self._refs:
121
+ return ActionResult(
122
+ success=False,
123
+ message="",
124
+ error=f"Element '{element_id}' not found in current tree snapshot",
125
+ )
126
+
127
+ # Validate required parameters
128
+ if action in ("type", "setvalue") and "value" not in (params or {}):
129
+ return ActionResult(
130
+ success=False,
131
+ message="",
132
+ error=f"Action '{action}' requires a 'value' parameter",
133
+ )
134
+ if action == "scroll":
135
+ direction = (params or {}).get("direction")
136
+ if direction not in ("up", "down", "left", "right"):
137
+ return ActionResult(
138
+ success=False,
139
+ message="",
140
+ error=f"Action 'scroll' requires 'direction' "
141
+ f"(up/down/left/right), got: {direction!r}",
142
+ )
143
+
144
+ native_ref = self._refs[element_id]
145
+ try:
146
+ return self._handler.action(native_ref, action, params or {})
147
+ except Exception as exc:
148
+ return ActionResult(success=False, message="", error=str(exc))
149
+
150
+ def press(self, combo: str) -> ActionResult:
151
+ """Send a keyboard shortcut (e.g., 'ctrl+s', 'enter')."""
152
+ try:
153
+ return self._handler.press(combo)
154
+ except Exception as exc:
155
+ return ActionResult(success=False, message="", error=str(exc))
156
+
157
+ def open_app(self, name: str) -> ActionResult:
158
+ """Open an application by name with fuzzy matching."""
159
+ try:
160
+ return self._handler.open_app(name)
161
+ except Exception as exc:
162
+ return ActionResult(success=False, message="", error=str(exc))