agentlink-sdk 1.0.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.
- agentlink_sdk/__init__.py +87 -0
- agentlink_sdk/browser.py +617 -0
- agentlink_sdk/client.py +672 -0
- agentlink_sdk/config.py +164 -0
- agentlink_sdk/desktop.py +559 -0
- agentlink_sdk/exceptions.py +86 -0
- agentlink_sdk/logger.py +157 -0
- agentlink_sdk/models/__init__.py +29 -0
- agentlink_sdk/models/results.py +297 -0
- agentlink_sdk/models/sandbox.py +121 -0
- agentlink_sdk/transport/__init__.py +9 -0
- agentlink_sdk/transport/mcp.py +503 -0
- agentlink_sdk-1.0.0.dist-info/METADATA +106 -0
- agentlink_sdk-1.0.0.dist-info/RECORD +15 -0
- agentlink_sdk-1.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AgentLink Python SDK
|
|
3
|
+
|
|
4
|
+
Official Python SDK for China Mobile Cloud AgentLink MCP Gateway.
|
|
5
|
+
Provides sandbox lifecycle management, browser automation, desktop control,
|
|
6
|
+
and tool invocation capabilities.
|
|
7
|
+
|
|
8
|
+
Usage::
|
|
9
|
+
|
|
10
|
+
from agentlink_sdk import AgentLink
|
|
11
|
+
|
|
12
|
+
al = AgentLink(api_key="your-api-key")
|
|
13
|
+
|
|
14
|
+
# Browser automation
|
|
15
|
+
al.browser.go("https://www.baidu.com")
|
|
16
|
+
shot = al.browser.capture()
|
|
17
|
+
shot.save("page.png")
|
|
18
|
+
|
|
19
|
+
# Windows desktop control
|
|
20
|
+
al.desktop.shell("Get-Date")
|
|
21
|
+
al.desktop.capture().save("desktop.png")
|
|
22
|
+
|
|
23
|
+
# Clean up
|
|
24
|
+
al.cleanup()
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
__version__ = "1.0.0"
|
|
28
|
+
|
|
29
|
+
from .client import AgentLink
|
|
30
|
+
from .browser import BrowserController
|
|
31
|
+
from .desktop import DesktopController
|
|
32
|
+
from .config import AgentLinkConfig
|
|
33
|
+
from .models.sandbox import SandboxSession, SandboxType, IMAGE_BROWSER, IMAGE_WINDOWS
|
|
34
|
+
from .models.results import (
|
|
35
|
+
ActionResult,
|
|
36
|
+
Screenshot,
|
|
37
|
+
SandboxResult,
|
|
38
|
+
ToolResult,
|
|
39
|
+
)
|
|
40
|
+
from .exceptions import (
|
|
41
|
+
AgentLinkError,
|
|
42
|
+
AgentLinkTimeoutError,
|
|
43
|
+
ConfigurationError,
|
|
44
|
+
SandboxError,
|
|
45
|
+
ToolCallError,
|
|
46
|
+
SessionError,
|
|
47
|
+
TransportError,
|
|
48
|
+
TimeoutError,
|
|
49
|
+
BrowserError,
|
|
50
|
+
CommandError,
|
|
51
|
+
FileSystemError,
|
|
52
|
+
)
|
|
53
|
+
from .logger import set_log_level
|
|
54
|
+
|
|
55
|
+
__all__ = [
|
|
56
|
+
"__version__",
|
|
57
|
+
# Client & controllers
|
|
58
|
+
"AgentLink",
|
|
59
|
+
"BrowserController",
|
|
60
|
+
"DesktopController",
|
|
61
|
+
# Config
|
|
62
|
+
"AgentLinkConfig",
|
|
63
|
+
# Models
|
|
64
|
+
"SandboxSession",
|
|
65
|
+
"SandboxType",
|
|
66
|
+
"IMAGE_BROWSER",
|
|
67
|
+
"IMAGE_WINDOWS",
|
|
68
|
+
# Results
|
|
69
|
+
"ActionResult",
|
|
70
|
+
"Screenshot",
|
|
71
|
+
"SandboxResult",
|
|
72
|
+
"ToolResult",
|
|
73
|
+
# Exceptions
|
|
74
|
+
"AgentLinkError",
|
|
75
|
+
"ConfigurationError",
|
|
76
|
+
"SandboxError",
|
|
77
|
+
"ToolCallError",
|
|
78
|
+
"SessionError",
|
|
79
|
+
"TransportError",
|
|
80
|
+
"AgentLinkTimeoutError",
|
|
81
|
+
"TimeoutError",
|
|
82
|
+
"BrowserError",
|
|
83
|
+
"CommandError",
|
|
84
|
+
"FileSystemError",
|
|
85
|
+
# Utilities
|
|
86
|
+
"set_log_level",
|
|
87
|
+
]
|
agentlink_sdk/browser.py
ADDED
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Browser automation controller.
|
|
3
|
+
|
|
4
|
+
Wraps the browser sandbox MCP tools into a fluent Python API for
|
|
5
|
+
web navigation, element interaction, screenshots, and JS evaluation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from .models.results import ActionResult, Screenshot
|
|
13
|
+
from .models.sandbox import IMAGE_BROWSER
|
|
14
|
+
|
|
15
|
+
# Avoid circular import at runtime; type-checkers resolve via TYPE_CHECKING.
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from .client import AgentLink
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BrowserController:
|
|
22
|
+
"""
|
|
23
|
+
Browser automation controller.
|
|
24
|
+
|
|
25
|
+
Wraps the browser sandbox MCP tools into a fluent Python API for
|
|
26
|
+
web navigation, element interaction, screenshots, and JS evaluation.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, client: AgentLink, image_id: str = IMAGE_BROWSER):
|
|
30
|
+
self._client = client
|
|
31
|
+
self._image_id = image_id
|
|
32
|
+
|
|
33
|
+
# ---- Navigation ----
|
|
34
|
+
|
|
35
|
+
def go(self, url: str, *, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
36
|
+
"""
|
|
37
|
+
Navigate to *url* in the browser.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
url: Target URL.
|
|
41
|
+
sandbox_id: Explicit sandbox ID.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
ActionResult: Navigation outcome.
|
|
45
|
+
"""
|
|
46
|
+
args: Dict[str, Any] = {"url": url}
|
|
47
|
+
if sandbox_id:
|
|
48
|
+
args["sandboxId"] = sandbox_id
|
|
49
|
+
return self._client._invoke(self._image_id, "browser_navigate", args)
|
|
50
|
+
|
|
51
|
+
def back(self, *, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
52
|
+
"""Go back to the previous page in browser history.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
ActionResult: Navigation outcome.
|
|
56
|
+
"""
|
|
57
|
+
args: Dict[str, Any] = {}
|
|
58
|
+
if sandbox_id:
|
|
59
|
+
args["sandboxId"] = sandbox_id
|
|
60
|
+
return self._client._invoke(self._image_id, "browser_navigate_back", args)
|
|
61
|
+
|
|
62
|
+
# ---- Snapshot & Screenshot ----
|
|
63
|
+
|
|
64
|
+
def a11y_tree(
|
|
65
|
+
self,
|
|
66
|
+
*,
|
|
67
|
+
filename: Optional[str] = None,
|
|
68
|
+
depth: Optional[int] = None,
|
|
69
|
+
sandbox_id: Optional[str] = None,
|
|
70
|
+
) -> ActionResult:
|
|
71
|
+
"""
|
|
72
|
+
Capture an accessibility tree of the current page.
|
|
73
|
+
|
|
74
|
+
Each interactive element is tagged with a numeric ``ref`` that
|
|
75
|
+
can be passed to :meth:`click` / :meth:`fill`.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
filename: Save snapshot to markdown file instead of returning inline.
|
|
79
|
+
depth: Limit the depth of the snapshot tree.
|
|
80
|
+
sandbox_id: Explicit sandbox ID.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
ActionResult: Accessibility text in ``.text``.
|
|
84
|
+
"""
|
|
85
|
+
args: Dict[str, Any] = {}
|
|
86
|
+
if filename is not None:
|
|
87
|
+
args["filename"] = filename
|
|
88
|
+
if depth is not None:
|
|
89
|
+
args["depth"] = depth
|
|
90
|
+
if sandbox_id:
|
|
91
|
+
args["sandboxId"] = sandbox_id
|
|
92
|
+
return self._client._invoke(self._image_id, "browser_snapshot", args)
|
|
93
|
+
|
|
94
|
+
def capture(
|
|
95
|
+
self,
|
|
96
|
+
*,
|
|
97
|
+
img_type: str = "png",
|
|
98
|
+
filename: Optional[str] = None,
|
|
99
|
+
element: Optional[str] = None,
|
|
100
|
+
ref: Optional[str] = None,
|
|
101
|
+
full_page: bool = False,
|
|
102
|
+
sandbox_id: Optional[str] = None,
|
|
103
|
+
) -> Screenshot:
|
|
104
|
+
"""
|
|
105
|
+
Capture a screenshot of the current browser page.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
img_type: Image format (``"png"`` or ``"jpeg"``).
|
|
109
|
+
filename: File name to save the screenshot to.
|
|
110
|
+
element: Human-readable element description for element screenshot.
|
|
111
|
+
ref: Element reference for element screenshot.
|
|
112
|
+
full_page: Take screenshot of full scrollable page.
|
|
113
|
+
sandbox_id: Explicit sandbox ID.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Screenshot: Image payload saveable via ``.save(path)``.
|
|
117
|
+
"""
|
|
118
|
+
args: Dict[str, Any] = {"type": img_type}
|
|
119
|
+
if filename is not None:
|
|
120
|
+
args["filename"] = filename
|
|
121
|
+
if element is not None:
|
|
122
|
+
args["element"] = element
|
|
123
|
+
if ref is not None:
|
|
124
|
+
args["ref"] = ref
|
|
125
|
+
if full_page:
|
|
126
|
+
args["fullPage"] = True
|
|
127
|
+
if sandbox_id:
|
|
128
|
+
args["sandboxId"] = sandbox_id
|
|
129
|
+
|
|
130
|
+
result = self._client._invoke_raw(self._image_id, "browser_take_screenshot", args)
|
|
131
|
+
return Screenshot.from_mcp(result)
|
|
132
|
+
|
|
133
|
+
# ---- Element Interaction ----
|
|
134
|
+
|
|
135
|
+
def click(
|
|
136
|
+
self,
|
|
137
|
+
ref: str,
|
|
138
|
+
*,
|
|
139
|
+
element: Optional[str] = None,
|
|
140
|
+
double_click: bool = False,
|
|
141
|
+
button: str = "left",
|
|
142
|
+
modifiers: Optional[List[str]] = None,
|
|
143
|
+
sandbox_id: Optional[str] = None,
|
|
144
|
+
) -> ActionResult:
|
|
145
|
+
"""
|
|
146
|
+
Click on an element identified by its accessibility ``ref``.
|
|
147
|
+
|
|
148
|
+
Call :meth:`a11y_tree` first to discover element refs.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
ref: Element reference from the accessibility tree.
|
|
152
|
+
element: Human-readable element description.
|
|
153
|
+
double_click: Perform double click.
|
|
154
|
+
button: ``"left"``, ``"right"``, or ``"middle"``.
|
|
155
|
+
modifiers: Modifier keys (e.g. ``["Shift", "Control"]``).
|
|
156
|
+
sandbox_id: Explicit sandbox ID.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
ActionResult: Click outcome.
|
|
160
|
+
"""
|
|
161
|
+
args: Dict[str, Any] = {"target": ref}
|
|
162
|
+
if element is not None:
|
|
163
|
+
args["element"] = element
|
|
164
|
+
if double_click:
|
|
165
|
+
args["doubleClick"] = True
|
|
166
|
+
if button != "left":
|
|
167
|
+
args["button"] = button
|
|
168
|
+
if modifiers:
|
|
169
|
+
args["modifiers"] = modifiers
|
|
170
|
+
if sandbox_id:
|
|
171
|
+
args["sandboxId"] = sandbox_id
|
|
172
|
+
return self._client._invoke(self._image_id, "browser_click", args)
|
|
173
|
+
|
|
174
|
+
def hover(self, ref: str, *, element: Optional[str] = None, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
175
|
+
"""Hover over an element.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
ref: Element reference from the accessibility tree.
|
|
179
|
+
element: Human-readable element description.
|
|
180
|
+
sandbox_id: Explicit sandbox ID.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
ActionResult: Hover outcome.
|
|
184
|
+
"""
|
|
185
|
+
args: Dict[str, Any] = {"target": ref}
|
|
186
|
+
if element is not None:
|
|
187
|
+
args["element"] = element
|
|
188
|
+
if sandbox_id:
|
|
189
|
+
args["sandboxId"] = sandbox_id
|
|
190
|
+
return self._client._invoke(self._image_id, "browser_hover", args)
|
|
191
|
+
|
|
192
|
+
def drag(
|
|
193
|
+
self,
|
|
194
|
+
start_ref: str,
|
|
195
|
+
end_ref: str,
|
|
196
|
+
*,
|
|
197
|
+
start_element: Optional[str] = None,
|
|
198
|
+
end_element: Optional[str] = None,
|
|
199
|
+
sandbox_id: Optional[str] = None,
|
|
200
|
+
) -> ActionResult:
|
|
201
|
+
"""Drag and drop between two elements.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
start_ref: Source element reference.
|
|
205
|
+
end_ref: Target element reference.
|
|
206
|
+
start_element: Human-readable source element description.
|
|
207
|
+
end_element: Human-readable target element description.
|
|
208
|
+
sandbox_id: Explicit sandbox ID.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
ActionResult: Drag outcome.
|
|
212
|
+
"""
|
|
213
|
+
args: Dict[str, Any] = {
|
|
214
|
+
"startTarget": start_ref,
|
|
215
|
+
"endTarget": end_ref,
|
|
216
|
+
}
|
|
217
|
+
if start_element is not None:
|
|
218
|
+
args["startElement"] = start_element
|
|
219
|
+
if end_element is not None:
|
|
220
|
+
args["endElement"] = end_element
|
|
221
|
+
if sandbox_id:
|
|
222
|
+
args["sandboxId"] = sandbox_id
|
|
223
|
+
return self._client._invoke(self._image_id, "browser_drag", args)
|
|
224
|
+
|
|
225
|
+
# ---- Typing & Form ----
|
|
226
|
+
|
|
227
|
+
def fill(
|
|
228
|
+
self,
|
|
229
|
+
ref: str,
|
|
230
|
+
text: str,
|
|
231
|
+
*,
|
|
232
|
+
element: Optional[str] = None,
|
|
233
|
+
submit: bool = False,
|
|
234
|
+
slowly: bool = False,
|
|
235
|
+
sandbox_id: Optional[str] = None,
|
|
236
|
+
) -> ActionResult:
|
|
237
|
+
"""
|
|
238
|
+
Type *text* into the input element identified by ``ref``.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
ref: Element reference from the accessibility tree.
|
|
242
|
+
text: Text to type.
|
|
243
|
+
element: Human-readable element description.
|
|
244
|
+
submit: Press Enter after typing.
|
|
245
|
+
slowly: Type one character at a time (triggers key handlers).
|
|
246
|
+
sandbox_id: Explicit sandbox ID.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
ActionResult: Typing outcome.
|
|
250
|
+
"""
|
|
251
|
+
args: Dict[str, Any] = {"target": ref, "text": text}
|
|
252
|
+
if element is not None:
|
|
253
|
+
args["element"] = element
|
|
254
|
+
if submit:
|
|
255
|
+
args["submit"] = True
|
|
256
|
+
if slowly:
|
|
257
|
+
args["slowly"] = True
|
|
258
|
+
if sandbox_id:
|
|
259
|
+
args["sandboxId"] = sandbox_id
|
|
260
|
+
return self._client._invoke(self._image_id, "browser_type", args)
|
|
261
|
+
|
|
262
|
+
def fill_form(
|
|
263
|
+
self,
|
|
264
|
+
fields: List[Dict[str, Any]],
|
|
265
|
+
*,
|
|
266
|
+
sandbox_id: Optional[str] = None,
|
|
267
|
+
) -> ActionResult:
|
|
268
|
+
"""Fill multiple form fields at once.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
fields: List of field dicts with keys ``name``, ``type``,
|
|
272
|
+
``ref`` (or ``selector``), and ``value``.
|
|
273
|
+
``type`` is one of: ``textbox``, ``checkbox``,
|
|
274
|
+
``radio``, ``combobox``, ``slider``.
|
|
275
|
+
sandbox_id: Explicit sandbox ID.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
ActionResult: Fill outcome.
|
|
279
|
+
"""
|
|
280
|
+
args: Dict[str, Any] = {"fields": fields}
|
|
281
|
+
if sandbox_id:
|
|
282
|
+
args["sandboxId"] = sandbox_id
|
|
283
|
+
return self._client._invoke(self._image_id, "browser_fill_form", args)
|
|
284
|
+
|
|
285
|
+
def select_option(
|
|
286
|
+
self,
|
|
287
|
+
ref: str,
|
|
288
|
+
values: List[str],
|
|
289
|
+
*,
|
|
290
|
+
element: Optional[str] = None,
|
|
291
|
+
sandbox_id: Optional[str] = None,
|
|
292
|
+
) -> ActionResult:
|
|
293
|
+
"""Select option(s) in a dropdown.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
ref: Element reference from the accessibility tree.
|
|
297
|
+
values: Array of values to select.
|
|
298
|
+
element: Human-readable element description.
|
|
299
|
+
sandbox_id: Explicit sandbox ID.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
ActionResult: Selection outcome.
|
|
303
|
+
"""
|
|
304
|
+
args: Dict[str, Any] = {"target": ref, "values": values}
|
|
305
|
+
if element is not None:
|
|
306
|
+
args["element"] = element
|
|
307
|
+
if sandbox_id:
|
|
308
|
+
args["sandboxId"] = sandbox_id
|
|
309
|
+
return self._client._invoke(self._image_id, "browser_select_option", args)
|
|
310
|
+
|
|
311
|
+
def press_key(self, key: str, *, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
312
|
+
"""Press a key on the keyboard.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
key: Key name (e.g. ``"Enter"``, ``"Tab"``, ``"ArrowLeft"``, ``"a"``).
|
|
316
|
+
sandbox_id: Explicit sandbox ID.
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
ActionResult: Key press outcome.
|
|
320
|
+
"""
|
|
321
|
+
args: Dict[str, Any] = {"key": key}
|
|
322
|
+
if sandbox_id:
|
|
323
|
+
args["sandboxId"] = sandbox_id
|
|
324
|
+
return self._client._invoke(self._image_id, "browser_press_key", args)
|
|
325
|
+
|
|
326
|
+
# ---- JavaScript & Code ----
|
|
327
|
+
|
|
328
|
+
def evaluate(
|
|
329
|
+
self,
|
|
330
|
+
script: str,
|
|
331
|
+
*,
|
|
332
|
+
element: Optional[str] = None,
|
|
333
|
+
ref: Optional[str] = None,
|
|
334
|
+
filename: Optional[str] = None,
|
|
335
|
+
sandbox_id: Optional[str] = None,
|
|
336
|
+
) -> ActionResult:
|
|
337
|
+
"""
|
|
338
|
+
Execute JavaScript in the browser context and return the result.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
script: JavaScript function: ``() => { ... }`` or ``(el) => { ... }``.
|
|
342
|
+
element: Human-readable element description (used with ref).
|
|
343
|
+
ref: Element reference to evaluate on.
|
|
344
|
+
filename: Save result to file instead of returning inline.
|
|
345
|
+
sandbox_id: Explicit sandbox ID.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
ActionResult: Return value of the script in ``.data``.
|
|
349
|
+
"""
|
|
350
|
+
args: Dict[str, Any] = {"function": script}
|
|
351
|
+
if element is not None:
|
|
352
|
+
args["element"] = element
|
|
353
|
+
if ref is not None:
|
|
354
|
+
args["ref"] = ref
|
|
355
|
+
if filename is not None:
|
|
356
|
+
args["filename"] = filename
|
|
357
|
+
if sandbox_id:
|
|
358
|
+
args["sandboxId"] = sandbox_id
|
|
359
|
+
return self._client._invoke(self._image_id, "browser_evaluate", args)
|
|
360
|
+
|
|
361
|
+
def run_code(self, code: str, *, filename: Optional[str] = None, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
362
|
+
"""Run a Playwright code snippet on the page.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
code: Async JavaScript function receiving ``page`` parameter,
|
|
366
|
+
e.g. ``async (page) => { await page.getByRole('button').click(); }``
|
|
367
|
+
filename: Load code from file instead of inline.
|
|
368
|
+
sandbox_id: Explicit sandbox ID.
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
ActionResult: Code execution result.
|
|
372
|
+
"""
|
|
373
|
+
args: Dict[str, Any] = {"code": code}
|
|
374
|
+
if filename is not None:
|
|
375
|
+
args["filename"] = filename
|
|
376
|
+
if sandbox_id:
|
|
377
|
+
args["sandboxId"] = sandbox_id
|
|
378
|
+
return self._client._invoke(self._image_id, "browser_run_code", args)
|
|
379
|
+
|
|
380
|
+
# ---- Window & Tab Management ----
|
|
381
|
+
|
|
382
|
+
def close_tab(self, *, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
383
|
+
"""Close the current browser tab/page.
|
|
384
|
+
|
|
385
|
+
This method closes the current browser tab. It is intentionally named
|
|
386
|
+
``close_tab`` (rather than ``close``) to avoid confusion with
|
|
387
|
+
:meth:`AgentLink.close`, which closes the client connection itself.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
sandbox_id: Explicit sandbox ID.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
ActionResult: Close outcome.
|
|
394
|
+
"""
|
|
395
|
+
args: Dict[str, Any] = {}
|
|
396
|
+
if sandbox_id:
|
|
397
|
+
args["sandboxId"] = sandbox_id
|
|
398
|
+
return self._client._invoke(self._image_id, "browser_close", args)
|
|
399
|
+
|
|
400
|
+
def resize(self, width: int, height: int, *, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
401
|
+
"""Resize the browser window.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
width: New width in pixels.
|
|
405
|
+
height: New height in pixels.
|
|
406
|
+
sandbox_id: Explicit sandbox ID.
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
ActionResult: Resize outcome.
|
|
410
|
+
"""
|
|
411
|
+
args: Dict[str, Any] = {"width": width, "height": height}
|
|
412
|
+
if sandbox_id:
|
|
413
|
+
args["sandboxId"] = sandbox_id
|
|
414
|
+
return self._client._invoke(self._image_id, "browser_resize", args)
|
|
415
|
+
|
|
416
|
+
def tabs(
|
|
417
|
+
self,
|
|
418
|
+
action: str = "list",
|
|
419
|
+
*,
|
|
420
|
+
index: Optional[int] = None,
|
|
421
|
+
sandbox_id: Optional[str] = None,
|
|
422
|
+
) -> ActionResult:
|
|
423
|
+
"""List, create, close, or select browser tabs.
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
action: ``"list"``, ``"new"``, ``"close"``, or ``"select"``.
|
|
427
|
+
index: Tab index (for close/select).
|
|
428
|
+
sandbox_id: Explicit sandbox ID.
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
ActionResult: Tab operation outcome.
|
|
432
|
+
"""
|
|
433
|
+
args: Dict[str, Any] = {"action": action}
|
|
434
|
+
if index is not None:
|
|
435
|
+
args["index"] = index
|
|
436
|
+
if sandbox_id:
|
|
437
|
+
args["sandboxId"] = sandbox_id
|
|
438
|
+
return self._client._invoke(self._image_id, "browser_tabs", args)
|
|
439
|
+
|
|
440
|
+
# ---- Dialog & File Upload ----
|
|
441
|
+
|
|
442
|
+
def handle_dialog(
|
|
443
|
+
self,
|
|
444
|
+
accept: bool,
|
|
445
|
+
*,
|
|
446
|
+
prompt_text: Optional[str] = None,
|
|
447
|
+
sandbox_id: Optional[str] = None,
|
|
448
|
+
) -> ActionResult:
|
|
449
|
+
"""Handle a browser dialog (alert, confirm, prompt).
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
accept: Whether to accept the dialog.
|
|
453
|
+
prompt_text: Text for prompt dialog.
|
|
454
|
+
sandbox_id: Explicit sandbox ID.
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
ActionResult: Dialog handling outcome.
|
|
458
|
+
"""
|
|
459
|
+
args: Dict[str, Any] = {"accept": accept}
|
|
460
|
+
if prompt_text is not None:
|
|
461
|
+
args["promptText"] = prompt_text
|
|
462
|
+
if sandbox_id:
|
|
463
|
+
args["sandboxId"] = sandbox_id
|
|
464
|
+
return self._client._invoke(self._image_id, "browser_handle_dialog", args)
|
|
465
|
+
|
|
466
|
+
def file_upload(self, paths: Optional[List[str]] = None, *, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
467
|
+
"""Upload one or multiple files.
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
paths: Absolute paths to files. If omitted, file chooser is cancelled.
|
|
471
|
+
sandbox_id: Explicit sandbox ID.
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
ActionResult: Upload outcome.
|
|
475
|
+
"""
|
|
476
|
+
args: Dict[str, Any] = {}
|
|
477
|
+
if paths is not None:
|
|
478
|
+
args["paths"] = paths
|
|
479
|
+
if sandbox_id:
|
|
480
|
+
args["sandboxId"] = sandbox_id
|
|
481
|
+
return self._client._invoke(self._image_id, "browser_file_upload", args)
|
|
482
|
+
|
|
483
|
+
# ---- Diagnostics ----
|
|
484
|
+
|
|
485
|
+
def console_messages(
|
|
486
|
+
self,
|
|
487
|
+
level: str = "info",
|
|
488
|
+
*,
|
|
489
|
+
all_messages: bool = False,
|
|
490
|
+
filename: Optional[str] = None,
|
|
491
|
+
sandbox_id: Optional[str] = None,
|
|
492
|
+
) -> ActionResult:
|
|
493
|
+
"""Get browser console messages.
|
|
494
|
+
|
|
495
|
+
Args:
|
|
496
|
+
level: Minimum severity level: ``"error"``, ``"warning"``,
|
|
497
|
+
``"info"``, or ``"debug"``.
|
|
498
|
+
all_messages: Return all messages since session start.
|
|
499
|
+
filename: Save messages to file instead of returning inline.
|
|
500
|
+
sandbox_id: Explicit sandbox ID.
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
ActionResult: Console messages in ``.text``.
|
|
504
|
+
"""
|
|
505
|
+
args: Dict[str, Any] = {"level": level}
|
|
506
|
+
if all_messages:
|
|
507
|
+
args["all"] = True
|
|
508
|
+
if filename is not None:
|
|
509
|
+
args["filename"] = filename
|
|
510
|
+
if sandbox_id:
|
|
511
|
+
args["sandboxId"] = sandbox_id
|
|
512
|
+
return self._client._invoke(self._image_id, "browser_console_messages", args)
|
|
513
|
+
|
|
514
|
+
def network_requests(
|
|
515
|
+
self,
|
|
516
|
+
*,
|
|
517
|
+
static: bool = False,
|
|
518
|
+
request_body: bool = False,
|
|
519
|
+
request_headers: bool = False,
|
|
520
|
+
filter_pattern: Optional[str] = None,
|
|
521
|
+
filename: Optional[str] = None,
|
|
522
|
+
sandbox_id: Optional[str] = None,
|
|
523
|
+
) -> ActionResult:
|
|
524
|
+
"""Get network requests since page load.
|
|
525
|
+
|
|
526
|
+
Args:
|
|
527
|
+
static: Include static resources (images, fonts, scripts).
|
|
528
|
+
request_body: Include request bodies.
|
|
529
|
+
request_headers: Include request headers.
|
|
530
|
+
filter_pattern: Regex filter for URLs.
|
|
531
|
+
filename: Save to file instead of returning inline.
|
|
532
|
+
sandbox_id: Explicit sandbox ID.
|
|
533
|
+
|
|
534
|
+
Returns:
|
|
535
|
+
ActionResult: Network request data in ``.text``.
|
|
536
|
+
"""
|
|
537
|
+
args: Dict[str, Any] = {
|
|
538
|
+
"static": static,
|
|
539
|
+
"requestBody": request_body,
|
|
540
|
+
"requestHeaders": request_headers,
|
|
541
|
+
}
|
|
542
|
+
if filter_pattern is not None:
|
|
543
|
+
args["filter"] = filter_pattern
|
|
544
|
+
if filename is not None:
|
|
545
|
+
args["filename"] = filename
|
|
546
|
+
if sandbox_id:
|
|
547
|
+
args["sandboxId"] = sandbox_id
|
|
548
|
+
return self._client._invoke(self._image_id, "browser_network_requests", args)
|
|
549
|
+
|
|
550
|
+
# ---- Wait ----
|
|
551
|
+
|
|
552
|
+
def wait_for(
|
|
553
|
+
self,
|
|
554
|
+
*,
|
|
555
|
+
time_secs: Optional[float] = None,
|
|
556
|
+
text: Optional[str] = None,
|
|
557
|
+
text_gone: Optional[str] = None,
|
|
558
|
+
sandbox_id: Optional[str] = None,
|
|
559
|
+
) -> ActionResult:
|
|
560
|
+
"""Wait for text to appear, disappear, or a time to pass.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
time_secs: Time to wait in seconds.
|
|
564
|
+
text: Text to wait for.
|
|
565
|
+
text_gone: Text to wait for to disappear.
|
|
566
|
+
sandbox_id: Explicit sandbox ID.
|
|
567
|
+
|
|
568
|
+
Returns:
|
|
569
|
+
ActionResult: Wait outcome.
|
|
570
|
+
|
|
571
|
+
Raises:
|
|
572
|
+
ValueError: If none of *time_secs*, *text*, or *text_gone* is provided.
|
|
573
|
+
"""
|
|
574
|
+
if time_secs is None and text is None and text_gone is None:
|
|
575
|
+
raise ValueError("At least one of 'time_secs', 'text', or 'text_gone' must be provided.")
|
|
576
|
+
args: Dict[str, Any] = {}
|
|
577
|
+
if time_secs is not None:
|
|
578
|
+
args["time"] = time_secs
|
|
579
|
+
if text is not None:
|
|
580
|
+
args["text"] = text
|
|
581
|
+
if text_gone is not None:
|
|
582
|
+
args["textGone"] = text_gone
|
|
583
|
+
if sandbox_id:
|
|
584
|
+
args["sandboxId"] = sandbox_id
|
|
585
|
+
return self._client._invoke(self._image_id, "browser_wait_for", args)
|
|
586
|
+
|
|
587
|
+
# ---- Shell (cross-cutting) ----
|
|
588
|
+
|
|
589
|
+
def shell(self, command: str, *, timeout: int = 30, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
590
|
+
"""Execute a shell command in the browser sandbox.
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
command: Shell command to execute.
|
|
594
|
+
timeout: Command timeout in seconds (default: 30).
|
|
595
|
+
sandbox_id: Explicit sandbox ID.
|
|
596
|
+
|
|
597
|
+
Returns:
|
|
598
|
+
ActionResult: Command output in ``.text``.
|
|
599
|
+
"""
|
|
600
|
+
args: Dict[str, Any] = {"command": command, "timeout": timeout}
|
|
601
|
+
if sandbox_id:
|
|
602
|
+
args["sandboxId"] = sandbox_id
|
|
603
|
+
return self._client._invoke(self._image_id, "shell_exec", args)
|
|
604
|
+
|
|
605
|
+
def get_os(self, *, sandbox_id: Optional[str] = None) -> ActionResult:
|
|
606
|
+
"""Get the operating system of the sandbox.
|
|
607
|
+
|
|
608
|
+
Returns:
|
|
609
|
+
ActionResult: OS information in ``.text``.
|
|
610
|
+
"""
|
|
611
|
+
args: Dict[str, Any] = {}
|
|
612
|
+
if sandbox_id:
|
|
613
|
+
args["sandboxId"] = sandbox_id
|
|
614
|
+
return self._client._invoke(self._image_id, "get_os", args)
|
|
615
|
+
|
|
616
|
+
def __repr__(self) -> str:
|
|
617
|
+
return f"BrowserController(image_id={self._image_id!r})"
|