agentscope-runtime 1.0.4a1__py3-none-any.whl → 1.0.5__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 (79) hide show
  1. agentscope_runtime/adapters/agentscope/stream.py +2 -8
  2. agentscope_runtime/adapters/langgraph/stream.py +120 -70
  3. agentscope_runtime/adapters/ms_agent_framework/__init__.py +0 -0
  4. agentscope_runtime/adapters/ms_agent_framework/message.py +205 -0
  5. agentscope_runtime/adapters/ms_agent_framework/stream.py +418 -0
  6. agentscope_runtime/adapters/utils.py +6 -0
  7. agentscope_runtime/cli/commands/deploy.py +836 -1
  8. agentscope_runtime/cli/commands/stop.py +16 -0
  9. agentscope_runtime/common/container_clients/__init__.py +52 -0
  10. agentscope_runtime/common/container_clients/agentrun_client.py +6 -4
  11. agentscope_runtime/common/container_clients/boxlite_client.py +442 -0
  12. agentscope_runtime/common/container_clients/docker_client.py +0 -20
  13. agentscope_runtime/common/container_clients/fc_client.py +6 -4
  14. agentscope_runtime/common/container_clients/gvisor_client.py +38 -0
  15. agentscope_runtime/common/container_clients/knative_client.py +467 -0
  16. agentscope_runtime/common/utils/deprecation.py +164 -0
  17. agentscope_runtime/engine/__init__.py +4 -0
  18. agentscope_runtime/engine/app/agent_app.py +16 -4
  19. agentscope_runtime/engine/constant.py +1 -0
  20. agentscope_runtime/engine/deployers/__init__.py +34 -11
  21. agentscope_runtime/engine/deployers/adapter/__init__.py +8 -0
  22. agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +26 -51
  23. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +23 -13
  24. agentscope_runtime/engine/deployers/adapter/a2a/a2a_registry.py +4 -201
  25. agentscope_runtime/engine/deployers/adapter/a2a/nacos_a2a_registry.py +152 -25
  26. agentscope_runtime/engine/deployers/adapter/agui/__init__.py +8 -0
  27. agentscope_runtime/engine/deployers/adapter/agui/agui_adapter_utils.py +652 -0
  28. agentscope_runtime/engine/deployers/adapter/agui/agui_protocol_adapter.py +225 -0
  29. agentscope_runtime/engine/deployers/agentrun_deployer.py +2 -2
  30. agentscope_runtime/engine/deployers/fc_deployer.py +1506 -0
  31. agentscope_runtime/engine/deployers/knative_deployer.py +290 -0
  32. agentscope_runtime/engine/deployers/pai_deployer.py +2335 -0
  33. agentscope_runtime/engine/deployers/utils/net_utils.py +37 -0
  34. agentscope_runtime/engine/deployers/utils/oss_utils.py +38 -0
  35. agentscope_runtime/engine/deployers/utils/package.py +46 -42
  36. agentscope_runtime/engine/helpers/agent_api_client.py +372 -0
  37. agentscope_runtime/engine/runner.py +13 -0
  38. agentscope_runtime/engine/schemas/agent_schemas.py +9 -3
  39. agentscope_runtime/engine/services/agent_state/__init__.py +7 -0
  40. agentscope_runtime/engine/services/memory/__init__.py +7 -0
  41. agentscope_runtime/engine/services/memory/redis_memory_service.py +15 -16
  42. agentscope_runtime/engine/services/session_history/__init__.py +7 -0
  43. agentscope_runtime/engine/tracing/local_logging_handler.py +2 -3
  44. agentscope_runtime/engine/tracing/wrapper.py +18 -4
  45. agentscope_runtime/sandbox/__init__.py +14 -6
  46. agentscope_runtime/sandbox/box/base/__init__.py +2 -2
  47. agentscope_runtime/sandbox/box/base/base_sandbox.py +51 -1
  48. agentscope_runtime/sandbox/box/browser/__init__.py +2 -2
  49. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +198 -2
  50. agentscope_runtime/sandbox/box/filesystem/__init__.py +2 -2
  51. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +99 -2
  52. agentscope_runtime/sandbox/box/gui/__init__.py +2 -2
  53. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +117 -1
  54. agentscope_runtime/sandbox/box/mobile/__init__.py +2 -2
  55. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +247 -100
  56. agentscope_runtime/sandbox/box/sandbox.py +102 -65
  57. agentscope_runtime/sandbox/box/shared/routers/generic.py +36 -29
  58. agentscope_runtime/sandbox/client/__init__.py +6 -1
  59. agentscope_runtime/sandbox/client/async_http_client.py +339 -0
  60. agentscope_runtime/sandbox/client/base.py +74 -0
  61. agentscope_runtime/sandbox/client/http_client.py +108 -329
  62. agentscope_runtime/sandbox/enums.py +7 -0
  63. agentscope_runtime/sandbox/manager/sandbox_manager.py +275 -29
  64. agentscope_runtime/sandbox/manager/server/app.py +7 -1
  65. agentscope_runtime/sandbox/manager/server/config.py +3 -1
  66. agentscope_runtime/sandbox/model/manager_config.py +11 -9
  67. agentscope_runtime/tools/modelstudio_memory/__init__.py +106 -0
  68. agentscope_runtime/tools/modelstudio_memory/base.py +220 -0
  69. agentscope_runtime/tools/modelstudio_memory/config.py +86 -0
  70. agentscope_runtime/tools/modelstudio_memory/core.py +594 -0
  71. agentscope_runtime/tools/modelstudio_memory/exceptions.py +60 -0
  72. agentscope_runtime/tools/modelstudio_memory/schemas.py +253 -0
  73. agentscope_runtime/version.py +1 -1
  74. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/METADATA +186 -73
  75. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/RECORD +79 -55
  76. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/WHEEL +0 -0
  77. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/entry_points.txt +0 -0
  78. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/licenses/LICENSE +0 -0
  79. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ from urllib.parse import urljoin, urlencode
