opensteer 0.8.14 → 0.8.16

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: opensteer
3
- description: "Handles Opensteer browser automation, structured DOM extraction, and browser-backed API reverse engineering. Use when the user mentions Opensteer, browser automation, real Chromium sessions, DOM extraction, network capture, replay, cookies, reverse-engineering a site API, or browser-grade fetch."
3
+ description: "Handles Opensteer browser automation, structured DOM extraction, and browser-backed API reverse engineering. Use when the user mentions Opensteer, browser automation, real Chromium sessions, DOM extraction, network capture, reverse-engineering a site API, or browser-grade fetch."
4
4
  argument-hint: "[goal]"
5
5
  ---
6
6
 
@@ -9,7 +9,7 @@ argument-hint: "[goal]"
9
9
  Opensteer gives agents a real Chromium browser for two jobs normal code cannot do:
10
10
 
11
11
  1. **DOM automation** — interact with pages and extract structured data via snapshot-based element targeting.
12
- 2. **API reverse engineering** — capture real browser traffic, identify APIs, test transport portability, and write replay code.
12
+ 2. **API reverse engineering** — capture real browser traffic, identify APIs, test transport portability, and write reusable code.
13
13
 
14
14
  The workflow is always: **explore with CLI, then write reusable code with SDK.**
15
15
 
@@ -56,7 +56,7 @@ opensteer hover 3 --workspace demo --persist "menu trigger"
56
56
  opensteer scroll down 400 --workspace demo
57
57
  ```
58
58
 
59
- `--persist <name>` saves the element's structural DOM path for deterministic SDK replay. Element number is always required on CLI — persist is save-only, not a targeting mode.
59
+ `--persist <key>` saves the element's structural DOM path for deterministic SDK replay. Element number is always required on CLI — persist is save-only, not a targeting mode.
60
60
 
61
61
  ### Step 3: Extract
62
62
 
@@ -85,7 +85,7 @@ const opensteer = new Opensteer({ workspace: "demo", rootDir: process.cwd() });
85
85
  try {
86
86
  await opensteer.open("https://example.com");
87
87
 
88
- // Replay by persist name — no element numbers or snapshots needed
88
+ // Replay by persist key — no element numbers or snapshots needed
89
89
  await opensteer.input({ persist: "search input", text: "laptop", pressEnter: true });
90
90
  await opensteer.click({ persist: "search button" });
91
91
 
@@ -122,40 +122,51 @@ opensteer network query --workspace demo --capture search --hostname api.example
122
122
 
123
123
  `--json` filters to JSON and GraphQL responses only. Other filters: `--url`, `--path`, `--method`, `--status`, `--type`, `--before`, `--after`, `--limit`.
124
124
 
125
- ### Step 3: Inspect
125
+ ### Step 3: Inspect and probe transport
126
126
 
127
127
  ```bash
128
128
  opensteer network detail rec_123 --workspace demo
129
+ opensteer network detail rec_123 --probe --workspace demo
129
130
  ```
130
131
 
131
- Shows: URL, method, request headers, cookies sent, request/response body preview, GraphQL metadata, redirect chain.
132
+ The first command shows: URL, method, request headers, cookies sent, request/response body preview, GraphQL metadata, redirect chain.
132
133
 
133
- ### Step 4: Test replay
134
+ Add `--probe` to also test which transport works for this API:
135
+
136
+ | Transport | Meaning | SDK usage |
137
+ | ------------- | ------------------------------ | ----------------------------------------------- |
138
+ | `direct-http` | Plain HTTP works | `this.fetch(url)` (default) |
139
+ | `matched-tls` | Needs TLS fingerprint matching | `this.fetch(url, { transport: "matched-tls" })` |
140
+ | `page-http` | Needs a live browser page | `this.fetch(url, { transport: "page" })` |
141
+
142
+ ### Step 4: Check browser state (if 401/403)
134
143
 
135
144
  ```bash
136
- opensteer replay rec_123 --workspace demo
137
- opensteer replay rec_123 --workspace demo --query keyword=headphones --query count=10
145
+ opensteer state example.com --workspace demo
138
146
  ```
139
147
 
140
- Replay tries transports automatically and reports which succeeded:
148
+ Look for session cookies, CSRF tokens, or storage-backed auth that the request depends on.
141
149
 
142
- | Transport | Meaning | SDK usage |
143
- |---|---|---|
144
- | `direct-http` | Plain HTTP works | `opensteer.fetch(url)` (default) |
145
- | `matched-tls` | Needs TLS fingerprint matching | `opensteer.fetch(url, { transport: "matched-tls" })` |
146
- | `page-http` | Needs a live browser page | `opensteer.fetch(url, { transport: "page" })` |
150
+ ### Step 5: Test and write code with exec
147
151
 
148
- ### Step 5: Check browser state (if replay returns 401/403)
152
+ Use `exec` to test the API call with the SDK. The code you write here is the same code that goes in your final script:
149
153
 
150
154
  ```bash
