diff --git a/pyproject.toml b/pyproject.toml
index 84eda73..a8f3cce 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -3,7 +3,7 @@ name = "scrapybara"
[tool.poetry]
name = "scrapybara"
-version = "2.4.3"
+version = "2.4.4"
description = ""
readme = "README.md"
authors = []
diff --git a/reference.md b/reference.md
index 5cf384f..051386b 100644
--- a/reference.md
+++ b/reference.md
@@ -466,6 +466,22 @@ client.instance.bash(
-
+**get_background_processes:** `typing.Optional[bool]`
+
+
+
+
+
+-
+
+**kill_pid:** `typing.Optional[int]`
+
+
+
+
+
+-
+
**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -588,6 +604,207 @@ client.instance.edit(
+
+
+
+
+client.instance.filesystem(...)
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from scrapybara import Scrapybara
+
+client = Scrapybara(
+ api_key="YOUR_API_KEY",
+)
+client.instance.filesystem(
+ instance_id="instance_id",
+ command="command",
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**instance_id:** `str`
+
+
+
+
+
+-
+
+**command:** `str`
+
+
+
+
+
+-
+
+**path:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**content:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**mode:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**encoding:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**view_range:** `typing.Optional[typing.Sequence[int]]`
+
+
+
+
+
+-
+
+**recursive:** `typing.Optional[bool]`
+
+
+
+
+
+-
+
+**src:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**dst:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**old_str:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**new_str:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**line:** `typing.Optional[int]`
+
+
+
+
+
+-
+
+**text:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**lines:** `typing.Optional[typing.Sequence[int]]`
+
+
+
+
+
+-
+
+**all_occurrences:** `typing.Optional[bool]`
+
+
+
+
+
+-
+
+**pattern:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**case_sensitive:** `typing.Optional[bool]`
+
+
+
+
+
+-
+
+**line_numbers:** `typing.Optional[bool]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
diff --git a/src/scrapybara/__init__.py b/src/scrapybara/__init__.py
index 3787602..02e6e49 100644
--- a/src/scrapybara/__init__.py
+++ b/src/scrapybara/__init__.py
@@ -19,6 +19,7 @@
ExecuteCellRequest,
FileDownloadResponse,
FileReadResponse,
+ FilesystemResponse,
GetCursorPositionAction,
GetInstanceResponse,
GetInstanceResponseInstanceType,
@@ -83,6 +84,7 @@
"ExecuteCellRequest",
"FileDownloadResponse",
"FileReadResponse",
+ "FilesystemResponse",
"GetCursorPositionAction",
"GetInstanceResponse",
"GetInstanceResponseInstanceType",
diff --git a/src/scrapybara/client.py b/src/scrapybara/client.py
index 74271ad..f65c338 100644
--- a/src/scrapybara/client.py
+++ b/src/scrapybara/client.py
@@ -751,6 +751,7 @@ def computer(
action: Literal["move_mouse"],
coordinates: List[int],
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -764,6 +765,7 @@ def computer(
coordinates: Optional[List[int]] = None,
num_clicks: Optional[int] = 1,
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -774,6 +776,7 @@ def computer(
action: Literal["drag_mouse"],
path: List[List[int]],
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -786,6 +789,7 @@ def computer(
delta_x: Optional[float] = 0,
delta_y: Optional[float] = 0,
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -796,6 +800,7 @@ def computer(
action: Literal["press_key"],
keys: List[str],
duration: Optional[float] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -806,6 +811,7 @@ def computer(
action: Literal["type_text"],
text: str,
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -815,6 +821,7 @@ def computer(
*,
action: Literal["wait"],
duration: float,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -860,6 +867,7 @@ def computer(
keys: Optional[List[str]] = None,
text: Optional[str] = None,
duration: Optional[float] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse:
"""Control computer actions like mouse movements, clicks, and keyboard input.
@@ -910,7 +918,7 @@ def computer(
# If it wasn't an object or the object wasn't recognized, use the legacy string-based approach
if request is None:
if action == "move_mouse":
- request = Request_MoveMouse(coordinates=coordinates, hold_keys=hold_keys)
+ request = Request_MoveMouse(coordinates=coordinates, hold_keys=hold_keys, screenshot=screenshot)
elif action == "click_mouse":
request = Request_ClickMouse(
button=button,
@@ -918,22 +926,24 @@ def computer(
coordinates=coordinates,
num_clicks=num_clicks,
hold_keys=hold_keys,
+ screenshot=screenshot,
)
elif action == "drag_mouse":
- request = Request_DragMouse(path=path, hold_keys=hold_keys)
+ request = Request_DragMouse(path=path, hold_keys=hold_keys, screenshot=screenshot)
elif action == "scroll":
request = Request_Scroll(
coordinates=coordinates,
delta_x=delta_x,
delta_y=delta_y,
hold_keys=hold_keys,
+ screenshot=screenshot,
)
elif action == "press_key":
- request = Request_PressKey(keys=keys, duration=duration)
+ request = Request_PressKey(keys=keys, duration=duration, screenshot=screenshot)
elif action == "type_text":
- request = Request_TypeText(text=text, hold_keys=hold_keys)
+ request = Request_TypeText(text=text, hold_keys=hold_keys, screenshot=screenshot)
elif action == "wait":
- request = Request_Wait(duration=duration)
+ request = Request_Wait(duration=duration, screenshot=screenshot)
elif action == "take_screenshot":
request = Request_TakeScreenshot()
elif action == "get_cursor_position":
@@ -988,10 +998,17 @@ def bash(
*,
command: Optional[str] = OMIT,
restart: Optional[bool] = OMIT,
+ get_background_processes: Optional[bool] = OMIT,
+ kill_pid: Optional[int] = OMIT,
request_options: Optional[RequestOptions] = None,
) -> Optional[Any]:
return self._client.instance.bash(
- self.id, command=command, restart=restart, request_options=request_options
+ self.id,
+ command=command,
+ restart=restart,
+ get_background_processes=get_background_processes,
+ kill_pid=kill_pid,
+ request_options=request_options
)
def edit(
@@ -1017,7 +1034,52 @@ def edit(
insert_line=insert_line,
request_options=request_options,
)
-
+
+ async def filesystem(
+ self,
+ *,
+ command: str,
+ path: Optional[str] = OMIT,
+ content: Optional[str] = OMIT,
+ mode: Optional[str] = OMIT,
+ encoding: Optional[str] = OMIT,
+ view_range: Optional[Sequence[int]] = OMIT,
+ recursive: Optional[bool] = OMIT,
+ src: Optional[str] = OMIT,
+ dst: Optional[str] = OMIT,
+ old_str: Optional[str] = OMIT,
+ new_str: Optional[str] = OMIT,
+ line: Optional[int] = OMIT,
+ text: Optional[str] = OMIT,
+ lines: Optional[Sequence[int]] = OMIT,
+ all_occurrences: Optional[bool] = OMIT,
+ pattern: Optional[str] = OMIT,
+ case_sensitive: Optional[bool] = OMIT,
+ line_numbers: Optional[bool] = OMIT,
+ request_options: Optional[RequestOptions] = None,
+ ) -> Optional[Any]:
+ return self._client.instance.filesystem(
+ self.id,
+ command=command,
+ path=path,
+ content=content,
+ mode=mode,
+ encoding=encoding,
+ view_range=view_range,
+ recursive=recursive,
+ src=src,
+ dst=dst,
+ old_str=old_str,
+ new_str=new_str,
+ line=line,
+ text=text,
+ lines=lines,
+ all_occurrences=all_occurrences,
+ pattern=pattern,
+ case_sensitive=case_sensitive,
+ line_numbers=line_numbers,
+ request_options=request_options
+ )
class BrowserInstance(BaseInstance):
def __init__(
@@ -1194,6 +1256,7 @@ async def computer(
action: Literal["move_mouse"],
coordinates: List[int],
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -1207,6 +1270,7 @@ async def computer(
coordinates: Optional[List[int]] = None,
num_clicks: Optional[int] = 1,
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -1217,6 +1281,7 @@ async def computer(
action: Literal["drag_mouse"],
path: List[List[int]],
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -1229,6 +1294,7 @@ async def computer(
delta_x: Optional[float] = 0,
delta_y: Optional[float] = 0,
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -1239,6 +1305,7 @@ async def computer(
action: Literal["press_key"],
keys: List[str],
duration: Optional[float] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -1249,6 +1316,7 @@ async def computer(
action: Literal["type_text"],
text: str,
hold_keys: Optional[List[str]] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -1258,6 +1326,7 @@ async def computer(
*,
action: Literal["wait"],
duration: float,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse: ...
@@ -1303,6 +1372,7 @@ async def computer(
keys: Optional[List[str]] = None,
text: Optional[str] = None,
duration: Optional[float] = None,
+ screenshot: Optional[bool] = True,
request_options: Optional[RequestOptions] = None,
) -> ComputerResponse:
"""Control computer actions like mouse movements, clicks, and keyboard input.
@@ -1353,7 +1423,7 @@ async def computer(
# If it wasn't an object or the object wasn't recognized, use the legacy string-based approach
if request is None:
if action == "move_mouse":
- request = Request_MoveMouse(coordinates=coordinates, hold_keys=hold_keys)
+ request = Request_MoveMouse(coordinates=coordinates, hold_keys=hold_keys, screenshot=screenshot)
elif action == "click_mouse":
request = Request_ClickMouse(
button=button,
@@ -1361,22 +1431,24 @@ async def computer(
coordinates=coordinates,
num_clicks=num_clicks,
hold_keys=hold_keys,
+ screenshot=screenshot,
)
elif action == "drag_mouse":
- request = Request_DragMouse(path=path, hold_keys=hold_keys)
+ request = Request_DragMouse(path=path, hold_keys=hold_keys, screenshot=screenshot)
elif action == "scroll":
request = Request_Scroll(
coordinates=coordinates,
delta_x=delta_x,
delta_y=delta_y,
hold_keys=hold_keys,
+ screenshot=screenshot,
)
elif action == "press_key":
- request = Request_PressKey(keys=keys, duration=duration)
+ request = Request_PressKey(keys=keys, duration=duration, screenshot=screenshot)
elif action == "type_text":
- request = Request_TypeText(text=text, hold_keys=hold_keys)
+ request = Request_TypeText(text=text, hold_keys=hold_keys, screenshot=screenshot)
elif action == "wait":
- request = Request_Wait(duration=duration)
+ request = Request_Wait(duration=duration, screenshot=screenshot)
elif action == "take_screenshot":
request = Request_TakeScreenshot()
elif action == "get_cursor_position":
@@ -1435,10 +1507,17 @@ async def bash(
*,
command: Optional[str] = OMIT,
restart: Optional[bool] = OMIT,
+ get_background_processes: Optional[bool] = OMIT,
+ kill_pid: Optional[int] = OMIT,
request_options: Optional[RequestOptions] = None,
) -> Optional[Any]:
return await self._client.instance.bash(
- self.id, command=command, restart=restart, request_options=request_options
+ self.id,
+ command=command,
+ restart=restart,
+ get_background_processes=get_background_processes,
+ kill_pid=kill_pid,
+ request_options=request_options
)
async def edit(
@@ -1464,7 +1543,52 @@ async def edit(
insert_line=insert_line,
request_options=request_options,
)
-
+
+ async def filesystem(
+ self,
+ *,
+ command: str,
+ path: Optional[str] = OMIT,
+ content: Optional[str] = OMIT,
+ mode: Optional[str] = OMIT,
+ encoding: Optional[str] = OMIT,
+ view_range: Optional[Sequence[int]] = OMIT,
+ recursive: Optional[bool] = OMIT,
+ src: Optional[str] = OMIT,
+ dst: Optional[str] = OMIT,
+ old_str: Optional[str] = OMIT,
+ new_str: Optional[str] = OMIT,
+ line: Optional[int] = OMIT,
+ text: Optional[str] = OMIT,
+ lines: Optional[Sequence[int]] = OMIT,
+ all_occurrences: Optional[bool] = OMIT,
+ pattern: Optional[str] = OMIT,
+ case_sensitive: Optional[bool] = OMIT,
+ line_numbers: Optional[bool] = OMIT,
+ request_options: Optional[RequestOptions] = None,
+ ) -> Optional[Any]:
+ return await self._client.instance.filesystem(
+ self.id,
+ command=command,
+ path=path,
+ content=content,
+ mode=mode,
+ encoding=encoding,
+ view_range=view_range,
+ recursive=recursive,
+ src=src,
+ dst=dst,
+ old_str=old_str,
+ new_str=new_str,
+ line=line,
+ text=text,
+ lines=lines,
+ all_occurrences=all_occurrences,
+ pattern=pattern,
+ case_sensitive=case_sensitive,
+ line_numbers=line_numbers,
+ request_options=request_options
+ )
class AsyncBrowserInstance(AsyncBaseInstance):
def __init__(
@@ -2477,7 +2601,8 @@ def _create_request_from_action(action):
if isinstance(action, MoveMouseAction):
return Request_MoveMouse(
coordinates=action.coordinates,
- hold_keys=action.hold_keys
+ hold_keys=action.hold_keys,
+ screenshot=action.screenshot
)
elif isinstance(action, ClickMouseAction):
return Request_ClickMouse(
@@ -2486,11 +2611,13 @@ def _create_request_from_action(action):
coordinates=action.coordinates,
num_clicks=action.num_clicks,
hold_keys=action.hold_keys,
+ screenshot=action.screenshot
)
elif isinstance(action, DragMouseAction):
return Request_DragMouse(
path=action.path,
- hold_keys=action.hold_keys
+ hold_keys=action.hold_keys,
+ screenshot=action.screenshot
)
elif isinstance(action, ScrollAction):
return Request_Scroll(
@@ -2498,20 +2625,24 @@ def _create_request_from_action(action):
delta_x=action.delta_x,
delta_y=action.delta_y,
hold_keys=action.hold_keys,
+ screenshot=action.screenshot
)
elif isinstance(action, PressKeyAction):
return Request_PressKey(
keys=action.keys,
- duration=action.duration
+ duration=action.duration,
+ screenshot=action.screenshot
)
elif isinstance(action, TypeTextAction):
return Request_TypeText(
text=action.text,
- hold_keys=action.hold_keys
+ hold_keys=action.hold_keys,
+ screenshot=action.screenshot
)
elif isinstance(action, WaitAction):
return Request_Wait(
- duration=action.duration
+ duration=action.duration,
+ screenshot=action.screenshot
)
elif isinstance(action, TakeScreenshotAction):
return Request_TakeScreenshot()
diff --git a/src/scrapybara/core/client_wrapper.py b/src/scrapybara/core/client_wrapper.py
index f03289d..0ead1a3 100644
--- a/src/scrapybara/core/client_wrapper.py
+++ b/src/scrapybara/core/client_wrapper.py
@@ -16,7 +16,7 @@ def get_headers(self) -> typing.Dict[str, str]:
headers: typing.Dict[str, str] = {
"X-Fern-Language": "Python",
"X-Fern-SDK-Name": "scrapybara",
- "X-Fern-SDK-Version": "2.4.3",
+ "X-Fern-SDK-Version": "2.4.4",
}
headers["x-api-key"] = self.api_key
return headers
diff --git a/src/scrapybara/instance/client.py b/src/scrapybara/instance/client.py
index 78e8799..3ef7a22 100644
--- a/src/scrapybara/instance/client.py
+++ b/src/scrapybara/instance/client.py
@@ -17,6 +17,7 @@
from ..types.bash_response import BashResponse
from .types.command import Command
from ..types.edit_response import EditResponse
+from ..types.filesystem_response import FilesystemResponse
from ..types.stop_instance_response import StopInstanceResponse
from ..types.get_instance_response import GetInstanceResponse
from ..core.client_wrapper import AsyncClientWrapper
@@ -211,6 +212,8 @@ def bash(
*,
command: typing.Optional[str] = OMIT,
restart: typing.Optional[bool] = OMIT,
+ get_background_processes: typing.Optional[bool] = OMIT,
+ kill_pid: typing.Optional[int] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> BashResponse:
"""
@@ -222,6 +225,10 @@ def bash(
restart : typing.Optional[bool]
+ get_background_processes : typing.Optional[bool]
+
+ kill_pid : typing.Optional[int]
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
@@ -247,6 +254,8 @@ def bash(
json={
"command": command,
"restart": restart,
+ "get_background_processes": get_background_processes,
+ "kill_pid": kill_pid,
},
headers={
"content-type": "application/json",
@@ -373,6 +382,144 @@ def edit(
raise ApiError(status_code=_response.status_code, body=_response.text)
raise ApiError(status_code=_response.status_code, body=_response_json)
+ def filesystem(
+ self,
+ instance_id: str,
+ *,
+ command: str,
+ path: typing.Optional[str] = OMIT,
+ content: typing.Optional[str] = OMIT,
+ mode: typing.Optional[str] = OMIT,
+ encoding: typing.Optional[str] = OMIT,
+ view_range: typing.Optional[typing.Sequence[int]] = OMIT,
+ recursive: typing.Optional[bool] = OMIT,
+ src: typing.Optional[str] = OMIT,
+ dst: typing.Optional[str] = OMIT,
+ old_str: typing.Optional[str] = OMIT,
+ new_str: typing.Optional[str] = OMIT,
+ line: typing.Optional[int] = OMIT,
+ text: typing.Optional[str] = OMIT,
+ lines: typing.Optional[typing.Sequence[int]] = OMIT,
+ all_occurrences: typing.Optional[bool] = OMIT,
+ pattern: typing.Optional[str] = OMIT,
+ case_sensitive: typing.Optional[bool] = OMIT,
+ line_numbers: typing.Optional[bool] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> FilesystemResponse:
+ """
+ Parameters
+ ----------
+ instance_id : str
+
+ command : str
+
+ path : typing.Optional[str]
+
+ content : typing.Optional[str]
+
+ mode : typing.Optional[str]
+
+ encoding : typing.Optional[str]
+
+ view_range : typing.Optional[typing.Sequence[int]]
+
+ recursive : typing.Optional[bool]
+
+ src : typing.Optional[str]
+
+ dst : typing.Optional[str]
+
+ old_str : typing.Optional[str]
+
+ new_str : typing.Optional[str]
+
+ line : typing.Optional[int]
+
+ text : typing.Optional[str]
+
+ lines : typing.Optional[typing.Sequence[int]]
+
+ all_occurrences : typing.Optional[bool]
+
+ pattern : typing.Optional[str]
+
+ case_sensitive : typing.Optional[bool]
+
+ line_numbers : typing.Optional[bool]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ FilesystemResponse
+ Successful Response
+
+ Examples
+ --------
+ from scrapybara import Scrapybara
+
+ client = Scrapybara(
+ api_key="YOUR_API_KEY",
+ )
+ client.instance.filesystem(
+ instance_id="instance_id",
+ command="command",
+ )
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"v1/instance/{jsonable_encoder(instance_id)}/filesystem",
+ method="POST",
+ json={
+ "command": command,
+ "path": path,
+ "content": content,
+ "mode": mode,
+ "encoding": encoding,
+ "view_range": view_range,
+ "recursive": recursive,
+ "src": src,
+ "dst": dst,
+ "old_str": old_str,
+ "new_str": new_str,
+ "line": line,
+ "text": text,
+ "lines": lines,
+ "all_occurrences": all_occurrences,
+ "pattern": pattern,
+ "case_sensitive": case_sensitive,
+ "line_numbers": line_numbers,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return typing.cast(
+ FilesystemResponse,
+ parse_obj_as(
+ type_=FilesystemResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, body=_response.text)
+ raise ApiError(status_code=_response.status_code, body=_response_json)
+
def stop(
self, instance_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> StopInstanceResponse:
@@ -761,6 +908,8 @@ async def bash(
*,
command: typing.Optional[str] = OMIT,
restart: typing.Optional[bool] = OMIT,
+ get_background_processes: typing.Optional[bool] = OMIT,
+ kill_pid: typing.Optional[int] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> BashResponse:
"""
@@ -772,6 +921,10 @@ async def bash(
restart : typing.Optional[bool]
+ get_background_processes : typing.Optional[bool]
+
+ kill_pid : typing.Optional[int]
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
@@ -805,6 +958,8 @@ async def main() -> None:
json={
"command": command,
"restart": restart,
+ "get_background_processes": get_background_processes,
+ "kill_pid": kill_pid,
},
headers={
"content-type": "application/json",
@@ -939,6 +1094,152 @@ async def main() -> None:
raise ApiError(status_code=_response.status_code, body=_response.text)
raise ApiError(status_code=_response.status_code, body=_response_json)
+ async def filesystem(
+ self,
+ instance_id: str,
+ *,
+ command: str,
+ path: typing.Optional[str] = OMIT,
+ content: typing.Optional[str] = OMIT,
+ mode: typing.Optional[str] = OMIT,
+ encoding: typing.Optional[str] = OMIT,
+ view_range: typing.Optional[typing.Sequence[int]] = OMIT,
+ recursive: typing.Optional[bool] = OMIT,
+ src: typing.Optional[str] = OMIT,
+ dst: typing.Optional[str] = OMIT,
+ old_str: typing.Optional[str] = OMIT,
+ new_str: typing.Optional[str] = OMIT,
+ line: typing.Optional[int] = OMIT,
+ text: typing.Optional[str] = OMIT,
+ lines: typing.Optional[typing.Sequence[int]] = OMIT,
+ all_occurrences: typing.Optional[bool] = OMIT,
+ pattern: typing.Optional[str] = OMIT,
+ case_sensitive: typing.Optional[bool] = OMIT,
+ line_numbers: typing.Optional[bool] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> FilesystemResponse:
+ """
+ Parameters
+ ----------
+ instance_id : str
+
+ command : str
+
+ path : typing.Optional[str]
+
+ content : typing.Optional[str]
+
+ mode : typing.Optional[str]
+
+ encoding : typing.Optional[str]
+
+ view_range : typing.Optional[typing.Sequence[int]]
+
+ recursive : typing.Optional[bool]
+
+ src : typing.Optional[str]
+
+ dst : typing.Optional[str]
+
+ old_str : typing.Optional[str]
+
+ new_str : typing.Optional[str]
+
+ line : typing.Optional[int]
+
+ text : typing.Optional[str]
+
+ lines : typing.Optional[typing.Sequence[int]]
+
+ all_occurrences : typing.Optional[bool]
+
+ pattern : typing.Optional[str]
+
+ case_sensitive : typing.Optional[bool]
+
+ line_numbers : typing.Optional[bool]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ FilesystemResponse
+ Successful Response
+
+ Examples
+ --------
+ import asyncio
+
+ from scrapybara import AsyncScrapybara
+
+ client = AsyncScrapybara(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.instance.filesystem(
+ instance_id="instance_id",
+ command="command",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"v1/instance/{jsonable_encoder(instance_id)}/filesystem",
+ method="POST",
+ json={
+ "command": command,
+ "path": path,
+ "content": content,
+ "mode": mode,
+ "encoding": encoding,
+ "view_range": view_range,
+ "recursive": recursive,
+ "src": src,
+ "dst": dst,
+ "old_str": old_str,
+ "new_str": new_str,
+ "line": line,
+ "text": text,
+ "lines": lines,
+ "all_occurrences": all_occurrences,
+ "pattern": pattern,
+ "case_sensitive": case_sensitive,
+ "line_numbers": line_numbers,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return typing.cast(
+ FilesystemResponse,
+ parse_obj_as(
+ type_=FilesystemResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, body=_response.text)
+ raise ApiError(status_code=_response.status_code, body=_response_json)
+
async def stop(
self, instance_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> StopInstanceResponse:
diff --git a/src/scrapybara/instance/types/request.py b/src/scrapybara/instance/types/request.py
index 141e199..f2fef5b 100644
--- a/src/scrapybara/instance/types/request.py
+++ b/src/scrapybara/instance/types/request.py
@@ -13,6 +13,7 @@ class Request_MoveMouse(UniversalBaseModel):
action: typing.Literal["move_mouse"] = "move_mouse"
coordinates: typing.List[int]
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -31,6 +32,7 @@ class Request_ClickMouse(UniversalBaseModel):
coordinates: typing.Optional[typing.List[int]] = None
num_clicks: typing.Optional[int] = None
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -46,6 +48,7 @@ class Request_DragMouse(UniversalBaseModel):
action: typing.Literal["drag_mouse"] = "drag_mouse"
path: typing.List[typing.List[int]]
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -63,6 +66,7 @@ class Request_Scroll(UniversalBaseModel):
delta_x: typing.Optional[float] = None
delta_y: typing.Optional[float] = None
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -78,6 +82,7 @@ class Request_PressKey(UniversalBaseModel):
action: typing.Literal["press_key"] = "press_key"
keys: typing.List[str]
duration: typing.Optional[float] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -93,6 +98,7 @@ class Request_TypeText(UniversalBaseModel):
action: typing.Literal["type_text"] = "type_text"
text: str
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -107,6 +113,7 @@ class Config:
class Request_Wait(UniversalBaseModel):
action: typing.Literal["wait"] = "wait"
duration: float
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/scrapybara/tools/__init__.py b/src/scrapybara/tools/__init__.py
index 5d8d605..c83f8c9 100644
--- a/src/scrapybara/tools/__init__.py
+++ b/src/scrapybara/tools/__init__.py
@@ -4,7 +4,7 @@
from ..types import Action, Button, ClickMouseActionClickType, Tool
from ..client import BaseInstance, UbuntuInstance
from ..instance.types import Command
-
+from typing import Literal
class ComputerToolParameters(BaseModel):
"""Parameters for computer interaction commands."""
@@ -164,8 +164,10 @@ def __call__(self, **kwargs: Any) -> Any:
class BashToolParameters(BaseModel):
"""Parameters for bash command execution."""
- command: str = Field(description="The bash command to execute")
+ command: Optional[str] = Field(description="The bash command to execute")
restart: Optional[bool] = Field(False, description="Whether to restart the shell")
+ get_background_processes: Optional[bool] = Field(None, description="Retrieve information (pid, status, command) about background processes")
+ kill_pid: Optional[int] = Field(None, description="Process ID to kill")
class BashTool(Tool):
@@ -185,4 +187,144 @@ def __init__(self, instance: UbuntuInstance) -> None:
def __call__(self, **kwargs: Any) -> Any:
params = BashToolParameters.model_validate(kwargs)
- return self._instance.bash(command=params.command, restart=params.restart)
+ return self._instance.bash(
+ command=params.command,
+ restart=params.restart,
+ get_background_processes=params.get_background_processes,
+ kill_pid=params.kill_pid
+ )
+
+
+class FilesystemToolParameters(BaseModel):
+ """Parameters for filesystem operations."""
+
+ command: Literal[
+ "read", "write", "append", "delete", "exists", "list", "mkdir", "rmdir", "move", "copy",
+ "view", "create", "replace", "insert", "delete_lines", "undo", "grep"
+ ] = Field(
+ description="The filesystem command to execute. Determines which other parameters are required or optional. Supported commands: read, write, append, delete, exists, list, mkdir, rmdir, move, copy, view, create, replace, insert, delete_lines, undo, grep."
+ )
+
+ path: Optional[str] = Field(
+ None,
+ description="Path to the file or directory. Required for commands: read, write, append, delete, exists, list, mkdir, rmdir, view, create, replace, insert, delete_lines, undo, grep."
+ )
+
+ content: Optional[str] = Field(
+ None,
+ description="Content to write, append, or create in a file. Required for commands: write, append, create."
+ )
+
+ mode: Optional[str] = Field(
+ None,
+ description="Mode for file operations ('text' or 'binary'). Optional for commands: read, write, append, create. Defaults to 'text' if not specified."
+ )
+
+ encoding: Optional[str] = Field(
+ None,
+ description="Encoding for text operations (e.g., 'utf-8'). Optional for commands: read, write, append, create. Defaults to 'utf-8' if not specified."
+ )
+
+ view_range: Optional[List[int]] = Field(
+ None,
+ description="Range of lines to view as [start, end]. Optional for command: view. Not applicable if viewing a directory."
+ )
+
+ recursive: Optional[bool] = Field(
+ None,
+ description="Whether to perform the operation recursively. Optional for command: delete (defaults to False); required to be True for grep when searching directories."
+ )
+
+ src: Optional[str] = Field(
+ None,
+ description="Source path for move and copy operations. Required for commands: move, copy."
+ )
+
+ dst: Optional[str] = Field(
+ None,
+ description="Destination path for move and copy operations. Required for commands: move, copy."
+ )
+
+ old_str: Optional[str] = Field(
+ None,
+ description="String to be replaced in the file. Required for command: replace."
+ )
+
+ new_str: Optional[str] = Field(
+ None,
+ description="Replacement string. Required for command: replace."
+ )
+
+ line: Optional[int] = Field(
+ None,
+ description="Line number where text should be inserted (1-based index). Required for command: insert."
+ )
+
+ text: Optional[str] = Field(
+ None,
+ description="Text to insert at the specified line. Required for command: insert."
+ )
+
+ lines: Optional[List[int]] = Field(
+ None,
+ description="List of line numbers to delete (1-based indices). Required for command: delete_lines."
+ )
+
+ all_occurrences: Optional[bool] = Field(
+ None,
+ description="Whether to replace all occurrences of old_str. Optional for command: replace. Defaults to False, replacing only the first occurrence."
+ )
+
+ pattern: Optional[str] = Field(
+ None,
+ description="Regex pattern to search for in files. Required for command: grep."
+ )
+
+ case_sensitive: Optional[bool] = Field(
+ None,
+ description="Whether the grep search is case-sensitive. Optional for command: grep. Defaults to True."
+ )
+
+ line_numbers: Optional[bool] = Field(
+ None,
+ description="Whether to include line numbers in grep output. Optional for command: grep. Defaults to True."
+ )
+
+
+class FilesystemTool(Tool):
+ """A filesystem manipulation tool that allows the agent to perform various filesystem operations.
+
+ Available for Ubuntu instances."""
+
+ _instance: UbuntuInstance
+
+ def __init__(self, instance: UbuntuInstance) -> None:
+ super().__init__(
+ name="filesystem",
+ description="Perform filesystem operations like reading, writing, and manipulating files",
+ parameters=FilesystemToolParameters,
+ )
+ self._instance = instance
+
+ def __call__(self, **kwargs: Any) -> Any:
+ params = FilesystemToolParameters.model_validate(kwargs)
+ return self._instance.filesystem(
+ command=params.command,
+ path=params.path,
+ content=params.content,
+ mode=params.mode,
+ encoding=params.encoding,
+ view_range=params.view_range,
+ recursive=params.recursive,
+ src=params.src,
+ dst=params.dst,
+ old_str=params.old_str,
+ new_str=params.new_str,
+ line=params.line,
+ text=params.text,
+ lines=params.lines,
+ all_occurrences=params.all_occurrences,
+ pattern=params.pattern,
+ case_sensitive=params.case_sensitive,
+ line_numbers=params.line_numbers,
+ )
diff --git a/src/scrapybara/types/__init__.py b/src/scrapybara/types/__init__.py
index 93d8c21..8221f74 100644
--- a/src/scrapybara/types/__init__.py
+++ b/src/scrapybara/types/__init__.py
@@ -17,6 +17,7 @@
from .execute_cell_request import ExecuteCellRequest
from .file_download_response import FileDownloadResponse
from .file_read_response import FileReadResponse
+from .filesystem_response import FilesystemResponse
from .get_cursor_position_action import GetCursorPositionAction
from .get_instance_response import GetInstanceResponse
from .get_instance_response_instance_type import GetInstanceResponseInstanceType
@@ -93,6 +94,7 @@
"ExecuteCellRequest",
"FileDownloadResponse",
"FileReadResponse",
+ "FilesystemResponse",
"GetCursorPositionAction",
"GetInstanceResponse",
"GetInstanceResponseInstanceType",
diff --git a/src/scrapybara/types/click_mouse_action.py b/src/scrapybara/types/click_mouse_action.py
index fdb5b8a..df7299b 100644
--- a/src/scrapybara/types/click_mouse_action.py
+++ b/src/scrapybara/types/click_mouse_action.py
@@ -14,6 +14,7 @@ class ClickMouseAction(UniversalBaseModel):
coordinates: typing.Optional[typing.List[int]] = None
num_clicks: typing.Optional[int] = None
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/scrapybara/types/drag_mouse_action.py b/src/scrapybara/types/drag_mouse_action.py
index 354caec..9daf7dd 100644
--- a/src/scrapybara/types/drag_mouse_action.py
+++ b/src/scrapybara/types/drag_mouse_action.py
@@ -9,6 +9,7 @@
class DragMouseAction(UniversalBaseModel):
path: typing.List[typing.List[int]]
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/scrapybara/types/filesystem_response.py b/src/scrapybara/types/filesystem_response.py
new file mode 100644
index 0000000..a9d9f8b
--- /dev/null
+++ b/src/scrapybara/types/filesystem_response.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+from ..core.pydantic_utilities import UniversalBaseModel
+import typing
+import typing_extensions
+from ..core.serialization import FieldMetadata
+from ..core.pydantic_utilities import IS_PYDANTIC_V2
+import pydantic
+
+
+class FilesystemResponse(UniversalBaseModel):
+ """
+ Response model for filesystem actions.
+ """
+
+ output: typing.Optional[str] = None
+ error: typing.Optional[str] = None
+ base_64_image: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="base64_image")] = None
+ system: typing.Optional[str] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/scrapybara/types/get_instance_response.py b/src/scrapybara/types/get_instance_response.py
index a0b60c4..d7850c4 100644
--- a/src/scrapybara/types/get_instance_response.py
+++ b/src/scrapybara/types/get_instance_response.py
@@ -4,8 +4,8 @@
import datetime as dt
from .get_instance_response_instance_type import GetInstanceResponseInstanceType
from .status import Status
-from ..core.pydantic_utilities import IS_PYDANTIC_V2
import typing
+from ..core.pydantic_utilities import IS_PYDANTIC_V2
import pydantic
@@ -14,6 +14,7 @@ class GetInstanceResponse(UniversalBaseModel):
launch_time: dt.datetime
instance_type: GetInstanceResponseInstanceType
status: Status
+ resolution: typing.Optional[typing.List[int]] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/scrapybara/types/move_mouse_action.py b/src/scrapybara/types/move_mouse_action.py
index 66e0335..d814c82 100644
--- a/src/scrapybara/types/move_mouse_action.py
+++ b/src/scrapybara/types/move_mouse_action.py
@@ -9,6 +9,7 @@
class MoveMouseAction(UniversalBaseModel):
coordinates: typing.List[int]
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/scrapybara/types/press_key_action.py b/src/scrapybara/types/press_key_action.py
index 432eea0..2d36fe4 100644
--- a/src/scrapybara/types/press_key_action.py
+++ b/src/scrapybara/types/press_key_action.py
@@ -9,6 +9,7 @@
class PressKeyAction(UniversalBaseModel):
keys: typing.List[str]
duration: typing.Optional[float] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/scrapybara/types/scroll_action.py b/src/scrapybara/types/scroll_action.py
index 9bee45f..5b20666 100644
--- a/src/scrapybara/types/scroll_action.py
+++ b/src/scrapybara/types/scroll_action.py
@@ -11,6 +11,7 @@ class ScrollAction(UniversalBaseModel):
delta_x: typing.Optional[float] = None
delta_y: typing.Optional[float] = None
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/scrapybara/types/status.py b/src/scrapybara/types/status.py
index 3fb3c9c..0c98034 100644
--- a/src/scrapybara/types/status.py
+++ b/src/scrapybara/types/status.py
@@ -2,4 +2,4 @@
import typing
-Status = typing.Union[typing.Literal["deploying", "running", "paused", "terminated", "error"], typing.Any]
+Status = typing.Union[typing.Literal["deploying", "running", "paused", "terminated", "error", "warm_pool"], typing.Any]
diff --git a/src/scrapybara/types/type_text_action.py b/src/scrapybara/types/type_text_action.py
index 6bf9e52..17f4758 100644
--- a/src/scrapybara/types/type_text_action.py
+++ b/src/scrapybara/types/type_text_action.py
@@ -9,6 +9,7 @@
class TypeTextAction(UniversalBaseModel):
text: str
hold_keys: typing.Optional[typing.List[str]] = None
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/scrapybara/types/wait_action.py b/src/scrapybara/types/wait_action.py
index 23e1131..e18b548 100644
--- a/src/scrapybara/types/wait_action.py
+++ b/src/scrapybara/types/wait_action.py
@@ -1,13 +1,14 @@
# This file was auto-generated by Fern from our API Definition.
from ..core.pydantic_utilities import UniversalBaseModel
-from ..core.pydantic_utilities import IS_PYDANTIC_V2
import typing
+from ..core.pydantic_utilities import IS_PYDANTIC_V2
import pydantic
class WaitAction(UniversalBaseModel):
duration: float
+ screenshot: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2