stagehand 4.1.0a0.dev1497__tar.gz → 4.1.0a0.dev1499__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.
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/PKG-INFO +1 -1
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/pyproject.toml +1 -1
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/__init__.py +2 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/service-worker.js +2 -2
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_generated/protocol_version.py +1 -1
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/cdp_client.py +131 -24
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/README.md +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/blank.html +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/content-script.js +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/manifest.json +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/offscreen/service-worker-heartbeat.html +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/offscreen/service-worker-heartbeat.js +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/wake-service-worker.html +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/wake-service-worker.js +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_generated/__init__.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_generated/input_types.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_generated/models.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_sdk_identity.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_validation.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/browser.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/browser_clipboard.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/browser_context.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/browserbase_services.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/browserbase_session.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/client_models.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/client_types.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/extension_assets.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/file_upload.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/locator.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/page.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/py.typed +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/response.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/rpc_client.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/stagehand.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/timeouts.py +0 -0
- {stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/webmcp.py +0 -0
|
@@ -58,6 +58,7 @@ from ._generated.models import (
|
|
|
58
58
|
from .browser import StagehandBrowser, browserbase, local_browser
|
|
59
59
|
from .browser_clipboard import BrowserClipboard
|
|
60
60
|
from .browser_context import BrowserContext
|
|
61
|
+
from .cdp_client import StagehandRuntimeIncompatibleError
|
|
61
62
|
from .client_models import (
|
|
62
63
|
BrowserbaseFetchResult,
|
|
63
64
|
BrowserbaseSearchResult,
|
|
@@ -147,6 +148,7 @@ __all__ = [
|
|
|
147
148
|
"StagehandClientLoggingConfig",
|
|
148
149
|
"StagehandMetrics",
|
|
149
150
|
"StagehandResultMetadata",
|
|
151
|
+
"StagehandRuntimeIncompatibleError",
|
|
150
152
|
"State",
|
|
151
153
|
"TelemetryConfig",
|
|
152
154
|
"Variables",
|
{stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/service-worker.js
RENAMED
|
@@ -7359,7 +7359,7 @@ function isRecord$1(value) {
|
|
|
7359
7359
|
}
|
|
7360
7360
|
var package_default$1 = {
|
|
7361
7361
|
name: "@browserbasehq/stagehand-protocol",
|
|
7362
|
-
version: "2.0.0-alpha-
|
|
7362
|
+
version: "2.0.0-alpha-67a46682bf06f5607180470007a65e5811c09bcf",
|
|
7363
7363
|
"private": true,
|
|
7364
7364
|
type: "module",
|
|
7365
7365
|
exports: {
|
|
@@ -53493,7 +53493,7 @@ var WebTracerProvider = class extends BasicTracerProvider {
|
|
|
53493
53493
|
};
|
|
53494
53494
|
var package_default = {
|
|
53495
53495
|
name: "@browserbasehq/stagehand-extension",
|
|
53496
|
-
version: "1.0.2-alpha-
|
|
53496
|
+
version: "1.0.2-alpha-67a46682bf06f5607180470007a65e5811c09bcf",
|
|
53497
53497
|
"private": true,
|
|
53498
53498
|
type: "module",
|
|
53499
53499
|
scripts: {
|
{stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_generated/protocol_version.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"""Generated from packages/protocol/package.json. Do not edit."""
|
|
2
2
|
|
|
3
3
|
PROTOCOL_SEMVER_PATTERN = '^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\\+([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?$'
|
|
4
|
-
STAGEHAND_PROTOCOL_VERSION = '2.0.0-alpha-
|
|
4
|
+
STAGEHAND_PROTOCOL_VERSION = '2.0.0-alpha-67a46682bf06f5607180470007a65e5811c09bcf'
|
|
@@ -7,7 +7,7 @@ import time
|
|
|
7
7
|
from collections.abc import Mapping
|
|
8
8
|
from contextlib import suppress
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
-
from typing import Any, Protocol, cast
|
|
10
|
+
from typing import Any, Literal, Protocol, cast
|
|
11
11
|
from urllib.request import urlopen
|
|
12
12
|
|
|
13
13
|
from stagehand._generated.protocol_version import (
|
|
@@ -59,42 +59,136 @@ def _callback_source_from_message(message: dict[str, object]) -> str | None:
|
|
|
59
59
|
return source
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
"
|
|
62
|
+
RUNTIME_INCOMPATIBLE_REMEDIATION = (
|
|
63
|
+
"Upgrade the Stagehand SDK and the Stagehand extension together so their protocol majors "
|
|
64
|
+
"match, or start the session with the extension bundled in this SDK."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
class _RuntimeNegotiation:
|
|
70
|
+
"""Outcome of reading the runtime marker the extension publishes.
|
|
71
|
+
|
|
72
|
+
``kind`` is three-way on purpose: ``"unknown"`` means the marker is absent or not yet
|
|
73
|
+
readable and the caller should keep polling, while ``"incompatible"`` means the marker
|
|
74
|
+
parsed but this client can never talk to that runtime, so polling is pointless.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
kind: Literal["compatible", "incompatible", "unknown"]
|
|
78
|
+
detail: str
|
|
79
|
+
reason: str | None = None
|
|
80
|
+
protocol_version: str | None = None
|
|
81
|
+
server_name: str | None = None
|
|
82
|
+
server_version: str | None = None
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def compatible(self) -> bool:
|
|
86
|
+
return self.kind == "compatible"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class StagehandRuntimeIncompatibleError(RuntimeError):
|
|
90
|
+
"""The connected Stagehand extension speaks a protocol this SDK cannot use.
|
|
91
|
+
|
|
92
|
+
Raised on the first readiness poll that observes the incompatible marker; initialization
|
|
93
|
+
does not wait for the timeout because the extension will not change its protocol version
|
|
94
|
+
while the session is open.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(self, negotiation: _RuntimeNegotiation) -> None:
|
|
98
|
+
self.reason = negotiation.reason or "protocol-incompatible"
|
|
99
|
+
self.client_protocol_version = STAGEHAND_PROTOCOL_VERSION
|
|
100
|
+
self.reported_protocol_version = negotiation.protocol_version
|
|
101
|
+
self.server_name = negotiation.server_name
|
|
102
|
+
self.server_version = negotiation.server_version
|
|
103
|
+
self.remediation = RUNTIME_INCOMPATIBLE_REMEDIATION
|
|
104
|
+
super().__init__(
|
|
105
|
+
f"Incompatible Stagehand runtime: {negotiation.detail}; "
|
|
106
|
+
f"client protocol {self.client_protocol_version}, "
|
|
107
|
+
f"reported protocol {self.reported_protocol_version}, "
|
|
108
|
+
f"server {self.server_name}/{self.server_version}. "
|
|
109
|
+
f"{self.remediation}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _negotiate_runtime(marker: object) -> _RuntimeNegotiation:
|
|
114
|
+
"""Classify the runtime marker. Never raises: hostile input is just ``unknown``."""
|
|
64
115
|
if not isinstance(marker, Mapping):
|
|
65
|
-
return
|
|
116
|
+
return _RuntimeNegotiation("unknown", "no Stagehand runtime marker")
|
|
66
117
|
|
|
67
118
|
server_info = marker.get("serverInfo")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
119
|
+
if not isinstance(server_info, Mapping):
|
|
120
|
+
return _RuntimeNegotiation("unknown", f"serverInfo={server_info!r}")
|
|
121
|
+
name = server_info.get("name")
|
|
122
|
+
version = server_info.get("version")
|
|
123
|
+
# Mirrors the protocol's ImplementationInfoSchema: both fields are non-empty strings.
|
|
124
|
+
# A marker missing either is malformed, not a foreign runtime, so keep polling.
|
|
125
|
+
if not isinstance(name, str) or not name or not isinstance(version, str) or not version:
|
|
126
|
+
return _RuntimeNegotiation(
|
|
127
|
+
"unknown", f"serverInfo.name={name!r} serverInfo.version={version!r}"
|
|
128
|
+
)
|
|
71
129
|
|
|
72
130
|
protocol_version = marker.get("protocolVersion")
|
|
73
|
-
if not isinstance(protocol_version, str):
|
|
74
|
-
return
|
|
75
|
-
compatibility = _protocol_compatibility(STAGEHAND_PROTOCOL_VERSION, protocol_version)
|
|
76
|
-
if compatibility is not None:
|
|
77
|
-
return False, compatibility
|
|
131
|
+
if not isinstance(protocol_version, str) or not protocol_version:
|
|
132
|
+
return _RuntimeNegotiation("unknown", f"protocolVersion={protocol_version!r}")
|
|
78
133
|
|
|
79
|
-
|
|
134
|
+
if name != _RUNTIME_NAME:
|
|
135
|
+
return _RuntimeNegotiation(
|
|
136
|
+
"incompatible",
|
|
137
|
+
f'Runtime name mismatch: expected "{_RUNTIME_NAME}", server reported "{name}"',
|
|
138
|
+
reason="runtime-name-mismatch",
|
|
139
|
+
protocol_version=protocol_version,
|
|
140
|
+
server_name=name,
|
|
141
|
+
server_version=version,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
incompatibility = _protocol_compatibility(STAGEHAND_PROTOCOL_VERSION, protocol_version)
|
|
145
|
+
if incompatibility is not None:
|
|
146
|
+
reason, detail = incompatibility
|
|
147
|
+
return _RuntimeNegotiation(
|
|
148
|
+
"incompatible",
|
|
149
|
+
detail,
|
|
150
|
+
reason=reason,
|
|
151
|
+
protocol_version=protocol_version,
|
|
152
|
+
server_name=name,
|
|
153
|
+
server_version=version,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return _RuntimeNegotiation(
|
|
157
|
+
"compatible",
|
|
158
|
+
f"protocolVersion={protocol_version}",
|
|
159
|
+
protocol_version=protocol_version,
|
|
160
|
+
server_name=name,
|
|
161
|
+
server_version=version,
|
|
162
|
+
)
|
|
80
163
|
|
|
81
164
|
|
|
82
|
-
def _protocol_compatibility(client_version: str, server_version: str) -> str | None:
|
|
165
|
+
def _protocol_compatibility(client_version: str, server_version: str) -> tuple[str, str] | None:
|
|
166
|
+
"""Return ``None`` when compatible, otherwise ``(reason, detail)``."""
|
|
83
167
|
client = _PROTOCOL_SEMVER_PATTERN.fullmatch(client_version)
|
|
84
168
|
server = _PROTOCOL_SEMVER_PATTERN.fullmatch(server_version)
|
|
85
169
|
if client is None or server is None:
|
|
86
|
-
return
|
|
170
|
+
return (
|
|
171
|
+
"protocol-invalid-version",
|
|
172
|
+
f"Invalid protocol version: client {client_version}, server {server_version}",
|
|
173
|
+
)
|
|
87
174
|
if client.group(4) is not None or server.group(4) is not None:
|
|
88
175
|
if client_version != server_version:
|
|
89
176
|
return (
|
|
90
|
-
"protocol
|
|
91
|
-
|
|
177
|
+
"protocol-prerelease-mismatch",
|
|
178
|
+
"Protocol prereleases must match exactly: "
|
|
179
|
+
f"client {client_version}, server {server_version}",
|
|
92
180
|
)
|
|
93
181
|
return None
|
|
94
182
|
if client.group(1) != server.group(1):
|
|
95
|
-
return
|
|
183
|
+
return (
|
|
184
|
+
"protocol-major-mismatch",
|
|
185
|
+
f"Protocol major mismatch: client {client_version}, server {server_version}",
|
|
186
|
+
)
|
|
96
187
|
if int(server.group(2)) < int(client.group(2)):
|
|
97
|
-
return
|
|
188
|
+
return (
|
|
189
|
+
"protocol-server-too-old",
|
|
190
|
+
f"Server protocol {server_version} is older than client requirement {client_version}",
|
|
191
|
+
)
|
|
98
192
|
return None
|
|
99
193
|
|
|
100
194
|
|
|
@@ -474,7 +568,17 @@ class CDPClient:
|
|
|
474
568
|
"Target.closeTarget", {"targetId": activation_target_id}
|
|
475
569
|
)
|
|
476
570
|
|
|
477
|
-
async def _wait_for_runtime_receiver(
|
|
571
|
+
async def _wait_for_runtime_receiver(
|
|
572
|
+
self,
|
|
573
|
+
session_id: str,
|
|
574
|
+
*,
|
|
575
|
+
allow_fallback_install: bool = False,
|
|
576
|
+
) -> None:
|
|
577
|
+
"""Poll until the extension runtime is ready.
|
|
578
|
+
|
|
579
|
+
``allow_fallback_install`` is reserved for flows that can replace an incompatible
|
|
580
|
+
preloaded extension; by default an incompatible marker fails on the first poll.
|
|
581
|
+
"""
|
|
478
582
|
while True:
|
|
479
583
|
try:
|
|
480
584
|
evaluated = await self.send_command(
|
|
@@ -485,17 +589,20 @@ class CDPClient:
|
|
|
485
589
|
},
|
|
486
590
|
session_id=session_id,
|
|
487
591
|
)
|
|
592
|
+
except Exception:
|
|
593
|
+
evaluated = None
|
|
594
|
+
if evaluated is not None:
|
|
488
595
|
exception = evaluated.get("exceptionDetails")
|
|
489
596
|
if not isinstance(exception, Mapping):
|
|
490
597
|
result = evaluated.get("result")
|
|
491
598
|
value = result.get("value") if isinstance(result, Mapping) else None
|
|
492
599
|
if isinstance(value, Mapping):
|
|
493
600
|
has_receiver = value.get("hasReceiver") is True
|
|
494
|
-
|
|
495
|
-
if
|
|
601
|
+
negotiation = _negotiate_runtime(value.get("marker"))
|
|
602
|
+
if negotiation.kind == "incompatible" and not allow_fallback_install:
|
|
603
|
+
raise StagehandRuntimeIncompatibleError(negotiation)
|
|
604
|
+
if negotiation.compatible and has_receiver:
|
|
496
605
|
return
|
|
497
|
-
except Exception:
|
|
498
|
-
pass
|
|
499
606
|
await asyncio.sleep(0.1)
|
|
500
607
|
|
|
501
608
|
def _schedule_best_effort_command(self, method: str, params: Mapping[str, object]) -> None:
|
|
File without changes
|
|
File without changes
|
{stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/content-script.js
RENAMED
|
File without changes
|
{stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_extension/manifest.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_generated/__init__.py
RENAMED
|
File without changes
|
{stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/_generated/input_types.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/browserbase_services.py
RENAMED
|
File without changes
|
{stagehand-4.1.0a0.dev1497 → stagehand-4.1.0a0.dev1499}/src/stagehand/browserbase_session.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
|