droidrun 0.1.0__py3-none-any.whl → 0.3.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.
- droidrun/__init__.py +22 -10
- droidrun/__main__.py +1 -2
- droidrun/adb/__init__.py +3 -3
- droidrun/adb/device.py +2 -2
- droidrun/adb/manager.py +2 -2
- droidrun/agent/__init__.py +5 -15
- droidrun/agent/codeact/__init__.py +11 -0
- droidrun/agent/codeact/codeact_agent.py +420 -0
- droidrun/agent/codeact/events.py +28 -0
- droidrun/agent/codeact/prompts.py +26 -0
- droidrun/agent/common/default.py +5 -0
- droidrun/agent/common/events.py +4 -0
- droidrun/agent/context/__init__.py +23 -0
- droidrun/agent/context/agent_persona.py +15 -0
- droidrun/agent/context/context_injection_manager.py +66 -0
- droidrun/agent/context/episodic_memory.py +15 -0
- droidrun/agent/context/personas/__init__.py +11 -0
- droidrun/agent/context/personas/app_starter.py +44 -0
- droidrun/agent/context/personas/default.py +95 -0
- droidrun/agent/context/personas/extractor.py +52 -0
- droidrun/agent/context/personas/ui_expert.py +107 -0
- droidrun/agent/context/reflection.py +20 -0
- droidrun/agent/context/task_manager.py +124 -0
- droidrun/agent/context/todo.txt +4 -0
- droidrun/agent/droid/__init__.py +13 -0
- droidrun/agent/droid/droid_agent.py +357 -0
- droidrun/agent/droid/events.py +28 -0
- droidrun/agent/oneflows/reflector.py +265 -0
- droidrun/agent/planner/__init__.py +13 -0
- droidrun/agent/planner/events.py +16 -0
- droidrun/agent/planner/planner_agent.py +268 -0
- droidrun/agent/planner/prompts.py +124 -0
- droidrun/agent/utils/__init__.py +3 -0
- droidrun/agent/utils/async_utils.py +17 -0
- droidrun/agent/utils/chat_utils.py +312 -0
- droidrun/agent/utils/executer.py +132 -0
- droidrun/agent/utils/llm_picker.py +147 -0
- droidrun/agent/utils/trajectory.py +184 -0
- droidrun/cli/__init__.py +1 -1
- droidrun/cli/logs.py +283 -0
- droidrun/cli/main.py +358 -149
- droidrun/run.py +105 -0
- droidrun/tools/__init__.py +4 -30
- droidrun/tools/adb.py +879 -0
- droidrun/tools/ios.py +594 -0
- droidrun/tools/tools.py +99 -0
- droidrun-0.3.0.dist-info/METADATA +149 -0
- droidrun-0.3.0.dist-info/RECORD +52 -0
- droidrun/agent/llm_reasoning.py +0 -567
- droidrun/agent/react_agent.py +0 -556
- droidrun/llm/__init__.py +0 -24
- droidrun/tools/actions.py +0 -854
- droidrun/tools/device.py +0 -29
- droidrun-0.1.0.dist-info/METADATA +0 -276
- droidrun-0.1.0.dist-info/RECORD +0 -20
- {droidrun-0.1.0.dist-info → droidrun-0.3.0.dist-info}/WHEEL +0 -0
- {droidrun-0.1.0.dist-info → droidrun-0.3.0.dist-info}/entry_points.txt +0 -0
- {droidrun-0.1.0.dist-info → droidrun-0.3.0.dist-info}/licenses/LICENSE +0 -0
droidrun/cli/main.py
CHANGED
@@ -5,138 +5,307 @@ DroidRun CLI - Command line interface for controlling Android devices through LL
|
|
5
5
|
import asyncio
|
6
6
|
import click
|
7
7
|
import os
|
8
|
+
import logging
|
9
|
+
import warnings
|
8
10
|
from rich.console import Console
|
9
|
-
from
|
10
|
-
from droidrun.
|
11
|
-
from droidrun.
|
11
|
+
from droidrun.agent.droid import DroidAgent
|
12
|
+
from droidrun.agent.utils.llm_picker import load_llm
|
13
|
+
from droidrun.adb import DeviceManager
|
14
|
+
from droidrun.tools import AdbTools, IOSTools, Tools
|
12
15
|
from functools import wraps
|
16
|
+
from droidrun.cli.logs import LogHandler
|
13
17
|
|
14
|
-
#
|
15
|
-
|
18
|
+
# Suppress all warnings
|
19
|
+
warnings.filterwarnings("ignore")
|
20
|
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
21
|
+
os.environ["GRPC_ENABLE_FORK_SUPPORT"] = "false"
|
16
22
|
|
17
23
|
console = Console()
|
18
24
|
device_manager = DeviceManager()
|
19
25
|
|
26
|
+
|
27
|
+
def configure_logging(goal: str, debug: bool):
|
28
|
+
logger = logging.getLogger("droidrun")
|
29
|
+
logger.handlers = []
|
30
|
+
|
31
|
+
handler = LogHandler(goal)
|
32
|
+
handler.setFormatter(
|
33
|
+
logging.Formatter("%(levelname)s %(message)s", "%H:%M:%S")
|
34
|
+
if debug
|
35
|
+
else logging.Formatter("%(message)s", "%H:%M:%S")
|
36
|
+
)
|
37
|
+
logger.addHandler(handler)
|
38
|
+
|
39
|
+
|
40
|
+
logger.setLevel(logging.DEBUG if debug else logging.INFO)
|
41
|
+
logger.propagate = False
|
42
|
+
|
43
|
+
return handler
|
44
|
+
|
45
|
+
|
20
46
|
def coro(f):
|
21
47
|
@wraps(f)
|
22
48
|
def wrapper(*args, **kwargs):
|
23
49
|
return asyncio.run(f(*args, **kwargs))
|
50
|
+
|
24
51
|
return wrapper
|
25
52
|
|
26
|
-
|
53
|
+
|
27
54
|
@coro
|
28
|
-
async def run_command(
|
55
|
+
async def run_command(
|
56
|
+
command: str,
|
57
|
+
device: str | None,
|
58
|
+
provider: str,
|
59
|
+
model: str,
|
60
|
+
steps: int,
|
61
|
+
base_url: str,
|
62
|
+
reasoning: bool,
|
63
|
+
reflection: bool,
|
64
|
+
tracing: bool,
|
65
|
+
debug: bool,
|
66
|
+
save_trajectory: bool = False,
|
67
|
+
ios: bool = False,
|
68
|
+
**kwargs,
|
69
|
+
):
|
29
70
|
"""Run a command on your Android device using natural language."""
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
# Print vision status
|
37
|
-
if vision:
|
38
|
-
console.print("[blue]Vision capabilities are enabled.[/]")
|
39
|
-
else:
|
40
|
-
console.print("[blue]Vision capabilities are disabled.[/]")
|
41
|
-
|
42
|
-
# Get API keys from environment variables
|
43
|
-
api_key = None
|
44
|
-
if provider.lower() == 'openai':
|
45
|
-
api_key = os.environ.get('OPENAI_API_KEY')
|
46
|
-
if not api_key:
|
47
|
-
console.print("[bold red]Error:[/] OPENAI_API_KEY environment variable not set")
|
48
|
-
return
|
49
|
-
if not model:
|
50
|
-
model = "gpt-4o-mini"
|
51
|
-
elif provider.lower() == 'anthropic':
|
52
|
-
api_key = os.environ.get('ANTHROPIC_API_KEY')
|
53
|
-
if not api_key:
|
54
|
-
console.print("[bold red]Error:[/] ANTHROPIC_API_KEY environment variable not set")
|
55
|
-
return
|
56
|
-
if not model:
|
57
|
-
model = "claude-3-sonnet-20240229"
|
58
|
-
elif provider.lower() == 'gemini':
|
59
|
-
api_key = os.environ.get('GEMINI_API_KEY')
|
60
|
-
if not api_key:
|
61
|
-
console.print("[bold red]Error:[/] GEMINI_API_KEY environment variable not set")
|
62
|
-
return
|
63
|
-
if not model:
|
64
|
-
model = "gemini-2.0-flash"
|
65
|
-
else:
|
66
|
-
console.print(f"[bold red]Error:[/] Unsupported provider: {provider}")
|
67
|
-
return
|
68
|
-
|
69
|
-
try:
|
70
|
-
# Try to find a device if none specified
|
71
|
-
if not device:
|
72
|
-
devices = await device_manager.list_devices()
|
73
|
-
if not devices:
|
74
|
-
console.print("[yellow]No devices connected.[/]")
|
75
|
-
return
|
76
|
-
|
77
|
-
device = devices[0].serial
|
78
|
-
console.print(f"[blue]Using device:[/] {device}")
|
79
|
-
|
80
|
-
# Set the device serial in the environment variable
|
81
|
-
os.environ["DROIDRUN_DEVICE_SERIAL"] = device
|
82
|
-
console.print(f"[blue]Set DROIDRUN_DEVICE_SERIAL to:[/] {device}")
|
83
|
-
|
84
|
-
# Run the agent
|
85
|
-
console.print("[bold blue]Running ReAct agent...[/]")
|
86
|
-
console.print("[yellow]Press Ctrl+C to stop execution[/]")
|
87
|
-
|
71
|
+
log_handler = configure_logging(command, debug)
|
72
|
+
logger = logging.getLogger("droidrun")
|
73
|
+
|
74
|
+
log_handler.update_step("Initializing...")
|
75
|
+
|
76
|
+
with log_handler.render() as live:
|
88
77
|
try:
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
console.print(" - Gemini: gemini-pro-vision")
|
108
|
-
return
|
78
|
+
logger.info(f"🚀 Starting: {command}")
|
79
|
+
|
80
|
+
if not kwargs.get("temperature"):
|
81
|
+
kwargs["temperature"] = 0
|
82
|
+
|
83
|
+
log_handler.update_step("Setting up tools...")
|
84
|
+
|
85
|
+
# Device setup
|
86
|
+
if device is None and not ios:
|
87
|
+
logger.info("🔍 Finding connected device...")
|
88
|
+
device_manager = DeviceManager()
|
89
|
+
devices = await device_manager.list_devices()
|
90
|
+
if not devices:
|
91
|
+
raise ValueError("No connected devices found.")
|
92
|
+
device = devices[0].serial
|
93
|
+
logger.info(f"📱 Using device: {device}")
|
94
|
+
elif device is None and ios:
|
95
|
+
raise ValueError("iOS device not specified. Please specify the device base url (http://device-ip:6643) via --device")
|
109
96
|
else:
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
97
|
+
logger.info(f"📱 Using device: {device}")
|
98
|
+
|
99
|
+
tools = AdbTools(serial=device) if not ios else IOSTools(url=device)
|
100
|
+
|
101
|
+
# LLM setup
|
102
|
+
log_handler.update_step("Initializing LLM...")
|
103
|
+
llm = load_llm(
|
104
|
+
provider_name=provider, model=model, base_url=base_url, **kwargs
|
105
|
+
)
|
106
|
+
logger.info(f"🧠 LLM ready: {provider}/{model}")
|
107
|
+
|
108
|
+
# Agent setup
|
109
|
+
log_handler.update_step("Initializing DroidAgent...")
|
110
|
+
|
111
|
+
mode = "planning with reasoning" if reasoning else "direct execution"
|
112
|
+
logger.info(f"🤖 Agent mode: {mode}")
|
113
|
+
|
114
|
+
if tracing:
|
115
|
+
logger.info("🔍 Tracing enabled")
|
116
|
+
|
117
|
+
droid_agent = DroidAgent(
|
118
|
+
goal=command,
|
119
|
+
llm=llm,
|
120
|
+
tools=tools,
|
121
|
+
max_steps=steps,
|
122
|
+
timeout=1000,
|
123
|
+
reasoning=reasoning,
|
124
|
+
reflection=reflection,
|
125
|
+
enable_tracing=tracing,
|
126
|
+
debug=debug,
|
127
|
+
save_trajectories=save_trajectory
|
128
|
+
)
|
129
|
+
|
130
|
+
logger.info("▶️ Starting agent execution...")
|
131
|
+
logger.info("Press Ctrl+C to stop")
|
132
|
+
log_handler.update_step("Running agent...")
|
133
|
+
|
134
|
+
try:
|
135
|
+
handler = droid_agent.run()
|
136
|
+
|
137
|
+
async for event in handler.stream_events():
|
138
|
+
log_handler.handle_event(event)
|
139
|
+
result = await handler
|
140
|
+
|
141
|
+
except KeyboardInterrupt:
|
142
|
+
log_handler.is_completed = True
|
143
|
+
log_handler.is_success = False
|
144
|
+
log_handler.current_step = "Stopped by user"
|
145
|
+
logger.info("⏹️ Stopped by user")
|
146
|
+
|
147
|
+
except Exception as e:
|
148
|
+
log_handler.is_completed = True
|
149
|
+
log_handler.is_success = False
|
150
|
+
log_handler.current_step = f"Error: {e}"
|
151
|
+
logger.error(f"💥 Error: {e}")
|
152
|
+
if debug:
|
153
|
+
import traceback
|
154
|
+
|
155
|
+
logger.debug(traceback.format_exc())
|
156
|
+
|
157
|
+
except Exception as e:
|
158
|
+
log_handler.current_step = f"Error: {e}"
|
159
|
+
logger.error(f"💥 Setup error: {e}")
|
160
|
+
if debug:
|
161
|
+
import traceback
|
162
|
+
|
163
|
+
logger.debug(traceback.format_exc())
|
164
|
+
|
114
165
|
|
115
|
-
# Custom Click multi-command class to handle both subcommands and default behavior
|
116
166
|
class DroidRunCLI(click.Group):
|
117
167
|
def parse_args(self, ctx, args):
|
118
|
-
#
|
119
|
-
if args and not args[0].startswith(
|
120
|
-
|
121
|
-
|
168
|
+
# If the first arg is not an option and not a known command, treat as 'run'
|
169
|
+
if args and """not args[0].startswith("-")""" and args[0] not in self.commands:
|
170
|
+
args.insert(0, "run")
|
171
|
+
|
122
172
|
return super().parse_args(ctx, args)
|
123
173
|
|
174
|
+
|
175
|
+
@click.option("--device", "-d", help="Device serial number or IP address", default=None)
|
176
|
+
@click.option(
|
177
|
+
"--provider",
|
178
|
+
"-p",
|
179
|
+
help="LLM provider (OpenAI, Ollama, Anthropic, Gemini, DeepSeek)",
|
180
|
+
default="Gemini",
|
181
|
+
)
|
182
|
+
@click.option(
|
183
|
+
"--model",
|
184
|
+
"-m",
|
185
|
+
help="LLM model name",
|
186
|
+
default="models/gemini-2.5-pro",
|
187
|
+
)
|
188
|
+
@click.option("--temperature", type=float, help="Temperature for LLM", default=0.2)
|
189
|
+
@click.option("--steps", type=int, help="Maximum number of steps", default=15)
|
190
|
+
@click.option(
|
191
|
+
"--base_url",
|
192
|
+
"-u",
|
193
|
+
help="Base URL for API (e.g., OpenRouter or Ollama)",
|
194
|
+
default=None,
|
195
|
+
)
|
196
|
+
@click.option(
|
197
|
+
"--reasoning", is_flag=True, help="Enable/disable planning with reasoning", default=False
|
198
|
+
)
|
199
|
+
@click.option(
|
200
|
+
"--reflection", is_flag=True, help="Enable reflection step for higher reasoning", default=False
|
201
|
+
)
|
202
|
+
@click.option(
|
203
|
+
"--tracing", is_flag=True, help="Enable Arize Phoenix tracing", default=False
|
204
|
+
)
|
205
|
+
@click.option(
|
206
|
+
"--debug", is_flag=True, help="Enable verbose debug logging", default=False
|
207
|
+
)
|
208
|
+
@click.option(
|
209
|
+
"--save-trajectory",
|
210
|
+
is_flag=True,
|
211
|
+
help="Save agent trajectory to file",
|
212
|
+
default=False,
|
213
|
+
)
|
124
214
|
@click.group(cls=DroidRunCLI)
|
125
|
-
def cli(
|
215
|
+
def cli(
|
216
|
+
device: str | None,
|
217
|
+
provider: str,
|
218
|
+
model: str,
|
219
|
+
steps: int,
|
220
|
+
base_url: str,
|
221
|
+
temperature: float,
|
222
|
+
reasoning: bool,
|
223
|
+
reflection: bool,
|
224
|
+
tracing: bool,
|
225
|
+
debug: bool,
|
226
|
+
save_trajectory: bool,
|
227
|
+
):
|
126
228
|
"""DroidRun - Control your Android device through LLM agents."""
|
127
229
|
pass
|
128
230
|
|
231
|
+
|
129
232
|
@cli.command()
|
130
|
-
@click.argument(
|
131
|
-
@click.option(
|
132
|
-
@click.option(
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
233
|
+
@click.argument("command", type=str)
|
234
|
+
@click.option("--device", "-d", help="Device serial number or IP address", default=None)
|
235
|
+
@click.option(
|
236
|
+
"--provider",
|
237
|
+
"-p",
|
238
|
+
help="LLM provider (OpenAI, Ollama, Anthropic, Gemini, DeepSeek)",
|
239
|
+
default="Gemini",
|
240
|
+
)
|
241
|
+
@click.option(
|
242
|
+
"--model",
|
243
|
+
"-m",
|
244
|
+
help="LLM model name",
|
245
|
+
default="models/gemini-2.5-pro",
|
246
|
+
)
|
247
|
+
@click.option("--temperature", type=float, help="Temperature for LLM", default=0.2)
|
248
|
+
@click.option("--steps", type=int, help="Maximum number of steps", default=15)
|
249
|
+
@click.option(
|
250
|
+
"--base_url",
|
251
|
+
"-u",
|
252
|
+
help="Base URL for API (e.g., OpenRouter or Ollama)",
|
253
|
+
default=None,
|
254
|
+
)
|
255
|
+
@click.option(
|
256
|
+
"--reasoning", is_flag=True, help="Enable/disable planning with reasoning", default=False
|
257
|
+
)
|
258
|
+
@click.option(
|
259
|
+
"--reflection", is_flag=True, help="Enable reflection step for higher reasoning", default=False
|
260
|
+
)
|
261
|
+
@click.option(
|
262
|
+
"--tracing", is_flag=True, help="Enable Arize Phoenix tracing", default=False
|
263
|
+
)
|
264
|
+
@click.option(
|
265
|
+
"--debug", is_flag=True, help="Enable verbose debug logging", default=False
|
266
|
+
)
|
267
|
+
@click.option(
|
268
|
+
"--save-trajectory",
|
269
|
+
is_flag=True,
|
270
|
+
help="Save agent trajectory to file",
|
271
|
+
default=False,
|
272
|
+
)
|
273
|
+
@click.option(
|
274
|
+
"--ios", is_flag=True, help="Run on iOS device", default=False
|
275
|
+
)
|
276
|
+
def run(
|
277
|
+
command: str,
|
278
|
+
device: str | None,
|
279
|
+
provider: str,
|
280
|
+
model: str,
|
281
|
+
steps: int,
|
282
|
+
base_url: str,
|
283
|
+
temperature: float,
|
284
|
+
reasoning: bool,
|
285
|
+
reflection: bool,
|
286
|
+
tracing: bool,
|
287
|
+
debug: bool,
|
288
|
+
save_trajectory: bool,
|
289
|
+
ios: bool,
|
290
|
+
):
|
137
291
|
"""Run a command on your Android device using natural language."""
|
138
292
|
# Call our standalone function
|
139
|
-
return run_command(
|
293
|
+
return run_command(
|
294
|
+
command,
|
295
|
+
device,
|
296
|
+
provider,
|
297
|
+
model,
|
298
|
+
steps,
|
299
|
+
base_url,
|
300
|
+
reasoning,
|
301
|
+
reflection,
|
302
|
+
tracing,
|
303
|
+
debug,
|
304
|
+
temperature=temperature,
|
305
|
+
save_trajectory=save_trajectory,
|
306
|
+
ios=ios
|
307
|
+
)
|
308
|
+
|
140
309
|
|
141
310
|
@cli.command()
|
142
311
|
@coro
|
@@ -154,9 +323,10 @@ async def devices():
|
|
154
323
|
except Exception as e:
|
155
324
|
console.print(f"[red]Error listing devices: {e}[/]")
|
156
325
|
|
326
|
+
|
157
327
|
@cli.command()
|
158
|
-
@click.argument(
|
159
|
-
@click.option(
|
328
|
+
@click.argument("ip_address")
|
329
|
+
@click.option("--port", "-p", default=5555, help="ADB port (default: 5555)")
|
160
330
|
@coro
|
161
331
|
async def connect(ip_address: str, port: int):
|
162
332
|
"""Connect to a device over TCP/IP."""
|
@@ -169,8 +339,9 @@ async def connect(ip_address: str, port: int):
|
|
169
339
|
except Exception as e:
|
170
340
|
console.print(f"[red]Error connecting to device: {e}[/]")
|
171
341
|
|
342
|
+
|
172
343
|
@cli.command()
|
173
|
-
@click.argument(
|
344
|
+
@click.argument("serial")
|
174
345
|
@coro
|
175
346
|
async def disconnect(serial: str):
|
176
347
|
"""Disconnect from a device."""
|
@@ -183,83 +354,121 @@ async def disconnect(serial: str):
|
|
183
354
|
except Exception as e:
|
184
355
|
console.print(f"[red]Error disconnecting from device: {e}[/]")
|
185
356
|
|
357
|
+
|
186
358
|
@cli.command()
|
187
|
-
@click.option(
|
188
|
-
@click.option(
|
359
|
+
@click.option("--path", required=True, help="Path to the APK file to install")
|
360
|
+
@click.option("--device", "-d", help="Device serial number or IP address", default=None)
|
189
361
|
@coro
|
190
362
|
async def setup(path: str, device: str | None):
|
191
363
|
"""Install an APK file and enable it as an accessibility service."""
|
192
364
|
try:
|
193
|
-
# Check if APK file exists
|
194
365
|
if not os.path.exists(path):
|
195
366
|
console.print(f"[bold red]Error:[/] APK file not found at {path}")
|
196
367
|
return
|
197
|
-
|
198
|
-
# Try to find a device if none specified
|
368
|
+
|
199
369
|
if not device:
|
200
370
|
devices = await device_manager.list_devices()
|
201
371
|
if not devices:
|
202
372
|
console.print("[yellow]No devices connected.[/]")
|
203
373
|
return
|
204
|
-
|
374
|
+
|
205
375
|
device = devices[0].serial
|
206
376
|
console.print(f"[blue]Using device:[/] {device}")
|
207
|
-
|
208
|
-
# Set the device serial in the environment variable
|
209
|
-
os.environ["DROIDRUN_DEVICE_SERIAL"] = device
|
210
|
-
console.print(f"[blue]Set DROIDRUN_DEVICE_SERIAL to:[/] {device}")
|
211
|
-
|
212
|
-
# Get a device object for ADB commands
|
377
|
+
|
213
378
|
device_obj = await device_manager.get_device(device)
|
214
379
|
if not device_obj:
|
215
|
-
console.print(
|
380
|
+
console.print(
|
381
|
+
f"[bold red]Error:[/] Could not get device object for {device}"
|
382
|
+
)
|
216
383
|
return
|
217
|
-
|
218
|
-
# Step 1: Install the APK file
|
384
|
+
tools = Tools(serial=device)
|
219
385
|
console.print(f"[bold blue]Step 1/2: Installing APK:[/] {path}")
|
220
|
-
result = await install_app(path, False, True
|
221
|
-
|
386
|
+
result = await tools.install_app(path, False, True)
|
387
|
+
|
222
388
|
if "Error" in result:
|
223
389
|
console.print(f"[bold red]Installation failed:[/] {result}")
|
224
390
|
return
|
225
391
|
else:
|
226
392
|
console.print(f"[bold green]Installation successful![/]")
|
227
|
-
|
228
|
-
# Step 2: Enable the accessibility service with the specific command
|
393
|
+
|
229
394
|
console.print(f"[bold blue]Step 2/2: Enabling accessibility service[/]")
|
230
|
-
|
231
|
-
# Package name for reference in error message
|
395
|
+
|
232
396
|
package = "com.droidrun.portal"
|
233
|
-
|
397
|
+
|
234
398
|
try:
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
399
|
+
await device_obj._adb.shell(
|
400
|
+
device,
|
401
|
+
"settings put secure enabled_accessibility_services com.droidrun.portal/com.droidrun.portal.DroidrunPortalService",
|
402
|
+
)
|
403
|
+
|
404
|
+
await device_obj._adb.shell(
|
405
|
+
device, "settings put secure accessibility_enabled 1"
|
406
|
+
)
|
407
|
+
|
241
408
|
console.print("[green]Accessibility service enabled successfully![/]")
|
242
|
-
console.print(
|
243
|
-
|
409
|
+
console.print(
|
410
|
+
"\n[bold green]Setup complete![/] The DroidRun Portal is now installed and ready to use."
|
411
|
+
)
|
412
|
+
|
244
413
|
except Exception as e:
|
245
|
-
console.print(
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
414
|
+
console.print(
|
415
|
+
f"[yellow]Could not automatically enable accessibility service: {e}[/]"
|
416
|
+
)
|
417
|
+
console.print(
|
418
|
+
"[yellow]Opening accessibility settings for manual configuration...[/]"
|
419
|
+
)
|
420
|
+
|
421
|
+
await device_obj._adb.shell(
|
422
|
+
device, "am start -a android.settings.ACCESSIBILITY_SETTINGS"
|
423
|
+
)
|
424
|
+
|
425
|
+
console.print(
|
426
|
+
"\n[yellow]Please complete the following steps on your device:[/]"
|
427
|
+
)
|
428
|
+
console.print(
|
429
|
+
f"1. Find [bold]{package}[/] in the accessibility services list"
|
430
|
+
)
|
253
431
|
console.print("2. Tap on the service name")
|
254
432
|
console.print("3. Toggle the switch to [bold]ON[/] to enable the service")
|
255
433
|
console.print("4. Accept any permission dialogs that appear")
|
256
|
-
|
257
|
-
console.print(
|
258
|
-
|
434
|
+
|
435
|
+
console.print(
|
436
|
+
"\n[bold green]APK installation complete![/] Please manually enable the accessibility service using the steps above."
|
437
|
+
)
|
438
|
+
|
259
439
|
except Exception as e:
|
260
440
|
console.print(f"[bold red]Error:[/] {e}")
|
261
441
|
import traceback
|
442
|
+
|
262
443
|
traceback.print_exc()
|
263
444
|
|
264
|
-
|
265
|
-
|
445
|
+
|
446
|
+
if __name__ == "__main__":
|
447
|
+
command = "Open the settings app"
|
448
|
+
device = None
|
449
|
+
provider = "GoogleGenAI"
|
450
|
+
model = "models/gemini-2.5-flash"
|
451
|
+
temperature = 0
|
452
|
+
api_key = os.getenv("GEMINI_API_KEY")
|
453
|
+
steps = 15
|
454
|
+
reasoning = True
|
455
|
+
reflection = False
|
456
|
+
tracing = True
|
457
|
+
debug = True
|
458
|
+
base_url = None
|
459
|
+
ios = False
|
460
|
+
run_command(
|
461
|
+
command=command,
|
462
|
+
device=device,
|
463
|
+
provider=provider,
|
464
|
+
model=model,
|
465
|
+
steps=steps,
|
466
|
+
temperature=temperature,
|
467
|
+
reasoning=reasoning,
|
468
|
+
reflection=reflection,
|
469
|
+
tracing=tracing,
|
470
|
+
debug=debug,
|
471
|
+
base_url=base_url,
|
472
|
+
api_key=api_key,
|
473
|
+
ios=ios
|
474
|
+
)
|