Skip to content

warpsurf/warpsurf-python-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

warpsurf python sdk

Python 3.11+ Node.js 18+


Warning

warpsurf is an open-source research project under active development. Browser automation carries inherent risks — monitor agents while they work, use capped API keys, and read the full disclaimer before use.


Supports local Chrome, Browserbase, and Kernels remote browsers — see Remote browsers to run agents in the cloud without a local browser.

Prerequisites

  • Python 3.11+
  • Node.js 18+ (Puppeteer is used to launch Chrome and load the extension)

Quick start

1. Get the warpsurf extension (pick one):

Option A — build from source:

git clone https://github.com/warpsurf/warpsurf.git
cd warpsurf
pnpm install
pnpm zip:api

pnpm zip:api builds the extension with the local API surface that the SDK communicates with and produces dist/ (unpacked) and dist.zip (for Browserbase upload).

Option B — download warpsurf-api-v*.zip from the releases page and unzip it to get the dist/ folder.

2. Install the SDK:

git clone https://github.com/warpsurf/warpsurf-python-sdk.git
cd warpsurf-python-sdk
pip install -e .
npm install

3. Run:

python example.py /path/to/warpsurf/dist $GOOGLE_API_KEY

Or use the SDK directly:

import asyncio, os
from warpsurf import WarpSurf, Config

async def main():
    async with WarpSurf("/path/to/warpsurf/dist") as ws:
        result = await ws.run(
            task="What are the top 3 trending repositories on GitHub right now? List their names and star counts.",
            config=Config(
                provider="google",
                model="gemini-3.1-flash-lite-preview",
                api_key=os.environ["GOOGLE_API_KEY"],
            ),
            workflow="agent",
        )
        print(result.output)
        if result.usage:
            print(f"${result.usage.cost:.4f} | {result.usage.latency_ms}ms")

asyncio.run(main())

Table of contents


SDK reference

WarpSurf

The main client. Launches Chrome via Puppeteer, loads the warpsurf extension, and communicates with its service worker.

ws = WarpSurf(
    extension_path="./dist",   # or set WARPSURF_EXTENSION_PATH
    chrome_path=None,          # auto-detected, or set CHROME_PATH
    headless=False,
    user_data_dir=None,        # temp dir created if omitted
)
Parameter Type Default Description
extension_path str | None env var Path to unpacked extension (not needed with browserbase or kernel)
chrome_path str | None auto-detect Custom Chrome binary
headless bool False Run Chrome headless
user_data_dir str | None temp dir Persistent profile directory
browserbase bool | dict | None None Use Browserbase remote browser (see below)
kernel bool | dict | None None Use Kernels remote browser (see below)

Supports async with for automatic connect/close. browserbase and kernel are mutually exclusive.

Config

LLM provider configuration passed to every run() call.

config = Config(
    provider="google",
    model="gemini-3.1-flash-lite-preview",
    api_key=os.environ["GOOGLE_API_KEY"],
    base_url=None,
    temperature=None,
    max_output_tokens=None,
    thinking_level=None,
)
Parameter Type Required Description
provider str yes Provider identifier (see table below)
model str yes Model name, e.g. "gemini-3.1-flash-lite-preview"
api_key str yes API key for the provider
base_url str | None no Custom API endpoint (required for "custom" provider)
temperature float | None no Sampling temperature
max_output_tokens int | None no Maximum output tokens
thinking_level str | None no "high", "medium", "low", "off", or "default"

Supported providers:

provider value Service
"google" or "gemini" Google Gemini
"openai" OpenAI
"anthropic" Anthropic
"grok" xAI Grok
"openrouter" OpenRouter
"custom_openai" or "custom" Any OpenAI-compatible endpoint (requires base_url)

Overrides

Optional per-run behaviour overrides.

from warpsurf import Overrides, GeneralOverrides, FirewallOverrides

overrides = Overrides(
    general=GeneralOverrides(
        max_steps=50,
        use_vision=True,
        max_worker_agents=3,
    ),
    firewall=FirewallOverrides(
        enabled=True,
        allow_list=["amazon.com", "bestbuy.com"],
    ),
)
All GeneralOverrides fields
Field Type
max_steps int
max_actions_per_step int
max_failures int
max_validator_failures int
retry_delay int
max_input_tokens int
use_vision bool
planning_interval int
min_wait_page_load int
max_worker_agents int
enable_planner bool
enable_validator bool
enable_workflow_estimation bool
show_tab_previews bool
response_timeout_seconds int

All fields are optional — only set fields are sent to the extension.

Result

Returned by run(), get_status(), and wait_for_completion().

result.ok                        # bool — True if status == 'completed'
result.status                    # 'completed' | 'error' | 'cancelled' | 'running' | 'pending'
result.output                    # str | None — the agent's response
result.error                     # str | None — error message if failed
result.task_id                   # str

Usage statistics (available on completion):

result.usage.cost                # float — total USD cost
result.usage.latency_ms          # int — end-to-end milliseconds
result.usage.input_tokens        # int
result.usage.output_tokens       # int
result.usage.api_calls           # int — number of LLM calls
result.usage.provider            # str
result.usage.model               # str

Execution trace (agent workflows):