8
8
  from ...utils import build_image_uri, get_platform
9
9
  from ...registry import SandboxRegistry
10
10
  from ...enums import SandboxType
11
- from ...box.base import BaseSandbox
11
+ from ...box.base import BaseSandbox, BaseSandboxAsync
12
12
  from ...constant import TIMEOUT
13
13
 
14
14
  logger = logging.getLogger(__name__)
@@ -34,6 +34,34 @@ class GUIMixin:
34
34
  )
35
35
 
36
36
 
37
+ class AsyncGUIMixin:
38
+ async def get_desktop_url_async(self):
39
+ # Check sandbox health asynchronously
40
+ is_healthy = await self.manager_api.check_health_async(
41
+ identity=self.sandbox_id,
42
+ )
43
+ if not is_healthy:
44
+ raise RuntimeError(f"Sandbox {self.sandbox_id} is not healthy")
45
+
46
+ # Retrieve container information asynchronously
47
+ info = await self.get_info_async()
48
+
49
+ # Default local VNC path and remote VNC relay path
50
+ path = "/vnc/vnc_lite.html"
51
+ remote_path = "/vnc/vnc_relay.html"
52
+ params = {"password": info["runtime_token"]}
53
+
54
+ # If base_url is not set, construct the local URL
55
+ if self.base_url is None:
56
+ return urljoin(info["url"], path) + "?" + urlencode(params)
57
+
58
+ # Construct the remote URL with sandbox ID and VNC relay path
59
+ return (
60
+ f"{self.base_url}/desktop/{self.sandbox_id}{remote_path}"
61
+ f"?{urlencode(params)}"
62
+ )
63
+
64
+
37
65
  @SandboxRegistry.register(
38
66
  build_image_uri("runtime-sandbox-gui"),
39
67
  sandbox_type=SandboxType.GUI,
@@ -121,3 +149,91 @@ class GuiSandbox(GUIMixin, BaseSandbox):
121
149
  payload["text"] = text
122
150
 
123
151
  return self.call_tool("computer", payload)
152
+
153
+
154
+ @SandboxRegistry.register(
155
+ build_image_uri("runtime-sandbox-gui"),
156
+ sandbox_type=SandboxType.GUI_ASYNC,
157
+ security_level="high",
158
+ timeout=TIMEOUT,
159
+ description="GUI Sandbox (Async)",
160
+ )
161
+ class GuiSandboxAsync(GUIMixin, AsyncGUIMixin, BaseSandboxAsync):
162
+ def __init__( # pylint: disable=useless-parent-delegation
163
+ self,
164
+ sandbox_id: Optional[str] = None,
165
+ timeout: int = 3000,
166
+ base_url: Optional[str] = None,
167
+ bearer_token: Optional[str] = None,
168
+ sandbox_type: SandboxType = SandboxType.GUI_ASYNC,
169
+ ):
170
+ super().__init__(
171
+ sandbox_id,
172
+ timeout,
173
+ base_url,
174
+ bearer_token,
175
+ sandbox_type,
176
+ )
177
+ # Architecture compatibility warning
178
+ if get_platform() == "linux/arm64":
179
+ logger.warning(
180
+ "\nCompatibility Notice: This GUI Sandbox may have issues on "
181
+ "arm64 CPU architectures, due to the computer-use-mcp not "
182
+ "providing linux/arm64 compatibility. It has been tested "
183
+ "on Apple M4 chips with Rosetta enabled. However, on M1, M2, "
184
+ "and M3 chips, Chromium browser might crash due to the missing "
185
+ "SSE3 instruction set.",
186
+ )
187
+
188
+ async def computer_use(
189
+ self,
190
+ action: str,
191
+ coordinate: Optional[Union[List[float], Tuple[float, float]]] = None,
192
+ text: Optional[str] = None,
193
+ ):
194
+ """
195
+ Asynchronously use mouse and keyboard to interact with a desktop GUI.
196
+
197
+ This method interfaces with the sandbox's GUI environment.
198
+ You do not have access to a terminal or applications menu;
199
+ interaction is performed by clicking on desktop icons or using
200
+ keyboard shortcuts.
201
+
202
+ Guidelines:
203
+ * Prefer keyboard shortcuts where possible over cursor actions.
204
+ * If visual keyboard hints (two-letter boxes) are shown, typing
205
+ those letters will click the element — use this where possible.
206
+ * Applications or actions may require waiting; e.g., repeat
207
+ screenshots if windows don’t open immediately.
208
+ * Always determine cursor coordinates using screenshots before moving
209
+ the cursor to click on elements.
210
+ * If clicks fail to load content, try adjusting cursor coordinates to
211
+ center on the target element.
212
+ * Click with the cursor tip centered on elements, not on edges.
213
+
214
+ Args:
215
+ action (str): The action to perform. Options include:
216
+ * `key` — Press a key or key combination.
217
+ * `type` — Type a string of text.
218
+ * `get_cursor_position` — Get current cursor coordinates (x, y).
219
+ * `mouse_move` — Move cursor to given (x, y).
220
+ * `left_click` — Left mouse click.
221
+ * `left_click_drag` — Click and drag to given (x, y).
222
+ * `right_click` — Right mouse click.
223
+ * `middle_click` — Middle mouse click.
224
+ * `double_click` — Double left mouse click.
225
+ * `get_screenshot` — Capture screen screenshot.
226
+ coordinate (list[float] | tuple[float, float], optional):
227
+ Pixel coordinates (x from left edge, y from top edge).
228
+ text (str, optional): String to type, or key-combination for `key` action.
229
+
230
+ Returns:
231
+ Any: Tool execution result from the sandbox.
232
+ """
233
+ payload = {"action": action}
234
+ if coordinate is not None:
235
+ payload["coordinate"] = coordinate
236
+ if text is not None:
237
+ payload["text"] = text
238
+
239
+ return await self.call_tool_async("computer", payload)
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
- from .mobile_sandbox import MobileSandbox
2
+ from .mobile_sandbox import MobileSandbox, MobileSandboxAsync
3
3
 
4
- __all__ = ["MobileSandbox"]
4
+ __all__ = ["MobileSandbox", "MobileSandboxAsync"]
@@ -5,7 +5,7 @@ import platform
5
5
  from typing import Optional, List, Union
6
6
  from urllib.parse import urlencode, urljoin
7
7
 
8
- from agentscope_runtime.sandbox.box.sandbox import Sandbox
8
+ from ..sandbox import Sandbox, SandboxAsync
9
9
 
10
10
  from ...utils import build_image_uri
11
11
  from ...registry import SandboxRegistry
@@ -15,6 +15,107 @@ from ...constant import TIMEOUT
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
17
 
18
+ class HostPrerequisiteError(Exception):
19
+ """Exception raised when host prerequisites
20
+ for MobileSandbox are not met."""
21
+
22
+
23
+ def _check_host_readiness() -> None:
24
+ logger.info(
25
+ "Performing one-time host environment check for MobileSandbox...",
26
+ )
27
+
28
+ architecture = platform.machine().lower()
29
+ if architecture in ("aarch64", "arm64"):
30
+ logger.warning(
31
+ "\n======================== WARNING ========================\n"
32
+ "ARM64/aarch64 architecture detected (e.g., Apple M-series).\n"
33
+ "Running this mobile sandbox on a non-x86_64 host may lead \n"
34
+ " to unexpected compatibility or performance issues.\n"
35
+ "=========================================================",
36
+ )
37
+
38
+ os_type = platform.system()
39
+ if os_type == "Linux":
40
+ try:
41
+ result = subprocess.run(
42
+ ["lsmod"],
43
+ capture_output=True,
44
+ text=True,
45
+ check=True,
46
+ )
47
+ loaded_modules = result.stdout
48
+ except (FileNotFoundError, subprocess.CalledProcessError):
49
+ loaded_modules = ""
50
+ logger.warning(
51
+ "Could not execute 'lsmod' to verify kernel modules.",
52
+ )
53
+
54
+ if "binder_linux" not in loaded_modules:
55
+ error_message = (
56
+ "\n========== HOST PREREQUISITE FAILED ==========\n"
57
+ "MobileSandbox requires specific kernel modules"
58
+ " that appear to be missing or not loaded.\n\n"
59
+ "To fix this, please run the following commands"
60
+ " on your Linux host:\n\n"
61
+ "## Install required kernel modules\n"
62
+ "sudo apt update"
63
+ " && sudo apt install -y linux-modules-extra-`uname -r`\n"
64
+ "sudo modprobe binder_linux"
65
+ ' devices="binder,hwbinder,vndbinder"\n'
66
+ "## (Optional) Load the ashmem driver for older kernels\n"
67
+ "sudo modprobe ashmem_linux\n"
68
+ "=================================================="
69
+ )
70
+ raise HostPrerequisiteError(error_message)
71
+
72
+ if os_type == "Windows":
73
+ try:
74
+ result = subprocess.run(
75
+ ["wsl", "lsmod"],
76
+ capture_output=True,
77
+ text=True,
78
+ check=True,
79
+ encoding="utf-8",
80
+ )
81
+ loaded_modules = result.stdout
82
+ except (FileNotFoundError, subprocess.CalledProcessError):
83
+ loaded_modules = ""
84
+ logger.warning(
85
+ "Could not execute 'wsl lsmod' to verify kernel modules.",
86
+ )
87
+
88
+ if "binder_linux" not in loaded_modules:
89
+ error_message = (
90
+ "\n========== HOST PREREQUISITE FAILED ==========\n"
91
+ "MobileSandbox on Windows requires Docker Desktop "
92
+ "with the WSL 2 backend.\n"
93
+ "The required kernel modules seem to be missing "
94
+ "in your WSL 2 environment.\n\n"
95
+ "To fix this, please follow these steps:\n\n"
96
+ "1. **Ensure Docker Desktop is using WSL 2**:\n"
97
+ " - Open Docker Desktop -> Settings -> General.\n"
98
+ " - Make sure 'Use the WSL 2 based engine' "
99
+ "is checked.\n\n"
100
+ "2. **Ensure WSL is installed and updated**:\n"
101
+ " - Open PowerShell or Command Prompt "
102
+ "as Administrator.\n"
103
+ " - Run: wsl --install\n"
104
+ " - Run: wsl --update\n"
105
+ " (An update usually installs a recent Linux kernel "
106
+ "with the required modules.)\n\n"
107
+ "3. **Verify manually (Optional)**:\n"
108
+ " - After updating, run 'wsl lsmod | findstr binder' "
109
+ "in your terminal.\n"
110
+ " - If it shows 'binder_linux', "
111
+ "the issue should be resolved.\n"
112
+ "=================================================="
113
+ )
114
+ raise HostPrerequisiteError(error_message)
115
+
116
+ logger.info("Host environment check passed.")
117
+
118
+
18
119
  class MobileMixin:
19
120
  @property
20
121
  def mobile_url(self):
@@ -41,9 +142,41 @@ class MobileMixin:
41
142
  )
