Metadata-Version: 2.4
Name: gl-browser-use-binary
Version: 0.1.6
Summary: A typed Python package for running browser automation tasks through `browser-use`
Author: GDP Labs
Requires-Python: <3.14,>=3.11
Description-Content-Type: text/markdown
Requires-Dist: browser-use<0.12.0,>=0.11.13
Requires-Dist: gllm-core-binary<0.5.0,>=0.4.57
Requires-Dist: json-repair<0.61.0,>=0.60.1
Requires-Dist: pydantic<3.0.0,>=2.11.7
Requires-Dist: requests<3.0.0,>=2.32.4
Requires-Dist: pydantic-settings<3.0.0,>=2.5.2
Provides-Extra: steel
Requires-Dist: steel-sdk<0.17.0,>=0.16.0; extra == "steel"
Provides-Extra: opensandbox
Requires-Dist: opensandbox<0.2.0,>=0.1.9; extra == "opensandbox"
Requires-Dist: playwright<2.0.0,>=1.40.0; extra == "opensandbox"
Provides-Extra: infrastructure
Requires-Dist: gl-browser-use-binary[opensandbox,steel]; extra == "infrastructure"
Provides-Extra: minio
Requires-Dist: minio<8.0.0,>=7.2.20; extra == "minio"
Provides-Extra: storage
Requires-Dist: gl-browser-use-binary[minio]; extra == "storage"
Provides-Extra: full
Requires-Dist: gl-browser-use-binary[infrastructure,storage]; extra == "full"

# GL Browser Use

GL Browser Use is a typed Python package for running browser automation tasks through `browser-use`. It provides a stable client facade, structured stream events, explicit result objects, optional Steel or OpenSandbox browser infrastructure, optional MinIO/S3-compatible recording storage, and bounded retries for recoverable browser-session failures.

## Installation

### Published binary package

Install the published binary distribution for normal application use:

```bash
pip install gl-browser-use-binary
```

Supported Python versions are 3.11, 3.12, and 3.13.

Install optional providers with the binary package only when you need them:

```bash
pip install "gl-browser-use-binary[steel]"           # Steel browser infrastructure
pip install "gl-browser-use-binary[opensandbox]"     # OpenSandbox browser infrastructure
pip install "gl-browser-use-binary[infrastructure]"  # All browser infrastructure providers
pip install "gl-browser-use-binary[minio]"           # MinIO/S3-compatible object storage
pip install "gl-browser-use-binary[storage]"         # All object storage providers
pip install "gl-browser-use-binary[full]"            # Infrastructure + storage providers
```

Use the concrete extras (`steel`, `opensandbox`, `minio`) in application dependency files when you want to pin exactly which provider you depend on. The slot extras (`infrastructure`, `storage`, `full`) are convenience aliases and may include more providers later.

The repository project is named `gl-browser-use`; the release workflow compiles and publishes it as `gl-browser-use-binary`. Both use the same `gl_browser_use` import namespace.

### Local development

From the repository checkout, install the source project and development dependencies with `uv`:

```bash
cd libs/gl-browser-use
uv sync --all-extras --group dev
```

## Quick Start

`BrowserUseClient` supports streaming and non-streaming execution. API keys can be passed directly or read from environment variables.

```python
import asyncio

from gl_browser_use import BrowserUseClient, BrowserUseClientConfig
from gl_browser_use.infrastructure import SteelBrowserInfrastructure
from gl_browser_use.storage import MinIOS3CompatibleStorage


async def main() -> None:
    client = BrowserUseClient(
        config=BrowserUseClientConfig(
            llm_openai_api_key="...",
            page_extraction_llm_openai_api_key="...",
            max_session_retries=2,
            session_retry_delay_in_s=3.0,
        ),
        infrastructure=SteelBrowserInfrastructure(),  # reads STEEL_API_KEY by default
        storage=MinIOS3CompatibleStorage.from_environment(),
    )

    async for event in client.run("Open Hacker News and list five article titles"):
        print(event.content)


asyncio.run(main())
```