151
- opensteer cookies example.com --workspace demo
152
- opensteer storage example.com --workspace demo
153
- opensteer state example.com --workspace demo
155
+ opensteer exec "
156
+ const r = await this.fetch('https://api.example.com/search', {
157
+ method: 'POST',
158
+ headers: { 'content-type': 'application/json' },
159
+ body: JSON.stringify({ keyword: 'laptop', count: 24 }),
160
+ });
161
+ return { status: r.status, data: await r.json() };
162
+ " --workspace demo
154
163
  ```
155
164
 
156
- Look for session cookies, CSRF tokens, or storage-backed auth that the request depends on.
165
+ `exec` runs JavaScript with the Opensteer SDK bound as `this`. It supports `await` and has access to `this.fetch()`, `this.cookies()`, `this.evaluate()`, and all other SDK methods.
166
+
167
+ IMPORTANT: Use `exec` instead of `evaluate` for API calls. `evaluate` runs inside the browser page where anti-bot scripts can detect and block programmatic requests. `exec` runs through the framework's transport stack which automatically finds the least detectable path.
157
168
 
158
- ### Step 6: Write SDK code
169
+ ### Step 6: Write the final SDK script
159
170
 
160
171
  ```bash
161
172
  opensteer close --workspace demo
@@ -165,31 +176,17 @@ opensteer close --workspace demo
165
176
  import { Opensteer } from "opensteer";
166
177
  const opensteer = new Opensteer({ workspace: "demo", rootDir: process.cwd() });
167
178
 
168
- async function ensureSession() {
169
- const cookies = await opensteer.cookies("example.com");
170
- if (cookies.has("session")) return;
171
- await opensteer.goto("https://example.com");
172
- }
173
-
174
179
  export async function search(keyword: string) {
175
- await ensureSession();
176
180
  const response = await opensteer.fetch("https://api.example.com/search", {
177
- query: { keyword, count: 24 },
181
+ method: "POST",
182
+ headers: { "content-type": "application/json" },
183
+ body: JSON.stringify({ keyword, count: 24 }),
178
184
  });
179
185
  return response.json();
180
186
  }
181
187
  ```
182
188
 
183
- If replay showed a required transport, carry it into `fetch()`:
184
-
185
- ```ts
186
- const response = await opensteer.fetch("https://api.example.com/search", {
187
- query: { keyword: "laptop" },
188
- transport: "matched-tls",
189
- });
190
- ```
191
-
192
- IMPORTANT: `opensteer.fetch()` does not require opening a browser page. It works directly with the session's cookies and transport stack. Only call `opensteer.open()` or `opensteer.goto()` if you need to establish session cookies first.
189
+ `opensteer.fetch()` accepts standard Web Fetch API syntax (`body` as string, `headers` as `Record<string, string>`). It auto-selects the best transport and includes browser cookies by default.
193
190
 
194
191
  ---
195
192
 
@@ -244,6 +241,14 @@ opensteer tab close 2 --workspace demo
244
241
 
245
242
  ## Run JavaScript
246
243
 
244
+ Use `exec` for API calls and SDK operations (runs in Node.js, avoids bot detection):
245
+
246
+ ```bash
247
+ opensteer exec "await this.fetch('https://api.example.com/data').then(r => r.json())" --workspace demo
248
+ ```
249
+
250
+ Use `evaluate` only for DOM inspection (runs inside the browser page):
251
+
247
252
  ```bash
248
253
  opensteer evaluate "document.title" --workspace demo