42
143
 
43
144
 
44
- class HostPrerequisiteError(Exception):
45
- """Exception raised when host prerequisites
46
- for MobileSandbox are not met."""
145
+ class AsyncMobileMixin:
146
+ async def get_mobile_url_async(self):
147
+ """
148
+ Asynchronously retrieve the mobile VNC/websockify connection URL.
149
+
150
+ Returns:
151
+ str: Fully qualified URL to access the mobile sandbox UI remotely.
152
+
153
+ Raises:
154
+ RuntimeError: If the sandbox is not healthy.
155
+ """
156
+ # Check health asynchronously
157
+ is_healthy = await self.manager_api.check_health_async(
158
+ identity=self.sandbox_id,
159
+ )
160
+ if not is_healthy:
161
+ raise RuntimeError(f"Sandbox {self.sandbox_id} is not healthy")
162
+
163
+ # Get container info asynchronously
164
+ info = await self.get_info_async()
165
+
166
+ # Local path and remote path (currently the same)
167
+ path = "/websockify/"
168
+ remote_path = "/websockify/"
169
+ params = {"password": info["runtime_token"]}
170
+
171
+ # Local URL if base_url is not set
172
+ if self.base_url is None:
173
+ return urljoin(info["url"], path) + "?" + urlencode(params)
174
+
175
+ # Remote URL
176
+ return (
177
+ f"{self.base_url}/desktop/{self.sandbox_id}{remote_path}"
178
+ f"?{urlencode(params)}"
179
+ )
47
180
 
