opensteer 0.7.1 → 0.8.2

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.
@@ -2,43 +2,66 @@
2
2
 
3
3
  Use the SDK when the workflow should become reusable TypeScript code in the repository.
4
4
 
5
- ## Session Ownership
5
+ ## Sections
6
6
 
7
- Owned session:
7
+ - [Construction](#construction)
8
+ - [DOM Automation And Extraction](#dom-automation-and-extraction)
9
+ - [Browser Admin](#browser-admin)
10
+ - [Request Capture, Plans, And Recipes](#request-capture-plans-and-recipes)
11
+ - [Common Methods](#common-methods)
12
+ - [Rules](#rules)
13
+
14
+ ## Construction
8
15
 
9
16
  ```ts
10
17
  import { Opensteer } from "opensteer";
11
18
 
12
19
  const opensteer = new Opensteer({
13
- name: "demo",
20
+ workspace: "github-sync",
14
21
  rootDir: process.cwd(),
15
- browser: { headless: true },
22
+ browser: "persistent",
23
+ launch: {
24
+ headless: false,
25
+ },
26
+ context: {
27
+ locale: "en-US",
28
+ },
16
29
  });
17
30
  ```
18
31
 
19
- Attached session:
32
+ - `workspace` creates a repo-local persistent root under `.opensteer/workspaces/<id>`.
33
+ - Omitting `workspace` creates a temporary root.
34
+ - `browser` can be `persistent`, `temporary`, or `{ mode: "attach", endpoint?, freshTab? }`.
35
+ - `opensteer.browser.status()`, `clone()`, `reset()`, and `delete()` manage the persistent workspace browser.
36
+ - `close()` shuts the current session and, for persistent workspaces, closes the live browser process.
37
+ - `disconnect()` detaches local runtime handles and leaves the workspace/browser files intact.
38
+ - The current public SDK does not expose `Opensteer.attach()`, cloud session helpers, or the ABP engine.
39
+
40
+ ## DOM Automation And Extraction
20
41
 
21
42
  ```ts
22
43
  import { Opensteer } from "opensteer";
23
44
 
24
- const opensteer = Opensteer.attach({
25
- name: "demo",
45
+ const opensteer = new Opensteer({
46
+ workspace: "demo",
26
47
  rootDir: process.cwd(),
27
48
  });
28
- ```
29
-
30
- Use `close()` for owned sessions. Use `disconnect()` for attached sessions.
31
-
32
- ## Core Browser Flow
33
49
 
34
- ```ts
35
50
  await opensteer.open("https://example.com");
36
- await opensteer.goto("https://example.com/products");
37
51
  await opensteer.snapshot("action");
38
- await opensteer.click({ description: "primary button" });
39
- await opensteer.input({ description: "search input", text: "laptop", pressEnter: true });
40
- await opensteer.hover({ description: "price filter" });
41
- await opensteer.scroll({ direction: "down", amount: 600 });
52
+
53
+ await opensteer.click({
54
+ selector: "button.primary",
55
+ description: "primary button",
56
+ });
57
+
58
+ await opensteer.input({
59
+ selector: "input[type=search]",
60
+ description: "search input",
61
+ text: "laptop",
62
+ pressEnter: true,
63
+ });
64
+
42
65
  const data = await opensteer.extract({
43
66
  description: "page summary",
44
67
  schema: {
@@ -46,14 +69,127 @@ const data = await opensteer.extract({
46
69
  url: { source: "current_url" },
47
70
  },
48
71
  });
72
+
73
+ const replayed = await opensteer.extract({
74
+ description: "page summary",
75
+ });
76
+ ```
77
+
78
+ DOM rules:
79
+
80
+ - Use `snapshot("action")` before counter-based interactions.
81
+ - Use `snapshot("extraction")` to inspect the page structure and design the output object.
82
+ - Treat snapshots as planning artifacts. `extract()` reads current page state and replays persisted extraction descriptors from deterministic, snapshot-backed payloads.
83
+ - `selector + description` or `element + description` persists a DOM action descriptor. Bare `description` replays it later.
84
+ - `description + schema` writes or updates a persisted extraction descriptor. Bare `description` replays it later.
85
+ - Keep DOM data collection in `extract()`, not `evaluate()` or raw page DOM parsing, when the result can be expressed as structured fields.
86
+
87
+ Supported extraction field shapes in the current public SDK:
88
+
89
+ - `{ element: N }`
90
+ - `{ element: N, attribute: "href" }`
91
+ - `{ selector: ".price" }`
92
+ - `{ selector: "img.hero", attribute: "src" }`
93
+ - `{ source: "current_url" }`
94
+
95
+ For arrays, include one or more representative objects in the schema. Add multiple examples when repeated rows have structural variants.
96
+
97
+ Do not use `prompt` or semantic placeholder values such as `"string"` in the current public SDK. The extractor expects explicit schema objects, arrays, and field descriptors.
98
+
99
+ ## Browser Admin
100
+
101
+ ```ts
102
+ const status = await opensteer.browser.status();
103
+
104
+ if (!status.live) {
105
+ await opensteer.browser.clone({
106
+ sourceUserDataDir: "/Users/me/Library/Application Support/Google/Chrome",
107
+ sourceProfileDirectory: "Default",
108
+ });
109
+ }
49
110
  ```
50
111
 
112
+ - `browser.clone()` is only for persistent workspace browsers.
113
+ - Clone before `open()` when the workflow needs local authenticated browser state.
114
+ - `browser.reset()` clears cloned browser state but keeps the workspace.
115
+ - `browser.delete()` removes workspace browser files.
116
+
117
+ ## Request Capture, Plans, And Recipes
118
+
119
+ ```ts
120
+ await opensteer.open();
121
+ await opensteer.goto({
122
+ url: "https://example.com/app",
123
+ networkTag: "page-load",
124
+ });
125
+
126
+ await opensteer.click({
127
+ selector: "button.load-products",
128
+ description: "load products",
129
+ networkTag: "products-load",
130
+ });
131
+
132
+ const records = await opensteer.queryNetwork({
133
+ tag: "products-load",
134
+ includeBodies: true,
135
+ limit: 20,
136
+ });
137
+
138
+ const response = await opensteer.rawRequest({
139
+ transport: "context-http",
140
+ url: "https://example.com/api/products",
141
+ method: "POST",
142
+ body: {
143
+ json: { page: 1 },
144
+ },
145
+ });
146
+
147
+ await opensteer.inferRequestPlan({
148
+ recordId: records.records[0]!.id,
149
+ key: "products.search",
150
+ version: "v1",
151
+ });
152
+
153
+ await opensteer.saveNetwork({
154
+ tag: "products-load",
155
+ });
156
+
157
+ await opensteer.request("products.search", {
158
+ query: { q: "laptop" },
159
+ });
160
+ ```
161
+
162
+ Rules:
163
+
164
+ - `networkTag` is supported on `goto()`, `click()`, `scroll()`, `input()`, and `hover()`. It is NOT supported on `open()`. Use `open()` then `goto({ url, networkTag })` to tag initial navigation.
165
+ - Query by tag first, then query all traffic to catch async requests that fire after page load.
166
+ - Probe discovered APIs with `rawRequest()` using `direct-http` first, then `context-http`.
167
+ - Save important captures with `saveNetwork()` before the session closes.
168
+ - Use recipes when replay needs deterministic setup work. Use auth recipes when the setup is specifically auth-related. They live in separate registries.
169
+
170
+ `rawRequest` input shapes:
171
+
172
+ - `headers` MUST be an array: `[{ name: "Authorization", value: "Bearer ..." }]`. NOT `{ Authorization: "Bearer ..." }`.
173
+ - `body` MUST be one of: `{ json: { ... } }`, `{ text: "..." }`, or `{ base64: "..." }`. NOT a raw string or object.
174
+ - `rawRequest()` may populate parsed JSON on `data`. If it does not, decode `response.body.data` with `Buffer.from(..., "base64").toString("utf8")`.
175
+
176
+ Common errors and fixes:
177
+
178
+ | Error | Cause | Fix |
179
+ | ------------------------------------------------------------ | ----------------------------------------------------- | ----------------------------------------------------------------- |
180
+ | `"networkTag is not allowed"` | Used `networkTag` on `open()` | Move to `goto({ url, networkTag })` |
181
+ | `"must be array"` on `rawRequest` | Headers passed as `{key: value}` | Use `[{name, value}]` array |
182
+ | `"must match exactly one supported shape"` | Body passed as raw string | Wrap in `{json: {...}}` or `{text: "..."}` |
183
+ | `"Specify exactly one of element, selector, or description"` | `scroll()` called without a target | Add `selector: "body"` or a `description` |
184
+ | `"registry record already exists"` | `inferRequestPlan` called twice with same key+version | Catch the error or use a new version |
185
+ | `"no stored extraction descriptor"` | `extract()` called with `description` but no `schema` | Always provide `schema` unless a descriptor was previously stored |
186
+
51
187
  ## Common Methods
52
188
 
53
189
  Session and page control:
54
190
 
55
- - `Opensteer.attach({ name?, rootDir? })`
56
- - `open(url | { url?, name?, browser?, context? })`
191
+ - `new Opensteer({ workspace?, rootDir?, browser?, launch?, context? })`
192
+ - `open(url | { url?, workspace?, browser?, launch?, context? })`
57
193
  - `goto(url | { url, networkTag? })`
58
194
  - `listPages()`
59
195
  - `newPage({ url?, openerPageRef? })`
@@ -92,30 +228,30 @@ Request capture and replay:
92
228
  - `getRecipe({ key, version? })`
93
229
  - `listRecipes({ key? })`
94
230
  - `runRecipe({ key, version?, input? })`
231
+ - `writeAuthRecipe({ key, version, payload, tags?, provenance? })`
232
+ - `getAuthRecipe({ key, version? })`
233
+ - `listAuthRecipes({ key? })`
234
+ - `runAuthRecipe({ key, version?, input? })`
95
235
 
96
- Instrumentation:
97
-
98
- - `captureScripts({ pageRef?, includeInline?, includeExternal?, includeDynamic?, includeWorkers?, urlFilter?, persist? })`
99
- - `addInitScript({ script, args?, pageRef? })`
100
- - `route({ urlPattern, resourceTypes?, times?, handler })`
101
- - `interceptScript({ urlPattern, handler, times? })`
102
-
103
- Browser and profile helpers:
236
+ Browser helpers:
104
237
 
105
238
  - `discoverLocalCdpBrowsers({ timeoutMs? })`
106
239
  - `inspectCdpEndpoint({ endpoint, headers?, timeoutMs? })`
107
- - `inspectLocalBrowserProfile({ userDataDir? })`
108
- - `unlockLocalBrowserProfile({ userDataDir })`
240
+ - `browser.status()`
241
+ - `browser.clone({ sourceUserDataDir, sourceProfileDirectory? })`
242
+ - `browser.reset()`
243
+ - `browser.delete()`
109
244
 
110
245
  Lifecycle:
111
246
 
112
- - `disconnect()`
113
247
  - `close()`
248
+ - `disconnect()`
114
249
 
115
250
  ## Rules
116
251
 
117
- - Wrap owned sessions in `try/finally` and call `close()`.
252
+ - Wrap long-running browser ownership in `try/finally` and call `close()`.
118
253
  - Use `networkTag` on actions that trigger requests you may inspect later.
119
254
  - Use `description` when the interaction should be replayable across sessions.
255
+ - Use `description` plus `schema` when an extraction should be replayable across sessions.
120
256
  - Use `element` targets only with a fresh snapshot from the same live session.
121
257
  - Prefer Opensteer methods over raw Playwright so browser, extraction, and replay semantics stay consistent.