result.trace                     # tuple[TraceEntry, ...] | None
result.trace[0].action           # str — e.g. "click", "navigate"
result.trace[0].status           # 'start' | 'ok' | 'fail'
result.trace[0].details          # str

Methods

run(task, config, *, workflow, overrides, task_id, timeout) → Result

Execute a task and block until completion.

result = await ws.run(
    "Find laptops under $1000",
    config,
    workflow="agent",          # 'auto' | 'chat' | 'search' | 'agent' | 'multiagent'
    overrides=overrides,       # optional
    task_id="my-task-001",     # optional — auto-generated if omitted
    timeout=600.0,             # seconds
)

get_status(task_id) → Result

Poll the current status of a running task.

cancel(task_id) → None

Cancel a running task. Raises on failure.

get_settings() → dict

Retrieve the extension's current settings (general, firewall, providers, agent models).

wait_for_completion(task_id, *, poll_interval, timeout) → Result

Poll get_status() until the task reaches a terminal state.


Remote browsers

Instead of launching a local Chrome instance, you can run agents on cloud browsers via Browserbase or Kernels. This is useful for CI/CD pipelines, keeping your desktop distraction-free, or running agents on machines without Chrome installed.

Note

Remote browsers run on shared cloud infrastructure. Be mindful when handling sensitive credentials in remote sessions. LLM API keys are passed at runtime into the browser's JS memory via CDP. Use capped API keys and monitor usage — browser agents can make many LLM calls.

Browserbase

Prerequisites:

  1. Create an account at browserbase.com and note your API key and Project ID
  2. Install the SDK extras: pip install "warpsurf[browserbase]"
  3. Build and zip the warpsurf extension (if you haven't already):
    cd /path/to/warpsurf
    pnpm install && pnpm zip:api
  4. Upload the extension to Browserbase (one-time):
    from browserbase import Browserbase
    client = Browserbase(api_key="your-api-key")
    ext = client.extensions.create(file=open("dist.zip", "rb"))
    print(ext.id)  # save this extension ID
  5. Set your credentials via a .env file or environment variables:
    BROWSERBASE_API_KEY=your-api-key
    BROWSERBASE_PROJECT_ID=your-project-id
    
    export BROWSERBASE_API_KEY="your-api-key"
    export BROWSERBASE_PROJECT_ID="your-project-id"
    Or pass them directly in the browserbase dict (see usage below).

Usage:

# Pass the extension_id from step 4
async with WarpSurf(browserbase={
    "extension_id": "your-extension-id",
}) as ws:
    result = await ws.run("Find trending repos on GitHub", config)

# With all options explicit (no env vars needed)
async with WarpSurf(browserbase={
    "api_key": "...",
    "project_id": "...",
    "extension_id": "...",
    "region": "us-west-2",  # optional
}) as ws:
    result = await ws.run("Find trending repos on GitHub", config)

# Multiple parallel sessions
async with (
    WarpSurf(browserbase={"extension_id": "..."}) as ws1,
    WarpSurf(browserbase={"extension_id": "..."}) as ws2,
    WarpSurf(browserbase={"extension_id": "..."}) as ws3,
):
    results = await asyncio.gather(
        ws1.run("Task 1", config),
        ws2.run("Task 2", config),
        ws3.run("Task 3", config),
    )

Kernels

Prerequisites:

  1. Create an account at kernel.sh (Start-Up plan or higher required for custom extensions)
  2. Install the SDK extras: pip install "warpsurf[kernel]"
  3. Upload the warpsurf extension (one-time) via the Kernels Python SDK:
    from kernel import Kernel
    client = Kernel(api_key="your-key")
    client.extensions.upload(file=open("dist.zip", "rb"), name="warpsurf")
  4. Set the KERNEL_API_KEY environment variable

Usage:

# Minimal — uses KERNEL_API_KEY env var
async with WarpSurf(kernel=True) as ws:
    result = await ws.run("Find trending repos on GitHub", config)

# With explicit options
async with WarpSurf(kernel={"api_key": "...", "extension_name": "warpsurf"}) as ws:
    result = await ws.run("Find trending repos on GitHub", config)

Architecture

warpsurf-python-sdk/
├── src/
│   ├── __init__.py        Public exports
│   ├── client.py          WarpSurf class — spawns bridge, communicates via JSON
│   └── types.py           Frozen dataclasses with beartype runtime validation
├── launch.mjs             Puppeteer bridge — launches Chrome, loads extension
├── package.json           Node.js dependencies (puppeteer, stealth plugin)
└── pyproject.toml         Python packaging

How it works:

  1. WarpSurf.connect() spawns node launch.mjs <extension_path> as a subprocess
  2. The bridge uses Puppeteer to launch Chrome with enableExtensions, which loads the extension via CDP's Extensions.loadUnpacked
  3. The bridge discovers the extension's service worker and opens a CDP session
  4. Python sends JSON commands over stdin, the bridge evaluates them on the service worker via Runtime.evaluate, and sends JSON responses on stdout
  5. WarpSurf.close() sends a close command and terminates the subprocess

Dependencies: Python ≥ 3.11, beartype, Node.js ≥ 18, puppeteer.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors