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.
Files changed (58) hide show
  1. droidrun/__init__.py +22 -10
  2. droidrun/__main__.py +1 -2
  3. droidrun/adb/__init__.py +3 -3
  4. droidrun/adb/device.py +2 -2
  5. droidrun/adb/manager.py +2 -2
  6. droidrun/agent/__init__.py +5 -15
  7. droidrun/agent/codeact/__init__.py +11 -0
  8. droidrun/agent/codeact/codeact_agent.py +420 -0
  9. droidrun/agent/codeact/events.py +28 -0
  10. droidrun/agent/codeact/prompts.py +26 -0
  11. droidrun/agent/common/default.py +5 -0
  12. droidrun/agent/common/events.py +4 -0
  13. droidrun/agent/context/__init__.py +23 -0
  14. droidrun/agent/context/agent_persona.py +15 -0
  15. droidrun/agent/context/context_injection_manager.py +66 -0
  16. droidrun/agent/context/episodic_memory.py +15 -0
  17. droidrun/agent/context/personas/__init__.py +11 -0
  18. droidrun/agent/context/personas/app_starter.py +44 -0
  19. droidrun/agent/context/personas/default.py +95 -0
  20. droidrun/agent/context/personas/extractor.py +52 -0
  21. droidrun/agent/context/personas/ui_expert.py +107 -0
  22. droidrun/agent/context/reflection.py +20 -0
  23. droidrun/agent/context/task_manager.py +124 -0
  24. droidrun/agent/context/todo.txt +4 -0
  25. droidrun/agent/droid/__init__.py +13 -0
  26. droidrun/agent/droid/droid_agent.py +357 -0
  27. droidrun/agent/droid/events.py +28 -0
  28. droidrun/agent/oneflows/reflector.py +265 -0
  29. droidrun/agent/planner/__init__.py +13 -0
  30. droidrun/agent/planner/events.py +16 -0
  31. droidrun/agent/planner/planner_agent.py +268 -0
  32. droidrun/agent/planner/prompts.py +124 -0
  33. droidrun/agent/utils/__init__.py +3 -0
  34. droidrun/agent/utils/async_utils.py +17 -0
  35. droidrun/agent/utils/chat_utils.py +312 -0
  36. droidrun/agent/utils/executer.py +132 -0
  37. droidrun/agent/utils/llm_picker.py +147 -0
  38. droidrun/agent/utils/trajectory.py +184 -0
  39. droidrun/cli/__init__.py +1 -1
  40. droidrun/cli/logs.py +283 -0
  41. droidrun/cli/main.py +358 -149
  42. droidrun/run.py +105 -0
  43. droidrun/tools/__init__.py +4 -30
  44. droidrun/tools/adb.py +879 -0
  45. droidrun/tools/ios.py +594 -0
  46. droidrun/tools/tools.py +99 -0
  47. droidrun-0.3.0.dist-info/METADATA +149 -0
  48. droidrun-0.3.0.dist-info/RECORD +52 -0
  49. droidrun/agent/llm_reasoning.py +0 -567
  50. droidrun/agent/react_agent.py +0 -556
  51. droidrun/llm/__init__.py +0 -24
  52. droidrun/tools/actions.py +0 -854
  53. droidrun/tools/device.py +0 -29
  54. droidrun-0.1.0.dist-info/METADATA +0 -276
  55. droidrun-0.1.0.dist-info/RECORD +0 -20
  56. {droidrun-0.1.0.dist-info → droidrun-0.3.0.dist-info}/WHEEL +0 -0
  57. {droidrun-0.1.0.dist-info → droidrun-0.3.0.dist-info}/entry_points.txt +0 -0
  58. {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 rich import print as rprint
10
- from droidrun.tools import DeviceManager
11
- from droidrun.agent import run_agent
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
- # Import the install_app function directly for the setup command
15
- from droidrun.tools.actions import install_app
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
- # Define the run command as a standalone function to be used as both a command and default
53
+
27
54
  @coro
28
- async def run_command(command: str, device: str | None, provider: str, model: str, steps: int, vision: bool):
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
- console.print(f"[bold blue]Executing command:[/] {command}")
31
-
32
- # Auto-detect Gemini if model starts with "gemini-"
33
- if model and model.startswith("gemini-"):
34
- provider = "gemini"
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
- steps = await run_agent(
90
- task=command,
91
- device_serial=device, # Still pass for backward compatibility
92
- llm_provider=provider,
93
- model_name=model,
94
- api_key=api_key,
95
- vision=vision
96
- )
97
-
98
- # Final message
99
- console.print(f"[bold green]Execution completed with {len(steps)} steps[/]")
100
- except ValueError as e:
101
- if "does not support vision" in str(e):
102
- console.print(f"[bold red]Vision Error:[/] {e}")
103
- console.print("[yellow]Please specify a vision-capable model with the --model flag.[/]")
104
- console.print("[blue]Recommended models:[/]")
105
- console.print(" - OpenAI: gpt-4o or gpt-4-vision")
106
- console.print(" - Anthropic: claude-3-opus-20240229 or claude-3-sonnet-20240229")
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
- raise # Re-raise other ValueError exceptions
111
-
112
- except Exception as e:
113
- console.print(f"[bold red]Error:[/] {e}")
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
- # Check if the first argument might be a task rather than a command
119
- if args and not args[0].startswith('-') and args[0] not in self.commands:
120
- # Insert the 'run' command before the first argument if it's not a known command
121
- args.insert(0, 'run')
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('command', type=str)
131
- @click.option('--device', '-d', help='Device serial number or IP address', default=None)
132
- @click.option('--provider', '-p', help='LLM provider (openai, anthropic, or gemini)', default='openai')
133
- @click.option('--model', '-m', help='LLM model name', default=None)
134
- @click.option('--steps', type=int, help='Maximum number of steps', default=15)
135
- @click.option('--vision', is_flag=True, help='Enable vision capabilities')
136
- def run(command: str, device: str | None, provider: str, model: str, steps: int, vision: bool):
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(command, device, provider, model, steps, vision)
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('ip_address')
159
- @click.option('--port', '-p', default=5555, help='ADB port (default: 5555)')
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('serial')
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('--path', required=True, help='Path to the APK file to install')
188
- @click.option('--device', '-d', help='Device serial number or IP address', default=None)
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(f"[bold red]Error:[/] Could not get device object for {device}")
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, device)
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
- # Use the exact command provided
236
- await device_obj._adb.shell(device, "settings put secure enabled_accessibility_services com.droidrun.portal/com.droidrun.portal.DroidrunPortalService")
237
-
238
- # Also enable accessibility services globally
239
- await device_obj._adb.shell(device, "settings put secure accessibility_enabled 1")
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("\n[bold green]Setup complete![/] The DroidRun Portal is now installed and ready to use.")
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(f"[yellow]Could not automatically enable accessibility service: {e}[/]")
246
- console.print("[yellow]Opening accessibility settings for manual configuration...[/]")
247
-
248
- # Fallback: Open the accessibility settings page
249
- await device_obj._adb.shell(device, "am start -a android.settings.ACCESSIBILITY_SETTINGS")
250
-
251
- console.print("\n[yellow]Please complete the following steps on your device:[/]")
252
- console.print(f"1. Find [bold]{package}[/] in the accessibility services list")
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("\n[bold green]APK installation complete![/] Please manually enable the accessibility service using the steps above.")
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
- if __name__ == '__main__':
265
- cli()
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
+ )