Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "3.19.3"
".": "3.20.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 8
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-b969ce378479c79ee64c05127c0ed6c6ce2edbee017ecd037242fb618a5ebc9f.yml
openapi_spec_hash: a24aabaa5214effb679808b7f2be0ad4
config_hash: a962ae71493deb11a1c903256fb25386
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-6f6bfb81d092f30a5e2005328c97d61b9ea36132bb19e9e79e55294b9534ce20.yml
openapi_spec_hash: f3fc1e3688a38dc2c28f7178f7d534e5
config_hash: 1fb12ae9b478488bc1e56bfbdc210b01
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Changelog

## 3.20.0 (2026-05-06)

Full Changelog: [v3.19.3...v3.20.0](https://github.com/browserbase/stagehand-ruby/compare/v3.19.3...v3.20.0)

### Features

* [feat]: add `ignoreSelectors` to `extract()` ([db24a9a](https://github.com/browserbase/stagehand-ruby/commit/db24a9ab45dc464d2ad88f9b50d39952c4c76801))
* [STG-1798] feat: support Browserbase verified sessions ([9647eb3](https://github.com/browserbase/stagehand-ruby/commit/9647eb3a4df86cc9d7aedb2f0a9ddd0babd39565))
* [STG-1808] Deprecate Browserbase project ID ([7af1c21](https://github.com/browserbase/stagehand-ruby/commit/7af1c21a07e7163ffb0b86f6c199989b750e0365))
* Bedrock auth passthrough ([5cb6ecb](https://github.com/browserbase/stagehand-ruby/commit/5cb6ecbe621e894dc6c5aec379fdca1183eec4fc))
* remove experimental requirement on agent variables ([#2079](https://github.com/browserbase/stagehand-ruby/issues/2079)) ([6f106d7](https://github.com/browserbase/stagehand-ruby/commit/6f106d74feb1b3f036433ec67dda5e4c395f434e))
* Revert "[STG-1573] Add providerOptions for extensible model auth ([#1822](https://github.com/browserbase/stagehand-ruby/issues/1822))" ([0c83a09](https://github.com/browserbase/stagehand-ruby/commit/0c83a0978116f8fc11a4edf78586f3d61b8af5e8))
* support setting headers via env ([5515959](https://github.com/browserbase/stagehand-ruby/commit/5515959505685e62f9324aebf470a295dd6d8292))


### Bug Fixes

* avoid gzip buffering during streaming ([23c3eef](https://github.com/browserbase/stagehand-ruby/commit/23c3eef17497edbf3ec03049d12c548607112c8d))
* multipart encoding for file arrays ([149b303](https://github.com/browserbase/stagehand-ruby/commit/149b303eb54f4c9af037a5e073a2d6c92d959b44))


### Chores

* **internal:** more robust bootstrap script ([69c050a](https://github.com/browserbase/stagehand-ruby/commit/69c050a467b5fcab904331bd30bd18d46cee8cae))

## 3.19.3 (2026-04-03)

Full Changelog: [v3.18.0...v3.19.3](https://github.com/browserbase/stagehand-ruby/compare/v3.18.0...v3.19.3)
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ GIT
PATH
remote: .
specs:
stagehand (3.19.3)
stagehand (3.20.0)
cgi
connection_pool

Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ require "playwright"

client = Stagehand::Client.new(
browserbase_api_key: ENV["BROWSERBASE_API_KEY"],
browserbase_project_id: ENV["BROWSERBASE_PROJECT_ID"],
model_api_key: ENV["MODEL_API_KEY"],
server: "remote"
)
Expand Down Expand Up @@ -162,10 +161,10 @@ client.sessions.end_(session_id)

Set your environment variables (from `examples/.env.example`):

- `STAGEHAND_API_URL`
- `MODEL_API_KEY`
- `BROWSERBASE_API_KEY`
- `BROWSERBASE_PROJECT_ID`

`STAGEHAND_API_URL` is optional and defaults to the hosted Stagehand API. `STAGEHAND_BASE_URL` remains supported as a deprecated fallback when `STAGEHAND_API_URL` is unset.

```bash
cp examples/.env.example examples/.env
Expand Down
2 changes: 0 additions & 2 deletions examples/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
STAGEHAND_API_URL=https://api.stagehand.browserbase.com
MODEL_API_KEY=sk-proj-your-llm-api-key-here
BROWSERBASE_API_KEY=bb_live_your_api_key_here
BROWSERBASE_PROJECT_ID=your-bb-project-uuid-here
8 changes: 2 additions & 6 deletions examples/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

module ExampleEnv
REQUIRED_KEYS = %w[
STAGEHAND_API_URL
MODEL_API_KEY
BROWSERBASE_API_KEY
BROWSERBASE_PROJECT_ID
].freeze

def self.load!
Expand All @@ -23,11 +21,9 @@ def self.load!
end

missing = REQUIRED_KEYS.select { |key| ENV[key].to_s.empty? }
unless missing.empty?
raise "Missing required env vars: #{missing.join(', ')} (from examples/.env)"
end
return if missing.empty?

ENV["STAGEHAND_BASE_URL"] ||= ENV["STAGEHAND_API_URL"]
raise "Missing required env vars: #{missing.join(', ')} (from examples/.env)"
end

def self.find_env_path
Expand Down
3 changes: 0 additions & 3 deletions examples/local_browser_playwright_example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@

model_key = ENV["MODEL_API_KEY"]
browserbase_api_key = ENV["BROWSERBASE_API_KEY"].to_s
browserbase_project_id = ENV["BROWSERBASE_PROJECT_ID"].to_s

missing = []
missing << "MODEL_API_KEY" if model_key.to_s.empty?
missing << "BROWSERBASE_API_KEY" if browserbase_api_key.empty?
missing << "BROWSERBASE_PROJECT_ID" if browserbase_project_id.empty?

unless missing.empty?
warn("Set #{missing.join(', ')} to run the local Playwright example.")
Expand Down Expand Up @@ -60,7 +58,6 @@ def stream_with_result(label, stream)

client = Stagehand::Client.new(
browserbase_api_key: browserbase_api_key,
browserbase_project_id: browserbase_project_id,
model_api_key: model_key,
server: "local"
)
Expand Down
3 changes: 0 additions & 3 deletions examples/local_server_multiregion_browser_example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@
require_relative "env"
ExampleEnv.load!
browserbase_api_key = ENV["BROWSERBASE_API_KEY"].to_s
browserbase_project_id = ENV["BROWSERBASE_PROJECT_ID"].to_s
model_key = ENV["MODEL_API_KEY"].to_s

missing = []
missing << "BROWSERBASE_API_KEY" if browserbase_api_key.empty?
missing << "BROWSERBASE_PROJECT_ID" if browserbase_project_id.empty?
missing << "MODEL_API_KEY" if model_key.empty?

unless missing.empty?
Expand All @@ -22,7 +20,6 @@

client = Stagehand::Client.new(
browserbase_api_key: browserbase_api_key,
browserbase_project_id: browserbase_project_id,
model_api_key: model_key,
server: "local"
)
Expand Down
3 changes: 0 additions & 3 deletions examples/remote_browser_example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@
require_relative "env"
ExampleEnv.load!
browserbase_api_key = ENV["BROWSERBASE_API_KEY"].to_s
browserbase_project_id = ENV["BROWSERBASE_PROJECT_ID"].to_s
model_key = ENV["MODEL_API_KEY"].to_s

missing = []
missing << "BROWSERBASE_API_KEY" if browserbase_api_key.empty?
missing << "BROWSERBASE_PROJECT_ID" if browserbase_project_id.empty?
missing << "MODEL_API_KEY" if model_key.empty?

unless missing.empty?
Expand All @@ -22,7 +20,6 @@

client = Stagehand::Client.new(
browserbase_api_key: browserbase_api_key,
browserbase_project_id: browserbase_project_id,
model_api_key: model_key
)

Expand Down
3 changes: 0 additions & 3 deletions examples/remote_browser_playwright_example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@

model_key = ENV["MODEL_API_KEY"]
browserbase_api_key = ENV["BROWSERBASE_API_KEY"].to_s
browserbase_project_id = ENV["BROWSERBASE_PROJECT_ID"].to_s

missing = []
missing << "MODEL_API_KEY" if model_key.to_s.empty?
missing << "BROWSERBASE_API_KEY" if browserbase_api_key.empty?
missing << "BROWSERBASE_PROJECT_ID" if browserbase_project_id.empty?

unless missing.empty?
warn("Set #{missing.join(', ')} to run the remote Playwright example.")
Expand Down Expand Up @@ -83,7 +81,6 @@ def stream_with_result(label, stream)

client = Stagehand::Client.new(
browserbase_api_key: browserbase_api_key,
browserbase_project_id: browserbase_project_id,
model_api_key: model_key,
server: "remote"
)
Expand Down
38 changes: 25 additions & 13 deletions lib/stagehand/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ class Client < Stagehand::Internal::Transport::BaseClient
# @return [String]
attr_reader :browserbase_api_key

# Your [Browserbase Project ID](https://www.browserbase.com/settings)
# Deprecated. Browserbase API keys are now project-scoped, so this value is accepted for
# backwards compatibility and ignored.
# @return [String]
attr_reader :browserbase_project_id

Expand All @@ -34,7 +35,7 @@ class Client < Stagehand::Internal::Transport::BaseClient
#
# @return [Hash{String=>String}]
private def auth_headers
{**bb_api_key_auth, **bb_project_id_auth, **llm_model_api_key_auth}
{**bb_api_key_auth, **llm_model_api_key_auth}
end

# @api private
Expand All @@ -48,7 +49,7 @@ class Client < Stagehand::Internal::Transport::BaseClient
#
# @return [Hash{String=>String}]
private def bb_project_id_auth
{"x-bb-project-id" => @browserbase_project_id}
{}
end

# @api private
Expand All @@ -63,16 +64,17 @@ class Client < Stagehand::Internal::Transport::BaseClient
# @param browserbase_api_key [String, nil] Your [Browserbase API Key](https://www.browserbase.com/settings) Defaults to
# `ENV["BROWSERBASE_API_KEY"]`
#
# @param browserbase_project_id [String, nil] Your [Browserbase Project ID](https://www.browserbase.com/settings) Defaults to
# `ENV["BROWSERBASE_PROJECT_ID"]`
# @param browserbase_project_id [String, nil] Deprecated. Browserbase API keys are now project-scoped, so
# this value is accepted for backwards compatibility and ignored.
#
# @param model_api_key [String, nil] Your LLM provider API key (e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.)
# Defaults to `ENV["MODEL_API_KEY"]`
#
# @param server [String] Server mode to use ("remote" or "local"). Defaults to "remote"
#
# @param base_url [String, nil] Override the default base URL for the API, e.g.,
# `"https://api.example.com/v2/"`. Defaults to `ENV["STAGEHAND_BASE_URL"]`
# `"https://api.example.com/v2/"`. Defaults to `ENV["STAGEHAND_API_URL"]`,
# then `ENV["STAGEHAND_BASE_URL"]`
#
# @param max_retries [Integer] Max number of retries to attempt after a failed retryable request.
#
Expand All @@ -83,10 +85,10 @@ class Client < Stagehand::Internal::Transport::BaseClient
# @param max_retry_delay [Float]
def initialize(
browserbase_api_key: ENV["BROWSERBASE_API_KEY"],
browserbase_project_id: ENV["BROWSERBASE_PROJECT_ID"],
browserbase_project_id: nil,
model_api_key: ENV["MODEL_API_KEY"],
server: "remote",
base_url: ENV["STAGEHAND_BASE_URL"],
base_url: ENV["STAGEHAND_API_URL"] || ENV["STAGEHAND_BASE_URL"],
max_retries: self.class::DEFAULT_MAX_RETRIES,
timeout: self.class::DEFAULT_TIMEOUT_IN_SECONDS,
initial_retry_delay: self.class::DEFAULT_INITIAL_RETRY_DELAY,
Expand All @@ -99,15 +101,24 @@ def initialize(
raise ArgumentError,
"browserbase_api_key is required, and can be set via environ: \"BROWSERBASE_API_KEY\""
end
if browserbase_project_id.nil?
raise ArgumentError,
"browserbase_project_id is required, and can be set via environ: \"BROWSERBASE_PROJECT_ID\""
end
if model_api_key.nil?
raise ArgumentError,
"model_api_key is required, and can be set via environ: \"MODEL_API_KEY\""
end

headers = {}
custom_headers_env = ENV["STAGEHAND_CUSTOM_HEADERS"]
unless custom_headers_env.nil?
parsed = {}
custom_headers_env.split("\n").each do |line|
colon = line.index(":")
unless colon.nil?
parsed[line[0...colon].strip] = line[(colon + 1)..].strip
end
end
headers = parsed.merge(headers)
end

@browserbase_api_key = browserbase_api_key.to_s
@browserbase_project_id = browserbase_project_id.to_s
@model_api_key = model_api_key.to_s
Expand All @@ -117,7 +128,8 @@ def initialize(
timeout: timeout,
max_retries: max_retries,
initial_retry_delay: initial_retry_delay,
max_retry_delay: max_retry_delay
max_retry_delay: max_retry_delay,
headers: headers
)

@sessions = Stagehand::Resources::Sessions.new(client: self)
Expand Down
4 changes: 3 additions & 1 deletion lib/stagehand/internal/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ def encode_query_params(query)
#
# @return [Array(String, Enumerable<String>)]
private def encode_multipart_streaming(body)
# rubocop:disable Style/CaseEquality
# RFC 1521 Section 7.2.1 says we should have 70 char maximum for boundary length
boundary = SecureRandom.urlsafe_base64(46)

Expand All @@ -619,7 +620,7 @@ def encode_query_params(query)
in Hash
body.each do |key, val|
case val
in Array if val.all? { primitive?(_1) }
in Array if val.all? { primitive?(_1) || Stagehand::Internal::Type::FileInput === _1 }
val.each do |v|
write_multipart_chunk(y, boundary: boundary, key: key, val: v, closing: closing)
end
Expand All @@ -635,6 +636,7 @@ def encode_query_params(query)

fused_io = fused_enum(strio) { closing.each(&:call) }
[boundary, fused_io]
# rubocop:enable Style/CaseEquality
end

# @api private
Expand Down
15 changes: 3 additions & 12 deletions lib/stagehand/local.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ def self.ensure_browserbase_credentials!(client:, params:)

missing = []
missing << "browserbase_api_key" if client.browserbase_api_key.to_s.empty?
missing << "browserbase_project_id" if client.browserbase_project_id.to_s.empty?
return if missing.empty?

message =
Expand Down Expand Up @@ -213,10 +212,9 @@ def ensure_executable(path)
end

class ServerManager
def initialize(model_api_key:, browserbase_api_key:, browserbase_project_id:)
def initialize(model_api_key:, browserbase_api_key:)
@model_api_key = model_api_key
@browserbase_api_key = browserbase_api_key
@browserbase_project_id = browserbase_project_id
@host = DEFAULT_HOST
@port = 0
@mutex = Mutex.new
Expand Down Expand Up @@ -335,9 +333,6 @@ def build_env(host:, port:)
if @browserbase_api_key.to_s != ""
env["BROWSERBASE_API_KEY"] = @browserbase_api_key
end
if @browserbase_project_id.to_s != ""
env["BROWSERBASE_PROJECT_ID"] = @browserbase_project_id
end
env
end

Expand Down Expand Up @@ -376,8 +371,6 @@ def initialize(server: "remote", **kwargs)
kwargs[:base_url] = base_url.nil? ? "http://#{DEFAULT_HOST}" : base_url
kwargs[:browserbase_api_key] =
kwargs[:browserbase_api_key] || ENV["BROWSERBASE_API_KEY"] || ""
kwargs[:browserbase_project_id] =
kwargs[:browserbase_project_id] || ENV["BROWSERBASE_PROJECT_ID"] || ""
end

super(**kwargs)
Expand All @@ -386,8 +379,7 @@ def initialize(server: "remote", **kwargs)

@local_server_manager = Stagehand::Local::ServerManager.new(
model_api_key: @model_api_key,
browserbase_api_key: @browserbase_api_key,
browserbase_project_id: @browserbase_project_id
browserbase_api_key: @browserbase_api_key
)
end

Expand Down Expand Up @@ -421,8 +413,7 @@ def bb_api_key_auth
end

def bb_project_id_auth
return {} if @browserbase_project_id.to_s.empty?
super
{}
end
end

Expand Down
Loading