48
181
 
49
182
  @SandboxRegistry.register(
@@ -66,7 +199,7 @@ class MobileSandbox(MobileMixin, Sandbox):
66
199
  sandbox_type: SandboxType = SandboxType.MOBILE,
67
200
  ):
68
201
  if base_url is None and not self.__class__._host_check_done:
69
- self._check_host_readiness()
202
+ _check_host_readiness()
70
203
  self.__class__._host_check_done = True
71
204
 
72
205
  super().__init__(
@@ -77,101 +210,6 @@ class MobileSandbox(MobileMixin, Sandbox):
77
210
  sandbox_type,
78
211
  )
79
212
 
80
- def _check_host_readiness(self) -> None:
81
- logger.info(
82
- "Performing one-time host environment check for MobileSandbox...",
83
- )
84
-
85
- architecture = platform.machine().lower()
86
- if architecture in ("aarch64", "arm64"):
87
- logger.warning(
88
- "\n======================== WARNING ========================\n"
89
- "ARM64/aarch64 architecture detected (e.g., Apple M-series).\n"
90
- "Running this mobile sandbox on a non-x86_64 host may lead \n"
91
- " to unexpected compatibility or performance issues.\n"
92
- "=========================================================",
93
- )
94
-
95
- os_type = platform.system()
96
- if os_type == "Linux":
97
- try:
98
- result = subprocess.run(
99
- ["lsmod"],
100
- capture_output=True,
101
- text=True,
102
- check=True,
103
- )
104
- loaded_modules = result.stdout
105
- except (FileNotFoundError, subprocess.CalledProcessError):
106
- loaded_modules = ""
107
- logger.warning(
108
- "Could not execute 'lsmod' to verify kernel modules.",
109
- )
110
-
111
- if "binder_linux" not in loaded_modules:
112
- error_message = (
113
- "\n========== HOST PREREQUISITE FAILED ==========\n"
114
- "MobileSandbox requires specific kernel modules"
115
- " that appear to be missing or not loaded.\n\n"
116
- "To fix this, please run the following commands"
117
- " on your Linux host:\n\n"
118
- "## Install required kernel modules\n"
119
- "sudo apt update"
120
- " && sudo apt install -y linux-modules-extra-`uname -r`\n"
121
- "sudo modprobe binder_linux"
122
- ' devices="binder,hwbinder,vndbinder"\n'
123
- "## (Optional) Load the ashmem driver for older kernels\n"
124
- "sudo modprobe ashmem_linux\n"
125
- "=================================================="
126
- )
127
- raise HostPrerequisiteError(error_message)
128
-
129
- if os_type == "Windows":
130
- try:
131
- result = subprocess.run(
132
- ["wsl", "lsmod"],
133
- capture_output=True,
134
- text=True,
135
- check=True,
136
- encoding="utf-8",
137
- )
138
- loaded_modules = result.stdout
139
- except (FileNotFoundError, subprocess.CalledProcessError):
140
- loaded_modules = ""
141
- logger.warning(
142
- "Could not execute 'wsl lsmod' to verify kernel modules.",
143
- )
144
-
145
- if "binder_linux" not in loaded_modules:
146
- error_message = (
147
- "\n========== HOST PREREQUISITE FAILED ==========\n"
148
- "MobileSandbox on Windows requires Docker Desktop "
149
- "with the WSL 2 backend.\n"
150
- "The required kernel modules seem to be missing "
151
- "in your WSL 2 environment.\n\n"
152
- "To fix this, please follow these steps:\n\n"
153
- "1. **Ensure Docker Desktop is using WSL 2**:\n"
154
- " - Open Docker Desktop -> Settings -> General.\n"
155
- " - Make sure 'Use the WSL 2 based engine' "
156
- "is checked.\n\n"
157
- "2. **Ensure WSL is installed and updated**:\n"
158
- " - Open PowerShell or Command Prompt "
159
- "as Administrator.\n"
160
- " - Run: wsl --install\n"
161
- " - Run: wsl --update\n"
162
- " (An update usually installs a recent Linux kernel "
163
- "with the required modules.)\n\n"
164
- "3. **Verify manually (Optional)**:\n"
165
- " - After updating, run 'wsl lsmod | findstr binder' "
166
- "in your terminal.\n"
167
- " - If it shows 'binder_linux', "
168
- "the issue should be resolved.\n"
169
- "=================================================="
170
- )
171
- raise HostPrerequisiteError(error_message)
172
-
173
- logger.info("Host environment check passed.")
174
-
175
213
  def adb_use(
176
214
  self,
177
215
  action: str,
@@ -288,3 +326,112 @@ class MobileSandbox(MobileMixin, Sandbox):
288
326
  def mobile_get_screenshot(self):
289
327
  """Take a screenshot of the current device screen."""
290
328
  return self.call_tool("adb", {"action": "get_screenshot"})
329
+
330
+
331
+ @SandboxRegistry.register(
332
+ build_image_uri("runtime-sandbox-mobile"),
333
+ sandbox_type=SandboxType.MOBILE_ASYNC,
334
+ security_level="high",
335
+ timeout=TIMEOUT,
336
+ description="Mobile Sandbox (Async)",
337
+ runtime_config={"privileged": True},
338
+ )
339
+ class MobileSandboxAsync(MobileMixin, AsyncMobileMixin, SandboxAsync):
340
+ _host_check_done = False
341
+
342
+ def __init__(
343
+ self,
344
+ sandbox_id: Optional[str] = None,
345
+ timeout: int = 3000,
346
+ base_url: Optional[str] = None,
347
+ bearer_token: Optional[str] = None,
348
+ sandbox_type: SandboxType = SandboxType.MOBILE_ASYNC,
349
+ ):
350
+ if base_url is None and not self.__class__._host_check_done:
351
+ _check_host_readiness()
352
+ self.__class__._host_check_done = True
353
+
354
+ super().__init__(
355
+ sandbox_id,
356
+ timeout,
357
+ base_url,
358
+ bearer_token,
359
+ sandbox_type,
360
+ )
361
+
362
+ async def adb_use(
363
+ self,
364
+ action: str,
365
+ coordinate: Optional[List[int]] = None,
366
+ start: Optional[List[int]] = None,
367
+ end: Optional[List[int]] = None,
368
+ duration: Optional[int] = None,
369
+ code: Optional[Union[int, str]] = None,
370
+ text: Optional[str] = None,
371
+ ):
372
+ """
373
+ Asynchronously execute a general-purpose ADB action.
374
+ """
375
+ payload = {"action": action}
376
+ if coordinate is not None:
377
+ payload["coordinate"] = coordinate
378
+ if start is not None:
379
+ payload["start"] = start
380
+ if end is not None:
381
+ payload["end"] = end
382
+ if duration is not None:
383
+ payload["duration"] = duration
384
+ if code is not None:
385
+ payload["code"] = code
386
+ if text is not None:
387
+ payload["text"] = text
388
+
389
+ return await self.call_tool_async("adb", payload)
390
+
391
+ async def mobile_get_screen_resolution(self):
392
+ """Asynchronously get the screen resolution."""
393
+ return await self.call_tool_async(
394
+ "adb",
395
+ {"action": "get_screen_resolution"},
396
+ )
397
+
398
+ async def mobile_tap(self, coordinate: List[int]):
399
+ """Asynchronously tap specific screen coordinates."""
400
+ return await self.call_tool_async(
401
+ "adb",
402
+ {"action": "tap", "coordinate": coordinate},
403
+ )
404
+
405
+ async def mobile_swipe(
406
+ self,
407
+ start: List[int],
408
+ end: List[int],
409
+ duration: Optional[int] = None,
410
+ ):
411
+ """Asynchronously perform a swipe gesture."""
412
+ payload = {
413
+ "action": "swipe",
414
+ "start": start,
415
+ "end": end,
416
+ }
417
+ if duration is not None:
418
+ payload["duration"] = duration
419
+ return await self.call_tool_async("adb", payload)
420
+
421
+ async def mobile_input_text(self, text: str):
422
+ """Asynchronously input text into the focused UI element."""
423
+ return await self.call_tool_async(
424
+ "adb",
425
+ {"action": "input_text", "text": text},
426
+ )
427
+
428
+ async def mobile_key_event(self, code: Union[int, str]):
429
+ """Asynchronously send a key event to the device."""
430
+ return await self.call_tool_async(
431
+ "adb",
432
+ {"action": "key_event", "code": code},
433
+ )
434
+
435
+ async def mobile_get_screenshot(self):
436
+ """Asynchronously take a screenshot."""
437
+ return await self.call_tool_async("adb", {"action": "get_screenshot"})