Skip to content

Commit 130a450

Browse files
committed
Rename examples and add remote Playwright
1 parent ed2cae0 commit 130a450

8 files changed

Lines changed: 200 additions & 4 deletions

File tree

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
BROWSERBASE_API_KEY=
2+
BROWSERBASE_PROJECT_ID=
3+
MODEL_API_KEY=

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
.idea/
33
.ignore
44
.prism.log
5+
.env
56
.ruby-lsp/
67
.yardoc/
78
bin/tapioca

AGENTS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Overview
2+
3+
This repo contains the Stagehand Ruby SDK generated by Stainless.
4+
5+
## Usage
6+
7+
Follow the README for user-facing guidance:
8+
- Usage: README.md#Usage
9+
- Running examples: README.md#Running-the-Examples
10+
11+
Example scripts live in `examples/` (see the README section above for the authoritative list and run steps).
12+
13+
## Development
14+
15+
- Format: `./scripts/format`
16+
- Lint: `./scripts/lint`

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AGENTS.md

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,27 @@ gem install playwright-ruby-client
209209
npm install playwright
210210
./node_modules/.bin/playwright install chromium
211211
export MODEL_API_KEY="your-openai-api-key"
212-
bundle exec ruby examples/playwright_local_example.rb
212+
bundle exec ruby examples/local_playwright_example.rb
213+
```
214+
215+
Playwright remote example:
216+
217+
```bash
218+
gem install playwright-ruby-client
219+
npm install playwright
220+
./node_modules/.bin/playwright install chromium
221+
export BROWSERBASE_API_KEY="your-browserbase-api-key"
222+
export BROWSERBASE_PROJECT_ID="your-browserbase-project-id"
223+
export MODEL_API_KEY="your-openai-api-key"
224+
bundle exec ruby examples/remote_playwright_example.rb
213225
```
214226

215227
Watir local example:
216228

217229
```bash
218230
gem install watir
219231
export MODEL_API_KEY="your-openai-api-key"
220-
bundle exec ruby examples/watir_local_example.rb
232+
bundle exec ruby examples/local_watir_example.rb
221233
```
222234

223235
### Streaming
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
# ./node_modules/.bin/playwright install chromium
2222
#
2323
# Run:
24-
# bundle exec ruby examples/playwright_local_example.rb
24+
# bundle exec ruby examples/local_playwright_example.rb
2525

2626
begin
2727
require("playwright")
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# gem install watir
2020
#
2121
# Run:
22-
# bundle exec ruby examples/watir_local_example.rb
22+
# bundle exec ruby examples/local_watir_example.rb
2323

2424
begin
2525
require("watir")
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#!/usr/bin/env ruby
2+
# typed: ignore
3+
# frozen_string_literal: true
4+
5+
require "bundler/setup"
6+
require "stagehand"
7+
8+
# Example: Using Playwright with Stagehand remote mode (Browserbase browser).
9+
#
10+
# Prerequisites:
11+
# - Set MODEL_API_KEY or OPENAI_API_KEY environment variable
12+
# - Set BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID
13+
# - Install Playwright (outside this gem):
14+
# gem install playwright-ruby-client
15+
# npm install playwright
16+
# ./node_modules/.bin/playwright install chromium
17+
#
18+
# Run:
19+
# bundle exec ruby examples/remote_playwright_example.rb
20+
21+
begin
22+
require("playwright")
23+
rescue LoadError
24+
warn("Playwright is not installed. Run: gem install playwright-ruby-client")
25+
exit(1)
26+
end
27+
28+
model_key = ENV["MODEL_API_KEY"] || ENV["OPENAI_API_KEY"]
29+
browserbase_api_key = ENV["BROWSERBASE_API_KEY"].to_s
30+
browserbase_project_id = ENV["BROWSERBASE_PROJECT_ID"].to_s
31+
32+
missing = []
33+
missing << "MODEL_API_KEY" if model_key.to_s.empty?
34+
missing << "BROWSERBASE_API_KEY" if browserbase_api_key.empty?
35+
missing << "BROWSERBASE_PROJECT_ID" if browserbase_project_id.empty?
36+
37+
unless missing.empty?
38+
warn("Set #{missing.join(', ')} to run the remote Playwright example.")
39+
exit(1)
40+
end
41+
42+
def normalize_url(url)
43+
url.to_s.sub(%r{/$}, "")
44+
end
45+
46+
def resolve_page_target_id(cdp_session, page_url)
47+
info = cdp_session.send("Target.getTargetInfo")
48+
target_id = info.dig("targetInfo", "targetId")
49+
return target_id unless target_id.to_s.empty?
50+
51+
targets = cdp_session.send("Target.getTargets")
52+
entries = targets["targetInfos"] || []
53+
normalized = normalize_url(page_url)
54+
55+
target =
56+
entries.find do |entry|
57+
next false unless entry.is_a?(Hash) && entry["type"] == "page"
58+
59+
normalize_url(entry["url"]) == normalized
60+
end
61+
target ||= entries.find { |entry| entry.is_a?(Hash) && entry["type"] == "page" }
62+
target && target["targetId"]
63+
end
64+
65+
client = Stagehand::Client.new(
66+
browserbase_api_key: browserbase_api_key,
67+
browserbase_project_id: browserbase_project_id,
68+
model_api_key: model_key,
69+
server: "remote"
70+
)
71+
72+
session_id = nil
73+
74+
begin
75+
start_response = client.sessions.start(
76+
model_name: "openai/gpt-5-nano",
77+
browser: {
78+
type: :browserbase
79+
}
80+
)
81+
session_id = start_response.data.session_id
82+
cdp_url = start_response.data.cdp_url
83+
if cdp_url.to_s.empty?
84+
raise "No CDP URL returned for this session."
85+
end
86+
87+
puts("Session started: #{session_id}")
88+
puts("Connecting Playwright over CDP...")
89+
90+
# rubocop:disable Metrics/BlockLength
91+
Playwright.create(playwright_cli_executable_path: "./node_modules/.bin/playwright") do |playwright|
92+
browser = playwright.chromium.connect_over_cdp(cdp_url)
93+
begin
94+
context = browser.contexts.first || browser.new_context
95+
page = context.pages.first || context.new_page
96+
page.goto("https://example.com", wait_until: "domcontentloaded")
97+
98+
cdp_session = context.new_cdp_session(page)
99+
page_target_id = resolve_page_target_id(cdp_session, page.url)
100+
if page_target_id.to_s.empty?
101+
raise "Page target id not found for page target"
102+
end
103+
104+
observe_response = client.sessions.observe(
105+
session_id,
106+
frame_id: page_target_id,
107+
instruction: "Find all clickable links on this page"
108+
)
109+
puts("Found #{observe_response.data.result.length} possible actions")
110+
111+
action = observe_response.data.result.first
112+
act_input = action ? action.to_h.merge(method: "click") : "Click the 'Learn more' link"
113+
act_response = client.sessions.act(
114+
session_id,
115+
frame_id: page_target_id,
116+
input: act_input
117+
)
118+
puts("Act completed: #{act_response.data.result[:message]}")
119+
120+
extract_response = client.sessions.extract(
121+
session_id,
122+
frame_id: page_target_id,
123+
instruction: "Extract the main heading and any links on this page",
124+
schema: {
125+
type: "object",
126+
properties: {
127+
heading: {type: "string"},
128+
links: {type: "array", items: {type: "string"}}
129+
}
130+
}
131+
)
132+
puts("Extracted: #{extract_response.data.result}")
133+
134+
execute_response = client.sessions.execute(
135+
session_id,
136+
frame_id: page_target_id,
137+
execute_options: {
138+
instruction: "Click on the 'Learn more' link if available",
139+
max_steps: 3
140+
},
141+
agent_config: {
142+
model: Stagehand::ModelConfig.new(
143+
model_name: "openai/gpt-5-nano",
144+
api_key: model_key
145+
),
146+
cua: false
147+
}
148+
)
149+
puts("Agent completed: #{execute_response.data.result[:message]}")
150+
puts("Agent success: #{execute_response.data.result[:success]}")
151+
152+
page.wait_for_load_state(state: "domcontentloaded")
153+
page.screenshot(path: "screenshot_remote_playwright.png", fullPage: true)
154+
puts("Screenshot saved to: screenshot_remote_playwright.png")
155+
ensure
156+
browser.close
157+
end
158+
end
159+
# rubocop:enable Metrics/BlockLength
160+
ensure
161+
client.sessions.end_(session_id) if session_id
162+
client.close
163+
end

0 commit comments

Comments
 (0)