hyperbrowser 0.88.2__tar.gz → 0.89.0__tar.gz
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.
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/PKG-INFO +113 -1
- hyperbrowser-0.89.0/README.md +217 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/sandbox.py +36 -5
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_files.py +2 -6
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_terminal.py +12 -2
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sandboxes/shared.py +31 -4
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/sandbox.py +36 -5
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_files.py +2 -6
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_terminal.py +9 -2
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/__init__.py +1 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/sandbox.py +41 -11
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/pyproject.toml +1 -1
- hyperbrowser-0.88.2/README.md +0 -105
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/LICENSE +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/__init__.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/async_client.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/base.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/agents/__init__.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/agents/browser_use.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/agents/claude_computer_use.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/agents/cua.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/agents/gemini_computer_use.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/agents/hyper_agent.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/computer_action.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/crawl.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/extension.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/extract.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/profile.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/sandboxes/__init__.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_processes.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_transport.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/scrape.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/session.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/team.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/web/__init__.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/web/batch_fetch.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/web/crawl.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sandboxes/__init__.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/agents/__init__.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/agents/browser_use.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/agents/claude_computer_use.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/agents/cua.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/agents/gemini_computer_use.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/agents/hyper_agent.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/computer_action.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/crawl.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/extension.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/extract.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/profile.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/sandboxes/__init__.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_processes.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_transport.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/scrape.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/session.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/team.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/web/__init__.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/web/batch_fetch.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/web/crawl.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/sync.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/config.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/exceptions.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/agents/browser_use.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/agents/claude_computer_use.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/agents/cua.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/agents/gemini_computer_use.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/agents/hyper_agent.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/computer_action.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/consts.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/crawl.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/extension.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/extract.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/profile.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/scrape.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/session.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/team.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/web/batch_fetch.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/web/common.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/web/crawl.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/web/fetch.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/web/search.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/sandbox_common.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/tools/__init__.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/tools/anthropic.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/tools/openai.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/tools/schema.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/transport/async_transport.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/transport/base.py +0 -0
- {hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/transport/sync.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperbrowser
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.89.0
|
|
4
4
|
Summary: Python SDK for hyperbrowser
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -126,6 +126,118 @@ def main():
|
|
|
126
126
|
# Run the asyncio event loop
|
|
127
127
|
main()
|
|
128
128
|
```
|
|
129
|
+
|
|
130
|
+
## Sandboxes
|
|
131
|
+
|
|
132
|
+
The sync and async clients expose the same sandbox APIs through `client.sandboxes`.
|
|
133
|
+
|
|
134
|
+
### Create a sandbox with pre-exposed ports
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from hyperbrowser import Hyperbrowser
|
|
138
|
+
from hyperbrowser.models import CreateSandboxParams, SandboxExposeParams
|
|
139
|
+
|
|
140
|
+
client = Hyperbrowser(api_key="test-key")
|
|
141
|
+
sandbox = client.sandboxes.create(
|
|
142
|
+
CreateSandboxParams(
|
|
143
|
+
image_name="node",
|
|
144
|
+
exposed_ports=[SandboxExposeParams(port=3000, auth=True)],
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
print(sandbox.exposed_ports[0].browser_url)
|
|
149
|
+
sandbox.stop()
|
|
150
|
+
client.close()
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### List sandboxes with filters
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from hyperbrowser import Hyperbrowser
|
|
157
|
+
from hyperbrowser.models import SandboxListParams
|
|
158
|
+
|
|
159
|
+
client = Hyperbrowser(api_key="test-key")
|
|
160
|
+
result = client.sandboxes.list(
|
|
161
|
+
SandboxListParams(
|
|
162
|
+
status="active",
|
|
163
|
+
search="sandbox",
|
|
164
|
+
start=1711929600000,
|
|
165
|
+
end=1712016000000,
|
|
166
|
+
limit=20,
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
for sandbox in result.sandboxes:
|
|
171
|
+
print(sandbox.id, sandbox.status)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### List snapshots for a specific image
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from hyperbrowser import Hyperbrowser
|
|
178
|
+
from hyperbrowser.models import SandboxSnapshotListParams
|
|
179
|
+
|
|
180
|
+
client = Hyperbrowser(api_key="test-key")
|
|
181
|
+
snapshots = client.sandboxes.list_snapshots(
|
|
182
|
+
SandboxSnapshotListParams(image_name="node", status="created", limit=10)
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Expose and unexpose ports
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
from hyperbrowser import Hyperbrowser
|
|
190
|
+
from hyperbrowser.models import CreateSandboxParams, SandboxExposeParams
|
|
191
|
+
|
|
192
|
+
client = Hyperbrowser(api_key="test-key")
|
|
193
|
+
sandbox = client.sandboxes.create(CreateSandboxParams(image_name="node"))
|
|
194
|
+
|
|
195
|
+
result = sandbox.expose(SandboxExposeParams(port=8080, auth=True))
|
|
196
|
+
print(result.url, result.browser_url)
|
|
197
|
+
|
|
198
|
+
sandbox.unexpose(8080)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Batch file writes with per-file options
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from hyperbrowser import Hyperbrowser
|
|
205
|
+
from hyperbrowser.models import CreateSandboxParams, SandboxFileWriteEntry
|
|
206
|
+
|
|
207
|
+
client = Hyperbrowser(api_key="test-key")
|
|
208
|
+
sandbox = client.sandboxes.create(CreateSandboxParams(image_name="node"))
|
|
209
|
+
|
|
210
|
+
sandbox.files.write(
|
|
211
|
+
[
|
|
212
|
+
SandboxFileWriteEntry(
|
|
213
|
+
path="/tmp/config.json",
|
|
214
|
+
data='{"debug":true}\n',
|
|
215
|
+
append=True,
|
|
216
|
+
mode="600",
|
|
217
|
+
),
|
|
218
|
+
SandboxFileWriteEntry(
|
|
219
|
+
path="/tmp/blob.bin",
|
|
220
|
+
data=b"\x00\x01\x02",
|
|
221
|
+
),
|
|
222
|
+
]
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Resume terminal output after reconnect
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from hyperbrowser import Hyperbrowser
|
|
230
|
+
from hyperbrowser.models import CreateSandboxParams, SandboxTerminalCreateParams
|
|
231
|
+
|
|
232
|
+
client = Hyperbrowser(api_key="test-key")
|
|
233
|
+
sandbox = client.sandboxes.create(CreateSandboxParams(image_name="node"))
|
|
234
|
+
terminal = sandbox.terminal.create(SandboxTerminalCreateParams(command="bash"))
|
|
235
|
+
|
|
236
|
+
connection = terminal.attach(cursor=10)
|
|
237
|
+
for event in connection.events():
|
|
238
|
+
print(event)
|
|
239
|
+
```
|
|
240
|
+
|
|
129
241
|
## License
|
|
130
242
|
|
|
131
243
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Hyperbrowser Python SDK
|
|
2
|
+
|
|
3
|
+
Checkout the full documentation [here](https://hyperbrowser.ai/docs)
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Currently Hyperbrowser supports creating a browser session in two ways:
|
|
8
|
+
|
|
9
|
+
- Async Client
|
|
10
|
+
- Sync Client
|
|
11
|
+
|
|
12
|
+
It can be installed from `pypi` by running :
|
|
13
|
+
|
|
14
|
+
```shell
|
|
15
|
+
pip install hyperbrowser
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Configuration
|
|
19
|
+
|
|
20
|
+
Both the sync and async client follow similar configuration params
|
|
21
|
+
|
|
22
|
+
### API Key
|
|
23
|
+
The API key can be configured either from the constructor arguments or environment variables using `HYPERBROWSER_API_KEY`
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Async
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import asyncio
|
|
31
|
+
from pyppeteer import connect
|
|
32
|
+
from hyperbrowser import AsyncHyperbrowser
|
|
33
|
+
|
|
34
|
+
HYPERBROWSER_API_KEY = "test-key"
|
|
35
|
+
|
|
36
|
+
async def main():
|
|
37
|
+
async with AsyncHyperbrowser(api_key=HYPERBROWSER_API_KEY) as client:
|
|
38
|
+
session = await client.sessions.create()
|
|
39
|
+
|
|
40
|
+
ws_endpoint = session.ws_endpoint
|
|
41
|
+
browser = await connect(browserWSEndpoint=ws_endpoint, defaultViewport=None)
|
|
42
|
+
|
|
43
|
+
# Get pages
|
|
44
|
+
pages = await browser.pages()
|
|
45
|
+
if not pages:
|
|
46
|
+
raise Exception("No pages available")
|
|
47
|
+
|
|
48
|
+
page = pages[0]
|
|
49
|
+
|
|
50
|
+
# Navigate to a website
|
|
51
|
+
print("Navigating to Hacker News...")
|
|
52
|
+
await page.goto("https://news.ycombinator.com/")
|
|
53
|
+
page_title = await page.title()
|
|
54
|
+
print("Page title:", page_title)
|
|
55
|
+
|
|
56
|
+
await page.close()
|
|
57
|
+
await browser.disconnect()
|
|
58
|
+
await client.sessions.stop(session.id)
|
|
59
|
+
print("Session completed!")
|
|
60
|
+
|
|
61
|
+
# Run the asyncio event loop
|
|
62
|
+
asyncio.get_event_loop().run_until_complete(main())
|
|
63
|
+
```
|
|
64
|
+
### Sync
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from playwright.sync_api import sync_playwright
|
|
68
|
+
from hyperbrowser import Hyperbrowser
|
|
69
|
+
|
|
70
|
+
HYPERBROWSER_API_KEY = "test-key"
|
|
71
|
+
|
|
72
|
+
def main():
|
|
73
|
+
client = Hyperbrowser(api_key=HYPERBROWSER_API_KEY)
|
|
74
|
+
session = client.sessions.create()
|
|
75
|
+
|
|
76
|
+
ws_endpoint = session.ws_endpoint
|
|
77
|
+
|
|
78
|
+
# Launch Playwright and connect to the remote browser
|
|
79
|
+
with sync_playwright() as p:
|
|
80
|
+
browser = p.chromium.connect_over_cdp(ws_endpoint)
|
|
81
|
+
context = browser.new_context()
|
|
82
|
+
|
|
83
|
+
# Get the first page or create a new one
|
|
84
|
+
if len(context.pages) == 0:
|
|
85
|
+
page = context.new_page()
|
|
86
|
+
else:
|
|
87
|
+
page = context.pages[0]
|
|
88
|
+
|
|
89
|
+
# Navigate to a website
|
|
90
|
+
print("Navigating to Hacker News...")
|
|
91
|
+
page.goto("https://news.ycombinator.com/")
|
|
92
|
+
page_title = page.title()
|
|
93
|
+
print("Page title:", page_title)
|
|
94
|
+
|
|
95
|
+
page.close()
|
|
96
|
+
browser.close()
|
|
97
|
+
print("Session completed!")
|
|
98
|
+
client.sessions.stop(session.id)
|
|
99
|
+
|
|
100
|
+
# Run the asyncio event loop
|
|
101
|
+
main()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Sandboxes
|
|
105
|
+
|
|
106
|
+
The sync and async clients expose the same sandbox APIs through `client.sandboxes`.
|
|
107
|
+
|
|
108
|
+
### Create a sandbox with pre-exposed ports
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from hyperbrowser import Hyperbrowser
|
|
112
|
+
from hyperbrowser.models import CreateSandboxParams, SandboxExposeParams
|
|
113
|
+
|
|
114
|
+
client = Hyperbrowser(api_key="test-key")
|
|
115
|
+
sandbox = client.sandboxes.create(
|
|
116
|
+
CreateSandboxParams(
|
|
117
|
+
image_name="node",
|
|
118
|
+
exposed_ports=[SandboxExposeParams(port=3000, auth=True)],
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
print(sandbox.exposed_ports[0].browser_url)
|
|
123
|
+
sandbox.stop()
|
|
124
|
+
client.close()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### List sandboxes with filters
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from hyperbrowser import Hyperbrowser
|
|
131
|
+
from hyperbrowser.models import SandboxListParams
|
|
132
|
+
|
|
133
|
+
client = Hyperbrowser(api_key="test-key")
|
|
134
|
+
result = client.sandboxes.list(
|
|
135
|
+
SandboxListParams(
|
|
136
|
+
status="active",
|
|
137
|
+
search="sandbox",
|
|
138
|
+
start=1711929600000,
|
|
139
|
+
end=1712016000000,
|
|
140
|
+
limit=20,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
for sandbox in result.sandboxes:
|
|
145
|
+
print(sandbox.id, sandbox.status)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### List snapshots for a specific image
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from hyperbrowser import Hyperbrowser
|
|
152
|
+
from hyperbrowser.models import SandboxSnapshotListParams
|
|
153
|
+
|
|
154
|
+
client = Hyperbrowser(api_key="test-key")
|
|
155
|
+
snapshots = client.sandboxes.list_snapshots(
|
|
156
|
+
SandboxSnapshotListParams(image_name="node", status="created", limit=10)
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Expose and unexpose ports
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from hyperbrowser import Hyperbrowser
|
|
164
|
+
from hyperbrowser.models import CreateSandboxParams, SandboxExposeParams
|
|
165
|
+
|
|
166
|
+
client = Hyperbrowser(api_key="test-key")
|
|
167
|
+
sandbox = client.sandboxes.create(CreateSandboxParams(image_name="node"))
|
|
168
|
+
|
|
169
|
+
result = sandbox.expose(SandboxExposeParams(port=8080, auth=True))
|
|
170
|
+
print(result.url, result.browser_url)
|
|
171
|
+
|
|
172
|
+
sandbox.unexpose(8080)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Batch file writes with per-file options
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from hyperbrowser import Hyperbrowser
|
|
179
|
+
from hyperbrowser.models import CreateSandboxParams, SandboxFileWriteEntry
|
|
180
|
+
|
|
181
|
+
client = Hyperbrowser(api_key="test-key")
|
|
182
|
+
sandbox = client.sandboxes.create(CreateSandboxParams(image_name="node"))
|
|
183
|
+
|
|
184
|
+
sandbox.files.write(
|
|
185
|
+
[
|
|
186
|
+
SandboxFileWriteEntry(
|
|
187
|
+
path="/tmp/config.json",
|
|
188
|
+
data='{"debug":true}\n',
|
|
189
|
+
append=True,
|
|
190
|
+
mode="600",
|
|
191
|
+
),
|
|
192
|
+
SandboxFileWriteEntry(
|
|
193
|
+
path="/tmp/blob.bin",
|
|
194
|
+
data=b"\x00\x01\x02",
|
|
195
|
+
),
|
|
196
|
+
]
|
|
197
|
+
)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Resume terminal output after reconnect
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from hyperbrowser import Hyperbrowser
|
|
204
|
+
from hyperbrowser.models import CreateSandboxParams, SandboxTerminalCreateParams
|
|
205
|
+
|
|
206
|
+
client = Hyperbrowser(api_key="test-key")
|
|
207
|
+
sandbox = client.sandboxes.create(CreateSandboxParams(image_name="node"))
|
|
208
|
+
terminal = sandbox.terminal.create(SandboxTerminalCreateParams(command="bash"))
|
|
209
|
+
|
|
210
|
+
connection = terminal.attach(cursor=10)
|
|
211
|
+
for event in connection.events():
|
|
212
|
+
print(event)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## License
|
|
216
|
+
|
|
217
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/sandbox.py
RENAMED
|
@@ -15,6 +15,7 @@ from ....models.sandbox import (
|
|
|
15
15
|
SandboxRuntimeSession,
|
|
16
16
|
SandboxSnapshotListParams,
|
|
17
17
|
SandboxSnapshotListResponse,
|
|
18
|
+
SandboxUnexposeResult,
|
|
18
19
|
StartSandboxFromSnapshotParams,
|
|
19
20
|
)
|
|
20
21
|
from ....models.session import BasicResponse
|
|
@@ -113,6 +114,10 @@ class SandboxHandle:
|
|
|
113
114
|
def session_url(self) -> str:
|
|
114
115
|
return self._detail.session_url
|
|
115
116
|
|
|
117
|
+
@property
|
|
118
|
+
def exposed_ports(self):
|
|
119
|
+
return self._detail.exposed_ports
|
|
120
|
+
|
|
116
121
|
def to_dict(self):
|
|
117
122
|
return self._detail.model_dump()
|
|
118
123
|
|
|
@@ -152,7 +157,27 @@ class SandboxHandle:
|
|
|
152
157
|
async def expose(self, params: SandboxExposeParams) -> SandboxExposeResult:
|
|
153
158
|
if not isinstance(params, SandboxExposeParams):
|
|
154
159
|
raise TypeError("params must be a SandboxExposeParams instance")
|
|
155
|
-
|
|
160
|
+
result = await self._service.expose(self.id, params, runtime=self.runtime)
|
|
161
|
+
exposed_ports = [
|
|
162
|
+
port for port in self._detail.exposed_ports if port.port != result.port
|
|
163
|
+
]
|
|
164
|
+
exposed_ports.append(result)
|
|
165
|
+
exposed_ports.sort(key=lambda port: port.port)
|
|
166
|
+
self._detail = self._detail.model_copy(update={"exposed_ports": exposed_ports})
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
async def unexpose(self, port: int) -> SandboxUnexposeResult:
|
|
170
|
+
result = await self._service.unexpose(self.id, port)
|
|
171
|
+
self._detail = self._detail.model_copy(
|
|
172
|
+
update={
|
|
173
|
+
"exposed_ports": [
|
|
174
|
+
exposed_port
|
|
175
|
+
for exposed_port in self._detail.exposed_ports
|
|
176
|
+
if exposed_port.port != port
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
return result
|
|
156
181
|
|
|
157
182
|
def get_exposed_url(self, port: int) -> str:
|
|
158
183
|
return _build_sandbox_exposed_url(self.runtime, port)
|
|
@@ -372,11 +397,17 @@ class SandboxManager:
|
|
|
372
397
|
data=params.model_dump(exclude_none=True, by_alias=True),
|
|
373
398
|
)
|
|
374
399
|
target_runtime = runtime or (await self.get_detail(sandbox_id)).runtime
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
400
|
+
if "url" not in payload:
|
|
401
|
+
payload["url"] = _build_sandbox_exposed_url(target_runtime, payload["port"])
|
|
402
|
+
return SandboxExposeResult(**payload)
|
|
403
|
+
|
|
404
|
+
async def unexpose(self, sandbox_id: str, port: int) -> SandboxUnexposeResult:
|
|
405
|
+
payload = await self._request(
|
|
406
|
+
"POST",
|
|
407
|
+
f"/sandbox/{sandbox_id}/unexpose",
|
|
408
|
+
data={"port": port},
|
|
379
409
|
)
|
|
410
|
+
return SandboxUnexposeResult(**payload)
|
|
380
411
|
|
|
381
412
|
async def _create_detail(self, params: CreateSandboxParams) -> SandboxDetail:
|
|
382
413
|
payload = await self._request(
|
|
@@ -32,6 +32,7 @@ from .....models.sandbox import (
|
|
|
32
32
|
from .....sandbox_common import build_headers, to_websocket_transport_target
|
|
33
33
|
from ...sandboxes.shared import (
|
|
34
34
|
DEFAULT_WATCH_TIMEOUT_MS,
|
|
35
|
+
_encode_batch_write_entry,
|
|
35
36
|
_copy_model,
|
|
36
37
|
_encode_write_data,
|
|
37
38
|
_normalize_event_type,
|
|
@@ -371,12 +372,7 @@ class SandboxFilesApi:
|
|
|
371
372
|
for entry in path_or_files:
|
|
372
373
|
if not isinstance(entry, SandboxFileWriteEntry):
|
|
373
374
|
raise TypeError("files must contain SandboxFileWriteEntry instances")
|
|
374
|
-
encoded_files.append(
|
|
375
|
-
{
|
|
376
|
-
"path": entry.path,
|
|
377
|
-
**_encode_write_data(entry.data),
|
|
378
|
-
}
|
|
379
|
-
)
|
|
375
|
+
encoded_files.append(_encode_batch_write_entry(entry))
|
|
380
376
|
|
|
381
377
|
payload = await self._transport.request_json(
|
|
382
378
|
"/sandbox/files/write",
|
|
@@ -2,6 +2,7 @@ import base64
|
|
|
2
2
|
import json
|
|
3
3
|
import socket
|
|
4
4
|
from typing import AsyncIterator, Dict, Optional, Union
|
|
5
|
+
from urllib.parse import urlencode
|
|
5
6
|
|
|
6
7
|
from websockets.asyncio.client import connect as async_ws_connect
|
|
7
8
|
from websockets.exceptions import ConnectionClosed
|
|
@@ -160,11 +161,20 @@ class SandboxTerminalHandle:
|
|
|
160
161
|
self._status = _normalize_terminal_status(payload["pty"])
|
|
161
162
|
return self.current
|
|
162
163
|
|
|
163
|
-
async def attach(
|
|
164
|
+
async def attach(
|
|
165
|
+
self,
|
|
166
|
+
cursor: Optional[int] = None,
|
|
167
|
+
) -> SandboxTerminalConnection:
|
|
164
168
|
connection = await self._get_connection_info()
|
|
169
|
+
query = urlencode(
|
|
170
|
+
[
|
|
171
|
+
("sessionId", connection.sandbox_id),
|
|
172
|
+
*([("cursor", str(cursor))] if cursor is not None else []),
|
|
173
|
+
]
|
|
174
|
+
)
|
|
165
175
|
target = to_websocket_transport_target(
|
|
166
176
|
connection.base_url,
|
|
167
|
-
f"/sandbox/pty/{self.id}/ws?
|
|
177
|
+
f"/sandbox/pty/{self.id}/ws?{query}",
|
|
168
178
|
self._runtime_proxy_override,
|
|
169
179
|
)
|
|
170
180
|
headers = build_headers(connection.token, host_header=target.host_header)
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sandboxes/shared.py
RENAMED
|
@@ -7,6 +7,7 @@ from urllib.parse import urlencode, urlsplit, urlunsplit
|
|
|
7
7
|
from ....exceptions import HyperbrowserError
|
|
8
8
|
from ....models.sandbox import (
|
|
9
9
|
SandboxFileInfo,
|
|
10
|
+
SandboxFileWriteEntry,
|
|
10
11
|
SandboxFileWriteInfo,
|
|
11
12
|
SandboxTerminalStatus,
|
|
12
13
|
)
|
|
@@ -27,7 +28,7 @@ def _build_sandbox_exposed_url(runtime, port: int) -> str:
|
|
|
27
28
|
parsed = urlsplit(runtime.base_url)
|
|
28
29
|
hostname = parsed.hostname
|
|
29
30
|
if not hostname:
|
|
30
|
-
return runtime.base_url
|
|
31
|
+
return runtime.base_url
|
|
31
32
|
|
|
32
33
|
exposed_host = f"{port}-{hostname}"
|
|
33
34
|
netloc = exposed_host
|
|
@@ -39,9 +40,9 @@ def _build_sandbox_exposed_url(runtime, port: int) -> str:
|
|
|
39
40
|
credentials = f"{credentials}:{parsed.password}"
|
|
40
41
|
netloc = f"{credentials}@{netloc}"
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
path = parsed.path or "/"
|
|
44
|
+
|
|
45
|
+
return urlunsplit((parsed.scheme, netloc, path, parsed.query, parsed.fragment))
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
def _expires_within_buffer(expires_at: Optional[datetime]) -> bool:
|
|
@@ -188,6 +189,32 @@ def _encode_write_data(data: Union[str, bytes, bytearray]) -> Dict[str, str]:
|
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
|
|
192
|
+
def _encode_batch_write_entry(entry: SandboxFileWriteEntry) -> Dict[str, object]:
|
|
193
|
+
if isinstance(entry.data, str):
|
|
194
|
+
encoding = entry.encoding or "utf8"
|
|
195
|
+
if encoding not in {"utf8", "base64"}:
|
|
196
|
+
raise ValueError("encoding should be one of: utf8, base64")
|
|
197
|
+
payload: Dict[str, object] = {
|
|
198
|
+
"path": entry.path,
|
|
199
|
+
"data": entry.data,
|
|
200
|
+
"encoding": encoding,
|
|
201
|
+
}
|
|
202
|
+
else:
|
|
203
|
+
if entry.encoding not in {None, "base64"}:
|
|
204
|
+
raise ValueError("encoding must be base64 when data is bytes")
|
|
205
|
+
payload = {
|
|
206
|
+
"path": entry.path,
|
|
207
|
+
"data": base64.b64encode(bytes(entry.data)).decode("ascii"),
|
|
208
|
+
"encoding": "base64",
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if entry.append is not None:
|
|
212
|
+
payload["append"] = entry.append
|
|
213
|
+
if entry.mode is not None:
|
|
214
|
+
payload["mode"] = entry.mode
|
|
215
|
+
return payload
|
|
216
|
+
|
|
217
|
+
|
|
191
218
|
def _normalize_terminal_output_chunk(entry: Dict[str, object]) -> Dict[str, object]:
|
|
192
219
|
raw = base64.b64decode(entry["data"])
|
|
193
220
|
return {
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/sandbox.py
RENAMED
|
@@ -15,6 +15,7 @@ from ....models.sandbox import (
|
|
|
15
15
|
SandboxRuntimeSession,
|
|
16
16
|
SandboxSnapshotListParams,
|
|
17
17
|
SandboxSnapshotListResponse,
|
|
18
|
+
SandboxUnexposeResult,
|
|
18
19
|
StartSandboxFromSnapshotParams,
|
|
19
20
|
)
|
|
20
21
|
from ....models.session import BasicResponse
|
|
@@ -113,6 +114,10 @@ class SandboxHandle:
|
|
|
113
114
|
def session_url(self) -> str:
|
|
114
115
|
return self._detail.session_url
|
|
115
116
|
|
|
117
|
+
@property
|
|
118
|
+
def exposed_ports(self):
|
|
119
|
+
return self._detail.exposed_ports
|
|
120
|
+
|
|
116
121
|
def to_dict(self):
|
|
117
122
|
return self._detail.model_dump()
|
|
118
123
|
|
|
@@ -152,7 +157,27 @@ class SandboxHandle:
|
|
|
152
157
|
def expose(self, params: SandboxExposeParams) -> SandboxExposeResult:
|
|
153
158
|
if not isinstance(params, SandboxExposeParams):
|
|
154
159
|
raise TypeError("params must be a SandboxExposeParams instance")
|
|
155
|
-
|
|
160
|
+
result = self._service.expose(self.id, params, runtime=self.runtime)
|
|
161
|
+
exposed_ports = [
|
|
162
|
+
port for port in self._detail.exposed_ports if port.port != result.port
|
|
163
|
+
]
|
|
164
|
+
exposed_ports.append(result)
|
|
165
|
+
exposed_ports.sort(key=lambda port: port.port)
|
|
166
|
+
self._detail = self._detail.model_copy(update={"exposed_ports": exposed_ports})
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
def unexpose(self, port: int) -> SandboxUnexposeResult:
|
|
170
|
+
result = self._service.unexpose(self.id, port)
|
|
171
|
+
self._detail = self._detail.model_copy(
|
|
172
|
+
update={
|
|
173
|
+
"exposed_ports": [
|
|
174
|
+
exposed_port
|
|
175
|
+
for exposed_port in self._detail.exposed_ports
|
|
176
|
+
if exposed_port.port != port
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
return result
|
|
156
181
|
|
|
157
182
|
def get_exposed_url(self, port: int) -> str:
|
|
158
183
|
return _build_sandbox_exposed_url(self.runtime, port)
|
|
@@ -372,11 +397,17 @@ class SandboxManager:
|
|
|
372
397
|
data=params.model_dump(exclude_none=True, by_alias=True),
|
|
373
398
|
)
|
|
374
399
|
target_runtime = runtime or self.get_detail(sandbox_id).runtime
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
400
|
+
if "url" not in payload:
|
|
401
|
+
payload["url"] = _build_sandbox_exposed_url(target_runtime, payload["port"])
|
|
402
|
+
return SandboxExposeResult(**payload)
|
|
403
|
+
|
|
404
|
+
def unexpose(self, sandbox_id: str, port: int) -> SandboxUnexposeResult:
|
|
405
|
+
payload = self._request(
|
|
406
|
+
"POST",
|
|
407
|
+
f"/sandbox/{sandbox_id}/unexpose",
|
|
408
|
+
data={"port": port},
|
|
379
409
|
)
|
|
410
|
+
return SandboxUnexposeResult(**payload)
|
|
380
411
|
|
|
381
412
|
def _create_detail(self, params: CreateSandboxParams) -> SandboxDetail:
|
|
382
413
|
payload = self._request(
|
|
@@ -31,6 +31,7 @@ from .....models.sandbox import (
|
|
|
31
31
|
from .....sandbox_common import build_headers, to_websocket_transport_target
|
|
32
32
|
from ...sandboxes.shared import (
|
|
33
33
|
DEFAULT_WATCH_TIMEOUT_MS,
|
|
34
|
+
_encode_batch_write_entry,
|
|
34
35
|
_copy_model,
|
|
35
36
|
_encode_write_data,
|
|
36
37
|
_normalize_event_type,
|
|
@@ -352,12 +353,7 @@ class SandboxFilesApi:
|
|
|
352
353
|
for entry in path_or_files:
|
|
353
354
|
if not isinstance(entry, SandboxFileWriteEntry):
|
|
354
355
|
raise TypeError("files must contain SandboxFileWriteEntry instances")
|
|
355
|
-
encoded_files.append(
|
|
356
|
-
{
|
|
357
|
-
"path": entry.path,
|
|
358
|
-
**_encode_write_data(entry.data),
|
|
359
|
-
}
|
|
360
|
-
)
|
|
356
|
+
encoded_files.append(_encode_batch_write_entry(entry))
|
|
361
357
|
|
|
362
358
|
payload = self._transport.request_json(
|
|
363
359
|
"/sandbox/files/write",
|
|
@@ -2,6 +2,7 @@ import base64
|
|
|
2
2
|
import json
|
|
3
3
|
import socket
|
|
4
4
|
from typing import Dict, Optional, Union
|
|
5
|
+
from urllib.parse import urlencode
|
|
5
6
|
|
|
6
7
|
from websockets.exceptions import ConnectionClosed
|
|
7
8
|
from websockets.sync.client import connect as sync_ws_connect
|
|
@@ -160,11 +161,17 @@ class SandboxTerminalHandle:
|
|
|
160
161
|
self._status = _normalize_terminal_status(payload["pty"])
|
|
161
162
|
return self.current
|
|
162
163
|
|
|
163
|
-
def attach(self) -> SandboxTerminalConnection:
|
|
164
|
+
def attach(self, cursor: Optional[int] = None) -> SandboxTerminalConnection:
|
|
164
165
|
connection = self._get_connection_info()
|
|
166
|
+
query = urlencode(
|
|
167
|
+
[
|
|
168
|
+
("sessionId", connection.sandbox_id),
|
|
169
|
+
*([("cursor", str(cursor))] if cursor is not None else []),
|
|
170
|
+
]
|
|
171
|
+
)
|
|
165
172
|
target = to_websocket_transport_target(
|
|
166
173
|
connection.base_url,
|
|
167
|
-
f"/sandbox/pty/{self.id}/ws?
|
|
174
|
+
f"/sandbox/pty/{self.id}/ws?{query}",
|
|
168
175
|
self._runtime_proxy_override,
|
|
169
176
|
)
|
|
170
177
|
headers = build_headers(connection.token, host_header=target.host_header)
|
|
@@ -69,6 +69,32 @@ class SandboxRuntimeTarget(SandboxBaseModel):
|
|
|
69
69
|
base_url: str = Field(alias="baseUrl")
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
class SandboxExposeParams(SandboxBaseModel):
|
|
73
|
+
port: int
|
|
74
|
+
auth: Optional[bool] = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class SandboxExposeResult(SandboxBaseModel):
|
|
78
|
+
port: int
|
|
79
|
+
auth: bool
|
|
80
|
+
url: str
|
|
81
|
+
browser_url: Optional[str] = Field(default=None, alias="browserUrl")
|
|
82
|
+
browser_url_expires_at: Optional[datetime] = Field(
|
|
83
|
+
default=None,
|
|
84
|
+
alias="browserUrlExpiresAt",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@field_validator("browser_url_expires_at", mode="before")
|
|
88
|
+
@classmethod
|
|
89
|
+
def parse_browser_url_expires_at(cls, value):
|
|
90
|
+
return _parse_optional_datetime(value)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class SandboxUnexposeResult(SandboxBaseModel):
|
|
94
|
+
port: int
|
|
95
|
+
exposed: bool
|
|
96
|
+
|
|
97
|
+
|
|
72
98
|
class Sandbox(SandboxBaseModel):
|
|
73
99
|
id: str
|
|
74
100
|
team_id: str = Field(alias="teamId")
|
|
@@ -91,6 +117,10 @@ class Sandbox(SandboxBaseModel):
|
|
|
91
117
|
duration: int
|
|
92
118
|
proxy_bytes_used: Optional[int] = Field(default=None, alias="proxyBytesUsed")
|
|
93
119
|
runtime: SandboxRuntimeTarget
|
|
120
|
+
exposed_ports: List[SandboxExposeResult] = Field(
|
|
121
|
+
default_factory=list,
|
|
122
|
+
alias="exposedPorts",
|
|
123
|
+
)
|
|
94
124
|
|
|
95
125
|
@field_validator(
|
|
96
126
|
"end_time",
|
|
@@ -140,6 +170,10 @@ class CreateSandboxParams(SandboxBaseModel):
|
|
|
140
170
|
enable_recording: Optional[bool] = Field(
|
|
141
171
|
default=None, serialization_alias="enableRecording"
|
|
142
172
|
)
|
|
173
|
+
exposed_ports: Optional[List[SandboxExposeParams]] = Field(
|
|
174
|
+
default=None,
|
|
175
|
+
serialization_alias="exposedPorts",
|
|
176
|
+
)
|
|
143
177
|
timeout_minutes: Optional[int] = Field(
|
|
144
178
|
default=None, serialization_alias="timeoutMinutes"
|
|
145
179
|
)
|
|
@@ -168,6 +202,9 @@ class SandboxListParams(SandboxBaseModel):
|
|
|
168
202
|
status: Optional[SandboxStatus] = Field(default=None, exclude=None)
|
|
169
203
|
page: int = Field(default=1, ge=1)
|
|
170
204
|
limit: int = Field(default=10, ge=1)
|
|
205
|
+
start: Optional[int] = None
|
|
206
|
+
end: Optional[int] = None
|
|
207
|
+
search: Optional[str] = None
|
|
171
208
|
|
|
172
209
|
|
|
173
210
|
class SandboxListResponse(SandboxBaseModel):
|
|
@@ -212,6 +249,7 @@ class SandboxSnapshotSummary(SandboxBaseModel):
|
|
|
212
249
|
class SandboxSnapshotListParams(SandboxBaseModel):
|
|
213
250
|
status: Optional[SandboxSnapshotStatus] = Field(default=None, exclude=None)
|
|
214
251
|
limit: Optional[int] = Field(default=None, ge=1)
|
|
252
|
+
image_name: Optional[str] = Field(default=None, serialization_alias="imageName")
|
|
215
253
|
|
|
216
254
|
|
|
217
255
|
class SandboxSnapshotListResponse(SandboxBaseModel):
|
|
@@ -238,17 +276,6 @@ class SandboxMemorySnapshotResult(SandboxBaseModel):
|
|
|
238
276
|
image_namespace: str = Field(alias="imageNamespace")
|
|
239
277
|
|
|
240
278
|
|
|
241
|
-
class SandboxExposeParams(SandboxBaseModel):
|
|
242
|
-
port: int
|
|
243
|
-
auth: Optional[bool] = None
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
class SandboxExposeResult(SandboxBaseModel):
|
|
247
|
-
port: int
|
|
248
|
-
auth: bool
|
|
249
|
-
url: str
|
|
250
|
-
|
|
251
|
-
|
|
252
279
|
class SandboxExecParams(SandboxBaseModel):
|
|
253
280
|
command: str
|
|
254
281
|
args: Optional[List[str]] = None
|
|
@@ -390,6 +417,9 @@ SandboxFileWriteData = Union[str, bytes]
|
|
|
390
417
|
class SandboxFileWriteEntry(SandboxBaseModel):
|
|
391
418
|
path: str
|
|
392
419
|
data: SandboxFileWriteData
|
|
420
|
+
encoding: Optional[SandboxFileEncoding] = None
|
|
421
|
+
append: Optional[bool] = None
|
|
422
|
+
mode: Optional[str] = None
|
|
393
423
|
|
|
394
424
|
|
|
395
425
|
class SandboxFileTextWriteOptions(SandboxBaseModel):
|
hyperbrowser-0.88.2/README.md
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# Hyperbrowser Python SDK
|
|
2
|
-
|
|
3
|
-
Checkout the full documentation [here](https://hyperbrowser.ai/docs)
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
Currently Hyperbrowser supports creating a browser session in two ways:
|
|
8
|
-
|
|
9
|
-
- Async Client
|
|
10
|
-
- Sync Client
|
|
11
|
-
|
|
12
|
-
It can be installed from `pypi` by running :
|
|
13
|
-
|
|
14
|
-
```shell
|
|
15
|
-
pip install hyperbrowser
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Configuration
|
|
19
|
-
|
|
20
|
-
Both the sync and async client follow similar configuration params
|
|
21
|
-
|
|
22
|
-
### API Key
|
|
23
|
-
The API key can be configured either from the constructor arguments or environment variables using `HYPERBROWSER_API_KEY`
|
|
24
|
-
|
|
25
|
-
## Usage
|
|
26
|
-
|
|
27
|
-
### Async
|
|
28
|
-
|
|
29
|
-
```python
|
|
30
|
-
import asyncio
|
|
31
|
-
from pyppeteer import connect
|
|
32
|
-
from hyperbrowser import AsyncHyperbrowser
|
|
33
|
-
|
|
34
|
-
HYPERBROWSER_API_KEY = "test-key"
|
|
35
|
-
|
|
36
|
-
async def main():
|
|
37
|
-
async with AsyncHyperbrowser(api_key=HYPERBROWSER_API_KEY) as client:
|
|
38
|
-
session = await client.sessions.create()
|
|
39
|
-
|
|
40
|
-
ws_endpoint = session.ws_endpoint
|
|
41
|
-
browser = await connect(browserWSEndpoint=ws_endpoint, defaultViewport=None)
|
|
42
|
-
|
|
43
|
-
# Get pages
|
|
44
|
-
pages = await browser.pages()
|
|
45
|
-
if not pages:
|
|
46
|
-
raise Exception("No pages available")
|
|
47
|
-
|
|
48
|
-
page = pages[0]
|
|
49
|
-
|
|
50
|
-
# Navigate to a website
|
|
51
|
-
print("Navigating to Hacker News...")
|
|
52
|
-
await page.goto("https://news.ycombinator.com/")
|
|
53
|
-
page_title = await page.title()
|
|
54
|
-
print("Page title:", page_title)
|
|
55
|
-
|
|
56
|
-
await page.close()
|
|
57
|
-
await browser.disconnect()
|
|
58
|
-
await client.sessions.stop(session.id)
|
|
59
|
-
print("Session completed!")
|
|
60
|
-
|
|
61
|
-
# Run the asyncio event loop
|
|
62
|
-
asyncio.get_event_loop().run_until_complete(main())
|
|
63
|
-
```
|
|
64
|
-
### Sync
|
|
65
|
-
|
|
66
|
-
```python
|
|
67
|
-
from playwright.sync_api import sync_playwright
|
|
68
|
-
from hyperbrowser import Hyperbrowser
|
|
69
|
-
|
|
70
|
-
HYPERBROWSER_API_KEY = "test-key"
|
|
71
|
-
|
|
72
|
-
def main():
|
|
73
|
-
client = Hyperbrowser(api_key=HYPERBROWSER_API_KEY)
|
|
74
|
-
session = client.sessions.create()
|
|
75
|
-
|
|
76
|
-
ws_endpoint = session.ws_endpoint
|
|
77
|
-
|
|
78
|
-
# Launch Playwright and connect to the remote browser
|
|
79
|
-
with sync_playwright() as p:
|
|
80
|
-
browser = p.chromium.connect_over_cdp(ws_endpoint)
|
|
81
|
-
context = browser.new_context()
|
|
82
|
-
|
|
83
|
-
# Get the first page or create a new one
|
|
84
|
-
if len(context.pages) == 0:
|
|
85
|
-
page = context.new_page()
|
|
86
|
-
else:
|
|
87
|
-
page = context.pages[0]
|
|
88
|
-
|
|
89
|
-
# Navigate to a website
|
|
90
|
-
print("Navigating to Hacker News...")
|
|
91
|
-
page.goto("https://news.ycombinator.com/")
|
|
92
|
-
page_title = page.title()
|
|
93
|
-
print("Page title:", page_title)
|
|
94
|
-
|
|
95
|
-
page.close()
|
|
96
|
-
browser.close()
|
|
97
|
-
print("Session completed!")
|
|
98
|
-
client.sessions.stop(session.id)
|
|
99
|
-
|
|
100
|
-
# Run the asyncio event loop
|
|
101
|
-
main()
|
|
102
|
-
```
|
|
103
|
-
## License
|
|
104
|
-
|
|
105
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/agents/cua.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/crawl.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/extension.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/extract.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/profile.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/scrape.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/session.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/team.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/async_manager/web/crawl.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sandboxes/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/agents/cua.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/crawl.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/extension.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/extract.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/profile.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/scrape.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/session.py
RENAMED
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/team.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/client/managers/sync_manager/web/crawl.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/agents/claude_computer_use.py
RENAMED
|
File without changes
|
|
File without changes
|
{hyperbrowser-0.88.2 → hyperbrowser-0.89.0}/hyperbrowser/models/agents/gemini_computer_use.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|