249
254
  ```
@@ -264,28 +269,26 @@ opensteer computer screenshot --workspace demo
264
269
 
265
270
  ## CLI Quick Reference
266
271
 
267
- | Command | Positional args | Key flags |
268
- |---|---|---|
269
- | `open <url>` | url | `--headless`, `--provider`, `--attach-endpoint`, `--attach-header` |
270
- | `close` | — | — |
271
- | `status` | — | — |
272
- | `goto <url>` | url | `--capture-network` |
273
- | `snapshot [mode]` | action \| extraction | — |
274
- | `click <element>` | element number | `--persist`, `--capture-network`, `--button` |
275
- | `hover <element>` | element number | `--persist`, `--capture-network` |
276
- | `input <element> <text>` | element, text | `--persist`, `--press-enter`, `--capture-network` |
277
- | `scroll <dir> <amount>` | direction, amount | `--element`, `--persist`, `--capture-network` |
278
- | `extract <schema>` | JSON schema | `--persist` |
279
- | `evaluate <script>` | JS expression | — |
280
- | `network query` | — | `--capture`, `--url`, `--hostname`, `--json`, `--limit`, +6 filters |
281
- | `network detail <id>` | recordId | |
282
- | `replay <id>` | recordId | `--query`, `--header`, `--body`, `--variables` |
283
- | `fetch <url>` | url | `--method`, `--header`, `--query`, `--body`, `--transport`, `--cookies` |
284
- | `cookies [domain]` | domain (optional) | — |
285
- | `storage [domain]` | domain (optional) | — |
286
- | `state [domain]` | domain (optional) | |
287
- | `tab list / new / <n> / close` | varies | — |
288
- | `computer click/type/key/scroll/move/drag/screenshot/wait` | varies | `--capture-network` |
272
+ | Command | Positional args | Key flags |
273
+ | ---------------------------------------------------------- | -------------------- | ----------------------------------------------------------------------- |
274
+ | `open <url>` | url | `--headless`, `--provider`, `--attach-endpoint`, `--attach-header` |
275
+ | `close` | — | — |
276
+ | `status` | — | — |
277
+ | `goto <url>` | url | `--capture-network` |
278
+ | `snapshot [mode]` | action \| extraction | — |
279
+ | `click <element>` | element number | `--persist`, `--capture-network`, `--button` |
280
+ | `hover <element>` | element number | `--persist`, `--capture-network` |
281
+ | `input <element> <text>` | element, text | `--persist`, `--press-enter`, `--capture-network` |
282
+ | `scroll <dir> <amount>` | direction, amount | `--element`, `--persist`, `--capture-network` |
283
+ | `extract <schema>` | JSON schema | `--persist` |
284
+ | `evaluate <script>` | JS expression | — |
285
+ | `network query` | — | `--capture`, `--url`, `--hostname`, `--json`, `--limit`, +6 filters |
286
+ | `network detail <id>` | recordId | `--probe` |
287
+ | `fetch <url>` | url | `--method`, `--header`, `--query`, `--body`, `--transport`, `--cookies` |
288
+ | `state [domain]` | domain (optional) | |
289
+ | `exec <expression>` | JS expression | — |
290
+ | `tab list / new / <n> / close` | varies | |
291
+ | `computer click/type/key/scroll/move/drag/screenshot/wait` | varies | `--capture-network` |
289
292
 
290
293
  ## SDK Quick Reference
291
294
 
@@ -312,20 +315,16 @@ await opensteer.extract({ persist: "name", schema: { ... } }); // inline schem
312
315
  // Network discovery
313
316
  const records = await opensteer.network.query({ capture: "label", limit: 20 });
314
317
  const detail = await opensteer.network.detail(recordId);
315
- const replay = await opensteer.network.replay(recordId, { query: { k: "v" } });
316
318
 
317
- // Fetch — works without a page open
319
+ // Fetch — standard Web Fetch API syntax, auto-selects transport
318
320
  const response = await opensteer.fetch(url, {
319
- query?: { key: "value" },
320
- method?: "POST",
321
- headers?: { Authorization: "Bearer ..." },
322
- body?: { ... },
323
- transport?: "auto" | "direct" | "matched-tls" | "page",
321
+ method: "POST",
322
+ headers: { "content-type": "application/json" },
323
+ body: JSON.stringify({ keyword: "laptop" }),
324
324
  });
325
325
 
326
326
  // Browser state
327
327
  const cookies = await opensteer.cookies("domain.com"); // .has(), .get(), .serialize()
328
- const storage = await opensteer.storage("domain.com", "local");
329
328
  const state = await opensteer.state("domain.com");
330
329
 
331
330
  // Snapshots
@@ -337,11 +336,11 @@ const html = await opensteer.snapshot("extraction");
337
336
 
338
337
  ## Common Issues
339
338
 
340
- | Symptom | Fix |
341
- |---|---|
342
- | Element numbers wrong after navigation | Re-snapshot before using element numbers |
343
- | `replay` returns 401/403 | Check `cookies`, `storage`, `state` — request depends on session tokens |
344
- | `replay` works but `fetch()` fails | Use the transport replay discovered: `transport: "matched-tls"` or `"page"` |
345
- | Direct HTTP blocked, browser transport works | Site uses TLS fingerprinting use `transport: "matched-tls"` |
346
- | Extract returns empty data | Element numbers changed re-snapshot and rebuild the schema |
347
- | `fetch()` fails with no session | Call `opensteer.goto(url)` first to establish cookies, then `fetch()` |
339
+ | Symptom | Fix |
340
+ | -------------------------------------------- | -------------------------------------------------------------------------------------------------- |
341
+ | Element numbers wrong after navigation | Re-snapshot before using element numbers |
342
+ | `fetch()` returns 401/403 | Check `state` — request depends on session cookies or tokens |
343
+ | Direct HTTP blocked, browser transport works | Site uses TLS fingerprinting use `transport: "matched-tls"` |
344
+ | Extract returns empty data | Element numbers changedre-snapshot and rebuild the schema |
345
+ | `fetch()` fails with no session | Call `opensteer.goto(url)` first to establish cookies, then `fetch()` |
346
+ | Using `evaluate` for API calls | Use `exec` instead `evaluate` runs inside the page where anti-bot scripts can intercept requests |