hyperbrowser 0.90.0__tar.gz → 0.90.2__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.90.0 → hyperbrowser-0.90.2}/PKG-INFO +30 -1
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/README.md +29 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/async_client.py +2 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/sandbox.py +18 -10
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_files.py +105 -58
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_processes.py +39 -8
- hyperbrowser-0.90.2/hyperbrowser/client/managers/async_manager/volume.py +26 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sandboxes/shared.py +60 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/sandbox.py +18 -10
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_files.py +105 -58
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_processes.py +39 -8
- hyperbrowser-0.90.2/hyperbrowser/client/managers/sync_manager/volume.py +26 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/sync.py +2 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/__init__.py +9 -0
- hyperbrowser-0.90.2/hyperbrowser/models/_parsers.py +11 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/sandbox.py +13 -10
- hyperbrowser-0.90.2/hyperbrowser/models/volume.py +29 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/pyproject.toml +1 -1
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/LICENSE +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/__init__.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/base.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/agents/__init__.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/agents/browser_use.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/agents/claude_computer_use.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/agents/cua.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/agents/gemini_computer_use.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/agents/hyper_agent.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/computer_action.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/crawl.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/extension.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/extract.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/profile.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/sandboxes/__init__.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_terminal.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_transport.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/scrape.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/session.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/team.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/web/__init__.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/web/batch_fetch.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/web/crawl.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sandboxes/__init__.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/agents/__init__.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/agents/browser_use.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/agents/claude_computer_use.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/agents/cua.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/agents/gemini_computer_use.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/agents/hyper_agent.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/computer_action.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/crawl.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/extension.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/extract.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/profile.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/sandboxes/__init__.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_terminal.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_transport.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/scrape.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/session.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/team.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/web/__init__.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/web/batch_fetch.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/web/crawl.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/config.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/exceptions.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/agents/browser_use.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/agents/claude_computer_use.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/agents/cua.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/agents/gemini_computer_use.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/agents/hyper_agent.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/computer_action.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/consts.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/crawl.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/extension.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/extract.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/profile.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/scrape.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/session.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/team.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/web/batch_fetch.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/web/common.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/web/crawl.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/web/fetch.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/models/web/search.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/sandbox_common.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/tools/__init__.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/tools/anthropic.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/tools/openai.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/tools/schema.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/transport/async_transport.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/transport/base.py +0 -0
- {hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/transport/sync.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperbrowser
|
|
3
|
-
Version: 0.90.
|
|
3
|
+
Version: 0.90.2
|
|
4
4
|
Summary: Python SDK for hyperbrowser
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -156,6 +156,35 @@ client.close()
|
|
|
156
156
|
|
|
157
157
|
`cpu`, `memory_mib`, and `disk_mib` are only supported for image launches.
|
|
158
158
|
|
|
159
|
+
### Manage volumes and mount them in a sandbox
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from hyperbrowser import Hyperbrowser
|
|
163
|
+
from hyperbrowser.models import CreateSandboxParams, CreateVolumeParams, SandboxVolumeMount
|
|
164
|
+
|
|
165
|
+
client = Hyperbrowser(api_key="test-key")
|
|
166
|
+
|
|
167
|
+
volume = client.volumes.create(CreateVolumeParams(name="project-cache"))
|
|
168
|
+
all_volumes = client.volumes.list()
|
|
169
|
+
same_volume = client.volumes.get(volume.id)
|
|
170
|
+
|
|
171
|
+
sandbox = client.sandboxes.create(
|
|
172
|
+
CreateSandboxParams(
|
|
173
|
+
image_name="node",
|
|
174
|
+
mounts={
|
|
175
|
+
"/workspace/cache": SandboxVolumeMount(
|
|
176
|
+
id=same_volume.id,
|
|
177
|
+
type="rw",
|
|
178
|
+
shared=True,
|
|
179
|
+
)
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
sandbox.stop()
|
|
185
|
+
client.close()
|
|
186
|
+
```
|
|
187
|
+
|
|
159
188
|
### List sandboxes with filters
|
|
160
189
|
|
|
161
190
|
```python
|
|
@@ -130,6 +130,35 @@ client.close()
|
|
|
130
130
|
|
|
131
131
|
`cpu`, `memory_mib`, and `disk_mib` are only supported for image launches.
|
|
132
132
|
|
|
133
|
+
### Manage volumes and mount them in a sandbox
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from hyperbrowser import Hyperbrowser
|
|
137
|
+
from hyperbrowser.models import CreateSandboxParams, CreateVolumeParams, SandboxVolumeMount
|
|
138
|
+
|
|
139
|
+
client = Hyperbrowser(api_key="test-key")
|
|
140
|
+
|
|
141
|
+
volume = client.volumes.create(CreateVolumeParams(name="project-cache"))
|
|
142
|
+
all_volumes = client.volumes.list()
|
|
143
|
+
same_volume = client.volumes.get(volume.id)
|
|
144
|
+
|
|
145
|
+
sandbox = client.sandboxes.create(
|
|
146
|
+
CreateSandboxParams(
|
|
147
|
+
image_name="node",
|
|
148
|
+
mounts={
|
|
149
|
+
"/workspace/cache": SandboxVolumeMount(
|
|
150
|
+
id=same_volume.id,
|
|
151
|
+
type="rw",
|
|
152
|
+
shared=True,
|
|
153
|
+
)
|
|
154
|
+
},
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
sandbox.stop()
|
|
159
|
+
client.close()
|
|
160
|
+
```
|
|
161
|
+
|
|
133
162
|
### List sandboxes with filters
|
|
134
163
|
|
|
135
164
|
```python
|
|
@@ -14,6 +14,7 @@ from .managers.async_manager.scrape import ScrapeManager
|
|
|
14
14
|
from .managers.async_manager.session import SessionManager
|
|
15
15
|
from .managers.async_manager.team import TeamManager
|
|
16
16
|
from .managers.async_manager.computer_action import ComputerActionManager
|
|
17
|
+
from .managers.async_manager.volume import VolumeManager
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class AsyncHyperbrowser(HyperbrowserBase):
|
|
@@ -47,6 +48,7 @@ class AsyncHyperbrowser(HyperbrowserBase):
|
|
|
47
48
|
self.team = TeamManager(self)
|
|
48
49
|
self.computer_action = ComputerActionManager(self)
|
|
49
50
|
self.sandboxes = SandboxManager(self)
|
|
51
|
+
self.volumes = VolumeManager(self)
|
|
50
52
|
|
|
51
53
|
async def close(self) -> None:
|
|
52
54
|
await self.transport.close()
|
{hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/async_manager/sandbox.py
RENAMED
|
@@ -217,16 +217,24 @@ class SandboxHandle:
|
|
|
217
217
|
)
|
|
218
218
|
return _copy_model(self._runtime_session)
|
|
219
219
|
|
|
220
|
-
async def exec(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
220
|
+
async def exec(
|
|
221
|
+
self,
|
|
222
|
+
input: Union[str, SandboxExecParams],
|
|
223
|
+
*,
|
|
224
|
+
cwd: Optional[str] = None,
|
|
225
|
+
env: Optional[Dict[str, str]] = None,
|
|
226
|
+
timeout_ms: Optional[int] = None,
|
|
227
|
+
timeout_sec: Optional[int] = None,
|
|
228
|
+
run_as: Optional[str] = None,
|
|
229
|
+
):
|
|
230
|
+
return await self.processes.exec(
|
|
231
|
+
input,
|
|
232
|
+
cwd=cwd,
|
|
233
|
+
env=env,
|
|
234
|
+
timeout_ms=timeout_ms,
|
|
235
|
+
timeout_sec=timeout_sec,
|
|
236
|
+
run_as=run_as,
|
|
237
|
+
)
|
|
230
238
|
|
|
231
239
|
async def get_process(self, process_id: str) -> SandboxProcessHandle:
|
|
232
240
|
return await self.processes.get(process_id)
|
|
@@ -5,7 +5,7 @@ import io
|
|
|
5
5
|
import json
|
|
6
6
|
import socket
|
|
7
7
|
from datetime import datetime
|
|
8
|
-
from typing import AsyncIterator, Callable, List, Optional, Union
|
|
8
|
+
from typing import Any, AsyncIterator, Callable, Dict, List, Optional, Union
|
|
9
9
|
from urllib.parse import urlencode
|
|
10
10
|
|
|
11
11
|
from websockets.asyncio.client import connect as async_ws_connect
|
|
@@ -249,10 +249,21 @@ class SandboxFilesApi:
|
|
|
249
249
|
transport: RuntimeTransport,
|
|
250
250
|
get_connection_info,
|
|
251
251
|
runtime_proxy_override: Optional[str] = None,
|
|
252
|
+
default_run_as: Optional[str] = None,
|
|
252
253
|
):
|
|
253
254
|
self._transport = transport
|
|
254
255
|
self._get_connection_info = get_connection_info
|
|
255
256
|
self._runtime_proxy_override = runtime_proxy_override
|
|
257
|
+
self._default_run_as = default_run_as.strip() if default_run_as else None
|
|
258
|
+
|
|
259
|
+
def with_run_as(self, run_as: Optional[str]):
|
|
260
|
+
normalized = run_as.strip() if run_as else None
|
|
261
|
+
return SandboxFilesApi(
|
|
262
|
+
self._transport,
|
|
263
|
+
self._get_connection_info,
|
|
264
|
+
self._runtime_proxy_override,
|
|
265
|
+
default_run_as=normalized,
|
|
266
|
+
)
|
|
256
267
|
|
|
257
268
|
async def list(
|
|
258
269
|
self,
|
|
@@ -266,17 +277,19 @@ class SandboxFilesApi:
|
|
|
266
277
|
|
|
267
278
|
payload = await self._transport.request_json(
|
|
268
279
|
"/sandbox/files",
|
|
269
|
-
params=
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
280
|
+
params=self._with_run_as_params(
|
|
281
|
+
{
|
|
282
|
+
"path": path,
|
|
283
|
+
"depth": depth,
|
|
284
|
+
}
|
|
285
|
+
),
|
|
273
286
|
)
|
|
274
287
|
return [_normalize_file_info(entry) for entry in payload.get("entries", [])]
|
|
275
288
|
|
|
276
289
|
async def get_info(self, path: str) -> SandboxFileInfo:
|
|
277
290
|
payload = await self._transport.request_json(
|
|
278
291
|
"/sandbox/files/stat",
|
|
279
|
-
params={"path": path},
|
|
292
|
+
params=self._with_run_as_params({"path": path}),
|
|
280
293
|
)
|
|
281
294
|
return _normalize_file_info(payload["file"])
|
|
282
295
|
|
|
@@ -357,10 +370,12 @@ class SandboxFilesApi:
|
|
|
357
370
|
payload = await self._transport.request_json(
|
|
358
371
|
"/sandbox/files/write",
|
|
359
372
|
method="POST",
|
|
360
|
-
json_body=
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
373
|
+
json_body=self._with_run_as_body(
|
|
374
|
+
{
|
|
375
|
+
"path": path_or_files,
|
|
376
|
+
**_encode_write_data(data),
|
|
377
|
+
}
|
|
378
|
+
),
|
|
364
379
|
headers={"content-type": "application/json"},
|
|
365
380
|
)
|
|
366
381
|
return _normalize_write_info(payload["files"][0])
|
|
@@ -377,7 +392,7 @@ class SandboxFilesApi:
|
|
|
377
392
|
payload = await self._transport.request_json(
|
|
378
393
|
"/sandbox/files/write",
|
|
379
394
|
method="POST",
|
|
380
|
-
json_body={"files": encoded_files},
|
|
395
|
+
json_body=self._with_run_as_body({"files": encoded_files}),
|
|
381
396
|
headers={"content-type": "application/json"},
|
|
382
397
|
)
|
|
383
398
|
return [_normalize_write_info(entry) for entry in payload.get("files", [])]
|
|
@@ -419,7 +434,7 @@ class SandboxFilesApi:
|
|
|
419
434
|
payload = await self._transport.request_json(
|
|
420
435
|
"/sandbox/files/upload",
|
|
421
436
|
method="PUT",
|
|
422
|
-
params={"path": path},
|
|
437
|
+
params=self._with_run_as_params({"path": path}),
|
|
423
438
|
content=body,
|
|
424
439
|
)
|
|
425
440
|
return SandboxFileTransferResult(**payload)
|
|
@@ -427,7 +442,7 @@ class SandboxFilesApi:
|
|
|
427
442
|
async def download(self, path: str) -> bytes:
|
|
428
443
|
return await self._transport.request_bytes(
|
|
429
444
|
"/sandbox/files/download",
|
|
430
|
-
params={"path": path},
|
|
445
|
+
params=self._with_run_as_params({"path": path}),
|
|
431
446
|
)
|
|
432
447
|
|
|
433
448
|
async def make_dir(
|
|
@@ -440,11 +455,13 @@ class SandboxFilesApi:
|
|
|
440
455
|
payload = await self._transport.request_json(
|
|
441
456
|
"/sandbox/files/mkdir",
|
|
442
457
|
method="POST",
|
|
443
|
-
json_body=
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
458
|
+
json_body=self._with_run_as_body(
|
|
459
|
+
{
|
|
460
|
+
"path": path,
|
|
461
|
+
"parents": parents,
|
|
462
|
+
"mode": mode,
|
|
463
|
+
}
|
|
464
|
+
),
|
|
448
465
|
headers={"content-type": "application/json"},
|
|
449
466
|
)
|
|
450
467
|
return bool(payload.get("created"))
|
|
@@ -475,7 +492,7 @@ class SandboxFilesApi:
|
|
|
475
492
|
payload = await self._transport.request_json(
|
|
476
493
|
"/sandbox/files/move",
|
|
477
494
|
method="POST",
|
|
478
|
-
json_body=payload,
|
|
495
|
+
json_body=self._with_run_as_body(payload),
|
|
479
496
|
headers={"content-type": "application/json"},
|
|
480
497
|
)
|
|
481
498
|
return _normalize_file_info(payload["entry"])
|
|
@@ -493,10 +510,12 @@ class SandboxFilesApi:
|
|
|
493
510
|
await self._transport.request_json(
|
|
494
511
|
"/sandbox/files/delete",
|
|
495
512
|
method="POST",
|
|
496
|
-
json_body=
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
513
|
+
json_body=self._with_run_as_body(
|
|
514
|
+
SandboxFileDeleteParams(
|
|
515
|
+
path=path,
|
|
516
|
+
recursive=recursive,
|
|
517
|
+
).model_dump(exclude_none=True)
|
|
518
|
+
),
|
|
500
519
|
headers={"content-type": "application/json"},
|
|
501
520
|
)
|
|
502
521
|
|
|
@@ -527,12 +546,14 @@ class SandboxFilesApi:
|
|
|
527
546
|
payload = await self._transport.request_json(
|
|
528
547
|
"/sandbox/files/copy",
|
|
529
548
|
method="POST",
|
|
530
|
-
json_body=
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
549
|
+
json_body=self._with_run_as_body(
|
|
550
|
+
{
|
|
551
|
+
"from": normalized.source,
|
|
552
|
+
"to": normalized.destination,
|
|
553
|
+
"recursive": normalized.recursive,
|
|
554
|
+
"overwrite": normalized.overwrite,
|
|
555
|
+
}
|
|
556
|
+
),
|
|
536
557
|
headers={"content-type": "application/json"},
|
|
537
558
|
)
|
|
538
559
|
return _normalize_file_info(payload["entry"])
|
|
@@ -558,7 +579,7 @@ class SandboxFilesApi:
|
|
|
558
579
|
await self._transport.request_json(
|
|
559
580
|
"/sandbox/files/chmod",
|
|
560
581
|
method="POST",
|
|
561
|
-
json_body=normalized.model_dump(exclude_none=True),
|
|
582
|
+
json_body=self._with_run_as_body(normalized.model_dump(exclude_none=True)),
|
|
562
583
|
headers={"content-type": "application/json"},
|
|
563
584
|
)
|
|
564
585
|
|
|
@@ -585,7 +606,7 @@ class SandboxFilesApi:
|
|
|
585
606
|
await self._transport.request_json(
|
|
586
607
|
"/sandbox/files/chown",
|
|
587
608
|
method="POST",
|
|
588
|
-
json_body=normalized.model_dump(exclude_none=True),
|
|
609
|
+
json_body=self._with_run_as_body(normalized.model_dump(exclude_none=True)),
|
|
589
610
|
headers={"content-type": "application/json"},
|
|
590
611
|
)
|
|
591
612
|
|
|
@@ -593,10 +614,12 @@ class SandboxFilesApi:
|
|
|
593
614
|
payload = await self._transport.request_json(
|
|
594
615
|
"/sandbox/files/watch",
|
|
595
616
|
method="POST",
|
|
596
|
-
json_body=
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
617
|
+
json_body=self._with_run_as_body(
|
|
618
|
+
{
|
|
619
|
+
"path": path,
|
|
620
|
+
"recursive": recursive,
|
|
621
|
+
}
|
|
622
|
+
),
|
|
600
623
|
headers={"content-type": "application/json"},
|
|
601
624
|
)
|
|
602
625
|
return SandboxFileWatchHandle(
|
|
@@ -646,11 +669,13 @@ class SandboxFilesApi:
|
|
|
646
669
|
payload = await self._transport.request_json(
|
|
647
670
|
"/sandbox/files/presign-upload",
|
|
648
671
|
method="POST",
|
|
649
|
-
json_body=
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
672
|
+
json_body=self._with_run_as_body(
|
|
673
|
+
SandboxPresignFileParams(
|
|
674
|
+
path=path,
|
|
675
|
+
expires_in_seconds=expires_in_seconds,
|
|
676
|
+
one_time=one_time,
|
|
677
|
+
).model_dump(exclude_none=True, by_alias=True)
|
|
678
|
+
),
|
|
654
679
|
headers={"content-type": "application/json"},
|
|
655
680
|
)
|
|
656
681
|
return SandboxPresignedUrl(**payload)
|
|
@@ -665,11 +690,13 @@ class SandboxFilesApi:
|
|
|
665
690
|
payload = await self._transport.request_json(
|
|
666
691
|
"/sandbox/files/presign-download",
|
|
667
692
|
method="POST",
|
|
668
|
-
json_body=
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
693
|
+
json_body=self._with_run_as_body(
|
|
694
|
+
SandboxPresignFileParams(
|
|
695
|
+
path=path,
|
|
696
|
+
expires_in_seconds=expires_in_seconds,
|
|
697
|
+
one_time=one_time,
|
|
698
|
+
).model_dump(exclude_none=True, by_alias=True)
|
|
699
|
+
),
|
|
673
700
|
headers={"content-type": "application/json"},
|
|
674
701
|
)
|
|
675
702
|
return SandboxPresignedUrl(**payload)
|
|
@@ -685,12 +712,14 @@ class SandboxFilesApi:
|
|
|
685
712
|
payload = await self._transport.request_json(
|
|
686
713
|
"/sandbox/files/read",
|
|
687
714
|
method="POST",
|
|
688
|
-
json_body=
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
715
|
+
json_body=self._with_run_as_body(
|
|
716
|
+
{
|
|
717
|
+
"path": path,
|
|
718
|
+
"offset": offset,
|
|
719
|
+
"length": length,
|
|
720
|
+
"encoding": encoding,
|
|
721
|
+
}
|
|
722
|
+
),
|
|
694
723
|
headers={"content-type": "application/json"},
|
|
695
724
|
)
|
|
696
725
|
return SandboxFileReadResult(**payload)
|
|
@@ -707,13 +736,31 @@ class SandboxFilesApi:
|
|
|
707
736
|
payload = await self._transport.request_json(
|
|
708
737
|
"/sandbox/files/write",
|
|
709
738
|
method="POST",
|
|
710
|
-
json_body=
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
739
|
+
json_body=self._with_run_as_body(
|
|
740
|
+
{
|
|
741
|
+
"path": path,
|
|
742
|
+
"data": data,
|
|
743
|
+
"append": append,
|
|
744
|
+
"mode": mode,
|
|
745
|
+
"encoding": encoding,
|
|
746
|
+
}
|
|
747
|
+
),
|
|
717
748
|
headers={"content-type": "application/json"},
|
|
718
749
|
)
|
|
719
750
|
return _normalize_write_info(payload["files"][0])
|
|
751
|
+
|
|
752
|
+
def _with_run_as_params(
|
|
753
|
+
self, params: Dict[str, Union[str, int, bool, None]]
|
|
754
|
+
) -> Dict[str, Union[str, int, bool, None]]:
|
|
755
|
+
if not self._default_run_as:
|
|
756
|
+
return params
|
|
757
|
+
enriched = dict(params)
|
|
758
|
+
enriched["runAs"] = self._default_run_as
|
|
759
|
+
return enriched
|
|
760
|
+
|
|
761
|
+
def _with_run_as_body(self, body: Dict[str, Any]) -> Dict[str, Any]:
|
|
762
|
+
if not self._default_run_as:
|
|
763
|
+
return body
|
|
764
|
+
enriched = dict(body)
|
|
765
|
+
enriched["runAs"] = self._default_run_as
|
|
766
|
+
return enriched
|
|
@@ -10,6 +10,7 @@ from .....models.sandbox import (
|
|
|
10
10
|
SandboxProcessStdinParams,
|
|
11
11
|
SandboxProcessSummary,
|
|
12
12
|
)
|
|
13
|
+
from ...sandboxes.shared import _normalize_exec_params
|
|
13
14
|
from .sandbox_transport import RuntimeTransport
|
|
14
15
|
|
|
15
16
|
DEFAULT_PROCESS_KILL_WAIT_SECONDS = 5.0
|
|
@@ -147,24 +148,54 @@ class SandboxProcessesApi:
|
|
|
147
148
|
def __init__(self, transport: RuntimeTransport):
|
|
148
149
|
self._transport = transport
|
|
149
150
|
|
|
150
|
-
async def exec(
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
async def exec(
|
|
152
|
+
self,
|
|
153
|
+
input: Union[str, SandboxExecParams],
|
|
154
|
+
*,
|
|
155
|
+
cwd: Optional[str] = None,
|
|
156
|
+
env: Optional[Dict[str, str]] = None,
|
|
157
|
+
timeout_ms: Optional[int] = None,
|
|
158
|
+
timeout_sec: Optional[int] = None,
|
|
159
|
+
run_as: Optional[str] = None,
|
|
160
|
+
) -> SandboxProcessResult:
|
|
161
|
+
params = _normalize_exec_params(
|
|
162
|
+
input,
|
|
163
|
+
cwd=cwd,
|
|
164
|
+
env=env,
|
|
165
|
+
timeout_ms=timeout_ms,
|
|
166
|
+
timeout_sec=timeout_sec,
|
|
167
|
+
run_as=run_as,
|
|
168
|
+
)
|
|
153
169
|
payload = await self._transport.request_json(
|
|
154
170
|
"/sandbox/exec",
|
|
155
171
|
method="POST",
|
|
156
|
-
json_body=
|
|
172
|
+
json_body=params.model_dump(exclude_none=True, by_alias=True),
|
|
157
173
|
headers={"content-type": "application/json"},
|
|
158
174
|
)
|
|
159
175
|
return SandboxProcessResult(**payload["result"])
|
|
160
176
|
|
|
161
|
-
async def start(
|
|
162
|
-
|
|
163
|
-
|
|
177
|
+
async def start(
|
|
178
|
+
self,
|
|
179
|
+
input: Union[str, SandboxExecParams],
|
|
180
|
+
*,
|
|
181
|
+
cwd: Optional[str] = None,
|
|
182
|
+
env: Optional[Dict[str, str]] = None,
|
|
183
|
+
timeout_ms: Optional[int] = None,
|
|
184
|
+
timeout_sec: Optional[int] = None,
|
|
185
|
+
run_as: Optional[str] = None,
|
|
186
|
+
) -> SandboxProcessHandle:
|
|
187
|
+
params = _normalize_exec_params(
|
|
188
|
+
input,
|
|
189
|
+
cwd=cwd,
|
|
190
|
+
env=env,
|
|
191
|
+
timeout_ms=timeout_ms,
|
|
192
|
+
timeout_sec=timeout_sec,
|
|
193
|
+
run_as=run_as,
|
|
194
|
+
)
|
|
164
195
|
payload = await self._transport.request_json(
|
|
165
196
|
"/sandbox/processes",
|
|
166
197
|
method="POST",
|
|
167
|
-
json_body=
|
|
198
|
+
json_body=params.model_dump(exclude_none=True, by_alias=True),
|
|
168
199
|
headers={"content-type": "application/json"},
|
|
169
200
|
)
|
|
170
201
|
return SandboxProcessHandle(
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from hyperbrowser.models.volume import CreateVolumeParams, Volume, VolumeListResponse
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class VolumeManager:
|
|
5
|
+
def __init__(self, client):
|
|
6
|
+
self._client = client
|
|
7
|
+
|
|
8
|
+
async def create(self, params: CreateVolumeParams) -> Volume:
|
|
9
|
+
if not isinstance(params, CreateVolumeParams):
|
|
10
|
+
raise TypeError("params must be a CreateVolumeParams instance")
|
|
11
|
+
|
|
12
|
+
response = await self._client.transport.post(
|
|
13
|
+
self._client._build_url("/volume"),
|
|
14
|
+
data=params.model_dump(exclude_none=True, by_alias=True),
|
|
15
|
+
)
|
|
16
|
+
return Volume(**response.data)
|
|
17
|
+
|
|
18
|
+
async def list(self) -> VolumeListResponse:
|
|
19
|
+
response = await self._client.transport.get(self._client._build_url("/volume"))
|
|
20
|
+
return VolumeListResponse(**response.data)
|
|
21
|
+
|
|
22
|
+
async def get(self, volume_id: str) -> Volume:
|
|
23
|
+
response = await self._client.transport.get(
|
|
24
|
+
self._client._build_url(f"/volume/{volume_id}")
|
|
25
|
+
)
|
|
26
|
+
return Volume(**response.data)
|
{hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sandboxes/shared.py
RENAMED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import posixpath
|
|
3
|
+
import re
|
|
3
4
|
from datetime import datetime, timedelta, timezone
|
|
4
5
|
from typing import Dict, Optional, Union
|
|
5
6
|
from urllib.parse import urlencode, urlsplit, urlunsplit
|
|
6
7
|
|
|
7
8
|
from ....exceptions import HyperbrowserError
|
|
8
9
|
from ....models.sandbox import (
|
|
10
|
+
SandboxExecParams,
|
|
9
11
|
SandboxFileInfo,
|
|
10
12
|
SandboxFileWriteEntry,
|
|
11
13
|
SandboxFileWriteInfo,
|
|
@@ -18,12 +20,70 @@ from ....sandbox_common import (
|
|
|
18
20
|
)
|
|
19
21
|
|
|
20
22
|
DEFAULT_WATCH_TIMEOUT_MS = 60_000
|
|
23
|
+
SHELL_SAFE_TOKEN_PATTERN = re.compile(r"^[A-Za-z0-9_@%+=:,./-]+$")
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
def _copy_model(model):
|
|
24
27
|
return model.model_copy(deep=True)
|
|
25
28
|
|
|
26
29
|
|
|
30
|
+
def _quote_shell_token(token: str) -> str:
|
|
31
|
+
if token == "":
|
|
32
|
+
return "''"
|
|
33
|
+
if SHELL_SAFE_TOKEN_PATTERN.fullmatch(token):
|
|
34
|
+
return token
|
|
35
|
+
return "'" + token.replace("'", "'\"'\"'") + "'"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _normalize_legacy_process_fields(params: SandboxExecParams) -> SandboxExecParams:
|
|
39
|
+
updates = {}
|
|
40
|
+
|
|
41
|
+
if params.args:
|
|
42
|
+
updates["command"] = " ".join(
|
|
43
|
+
_quote_shell_token(token) for token in [params.command, *params.args]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if params.args is not None:
|
|
47
|
+
updates["args"] = None
|
|
48
|
+
|
|
49
|
+
if params.use_shell is not None:
|
|
50
|
+
updates["use_shell"] = None
|
|
51
|
+
|
|
52
|
+
return params.model_copy(update=updates) if updates else params
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _normalize_exec_params(
|
|
56
|
+
input: Union[str, SandboxExecParams],
|
|
57
|
+
*,
|
|
58
|
+
cwd: Optional[str] = None,
|
|
59
|
+
env: Optional[Dict[str, str]] = None,
|
|
60
|
+
timeout_ms: Optional[int] = None,
|
|
61
|
+
timeout_sec: Optional[int] = None,
|
|
62
|
+
run_as: Optional[str] = None,
|
|
63
|
+
) -> SandboxExecParams:
|
|
64
|
+
if isinstance(input, str):
|
|
65
|
+
params = SandboxExecParams(command=input)
|
|
66
|
+
elif isinstance(input, SandboxExecParams):
|
|
67
|
+
params = input
|
|
68
|
+
else:
|
|
69
|
+
raise TypeError("input must be a command string or SandboxExecParams instance")
|
|
70
|
+
|
|
71
|
+
updates = {}
|
|
72
|
+
if cwd is not None:
|
|
73
|
+
updates["cwd"] = cwd
|
|
74
|
+
if env is not None:
|
|
75
|
+
updates["env"] = env
|
|
76
|
+
if timeout_ms is not None:
|
|
77
|
+
updates["timeout_ms"] = timeout_ms
|
|
78
|
+
if timeout_sec is not None:
|
|
79
|
+
updates["timeout_sec"] = timeout_sec
|
|
80
|
+
if run_as is not None:
|
|
81
|
+
updates["run_as"] = run_as
|
|
82
|
+
|
|
83
|
+
normalized = params.model_copy(update=updates) if updates else params
|
|
84
|
+
return _normalize_legacy_process_fields(normalized)
|
|
85
|
+
|
|
86
|
+
|
|
27
87
|
def _build_sandbox_exposed_url(runtime, port: int) -> str:
|
|
28
88
|
parsed = urlsplit(runtime.base_url)
|
|
29
89
|
hostname = parsed.hostname
|
{hyperbrowser-0.90.0 → hyperbrowser-0.90.2}/hyperbrowser/client/managers/sync_manager/sandbox.py
RENAMED
|
@@ -217,16 +217,24 @@ class SandboxHandle:
|
|
|
217
217
|
)
|
|
218
218
|
return _copy_model(self._runtime_session)
|
|
219
219
|
|
|
220
|
-
def exec(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
220
|
+
def exec(
|
|
221
|
+
self,
|
|
222
|
+
input: Union[str, SandboxExecParams],
|
|
223
|
+
*,
|
|
224
|
+
cwd: Optional[str] = None,
|
|
225
|
+
env: Optional[Dict[str, str]] = None,
|
|
226
|
+
timeout_ms: Optional[int] = None,
|
|
227
|
+
timeout_sec: Optional[int] = None,
|
|
228
|
+
run_as: Optional[str] = None,
|
|
229
|
+
):
|
|
230
|
+
return self.processes.exec(
|
|
231
|
+
input,
|
|
232
|
+
cwd=cwd,
|
|
233
|
+
env=env,
|
|
234
|
+
timeout_ms=timeout_ms,
|
|
235
|
+
timeout_sec=timeout_sec,
|
|
236
|
+
run_as=run_as,
|
|
237
|
+
)
|
|
230
238
|
|
|
231
239
|
def get_process(self, process_id: str) -> SandboxProcessHandle:
|
|
232
240
|
return self.processes.get(process_id)
|