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.
- package/dist/{chunk-RO6WAWWG.js → chunk-33FDEOQY.js} +1973 -55
- package/dist/chunk-33FDEOQY.js.map +1 -0
- package/dist/cli/bin.cjs +2474 -169
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +398 -10
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.cjs +206 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +82 -20
- package/dist/index.d.ts +82 -20
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/skills/opensteer/SKILL.md +33 -14
- package/skills/opensteer/references/request-workflow.md +312 -193
- package/skills/opensteer/references/sdk-reference.md +102 -14
- package/skills/recorder/SKILL.md +54 -0
- package/skills/recorder/references/recorder-reference.md +71 -0
- package/dist/chunk-RO6WAWWG.js.map +0 -1
|
@@ -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: "
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
222
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|