hyperbrowser 1.3.0__tar.gz → 1.4.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-1.3.0 → hyperbrowser-1.4.0}/PKG-INFO +1 -1
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/sandbox.py +30 -15
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_transport.py +4 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/sandbox.py +30 -15
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_transport.py +4 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/sandbox_common.py +64 -1
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/transport/async_transport.py +43 -14
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/transport/sync.py +45 -14
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/pyproject.toml +1 -1
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/LICENSE +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/README.md +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/_request.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/async_client.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/base.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/agents/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/agents/browser_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/agents/claude_computer_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/agents/cua.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/agents/gemini_computer_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/agents/grok_computer_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/agents/hyper_agent.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/computer_action.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/crawl.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/extension.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/extract.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/profile.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/sandboxes/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_files.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_processes.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_terminal.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/scrape.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/session.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/team.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/volume.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/web/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/web/batch_fetch.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/web/crawl.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sandboxes/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sandboxes/dockerfile_analysis.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sandboxes/dockerignore.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sandboxes/image_build.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sandboxes/shared.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/agents/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/agents/browser_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/agents/claude_computer_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/agents/cua.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/agents/gemini_computer_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/agents/grok_computer_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/agents/hyper_agent.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/computer_action.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/crawl.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/extension.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/extract.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/profile.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/sandboxes/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_files.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_processes.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_terminal.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/scrape.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/session.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/team.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/volume.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/web/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/web/batch_fetch.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/web/crawl.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/sync.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/config.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/exceptions.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/_parsers.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/agents/browser_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/agents/claude_computer_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/agents/cua.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/agents/gemini_computer_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/agents/grok_computer_use.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/agents/hyper_agent.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/computer_action.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/consts.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/crawl.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/extension.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/extract.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/profile.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/sandbox.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/scrape.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/session.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/team.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/volume.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/web/batch_fetch.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/web/branding.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/web/common.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/web/crawl.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/web/fetch.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/models/web/search.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/py.typed +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/tools/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/tools/anthropic.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/tools/openai.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/tools/schema.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/transport/base.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/__init__.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/_json.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/agents.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/computer_action.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/crawl.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/extension.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/extract.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/profile.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/sandbox.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/scrape.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/session.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/volume.py +0 -0
- {hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/types/web.py +0 -0
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/sandbox.py
RENAMED
|
@@ -57,8 +57,11 @@ from ....types import (
|
|
|
57
57
|
from ....sandbox_common import (
|
|
58
58
|
RuntimeConnection,
|
|
59
59
|
ensure_response_ok,
|
|
60
|
+
get_retry_delay_seconds,
|
|
60
61
|
normalize_network_error,
|
|
62
|
+
request_context,
|
|
61
63
|
parse_json_response,
|
|
64
|
+
should_retry_get,
|
|
62
65
|
)
|
|
63
66
|
from ..sandboxes.shared import (
|
|
64
67
|
_build_sandbox_exposed_url,
|
|
@@ -942,19 +945,31 @@ class SandboxManager:
|
|
|
942
945
|
params: Optional[Dict[str, object]] = None,
|
|
943
946
|
data: Optional[Dict[str, object]] = None,
|
|
944
947
|
):
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
self._client.
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
948
|
+
failed_attempt = 1
|
|
949
|
+
while True:
|
|
950
|
+
try:
|
|
951
|
+
response = await self._client.transport.client.request(
|
|
952
|
+
method,
|
|
953
|
+
self._client._build_url(path),
|
|
954
|
+
params={
|
|
955
|
+
key: value
|
|
956
|
+
for key, value in (params or {}).items()
|
|
957
|
+
if value is not None
|
|
958
|
+
},
|
|
959
|
+
json=data,
|
|
960
|
+
)
|
|
961
|
+
ensure_response_ok(response, "control")
|
|
962
|
+
except BaseException as cause:
|
|
963
|
+
error = normalize_network_error(
|
|
964
|
+
cause,
|
|
965
|
+
"control",
|
|
966
|
+
"Unknown error occurred",
|
|
967
|
+
request_context(method, path),
|
|
968
|
+
)
|
|
969
|
+
if not should_retry_get(method, error, failed_attempt):
|
|
970
|
+
raise error
|
|
971
|
+
await asyncio.sleep(get_retry_delay_seconds(failed_attempt))
|
|
972
|
+
failed_attempt += 1
|
|
973
|
+
continue
|
|
958
974
|
|
|
959
|
-
|
|
960
|
-
return parse_json_response(response, "control")
|
|
975
|
+
return parse_json_response(response, "control")
|
|
@@ -9,6 +9,7 @@ from .....sandbox_common import (
|
|
|
9
9
|
ensure_response_ok,
|
|
10
10
|
normalize_network_error,
|
|
11
11
|
parse_json_response,
|
|
12
|
+
request_context,
|
|
12
13
|
resolve_runtime_transport_target,
|
|
13
14
|
)
|
|
14
15
|
from ...sandboxes.shared import _build_query_path, _is_replayable_http_content
|
|
@@ -275,6 +276,7 @@ class RuntimeTransport:
|
|
|
275
276
|
error,
|
|
276
277
|
"runtime",
|
|
277
278
|
"Unknown runtime request error",
|
|
279
|
+
request_context(method, path),
|
|
278
280
|
)
|
|
279
281
|
|
|
280
282
|
await response.aread()
|
|
@@ -309,6 +311,7 @@ class RuntimeTransport:
|
|
|
309
311
|
error,
|
|
310
312
|
"runtime",
|
|
311
313
|
"Unknown runtime request error",
|
|
314
|
+
request_context(method, path),
|
|
312
315
|
)
|
|
313
316
|
|
|
314
317
|
async def _send_stream(
|
|
@@ -341,4 +344,5 @@ class RuntimeTransport:
|
|
|
341
344
|
error,
|
|
342
345
|
"runtime",
|
|
343
346
|
"Unknown runtime request error",
|
|
347
|
+
request_context("GET", path),
|
|
344
348
|
)
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/sandbox.py
RENAMED
|
@@ -55,8 +55,11 @@ from ....types import (
|
|
|
55
55
|
from ....sandbox_common import (
|
|
56
56
|
RuntimeConnection,
|
|
57
57
|
ensure_response_ok,
|
|
58
|
+
get_retry_delay_seconds,
|
|
58
59
|
normalize_network_error,
|
|
60
|
+
request_context,
|
|
59
61
|
parse_json_response,
|
|
62
|
+
should_retry_get,
|
|
60
63
|
)
|
|
61
64
|
from ..sandboxes.shared import (
|
|
62
65
|
_build_sandbox_exposed_url,
|
|
@@ -925,19 +928,31 @@ class SandboxManager:
|
|
|
925
928
|
params: Optional[Dict[str, object]] = None,
|
|
926
929
|
data: Optional[Dict[str, object]] = None,
|
|
927
930
|
):
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
self._client.
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
931
|
+
failed_attempt = 1
|
|
932
|
+
while True:
|
|
933
|
+
try:
|
|
934
|
+
response = self._client.transport.client.request(
|
|
935
|
+
method,
|
|
936
|
+
self._client._build_url(path),
|
|
937
|
+
params={
|
|
938
|
+
key: value
|
|
939
|
+
for key, value in (params or {}).items()
|
|
940
|
+
if value is not None
|
|
941
|
+
},
|
|
942
|
+
json=data,
|
|
943
|
+
)
|
|
944
|
+
ensure_response_ok(response, "control")
|
|
945
|
+
except BaseException as cause:
|
|
946
|
+
error = normalize_network_error(
|
|
947
|
+
cause,
|
|
948
|
+
"control",
|
|
949
|
+
"Unknown error occurred",
|
|
950
|
+
request_context(method, path),
|
|
951
|
+
)
|
|
952
|
+
if not should_retry_get(method, error, failed_attempt):
|
|
953
|
+
raise error
|
|
954
|
+
time.sleep(get_retry_delay_seconds(failed_attempt))
|
|
955
|
+
failed_attempt += 1
|
|
956
|
+
continue
|
|
941
957
|
|
|
942
|
-
|
|
943
|
-
return parse_json_response(response, "control")
|
|
958
|
+
return parse_json_response(response, "control")
|
|
@@ -9,6 +9,7 @@ from .....sandbox_common import (
|
|
|
9
9
|
ensure_response_ok,
|
|
10
10
|
normalize_network_error,
|
|
11
11
|
parse_json_response,
|
|
12
|
+
request_context,
|
|
12
13
|
resolve_runtime_transport_target,
|
|
13
14
|
)
|
|
14
15
|
from ...sandboxes.shared import _build_query_path, _is_replayable_http_content
|
|
@@ -273,6 +274,7 @@ class RuntimeTransport:
|
|
|
273
274
|
error,
|
|
274
275
|
"runtime",
|
|
275
276
|
"Unknown runtime request error",
|
|
277
|
+
request_context(method, path),
|
|
276
278
|
)
|
|
277
279
|
|
|
278
280
|
response.read()
|
|
@@ -307,6 +309,7 @@ class RuntimeTransport:
|
|
|
307
309
|
error,
|
|
308
310
|
"runtime",
|
|
309
311
|
"Unknown runtime request error",
|
|
312
|
+
request_context(method, path),
|
|
310
313
|
)
|
|
311
314
|
|
|
312
315
|
def _send_stream(
|
|
@@ -339,4 +342,5 @@ class RuntimeTransport:
|
|
|
339
342
|
error,
|
|
340
343
|
"runtime",
|
|
341
344
|
"Unknown runtime request error",
|
|
345
|
+
request_context("GET", path),
|
|
342
346
|
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import random
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from typing import Any, Dict, Optional, Tuple
|
|
4
5
|
from urllib.parse import urljoin, urlsplit, urlunsplit
|
|
@@ -8,6 +9,9 @@ import httpx
|
|
|
8
9
|
from .exceptions import HyperbrowserError, HyperbrowserService
|
|
9
10
|
|
|
10
11
|
RETRYABLE_STATUS_CODES = {429, 502, 503, 504}
|
|
12
|
+
GET_RETRY_MAX_ATTEMPTS = 3
|
|
13
|
+
GET_RETRY_INITIAL_DELAY_SECONDS = 0.25
|
|
14
|
+
GET_RETRY_MAX_DELAY_SECONDS = 1.0
|
|
11
15
|
RUNTIME_SESSION_REFRESH_BUFFER_MS = 60_000
|
|
12
16
|
|
|
13
17
|
|
|
@@ -45,6 +49,26 @@ def is_retryable_network_error(error: BaseException) -> bool:
|
|
|
45
49
|
)
|
|
46
50
|
|
|
47
51
|
|
|
52
|
+
def should_retry_get(
|
|
53
|
+
method: str,
|
|
54
|
+
error: HyperbrowserError,
|
|
55
|
+
failed_attempt: int,
|
|
56
|
+
) -> bool:
|
|
57
|
+
return (
|
|
58
|
+
method.upper() == "GET"
|
|
59
|
+
and error.retryable
|
|
60
|
+
and failed_attempt < GET_RETRY_MAX_ATTEMPTS
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_retry_delay_seconds(failed_attempt: int) -> float:
|
|
65
|
+
maximum_delay = min(
|
|
66
|
+
GET_RETRY_INITIAL_DELAY_SECONDS * (2 ** (failed_attempt - 1)),
|
|
67
|
+
GET_RETRY_MAX_DELAY_SECONDS,
|
|
68
|
+
)
|
|
69
|
+
return random.uniform(maximum_delay / 2, maximum_delay)
|
|
70
|
+
|
|
71
|
+
|
|
48
72
|
def parse_error_payload(
|
|
49
73
|
raw_text: str, fallback_message: str
|
|
50
74
|
) -> Tuple[str, Optional[str], Any]:
|
|
@@ -230,16 +254,55 @@ def to_websocket_transport_target(
|
|
|
230
254
|
)
|
|
231
255
|
|
|
232
256
|
|
|
257
|
+
def request_context(method: Optional[str], path_or_url: Optional[str]) -> str:
|
|
258
|
+
"""Render "[POST /sandbox]" for error messages.
|
|
259
|
+
|
|
260
|
+
Accepts either a bare path or a full URL. The query string is dropped so
|
|
261
|
+
request parameters never reach error text or logs.
|
|
262
|
+
"""
|
|
263
|
+
normalized_method = (method or "").strip().upper()
|
|
264
|
+
raw_target = (path_or_url or "").strip()
|
|
265
|
+
normalized_path = urlsplit(raw_target).path or raw_target.split("?", 1)[0]
|
|
266
|
+
if normalized_method and normalized_path:
|
|
267
|
+
return f"[{normalized_method} {normalized_path}]"
|
|
268
|
+
if normalized_path:
|
|
269
|
+
return f"[{normalized_path}]"
|
|
270
|
+
return f"[{normalized_method}]" if normalized_method else ""
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def describe_network_error(
|
|
274
|
+
error: BaseException,
|
|
275
|
+
default_message: str,
|
|
276
|
+
context: str = "",
|
|
277
|
+
) -> str:
|
|
278
|
+
suffix = f" {context}" if context else ""
|
|
279
|
+
detail = str(error).strip()
|
|
280
|
+
if detail:
|
|
281
|
+
return f"{detail}{suffix}"
|
|
282
|
+
# Several httpx transport exceptions are raised with no arguments, so str()
|
|
283
|
+
# is empty and the caller's fallback alone would not say which one failed.
|
|
284
|
+
name = type(error).__name__
|
|
285
|
+
base = f"{default_message} ({name})" if default_message else name
|
|
286
|
+
return f"{base}{suffix}"
|
|
287
|
+
|
|
288
|
+
|
|
233
289
|
def normalize_network_error(
|
|
234
290
|
error: BaseException,
|
|
235
291
|
service: HyperbrowserService,
|
|
236
292
|
default_message: str,
|
|
293
|
+
context: str = "",
|
|
237
294
|
) -> HyperbrowserError:
|
|
238
295
|
if isinstance(error, HyperbrowserError):
|
|
239
296
|
return error
|
|
297
|
+
if not isinstance(error, Exception):
|
|
298
|
+
# CancelledError, KeyboardInterrupt and SystemExit are control flow, not
|
|
299
|
+
# transport failures. Callers catch BaseException around their requests,
|
|
300
|
+
# so wrapping these would strand the cancellation and report a request
|
|
301
|
+
# the caller itself abandoned as a network error.
|
|
302
|
+
raise error
|
|
240
303
|
|
|
241
304
|
return HyperbrowserError(
|
|
242
|
-
|
|
305
|
+
describe_network_error(error, default_message, context),
|
|
243
306
|
retryable=is_retryable_network_error(error),
|
|
244
307
|
service=service,
|
|
245
308
|
cause=error,
|
|
@@ -3,6 +3,15 @@ import httpx
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from hyperbrowser.exceptions import HyperbrowserError
|
|
6
|
+
from hyperbrowser.sandbox_common import (
|
|
7
|
+
RETRYABLE_STATUS_CODES,
|
|
8
|
+
get_request_id,
|
|
9
|
+
get_retry_delay_seconds,
|
|
10
|
+
is_retryable_network_error,
|
|
11
|
+
normalize_network_error,
|
|
12
|
+
request_context,
|
|
13
|
+
should_retry_get,
|
|
14
|
+
)
|
|
6
15
|
from .base import TransportStrategy, APIResponse
|
|
7
16
|
|
|
8
17
|
|
|
@@ -49,6 +58,9 @@ class AsyncTransport(TransportStrategy):
|
|
|
49
58
|
status_code=response.status_code,
|
|
50
59
|
response=response,
|
|
51
60
|
original_error=e,
|
|
61
|
+
request_id=get_request_id(response),
|
|
62
|
+
retryable=response.status_code in RETRYABLE_STATUS_CODES,
|
|
63
|
+
service="control",
|
|
52
64
|
)
|
|
53
65
|
return APIResponse.from_status(response.status_code)
|
|
54
66
|
except httpx.HTTPStatusError as e:
|
|
@@ -62,9 +74,17 @@ class AsyncTransport(TransportStrategy):
|
|
|
62
74
|
status_code=response.status_code,
|
|
63
75
|
response=response,
|
|
64
76
|
original_error=e,
|
|
77
|
+
request_id=get_request_id(response),
|
|
78
|
+
retryable=response.status_code in RETRYABLE_STATUS_CODES,
|
|
79
|
+
service="control",
|
|
65
80
|
)
|
|
66
81
|
except httpx.RequestError as e:
|
|
67
|
-
raise HyperbrowserError(
|
|
82
|
+
raise HyperbrowserError(
|
|
83
|
+
"Request failed",
|
|
84
|
+
original_error=e,
|
|
85
|
+
retryable=is_retryable_network_error(e),
|
|
86
|
+
service="control",
|
|
87
|
+
)
|
|
68
88
|
|
|
69
89
|
async def post(
|
|
70
90
|
self,
|
|
@@ -82,25 +102,34 @@ class AsyncTransport(TransportStrategy):
|
|
|
82
102
|
else:
|
|
83
103
|
response = await self.client.post(url, json=data, **kwargs)
|
|
84
104
|
return await self._handle_response(response)
|
|
85
|
-
except
|
|
86
|
-
raise
|
|
87
|
-
|
|
88
|
-
|
|
105
|
+
except BaseException as e:
|
|
106
|
+
raise normalize_network_error(
|
|
107
|
+
e, "control", "Post request failed", request_context("POST", url)
|
|
108
|
+
)
|
|
89
109
|
|
|
90
110
|
async def get(
|
|
91
111
|
self, url: str, params: Optional[dict] = None, follow_redirects: bool = False
|
|
92
112
|
) -> APIResponse:
|
|
93
113
|
if params:
|
|
94
114
|
params = {k: v for k, v in params.items() if v is not None}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
failed_attempt = 1
|
|
116
|
+
while True:
|
|
117
|
+
try:
|
|
118
|
+
response = await self.client.get(
|
|
119
|
+
url, params=params, follow_redirects=follow_redirects
|
|
120
|
+
)
|
|
121
|
+
return await self._handle_response(response)
|
|
122
|
+
except BaseException as cause:
|
|
123
|
+
error = normalize_network_error(
|
|
124
|
+
cause,
|
|
125
|
+
"control",
|
|
126
|
+
"Get request failed",
|
|
127
|
+
request_context("GET", url),
|
|
128
|
+
)
|
|
129
|
+
if not should_retry_get("GET", error, failed_attempt):
|
|
130
|
+
raise error
|
|
131
|
+
await asyncio.sleep(get_retry_delay_seconds(failed_attempt))
|
|
132
|
+
failed_attempt += 1
|
|
104
133
|
|
|
105
134
|
async def put(self, url: str, data: Optional[dict] = None) -> APIResponse:
|
|
106
135
|
try:
|
|
@@ -1,7 +1,18 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
1
3
|
import httpx
|
|
2
4
|
from typing import Optional
|
|
3
5
|
|
|
4
6
|
from hyperbrowser.exceptions import HyperbrowserError
|
|
7
|
+
from hyperbrowser.sandbox_common import (
|
|
8
|
+
RETRYABLE_STATUS_CODES,
|
|
9
|
+
get_request_id,
|
|
10
|
+
get_retry_delay_seconds,
|
|
11
|
+
is_retryable_network_error,
|
|
12
|
+
normalize_network_error,
|
|
13
|
+
request_context,
|
|
14
|
+
should_retry_get,
|
|
15
|
+
)
|
|
5
16
|
from .base import TransportStrategy, APIResponse
|
|
6
17
|
|
|
7
18
|
|
|
@@ -25,6 +36,9 @@ class SyncTransport(TransportStrategy):
|
|
|
25
36
|
status_code=response.status_code,
|
|
26
37
|
response=response,
|
|
27
38
|
original_error=e,
|
|
39
|
+
request_id=get_request_id(response),
|
|
40
|
+
retryable=response.status_code in RETRYABLE_STATUS_CODES,
|
|
41
|
+
service="control",
|
|
28
42
|
)
|
|
29
43
|
return APIResponse.from_status(response.status_code)
|
|
30
44
|
except httpx.HTTPStatusError as e:
|
|
@@ -38,9 +52,17 @@ class SyncTransport(TransportStrategy):
|
|
|
38
52
|
status_code=response.status_code,
|
|
39
53
|
response=response,
|
|
40
54
|
original_error=e,
|
|
55
|
+
request_id=get_request_id(response),
|
|
56
|
+
retryable=response.status_code in RETRYABLE_STATUS_CODES,
|
|
57
|
+
service="control",
|
|
41
58
|
)
|
|
42
59
|
except httpx.RequestError as e:
|
|
43
|
-
raise HyperbrowserError(
|
|
60
|
+
raise HyperbrowserError(
|
|
61
|
+
"Request failed",
|
|
62
|
+
original_error=e,
|
|
63
|
+
retryable=is_retryable_network_error(e),
|
|
64
|
+
service="control",
|
|
65
|
+
)
|
|
44
66
|
|
|
45
67
|
def close(self) -> None:
|
|
46
68
|
self.client.close()
|
|
@@ -61,25 +83,34 @@ class SyncTransport(TransportStrategy):
|
|
|
61
83
|
else:
|
|
62
84
|
response = self.client.post(url, json=data, **kwargs)
|
|
63
85
|
return self._handle_response(response)
|
|
64
|
-
except
|
|
65
|
-
raise
|
|
66
|
-
|
|
67
|
-
|
|
86
|
+
except BaseException as e:
|
|
87
|
+
raise normalize_network_error(
|
|
88
|
+
e, "control", "Post request failed", request_context("POST", url)
|
|
89
|
+
)
|
|
68
90
|
|
|
69
91
|
def get(
|
|
70
92
|
self, url: str, params: Optional[dict] = None, follow_redirects: bool = False
|
|
71
93
|
) -> APIResponse:
|
|
72
94
|
if params:
|
|
73
95
|
params = {k: v for k, v in params.items() if v is not None}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
96
|
+
failed_attempt = 1
|
|
97
|
+
while True:
|
|
98
|
+
try:
|
|
99
|
+
response = self.client.get(
|
|
100
|
+
url, params=params, follow_redirects=follow_redirects
|
|
101
|
+
)
|
|
102
|
+
return self._handle_response(response)
|
|
103
|
+
except BaseException as cause:
|
|
104
|
+
error = normalize_network_error(
|
|
105
|
+
cause,
|
|
106
|
+
"control",
|
|
107
|
+
"Get request failed",
|
|
108
|
+
request_context("GET", url),
|
|
109
|
+
)
|
|
110
|
+
if not should_retry_get("GET", error, failed_attempt):
|
|
111
|
+
raise error
|
|
112
|
+
time.sleep(get_retry_delay_seconds(failed_attempt))
|
|
113
|
+
failed_attempt += 1
|
|
83
114
|
|
|
84
115
|
def put(self, url: str, data: Optional[dict] = None) -> APIResponse:
|
|
85
116
|
try:
|
|
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
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/agents/cua.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/crawl.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/extension.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/extract.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/profile.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/scrape.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/session.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/team.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/volume.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/web/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/async_manager/web/crawl.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sandboxes/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sandboxes/dockerignore.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sandboxes/image_build.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/agents/cua.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/crawl.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/extension.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/extract.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/profile.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/scrape.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/session.py
RENAMED
|
File without changes
|
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/volume.py
RENAMED
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.0}/hyperbrowser/client/managers/sync_manager/web/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{hyperbrowser-1.3.0 → hyperbrowser-1.4.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
|
|
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
|
|
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
|