opensteer 0.8.9 → 0.8.11

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.
@@ -177,6 +177,12 @@ if (!status.live) {
177
177
 
178
178
  ## Request Capture, Plans, And Recipes
179
179
 
180
+ For the complete pipeline with mandatory phases, see [Request Plan Pipeline](request-workflow.md). This section covers SDK method signatures and examples for reusable scripts.
181
+
182
+ The deliverable of a request capture workflow is a persisted request plan tested via `request()`. `rawRequest()` is a diagnostic probe — not the deliverable.
183
+
184
+ ### Capture, Probe, and Infer
185
+
180
186
  ```ts
181
187
  await opensteer.open();
182
188
  await opensteer.goto({
@@ -196,8 +202,10 @@ const records = await opensteer.queryNetwork({
196
202
  limit: 20,
197
203
  });
198
204
 
205
+ // DIAGNOSTIC ONLY — probe transport to determine portability.
206
+ // Do NOT return this data as the final answer. Proceed to inferRequestPlan.
199
207
  const response = await opensteer.rawRequest({
200
- transport: "context-http",
208
+ transport: "direct-http",
201
209
  url: "https://example.com/api/products",
202
210
  method: "POST",
203
211
  body: {
@@ -205,43 +213,123 @@ const response = await opensteer.rawRequest({
205
213
  },
206
214
  });
207
215
 
216
+ // Infer plan with the transport you proved works
208
217
  await opensteer.inferRequestPlan({
209
218
  recordId: records.records[0]!.recordId,
210
219
  key: "products.search",
211
220
  version: "v1",
221
+ transport: "direct-http",
212
222
  });
213
223
 
214
- await opensteer.inferRequestPlan({
215
- recordId: records.records[0]!.recordId,
216
- key: "products.search.portable",
224
+ // Replay the plan — this is the deliverable
225
+ await opensteer.request("products.search", {
226
+ query: { q: "laptop" },
227
+ });
228
+ ```
229
+
230
+ ### Read and Fix Plans
231
+
232
+ After inferring a plan, read it to validate auth and annotate parameters:
233
+
234
+ ```ts
235
+ // Read the inferred plan
236
+ const plan = await opensteer.getRequestPlan({
237
+ key: "products.search",
217
238
  version: "v1",
218
- transport: "direct-http",
219
239
  });
220
240
 
221
- await opensteer.tagNetwork({
222
- tag: "products-load",
241
+ // Inspect plan.payload.auth — if auth.strategy is set but the API is public,
242
+ // rewrite the plan with auth removed
243
+ await opensteer.writeRequestPlan({
244
+ key: "products.search",
245
+ version: "v1",
246
+ tags: ["products", "search"],
247
+ provenance: {
248
+ source: "manual",
249
+ notes: "Auth removed — API is public. Parameters annotated.",
250
+ },
251
+ payload: {
252
+ ...plan.payload,
253
+ auth: undefined, // Remove spurious auth classification
254
+ parameters: [
255
+ { name: "q", in: "query", required: true, description: "Search keyword" },
256
+ { name: "count", in: "query", defaultValue: "24", description: "Results per page" },
257
+ { name: "offset", in: "query", defaultValue: "0", description: "Pagination offset" },
258
+ ],
259
+ },
260
+ });
261
+ ```
262
+
263
+ ### Auth Recipes
264
+
265
+ When an API genuinely requires auth, create an auth recipe that acquires tokens automatically:
266
+
267
+ ```ts
268
+ // Write an auth recipe that acquires a bearer token
269
+ await opensteer.writeAuthRecipe({
270
+ key: "example.auth",
271
+ version: "v1",
272
+ payload: {
273
+ description: "Acquire guest bearer token for example.com API",
274
+ steps: [
275
+ {
276
+ kind: "directRequest",
277
+ request: {
278
+ url: "https://example.com/api/oauth/token",
279
+ transport: "direct-http",
280
+ method: "POST",
281
+ body: { json: { grant_type: "client_credentials" } },
282
+ },
283
+ capture: {
284
+ bodyJsonPointer: { pointer: "/access_token", saveAs: "token" },
285
+ },
286
+ },
287
+ ],
288
+ outputs: {
289
+ headers: { Authorization: "Bearer {{token}}" },
290
+ },
291
+ },
223
292
  });
224
293
 
225
- await opensteer.request("products.search", {
294
+ // Bind the auth recipe to a plan by rewriting the plan with auth.recipe
295
+ const plan = await opensteer.getRequestPlan({ key: "products.search" });
296
+ await opensteer.writeRequestPlan({
297
+ key: "products.search",
298
+ version: "v1",
299
+ payload: {
300
+ ...plan.payload,
301
+ auth: {
302
+ strategy: "bearer-token",
303
+ recipe: { key: "example.auth", version: "v1" },
304
+ failurePolicy: { on: "status", status: "401", action: "recover" },
305
+ },
306
+ },
307
+ });
308
+
309
+ // Now request.execute automatically runs the auth recipe on 401
310
+ const result = await opensteer.request("products.search", {
226
311
  query: { q: "laptop" },
227
312
  });
228
313
  ```
229
314
 
230
- Rules:
315
+ **Recipe step types:** `directRequest`, `sessionRequest`, `request`, `readCookie`, `readStorage`, `evaluate`, `waitForNetwork`, `waitForCookie`, `goto`, `solveCaptcha`, `hook`. Each step can `capture` values; `outputs` maps captured variables to `headers`, `query`, `params`, or `body` overrides.
316
+
317
+ ### Rules
231
318
 
232
319
  - `captureNetwork` is supported on `goto()`, `click()`, `scroll()`, `input()`, and `hover()`. It is NOT supported on `open()`. Use `open()` then `goto({ url, captureNetwork })` to name initial navigation capture.
233
320
  - Query by capture first, then query all traffic to catch async requests that fire after page load.
234
- - Probe discovered APIs with `rawRequest()` using `direct-http` first, then `context-http`.
321
+ - Probe discovered APIs with `rawRequest()` using `direct-http` first, then `context-http`. `rawRequest()` is diagnostic — always proceed to `inferRequestPlan`.
235
322
  - Persistence is automatic; use `tagNetwork()` when you want to label a slice of already-persisted history for later lookup.
236
323
  - Use recipes when replay needs deterministic setup work. Use auth recipes when the setup is specifically auth-related. They live in separate registries.
237
324
 
238
- `rawRequest` input shapes:
325
+ ### Input Shapes
239
326
 
240
- - `headers` MUST be an array: `[{ name: "Authorization", value: "Bearer ..." }]`. NOT `{ Authorization: "Bearer ..." }`.
241
- - `body` MUST be one of: `{ json: { ... } }`, `{ text: "..." }`, or `{ base64: "..." }`. NOT a raw string or object.
327
+ - `rawRequest` `headers` MUST be an array: `[{ name: "Authorization", value: "Bearer ..." }]`. NOT `{ Authorization: "Bearer ..." }`.
328
+ - `rawRequest` `body` MUST be one of: `{ json: { ... } }`, `{ text: "..." }`, or `{ base64: "..." }`. NOT a raw string or object.
242
329
  - `rawRequest()` may populate parsed JSON on `data`. If it does not, decode `response.body.data` with `Buffer.from(..., "base64").toString("utf8")`.
330
+ - Recipe step `request` fields accept `{ key: value }` objects for headers (unlike `rawRequest`).
243
331
 
244
- Common errors and fixes:
332
+ ### Common Errors
245
333
 
246
334
  | Error | Cause | Fix |
247
335
  | ------------------------------------------------------------ | ----------------------------------------------------- | ----------------------------------------------------------------- |
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: recorder
3
+ description: "Use when the user wants to record a live browser workflow and turn it into a deterministic Opensteer replay script. Prefer this for manual browser capture, multi-tab flow recording, and agent-guided record-and-replay setup with the Opensteer CLI."
4
+ argument-hint: "[url]"
5
+ ---
6
+
7
+ # Recorder
8
+
9
+ Use the Opensteer recorder to open a headed local Playwright browser, capture DOM-level actions, and write a replayable TypeScript script.
10
+
11
+ ## When to use
12
+
13
+ - Record a real browser flow for later replay.
14
+ - Turn a manual QA walkthrough into Opensteer SDK code.
15
+ - Capture a multi-tab browsing session as a starting point for automation.
16
+
17
+ ## Quick start
18
+
19
+ ```bash
20
+ opensteer record --workspace <id> --url <url>
21
+ opensteer record --workspace <id> --url <url> --output <path>
22
+ ```
23
+
24
+ 1. Start recording with `opensteer record --workspace <id> --url <url>`.
25
+ 2. Perform the workflow in the headed browser.
26
+ 3. Stop recording with the injected `Stop recording` button in the browser.
27
+ 4. Wait for the CLI to write the script path and close the browser session.
28
+ 5. Inspect the generated file before replaying or editing it.
29
+
30
+ ## Guardrails
31
+
32
+ - Recording requires `provider=local`.
33
+ - Recording requires `engine=playwright`.
34
+ - Recording always uses a headed persistent browser for the target workspace.
35
+ - Stopping is browser-driven. Do not rely on `Ctrl+C` or removed timeout flags.
36
+ - If a launch argument value starts with `--`, pass it as `--arg=...`, not `--arg ...`.
37
+
38
+ ## Output
39
+
40
+ - Default output path: `.opensteer/workspaces/<id>/recorded-flow.ts`
41
+ - The generated script imports `Opensteer` from `opensteer`.
42
+ - Replay uses public SDK methods such as `open`, `goto`, `click`, `input`, `scroll`, `newPage`, `closePage`, `activatePage`, and `evaluate`.
43
+
44
+ ## Agent workflow
45
+
46
+ - Use the browser stop button as the primary stop path.
47
+ - After recording, read the generated script and summarize what was captured before changing it.
48
+ - If the user asks for replay verification, run the generated script instead of only inspecting the file.
49
+ - If the flow depends on recorder limits such as iframes or file upload, read the reference file before promising support.
50
+
51
+ ## References
52
+
53
+ - [Recorder Reference](references/recorder-reference.md)
54
+ - [Opensteer SDK Reference](../opensteer/references/sdk-reference.md)
@@ -0,0 +1,71 @@
1
+ # Recorder Reference
2
+
3
+ Read this file when you need recorder constraints, replay details, or non-default invocation patterns.
4
+
5
+ ## Command
6
+
7
+ ```bash
8
+ opensteer record --workspace <id> --url <url> [--output <path>]
9
+ ```
10
+
11
+ Useful launch-arg pattern when the value begins with dashes:
12
+
13
+ ```bash
14
+ opensteer record --workspace <id> --url <url> --arg=--remote-debugging-port=9333
15
+ ```
16
+
17
+ ## Stop behavior
18
+
19
+ - The recorder stops when the user clicks the injected `Stop recording` button in the browser.
20
+ - After stop, the CLI writes the replay script and closes the owned browser session.
21
+ - Do not use removed timeout flags such as `--record-timeout-ms`.
22
+
23
+ ## What Gets Recorded
24
+
25
+ - Clicks and double-clicks
26
+ - Text entry with idle-based coalescing
27
+ - Special key presses such as `Enter`, `Tab`, `Escape`, `Backspace`, `Delete`, and arrow keys
28
+ - Scroll gestures
29
+ - `<select>` value changes
30
+ - Same-document navigation via `pushState`, `replaceState`, `popstate`, and `hashchange`
31
+ - Full-page navigations, reloads, and back/forward traversal when they can be inferred
32
+ - New tabs, closed tabs, and tab switches
33
+
34
+ ## Selector Strategy
35
+
36
+ - Prefer stable single-attribute selectors such as `data-testid`, `data-test`, `data-qa`, `data-cy`, `id`, `name`, `role`, and `aria-label`
37
+ - Reuse the same attribute-priority ordering as Opensteer DOM match policy
38
+ - Fall back to short ancestor paths with attribute hints and `:nth-of-type()` when needed
39
+ - Require selector uniqueness at capture time
40
+
41
+ ## Generated Replay
42
+
43
+ - Opens the recorded start URL in the target workspace
44
+ - Keeps stable page variables such as `page0`, `page1`, and `page2`
45
+ - Activates the correct page before page-scoped SDK actions
46
+ - Merges `Enter` into the preceding `input()` call when the capture sequence allows it
47
+ - Uses `evaluate()` helpers when replay needs behavior that is not yet exposed as a first-class SDK helper
48
+
49
+ ## Replay verification
50
+
51
+ - Default generated path: `.opensteer/workspaces/<id>/recorded-flow.ts`
52
+ - Typical replay command: `pnpm exec tsx <path-to-recorded-flow.ts>`
53
+ - Review the script before replay if the user performed unsupported or approximate browser actions
54
+
55
+ ## Limitations
56
+
57
+ - v1 records only the top frame
58
+ - Cross-origin iframes are not recorded
59
+ - Shadow DOM selectors are best effort
60
+ - File uploads, drag-and-drop, and canvas interactions are not fully modeled
61
+ - Some browser-native key and pointer modifiers are approximated in replay because the public SDK surface is narrower than the raw browser event stream
62
+ - Back and forward detection is best effort and may fall back to direct navigation replay in ambiguous cases
63
+
64
+ ## Workflow
65
+
66
+ 1. Start recording with `opensteer record`.
67
+ 2. Perform the workflow manually in the headed browser.
68
+ 3. Click the `Stop recording` button in the browser.
69
+ 4. Wait for the recorder to write `recorded-flow.ts` and close the browser session.
70
+ 5. Review the generated `recorded-flow.ts`.
71
+ 6. Run the script with `tsx` or integrate it into a larger automation.