For a single aggregated result:

```python
from gl_browser_use import BrowserUseClient, BrowserUseClientConfig
from gl_browser_use.infrastructure import SteelBrowserInfrastructure

client = BrowserUseClient(
    config=BrowserUseClientConfig(
        llm_openai_api_key="...",
        page_extraction_llm_openai_api_key="...",
    ),
    infrastructure=SteelBrowserInfrastructure(),
)

result = client.run_sync("Open Hacker News and list five article titles")

print(result.status)
print(result.final_output)
print(result.session_id)
print(result.streaming_url)
print(result.recording_url)
print(result.metadata)
```

## Configuration

`BrowserUseClientConfig` validates required values when the client is created. If `llm_openai_api_key` or `page_extraction_llm_openai_api_key` is not passed, both default to `OPENAI_API_KEY`.

Common client options:

1. `llm_openai_model`: primary browser-control model. Default: `o3`.
2. `page_extraction_llm_openai_model`: page extraction model. Default: `gpt-5-mini`.
3. `sensitive_data`: optional browser-use placeholder map. It accepts either a flat
    placeholder-to-value map or a domain-scoped map. Values are excluded from normal
    config serialization and redacted from `redacted_dict()` output.
4. `extend_system_message`: optional system prompt extension.
5. `vision_detail_level`: `auto`, `low`, or `high`. Default: `auto`.
6. `llm_timeout_in_s`: optional LLM timeout.
7. `step_timeout_in_s`: per-step browser timeout. Default: `180`.
8. `agent_kwargs`: additional `browser_use.Agent` constructor arguments. It cannot
    override client-managed arguments such as the task, LLMs, browser session, `sensitive_data`,
    or timeouts.
9. `enable_cloud_sync`: controls `browser-use` cloud sync. Default: `False`.
10. `logging_level`: `debug`, `info`, `warning`, `error`, or `result`. Default: `info`.
11. `max_session_retries`: recoverable session retries. Default: `2`.
12. `session_retry_delay_in_s`: delay between retries. Default: `3.0`.

### Sensitive data and Agent options

```python
config = BrowserUseClientConfig(
    llm_openai_api_key="...",
    page_extraction_llm_openai_api_key="...",
    sensitive_data={"username": "example-user", "password": "example-password"},
    agent_kwargs={"use_vision": False, "max_actions_per_step": 2},
)
```

Keep one configured client per credential scope. Browser-use also accepts domain-scoped
`sensitive_data` maps. Never put real values in logs, serialized configuration, or
`agent_kwargs`.

