droidrun 0.3.2__py3-none-any.whl → 0.3.3__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.
- droidrun/__init__.py +6 -2
- droidrun/agent/codeact/codeact_agent.py +7 -6
- droidrun/agent/common/events.py +44 -1
- droidrun/agent/context/personas/__init__.py +2 -0
- droidrun/agent/context/personas/big_agent.py +96 -0
- droidrun/agent/context/personas/ui_expert.py +1 -0
- droidrun/agent/droid/droid_agent.py +15 -6
- droidrun/agent/planner/planner_agent.py +2 -2
- droidrun/agent/utils/executer.py +10 -2
- droidrun/agent/utils/trajectory.py +258 -11
- droidrun/cli/main.py +73 -36
- droidrun/macro/__init__.py +14 -0
- droidrun/macro/__main__.py +10 -0
- droidrun/macro/cli.py +228 -0
- droidrun/macro/replay.py +309 -0
- droidrun/portal.py +16 -17
- droidrun/telemetry/tracker.py +3 -2
- droidrun/tools/adb.py +668 -200
- droidrun/tools/ios.py +163 -163
- droidrun/tools/tools.py +32 -14
- {droidrun-0.3.2.dist-info → droidrun-0.3.3.dist-info}/METADATA +21 -8
- {droidrun-0.3.2.dist-info → droidrun-0.3.3.dist-info}/RECORD +25 -24
- droidrun/adb/__init__.py +0 -13
- droidrun/adb/device.py +0 -345
- droidrun/adb/manager.py +0 -93
- droidrun/adb/wrapper.py +0 -226
- {droidrun-0.3.2.dist-info → droidrun-0.3.3.dist-info}/WHEEL +0 -0
- {droidrun-0.3.2.dist-info → droidrun-0.3.3.dist-info}/entry_points.txt +0 -0
- {droidrun-0.3.2.dist-info → droidrun-0.3.3.dist-info}/licenses/LICENSE +0 -0
droidrun/adb/wrapper.py
DELETED
@@ -1,226 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
ADB Wrapper - Lightweight wrapper around ADB for Android device control.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import asyncio
|
6
|
-
import os
|
7
|
-
import shlex
|
8
|
-
from typing import Dict, List, Optional, Tuple
|
9
|
-
|
10
|
-
class ADBWrapper:
|
11
|
-
"""Lightweight wrapper around ADB for Android device control."""
|
12
|
-
|
13
|
-
def __init__(self, adb_path: Optional[str] = None):
|
14
|
-
"""Initialize ADB wrapper.
|
15
|
-
|
16
|
-
Args:
|
17
|
-
adb_path: Path to ADB binary (defaults to 'adb' in PATH)
|
18
|
-
"""
|
19
|
-
self.adb_path = adb_path or "adb"
|
20
|
-
self._devices_cache: List[Dict[str, str]] = []
|
21
|
-
|
22
|
-
async def _run_command(
|
23
|
-
self,
|
24
|
-
args: List[str],
|
25
|
-
timeout: Optional[float] = None,
|
26
|
-
check: bool = True
|
27
|
-
) -> Tuple[str, str]:
|
28
|
-
"""Run an ADB command.
|
29
|
-
|
30
|
-
Args:
|
31
|
-
args: Command arguments
|
32
|
-
timeout: Command timeout in seconds
|
33
|
-
check: Whether to check return code
|
34
|
-
|
35
|
-
Returns:
|
36
|
-
Tuple of (stdout, stderr)
|
37
|
-
"""
|
38
|
-
cmd = [self.adb_path, *args]
|
39
|
-
|
40
|
-
try:
|
41
|
-
process = await asyncio.create_subprocess_exec(
|
42
|
-
*cmd,
|
43
|
-
stdout=asyncio.subprocess.PIPE,
|
44
|
-
stderr=asyncio.subprocess.PIPE,
|
45
|
-
)
|
46
|
-
|
47
|
-
if timeout is not None:
|
48
|
-
stdout_bytes, stderr_bytes = await asyncio.wait_for(
|
49
|
-
process.communicate(), timeout
|
50
|
-
)
|
51
|
-
else:
|
52
|
-
stdout_bytes, stderr_bytes = await process.communicate()
|
53
|
-
|
54
|
-
stdout = stdout_bytes.decode("utf-8", errors="replace").strip()
|
55
|
-
stderr = stderr_bytes.decode("utf-8", errors="replace").strip()
|
56
|
-
|
57
|
-
if check and process.returncode != 0:
|
58
|
-
raise RuntimeError(f"ADB command failed: {stderr or stdout}")
|
59
|
-
|
60
|
-
return stdout, stderr
|
61
|
-
|
62
|
-
except asyncio.TimeoutError:
|
63
|
-
raise TimeoutError(f"ADB command timed out: {' '.join(cmd)}")
|
64
|
-
except FileNotFoundError:
|
65
|
-
raise FileNotFoundError(f"ADB not found at {self.adb_path}")
|
66
|
-
|
67
|
-
async def _run_device_command(
|
68
|
-
self,
|
69
|
-
serial: str,
|
70
|
-
args: List[str],
|
71
|
-
timeout: Optional[float] = None,
|
72
|
-
check: bool = True
|
73
|
-
) -> Tuple[str, str]:
|
74
|
-
"""Run an ADB command for a specific device."""
|
75
|
-
return await self._run_command(["-s", serial, *args], timeout, check)
|
76
|
-
|
77
|
-
async def get_devices(self) -> List[Dict[str, str]]:
|
78
|
-
"""Get list of connected devices.
|
79
|
-
|
80
|
-
Returns:
|
81
|
-
List of device info dictionaries with 'serial' and 'status' keys
|
82
|
-
"""
|
83
|
-
stdout, _ = await self._run_command(["devices", "-l"])
|
84
|
-
|
85
|
-
devices = []
|
86
|
-
for line in stdout.splitlines()[1:]: # Skip first line (header)
|
87
|
-
if not line.strip():
|
88
|
-
continue
|
89
|
-
|
90
|
-
parts = line.split()
|
91
|
-
if not parts:
|
92
|
-
continue
|
93
|
-
|
94
|
-
serial = parts[0]
|
95
|
-
status = parts[1] if len(parts) > 1 else "unknown"
|
96
|
-
|
97
|
-
devices.append({
|
98
|
-
"serial": serial,
|
99
|
-
"status": status
|
100
|
-
})
|
101
|
-
|
102
|
-
self._devices_cache = devices
|
103
|
-
return devices
|
104
|
-
|
105
|
-
async def connect(self, host: str, port: int = 5555) -> str:
|
106
|
-
"""Connect to a device over TCP/IP.
|
107
|
-
|
108
|
-
Args:
|
109
|
-
host: Device IP address
|
110
|
-
port: Device port
|
111
|
-
|
112
|
-
Returns:
|
113
|
-
Device serial number (host:port)
|
114
|
-
"""
|
115
|
-
serial = f"{host}:{port}"
|
116
|
-
|
117
|
-
# Check if already connected
|
118
|
-
devices = await self.get_devices()
|
119
|
-
if any(d["serial"] == serial for d in devices):
|
120
|
-
return serial
|
121
|
-
|
122
|
-
stdout, _ = await self._run_command(["connect", serial], timeout=10.0)
|
123
|
-
|
124
|
-
if "connected" not in stdout.lower():
|
125
|
-
raise RuntimeError(f"Failed to connect: {stdout}")
|
126
|
-
|
127
|
-
return serial
|
128
|
-
|
129
|
-
async def disconnect(self, serial: str) -> bool:
|
130
|
-
"""Disconnect from a device.
|
131
|
-
|
132
|
-
Args:
|
133
|
-
serial: Device serial number
|
134
|
-
|
135
|
-
Returns:
|
136
|
-
True if disconnected successfully
|
137
|
-
"""
|
138
|
-
try:
|
139
|
-
stdout, _ = await self._run_command(["disconnect", serial])
|
140
|
-
return "disconnected" in stdout.lower()
|
141
|
-
except Exception:
|
142
|
-
return False
|
143
|
-
|
144
|
-
async def shell(self, serial: str, command: str, timeout: Optional[float] = None) -> str:
|
145
|
-
"""Run a shell command on the device.
|
146
|
-
|
147
|
-
Args:
|
148
|
-
serial: Device serial number
|
149
|
-
command: Shell command to run
|
150
|
-
timeout: Command timeout in seconds
|
151
|
-
|
152
|
-
Returns:
|
153
|
-
Command output
|
154
|
-
"""
|
155
|
-
stdout, _ = await self._run_device_command(serial, ["shell", command], timeout=timeout)
|
156
|
-
return stdout
|
157
|
-
|
158
|
-
async def get_properties(self, serial: str) -> Dict[str, str]:
|
159
|
-
"""Get device properties.
|
160
|
-
|
161
|
-
Args:
|
162
|
-
serial: Device serial number
|
163
|
-
|
164
|
-
Returns:
|
165
|
-
Dictionary of device properties
|
166
|
-
"""
|
167
|
-
output = await self.shell(serial, "getprop")
|
168
|
-
|
169
|
-
properties = {}
|
170
|
-
for line in output.splitlines():
|
171
|
-
if not line or "[" not in line or "]" not in line:
|
172
|
-
continue
|
173
|
-
|
174
|
-
try:
|
175
|
-
key = line.split("[")[1].split("]")[0]
|
176
|
-
value = line.split("[")[2].split("]")[0]
|
177
|
-
properties[key] = value
|
178
|
-
except IndexError:
|
179
|
-
continue
|
180
|
-
|
181
|
-
return properties
|
182
|
-
|
183
|
-
async def install_app(
|
184
|
-
self,
|
185
|
-
serial: str,
|
186
|
-
apk_path: str,
|
187
|
-
reinstall: bool = False,
|
188
|
-
grant_permissions: bool = True
|
189
|
-
) -> Tuple[str, str]:
|
190
|
-
"""Install an APK on the device.
|
191
|
-
|
192
|
-
Args:
|
193
|
-
serial: Device serial number
|
194
|
-
apk_path: Path to the APK file
|
195
|
-
reinstall: Whether to reinstall if app exists
|
196
|
-
grant_permissions: Whether to grant all permissions
|
197
|
-
|
198
|
-
Returns:
|
199
|
-
Tuple of (stdout, stderr)
|
200
|
-
"""
|
201
|
-
args = ["install"]
|
202
|
-
if reinstall:
|
203
|
-
args.append("-r")
|
204
|
-
if grant_permissions:
|
205
|
-
args.append("-g")
|
206
|
-
args.append(apk_path)
|
207
|
-
|
208
|
-
return await self._run_device_command(serial, args, timeout=120.0)
|
209
|
-
|
210
|
-
async def pull_file(self, serial: str, device_path: str, local_path: str) -> Tuple[str, str]:
|
211
|
-
"""Pull a file from the device.
|
212
|
-
|
213
|
-
Args:
|
214
|
-
serial: Device serial number
|
215
|
-
device_path: Path on the device
|
216
|
-
local_path: Path on the local machine
|
217
|
-
|
218
|
-
Returns:
|
219
|
-
Tuple of (stdout, stderr)
|
220
|
-
"""
|
221
|
-
# Create directory if it doesn't exist
|
222
|
-
local_dir = os.path.dirname(local_path)
|
223
|
-
if local_dir and not os.path.exists(local_dir):
|
224
|
-
os.makedirs(local_dir)
|
225
|
-
|
226
|
-
return await self._run_device_command(serial, ["pull", device_path, local_path], timeout=60.0)
|
File without changes
|
File without changes
|
File without changes
|