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.
- computeruseprotocol-0.1.0.dist-info/METADATA +225 -0
- computeruseprotocol-0.1.0.dist-info/RECORD +27 -0
- computeruseprotocol-0.1.0.dist-info/WHEEL +4 -0
- computeruseprotocol-0.1.0.dist-info/entry_points.txt +3 -0
- computeruseprotocol-0.1.0.dist-info/licenses/LICENSE +21 -0
- cup/__init__.py +548 -0
- cup/__main__.py +222 -0
- cup/_base.py +123 -0
- cup/_router.py +63 -0
- cup/actions/__init__.py +9 -0
- cup/actions/_handler.py +62 -0
- cup/actions/_keys.py +56 -0
- cup/actions/_linux.py +1008 -0
- cup/actions/_macos.py +1090 -0
- cup/actions/_web.py +555 -0
- cup/actions/_windows.py +984 -0
- cup/actions/executor.py +162 -0
- cup/format.py +653 -0
- cup/mcp/__init__.py +1 -0
- cup/mcp/__main__.py +11 -0
- cup/mcp/server.py +418 -0
- cup/platforms/__init__.py +0 -0
- cup/platforms/linux.py +1060 -0
- cup/platforms/macos.py +1005 -0
- cup/platforms/web.py +1009 -0
- cup/platforms/windows.py +935 -0
- cup/search.py +583 -0
cup/actions/executor.py
ADDED
|
@@ -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))
|