When supplying sensitive data, restrict browser navigation to the expected domains (for
example, through the infrastructure session's browser-use `BrowserProfile(allowed_domains=...)`)
to reduce prompt-injection exfiltration risk. `agent_kwargs` cannot override browser/session
ownership (`browser`, `browser_profile`, and `browser_session` are reserved); configure those
controls through the selected infrastructure instead.

Optional provider environment variables:

1. `STEEL_API_KEY`: used by `SteelBrowserInfrastructure()` when `api_key` is not passed.
2. `OPENSANDBOX_DOMAIN`: OpenSandbox control-plane host. Default: `localhost:8080`.
3. `OPENSANDBOX_API_KEY`: required for shared OpenSandbox clusters; optional for local insecure servers.
4. `OBJECT_STORAGE_URL`: MinIO/S3 endpoint, for example `localhost:9001` or `https://storage.example`.
5. `OBJECT_STORAGE_USERNAME`: object storage access key.
6. `OBJECT_STORAGE_PASSWORD`: object storage secret key.
7. `OBJECT_STORAGE_BUCKET_NAME`: target bucket name.
8. `OBJECT_STORAGE_DIRECTORY_PREFIX`: optional object key prefix.
9. `OBJECT_STORAGE_SECURE`: `true` for HTTPS when the endpoint has no scheme.

Copy `.env.example` to `.env` for local development, then load it with your application environment manager.

## Runtime API

The client exposes three run methods:

1. `run(task)`: async generator that yields `BrowserUseStreamEvent` values as work progresses.
2. `run_once(task)`: async method that returns one `BrowserUseRunResult` and includes emitted events in `result.events`.
3. `run_sync(task)`: blocking wrapper around `run_once()` using `asyncio.run()`.

Do not call `run_sync()` from an already running event loop. Use `await run_once()` or `async for event in run()` in async applications.

Cancelling the stream by breaking from `async for` or calling `await stream.aclose()` cancels the underlying run task. Browser sessions and infrastructure sessions are released in cleanup.

## Error Contract

Failures return safe, actionable messages and preserve the legacy `error` string for compatibility. Error results and terminal stream events also expose `error_info` with:

1. `code`: a stable, library-scoped machine-readable code;
2. `message`: a bounded summary that explains what failed without secrets or provider internals;
3. `next_action`: the next step for the caller;
4. `retryable`: whether a retry may be appropriate;
5. `reference_id`: a run-level ID shared by the result, events, and structured logs; and
6. `details`: JSON-safe public diagnostic fields (see [Diagnostics and privacy](#diagnostics-and-privacy)); and
7. `recovery`: how to react (see [Recovery](#recovery)).

Each error type has two stable codes. The public `gl_browser_use.<category>.<condition>` code is exposed as `error.public_code` and `error_info.code`. The `GL-BU-<NAME>` code is the shared `gllm_core.exceptions.BaseError` code, exposed as `error.code` and attached to log records by `gllm_core` logging (installed from the public `gllm-core-binary` wheel). `gl_browser_use.errors.registered_error_codes()` returns the same mapping at runtime.

| Public code (`error_info.code`) | `BaseError` code (`error.code`) | Error type | Meaning |
| --- | --- | --- | --- |
| `gl_browser_use.error` | `GL-BU-ERROR` | `BrowserUseSDKError` | Unclassified SDK failure. |
| `gl_browser_use.dependency.missing` | `GL-BU-DEPENDENCY-MISSING` | `BrowserUseMissingDependencyError` | An optional provider dependency is unavailable. |
| `gl_browser_use.config.invalid` | `GL-BU-CONFIG-INVALID` | `BrowserUseConfigurationError` | Configuration is missing or invalid. |
| `gl_browser_use.execution.failed` | `GL-BU-EXECUTION-FAILED` | `BrowserUseExecutionError` | Browser task execution failed. |
| `gl_browser_use.task.failed` | `GL-BU-TASK-FAILED` | `BrowserUseTaskFailedError` | The underlying agent reported a failed task. |
| `gl_browser_use.task.output_invalid` | `GL-BU-TASK-OUTPUT-INVALID` | `BrowserUseTaskOutputError` | Structured task output could not be parsed or validated. |
| `gl_browser_use.session.retry_exhausted` | `GL-BU-SESSION-RETRY-EXHAUSTED` | `BrowserUseRetryExhaustedError` | Recoverable session failures exhausted the retry budget. |
| `gl_browser_use.execution.timeout` | `GL-BU-TIMEOUT` | `BrowserUseTimeoutError` | A browser step or dependency call timed out. |
| `gl_browser_use.provider.auth_failed` | `GL-BU-PROVIDER-AUTH` | `BrowserUseProviderAuthError` | A model or browser provider rejected the credentials or permissions. |
| `gl_browser_use.provider.rate_limited` | `GL-BU-PROVIDER-RATE-LIMIT` | `BrowserUseRateLimitError` | A model or browser provider rate-limited the request. |
| `gl_browser_use.provider.unavailable` | `GL-BU-PROVIDER-UNAVAILABLE` | `BrowserUseProviderUnavailableError` | A model or browser provider could not be reached or failed. |

Codes are append-only. A code is never reused for a different failure or removed; a code that is no longer emitted is marked deprecated here for at least one minor release first. Every new concrete error type must define both codes, a `default_message`, and a `DEFAULT_NEXT_ACTION`; class definition fails on a missing, malformed, or duplicate code.

Diagnostic logs use the same `gl_browser_use.` prefix. Recording diagnostics include `gl_browser_use.recording.resolve_failed`, `gl_browser_use.recording.schedule_failed`, `gl_browser_use.recording.storage_unavailable`, `gl_browser_use.recording.download_failed`, `gl_browser_use.recording.background_failed`, `gl_browser_use.recording.background_unavailable`, `gl_browser_use.recording.navigation_failed`, and `gl_browser_use.recording.upload_failed`. Cleanup diagnostics include `gl_browser_use.cleanup.endpoint_unavailable`, `gl_browser_use.cleanup.recorder_failed`, `gl_browser_use.cleanup.eventbus_failed`, `gl_browser_use.cleanup.agent_failed`, `gl_browser_use.cleanup.browser_failed`, `gl_browser_use.cleanup.infrastructure_failed`, and `gl_browser_use.cleanup.sandbox_failed`.

`str(error)`, terminal event `content`, error-event `thinking_and_activity_info["data_value"]`, and the legacy result `error` combine `message` with `next_action`, so callers can act without reading logs first. Public exception strings and error messages are bounded, sanitized summaries; detailed causes remain in redacted structured diagnostics.

Reference IDs use the `glbu_<hex>` format and are distinct from error codes. Use the `reference_id` when searching centralized logs or contacting support. Configuration diagnostics are redacted recursively, including API keys, nested secret-shaped values, and URL credentials; arbitrary non-JSON values are replaced with a safe placeholder. A recoverable session retry restarts the task from the beginning, so external actions can be repeated; make retried tasks idempotent when possible. Recording is optional telemetry: a recording failure is reported in safe metadata and cannot change the primary task status.

### Recovery

`error_info.recovery` (`BrowserUseRecoveryInfo`) tells callers how to react:

| Field | Meaning |
| --- | --- |
| `retryable` | Whether retrying the same task can succeed. Non-retryable failures, such as invalid configuration or rejected credentials, are always `False`. |
| `retry_after_seconds` | Seconds to wait before retrying, when known: the provider's `retry-after` value for rate limits, or `session_retry_delay_in_s` for session retries. `None` when not retryable or unknown. |
| `attempt`, `max_attempts` | The SDK's own retry budget for the run. `max_attempts` is `max_session_retries + 1`. |
| `escalation` | When to stop retrying and who to contact, for example support with the `reference_id`. |

| Failure | `retryable` | Default wait |
| --- | --- | --- |
| `provider.rate_limited` | `True` | Provider `retry-after` or `try again in <n>s`, when reported. |
| `provider.unavailable`, `execution.timeout` | `True` | None |
| `session.retry_exhausted` | `True` | `session_retry_delay_in_s` |
| `provider.auth_failed`, `config.invalid`, `dependency.missing`, `task.*`, `execution.failed` | `False` | None |

Provider failures are classified from exception types and HTTP status codes first, then from provider error text. browser-use records most model-provider errors as failed steps instead of raising them, so after repeated failed steps the SDK classifies the last recorded step error. The SDK retries only recoverable browser-session failures itself; for other retryable failures, the caller decides whether to retry using `recovery`.

### Logging and reference lookup

The SDK logs through the standard `logging` module under the `gl_browser_use` logger and never removes handlers that the embedding application installed. Route these records to your centralized log store with your own handlers, for example the `gllm-core` JSON formatter.

Each run creates one `reference_id` that is shared by every attempt, retry event, terminal event, result, and log record of that run. Failure records carry these `extra` fields:

| Field | Meaning |
| --- | --- |
| `reference_id` | The run's `glbu_<hex>` reference ID. |
| `error_code` / `base_error_code` | The public code and the `GL-BU-*` code. |
| `error_message`, `next_action` | The safe public message and next step. |
| `stage`, `attempt`, `max_attempts` | Where the failure happened and which attempt it was. |
| `retryable`, `retry_after_seconds` | The recovery guidance. |
| `details`, `diagnostics` | Public and internal diagnostics, both redacted. |
| `failure_type`, `cause` | The raw failure type and a redacted, bounded cause. |

A terminal failure writes exactly one `ERROR` record for its `reference_id` (`Browser-use execution failed` or `Browser-use run failed`). Session retries write a `WARNING` record (`Browser-use session failed; retrying`) per retried attempt. To investigate a failure a user reports, search the log store for `reference_id = <id>` and level `ERROR`.

If a log handler or formatter fails, the SDK attempts one fallback record with only `reference_id` and `error_code`, and the run still returns its original result, error code, message, recovery data, and reference ID.

### Diagnostics and privacy

Error diagnostics follow a fixed schema, `gl_browser_use.errors.DIAGNOSTIC_FIELDS`:

| Visibility | Fields | Where they appear |
| --- | --- | --- |
| Public | `stage`, `attempt`, `max_attempts`, `step_count`, `streaming_url`, `recording_url`, `session_id`, `missing_dependency`, `package_hint`, `missing`, `errors` | `error.details`, `error_info.details`, logs |
| Internal | `url`, `last_error`, `web_socket_debugger_url`, `config`, `browser_context`, and any unlisted field | `error.debug_info["diagnostics"]` and logs only |

All diagnostic values are redacted recursively and made JSON-safe. Raw objects, including exceptions, become `<non-serializable>` instead of their representation.

Redaction (`redact_text` / `redact_value`) masks:

- configured API keys, `sensitive_data` values, and the task prompt when it is at least 12 characters;
- values under secret-shaped keys such as `api_key`, `token`, `password`, `authorization`, and `secret`;
- URL credentials, secret query parameters, inline `key=value` secrets, `Bearer` tokens, and `sk-`/`pk-`/`rk-` style API keys;
- email addresses.

It applies to error messages, `error_info`, error diagnostics, structured log fields, and step-event tool-call arguments and outputs. The run result keeps `task` and `final_output` unchanged because they are the caller's own input and output. Activity events keep the streaming URL unchanged so it can be embedded.

## Result Contract

`BrowserUseRunResult` contains:

1. `status`: `success`, `error`, or `cancelled`.
2. `task`: normalized task string.
3. `final_output`: final text extracted from the underlying agent, or `Task completed` when no final text is available.
4. `session_id`: infrastructure session ID when an external browser session was used.
5. `streaming_url`: browser debug or streaming URL when available.
6. `recording_url`: expected recording URL when recording is configured.
7. `steps`: number of browser-use steps executed.
8. `error`: terminal error message for error results.
9. `events`: collected stream events for `run_once()`.
10. `metadata`: attempt, retry, and recording metadata.

Recording metadata uses these statuses:

1. `disabled`: infrastructure, storage, or browser context is not available.
2. `unsupported`: the selected infrastructure does not support recording.
3. `unavailable`: storage is configured but not available.
4. `scheduled`: a background recording upload has been scheduled.
5. `failed`: recording was eligible but could not be scheduled, resolved, or uploaded.
6. `unknown`: recording may have started, but the terminal error did not include enough context to determine the final state.

## Streaming Contract

`BrowserUseStreamEvent` contains:

1. `event_type`
2. `content`
3. `thinking_and_activity_info`
4. `is_final`
5. `tool_info`
6. `metadata`

Important event content values:

1. `Receive streaming URL`: emitted when a browser debug or streaming URL is available.
2. `Receive recording URL`: emitted when a recording URL can be resolved.
3. `Task completed`: emitted for the final successful step.

Activity events encode iframe URLs in `thinking_and_activity_info["data_value"]` as a JSON string:

```json
{"type": "iframe", "message": "<url>"}
```

Step events include serialized tool calls in `tool_info["tool_calls"]`.

## Retries And Errors

The client retries only classified recoverable browser-session failures, such as browser closure or websocket disconnect messages. Before each retry, it emits a retry status event. Retries are bounded by `max_session_retries`, so total attempts are `max_session_retries + 1`. Each retry restarts the task from the beginning and may replay external side effects, so use idempotent tasks or set `max_session_retries=0` when replay is unsafe.

Non-recoverable task failures return `BrowserUseRunResult(status="error")`. Recoverable failures that exhaust all attempts raise `BrowserUseRetryExhaustedError`.

Error types:

All error types derive from `BrowserUseSDKError`, which derives from `gllm_core.exceptions.BaseError` and `RuntimeError`.

1. `BrowserUseConfigurationError`: missing or invalid runtime configuration. `BrowserUseClientConfig` raises it for every validation failure, including construction, `model_validate`, and attribute assignment, instead of a raw pydantic `ValidationError`. Rejected input values are never included.
2. `BrowserUseDependencyError`: abstract base for optional provider dependency problems.
3. `BrowserUseMissingDependencyError`: optional provider extra is not installed.
4. `BrowserUseExecutionError`: execution-time failure.
5. `BrowserUseTaskFailedError`: the agent reported completion without a successful result.
6. `BrowserUseTaskOutputError`: structured task output could not be parsed or validated.
7. `BrowserUseRetryExhaustedError`: recoverable session retries were exhausted.
8. `BrowserUseTimeoutError`: a browser step or dependency call timed out.
9. `BrowserUseProviderAuthError`: a provider rejected the credentials or permissions.
10. `BrowserUseRateLimitError`: a provider rate-limited the request.
11. `BrowserUseProviderUnavailableError`: a provider could not be reached or failed.

Unexpected third-party errors are converted at one boundary, `gl_browser_use.errors.normalize_error()`, before they reach results, events, or logs.

## Optional Providers

Optional providers are lazy-loaded. Importing `gl_browser_use` does not require Steel, OpenSandbox, or MinIO to be installed.

```python
from gl_browser_use.infrastructure import OpenSandboxBrowserInfrastructure, SteelBrowserInfrastructure
from gl_browser_use.storage import MinIOS3CompatibleStorage
```

`SteelBrowserInfrastructure` creates Steel browser sessions and provides CDP/streaming URLs.

`OpenSandboxBrowserInfrastructure` provisions the repo-committed Chrome example image (`opensandbox/chrome:latest`, built from `examples/opensandbox/chrome`) through a local or shared OpenSandbox server, exposes noVNC inspection URLs, connects over proxied CDP, and uploads WebM session recordings to object storage with the same deferred `recording_url` semantics as Steel. Pass `storage` to `BrowserUseClient` when recording is enabled.

**DevTools note:** the Chrome image exposes CDP on `0.0.0.0:9222` (`--remote-debugging-address=0.0.0.0` plus a `socat` forward for Chromium M113+). Rebuild from `examples/opensandbox/chrome` after pulling changes; GL Browser Use discovers CDP via `sandbox.get_endpoint(9222)`. See `examples/opensandbox/chrome/README.md`.

OpenSandbox local setup:

```bash
DOCKER_HOST=unix://${HOME}/.docker/desktop/docker.sock \
  OPENSANDBOX_INSECURE_SERVER=YES opensandbox-server

cd libs/gl-browser-use/examples/opensandbox/chrome
docker build -t opensandbox/chrome:latest .
playwright install chromium
```

`MinIOS3CompatibleStorage` uploads session recordings to MinIO or an S3-compatible service and returns presigned URLs.

## Development

From `libs/gl-browser-use`:

```bash
make install-dev
make test-unit
make test-integration
make lint
make build-check
```

Useful targets:

1. `make test`: run all tests.
2. `make test-unit`: run unit tests with coverage.
3. `make test-integration`: run integration-marked contract tests.
4. `make lint`: run Ruff checks.
5. `make format`: run Ruff fixes and formatter.
6. `make pre-commit`: run pre-commit hooks.
7. `make build-check`: build the package and validate artifacts with Twine.
