opensteer 0.9.0 → 0.9.1

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.
Files changed (38) hide show
  1. package/dist/chunk-4LP7QP2O.js +4336 -0
  2. package/dist/chunk-4LP7QP2O.js.map +1 -0
  3. package/dist/{chunk-656MQUSM.js → chunk-6PGXWW3X.js} +4787 -9519
  4. package/dist/chunk-6PGXWW3X.js.map +1 -0
  5. package/dist/chunk-BMPUL66S.js +1170 -0
  6. package/dist/chunk-BMPUL66S.js.map +1 -0
  7. package/dist/{chunk-OIKLSFXA.js → chunk-L4FWHBQJ.js} +4 -3
  8. package/dist/chunk-L4FWHBQJ.js.map +1 -0
  9. package/dist/chunk-Z53HNZ7Z.js +1800 -0
  10. package/dist/chunk-Z53HNZ7Z.js.map +1 -0
  11. package/dist/cli/bin.cjs +3050 -281
  12. package/dist/cli/bin.cjs.map +1 -1
  13. package/dist/cli/bin.js +124 -7
  14. package/dist/cli/bin.js.map +1 -1
  15. package/dist/index.cjs +918 -263
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +1 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +4 -2
  20. package/dist/local-view/public/assets/app.css +770 -0
  21. package/dist/local-view/public/assets/app.js +2053 -0
  22. package/dist/local-view/public/index.html +235 -0
  23. package/dist/local-view/serve-entry.cjs +7436 -0
  24. package/dist/local-view/serve-entry.cjs.map +1 -0
  25. package/dist/local-view/serve-entry.d.cts +1 -0
  26. package/dist/local-view/serve-entry.d.ts +1 -0
  27. package/dist/local-view/serve-entry.js +23 -0
  28. package/dist/local-view/serve-entry.js.map +1 -0
  29. package/dist/opensteer-KZCRP425.js +6 -0
  30. package/dist/{opensteer-LKX3233A.js.map → opensteer-KZCRP425.js.map} +1 -1
  31. package/dist/session-control-VGBFOH3Y.js +39 -0
  32. package/dist/session-control-VGBFOH3Y.js.map +1 -0
  33. package/package.json +8 -8
  34. package/skills/README.md +3 -0
  35. package/skills/opensteer/SKILL.md +229 -48
  36. package/dist/chunk-656MQUSM.js.map +0 -1
  37. package/dist/chunk-OIKLSFXA.js.map +0 -1
  38. package/dist/opensteer-LKX3233A.js +0 -4
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import { runLocalViewService } from '../chunk-Z53HNZ7Z.js';
3
+ import '../chunk-BMPUL66S.js';
4
+
5
+ // src/local-view/serve-entry.ts
6
+ runLocalViewService().catch((error) => {
7
+ const payload = error instanceof Error ? {
8
+ error: {
9
+ name: error.name,
10
+ message: error.message
11
+ }
12
+ } : {
13
+ error: {
14
+ name: "Error",
15
+ message: String(error)
16
+ }
17
+ };
18
+ process.stderr.write(`${JSON.stringify(payload)}
19
+ `);
20
+ process.exitCode = 1;
21
+ });
22
+ //# sourceMappingURL=serve-entry.js.map
23
+ //# sourceMappingURL=serve-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/local-view/serve-entry.ts"],"names":[],"mappings":";;;;;AAIA,mBAAA,EAAoB,CAAE,KAAA,CAAM,CAAC,KAAA,KAAU;AACrC,EAAA,MAAM,OAAA,GACJ,iBAAiB,KAAA,GACb;AAAA,IACE,KAAA,EAAO;AAAA,MACL,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,SAAS,KAAA,CAAM;AAAA;AACjB,GACF,GACA;AAAA,IACE,KAAA,EAAO;AAAA,MACL,IAAA,EAAM,OAAA;AAAA,MACN,OAAA,EAAS,OAAO,KAAK;AAAA;AACvB,GACF;AACN,EAAA,OAAA,CAAQ,OAAO,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC;AAAA,CAAI,CAAA;AACnD,EAAA,OAAA,CAAQ,QAAA,GAAW,CAAA;AACrB,CAAC,CAAA","file":"serve-entry.js","sourcesContent":["#!/usr/bin/env node\n\nimport { runLocalViewService } from \"./serve.js\";\n\nrunLocalViewService().catch((error) => {\n const payload =\n error instanceof Error\n ? {\n error: {\n name: error.name,\n message: error.message,\n },\n }\n : {\n error: {\n name: \"Error\",\n message: String(error),\n },\n };\n process.stderr.write(`${JSON.stringify(payload)}\\n`);\n process.exitCode = 1;\n});\n"]}
@@ -0,0 +1,6 @@
1
+ export { Opensteer } from './chunk-L4FWHBQJ.js';
2
+ import './chunk-6PGXWW3X.js';
3
+ import './chunk-4LP7QP2O.js';
4
+ import './chunk-BMPUL66S.js';
5
+ //# sourceMappingURL=opensteer-KZCRP425.js.map
6
+ //# sourceMappingURL=opensteer-KZCRP425.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"opensteer-LKX3233A.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"opensteer-KZCRP425.js"}
@@ -0,0 +1,39 @@
1
+ import { OpensteerBrowserManager } from './chunk-4LP7QP2O.js';
2
+ import { readLocalViewSessionManifest, readPersistedLocalBrowserSessionRecord, deleteLocalViewSessionManifest } from './chunk-BMPUL66S.js';
3
+
4
+ // src/local-view/session-control.ts
5
+ var LocalViewSessionCloseError = class extends Error {
6
+ constructor(message, statusCode) {
7
+ super(message);
8
+ this.statusCode = statusCode;
9
+ this.name = "LocalViewSessionCloseError";
10
+ }
11
+ };
12
+ async function closeLocalViewSessionBrowser(sessionId) {
13
+ const manifest = await readLocalViewSessionManifest(sessionId);
14
+ if (!manifest) {
15
+ throw new LocalViewSessionCloseError("Session not found.", 404);
16
+ }
17
+ if (manifest.ownership !== "owned") {
18
+ throw new LocalViewSessionCloseError(
19
+ "Only Opensteer-owned local browsers can be closed from the local view.",
20
+ 409
21
+ );
22
+ }
23
+ const record = await readPersistedLocalBrowserSessionRecord(manifest.rootPath);
24
+ if (!record || record.pid !== manifest.pid || record.startedAt !== manifest.startedAt || record.engine !== manifest.engine) {
25
+ await deleteLocalViewSessionManifest(sessionId).catch(() => void 0);
26
+ throw new LocalViewSessionCloseError("Session not found.", 404);
27
+ }
28
+ const manager = new OpensteerBrowserManager({
29
+ rootPath: manifest.rootPath,
30
+ ...manifest.workspace === void 0 ? {} : { workspace: manifest.workspace },
31
+ engineName: record.engine,
32
+ browser: "persistent"
33
+ });
34
+ await manager.close();
35
+ }
36
+
37
+ export { LocalViewSessionCloseError, closeLocalViewSessionBrowser };
38
+ //# sourceMappingURL=session-control-VGBFOH3Y.js.map
39
+ //# sourceMappingURL=session-control-VGBFOH3Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/local-view/session-control.ts"],"names":[],"mappings":";;;;AAOO,IAAM,0BAAA,GAAN,cAAyC,KAAA,CAAM;AAAA,EACpD,WAAA,CACE,SACS,UAAA,EACT;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AAFJ,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AAGT,IAAA,IAAA,CAAK,IAAA,GAAO,4BAAA;AAAA,EACd;AACF;AAEA,eAAsB,6BAA6B,SAAA,EAAkC;AACnF,EAAA,MAAM,QAAA,GAAW,MAAM,4BAAA,CAA6B,SAAS,CAAA;AAC7D,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,0BAAA,CAA2B,oBAAA,EAAsB,GAAG,CAAA;AAAA,EAChE;AAEA,EAAA,IAAI,QAAA,CAAS,cAAc,OAAA,EAAS;AAClC,IAAA,MAAM,IAAI,0BAAA;AAAA,MACR,wEAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAAS,MAAM,sCAAA,CAAuC,QAAA,CAAS,QAAQ,CAAA;AAC7E,EAAA,IACE,CAAC,MAAA,IACD,MAAA,CAAO,GAAA,KAAQ,QAAA,CAAS,GAAA,IACxB,MAAA,CAAO,SAAA,KAAc,QAAA,CAAS,SAAA,IAC9B,MAAA,CAAO,MAAA,KAAW,SAAS,MAAA,EAC3B;AACA,IAAA,MAAM,8BAAA,CAA+B,SAAS,CAAA,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA;AACrE,IAAA,MAAM,IAAI,0BAAA,CAA2B,oBAAA,EAAsB,GAAG,CAAA;AAAA,EAChE;AAEA,EAAA,MAAM,OAAA,GAAU,IAAI,uBAAA,CAAwB;AAAA,IAC1C,UAAU,QAAA,CAAS,QAAA;AAAA,IACnB,GAAI,SAAS,SAAA,KAAc,MAAA,GAAY,EAAC,GAAI,EAAE,SAAA,EAAW,QAAA,CAAS,SAAA,EAAU;AAAA,IAC5E,YAAY,MAAA,CAAO,MAAA;AAAA,IACnB,OAAA,EAAS;AAAA,GACV,CAAA;AACD,EAAA,MAAM,QAAQ,KAAA,EAAM;AACtB","file":"session-control-VGBFOH3Y.js","sourcesContent":["import { readPersistedLocalBrowserSessionRecord } from \"../live-session.js\";\nimport { OpensteerBrowserManager } from \"../browser-manager.js\";\nimport {\n deleteLocalViewSessionManifest,\n readLocalViewSessionManifest,\n} from \"./session-manifest.js\";\n\nexport class LocalViewSessionCloseError extends Error {\n constructor(\n message: string,\n readonly statusCode: 404 | 409,\n ) {\n super(message);\n this.name = \"LocalViewSessionCloseError\";\n }\n}\n\nexport async function closeLocalViewSessionBrowser(sessionId: string): Promise<void> {\n const manifest = await readLocalViewSessionManifest(sessionId);\n if (!manifest) {\n throw new LocalViewSessionCloseError(\"Session not found.\", 404);\n }\n\n if (manifest.ownership !== \"owned\") {\n throw new LocalViewSessionCloseError(\n \"Only Opensteer-owned local browsers can be closed from the local view.\",\n 409,\n );\n }\n\n const record = await readPersistedLocalBrowserSessionRecord(manifest.rootPath);\n if (\n !record ||\n record.pid !== manifest.pid ||\n record.startedAt !== manifest.startedAt ||\n record.engine !== manifest.engine\n ) {\n await deleteLocalViewSessionManifest(sessionId).catch(() => undefined);\n throw new LocalViewSessionCloseError(\"Session not found.\", 404);\n }\n\n const manager = new OpensteerBrowserManager({\n rootPath: manifest.rootPath,\n ...(manifest.workspace === undefined ? {} : { workspace: manifest.workspace }),\n engineName: record.engine,\n browser: \"persistent\",\n });\n await manager.close();\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opensteer",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Opensteer browser automation, replay, and reverse-engineering toolkit.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -56,14 +56,14 @@
56
56
  "sharp": "^0.34.5",
57
57
  "skills": "^1.4.6",
58
58
  "ws": "^8.18.0",
59
- "@opensteer/runtime-core": "0.2.0",
60
- "@opensteer/engine-playwright": "0.8.6"
59
+ "@opensteer/engine-playwright": "0.8.7",
60
+ "@opensteer/runtime-core": "0.2.1"
61
61
  },
62
62
  "optionalDependencies": {
63
63
  "webcrack": "^2.15.1"
64
64
  },
65
65
  "peerDependencies": {
66
- "@opensteer/engine-abp": "0.8.7"
66
+ "@opensteer/engine-abp": "0.8.8"
67
67
  },
68
68
  "peerDependenciesMeta": {
69
69
  "@opensteer/engine-abp": {
@@ -71,12 +71,12 @@
71
71
  }
72
72
  },
73
73
  "devDependencies": {
74
- "@opensteer/browser-core": "0.7.8",
75
- "@opensteer/engine-abp": "0.8.7",
76
- "@opensteer/protocol": "0.8.0"
74
+ "@opensteer/engine-abp": "0.8.8",
75
+ "@opensteer/browser-core": "0.7.9",
76
+ "@opensteer/protocol": "0.8.1"
77
77
  },
78
78
  "scripts": {
79
- "build": "tsup && node ../../scripts/sync-package-skills.mjs",
79
+ "build": "tsup && node ../../scripts/copy-opensteer-local-view-assets.mjs && node ../../scripts/sync-package-skills.mjs",
80
80
  "clean": "rimraf dist skills",
81
81
  "typecheck": "tsc --noEmit -p tsconfig.json"
82
82
  }
package/skills/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  First-party Opensteer skills published from this repository. Install them through the upstream [`skills`](https://skills.sh) CLI into Codex, Cursor, Claude Code, and other compatible agents.
4
4
 
5
+ From an Opensteer checkout, the installer prefers the local `skills/` directory so
6
+ Codex, Cursor, and Claude Code see the current repo `SKILL.md` immediately.
7
+
5
8
  ## Install
6
9
 
7
10
  ```bash
@@ -6,31 +6,34 @@ argument-hint: "[goal]"
6
6
 
7
7
  # Opensteer
8
8
 
9
- Use Opensteer when normal code is not enough because the task depends on a real browser:
9
+ Opensteer gives AI agents a real Chromium browser — local or cloud. Use it when normal code is not enough because the task depends on a live browser session.
10
10
 
11
- - interact with a real page
12
- - inspect browser state like cookies or storage
13
- - capture real network traffic from real browser actions
14
- - replay requests through the browser session
15
- - turn the discovery into plain TypeScript
11
+ Default workflow:
16
12
 
17
- The default workflow is:
13
+ 1. CLI to explore the site and discover behavior.
14
+ 2. Save stable targets with `persist`.
15
+ 3. SDK to write the final reusable TypeScript.
18
16
 
19
- 1. Use the CLI to explore the site.
20
- 2. Figure out the page or API behavior.
21
- 3. Save stable targets with `persist` when useful.
22
- 4. Write the final reusable code with the SDK.
17
+ Do not stop at manual exploration if the user needs automation.
23
18
 
24
- Do not stop at manual exploration if the user needs automation. Explore first, then convert the result into SDK code.
19
+ ## Setup
20
+
21
+ Install the Opensteer skill so the coding agent can use it:
22
+
23
+ ```bash
24
+ opensteer skills install
25
+ ```
26
+
27
+ This registers the skill with the agent's tool system. Only needed once per environment.
25
28
 
26
29
  ## When To Use
27
30
 
28
- - The task needs a real browser session.
29
- - The task involves clicks, forms, DOM extraction, or navigation.
30
- - The task involves cookies, localStorage, sessionStorage, or auth state.
31
- - The task involves reverse-engineering a site API from real browser traffic.
32
- - The task needs browser-backed `fetch()` instead of plain Node HTTP.
33
- - The task needs coordinate-based fallback because DOM targeting is not enough.
31
+ - Real browser session needed (clicks, forms, DOM extraction, navigation).
32
+ - Cookies, localStorage, sessionStorage, or auth state involved.
33
+ - Reverse-engineering a site API from real browser traffic.
34
+ - Browser-backed `fetch()` instead of plain Node HTTP.
35
+ - Coordinate-based interaction (canvas, WebGL, hard-to-target UI).
36
+ - Need to reuse a real user's logged-in browser profile.
34
37
 
35
38
  If the user wants to manually drive a browser and record the flow, use the `recorder` skill instead.
36
39
 
@@ -39,20 +42,27 @@ If the user wants to manually drive a browser and record the flow, use the `reco
39
42
  1. Always use a workspace for stateful commands: `--workspace <id>` or `OPENSTEER_WORKSPACE`.
40
43
  2. In this repo, prefer `pnpm run opensteer:local -- <command>` instead of bare `opensteer ...`.
41
44
  3. Re-snapshot after navigation or big UI changes before reusing element numbers.
42
- 4. Use the CLI to discover. Use the SDK for the final implementation.
45
+ 4. CLI to discover, SDK for the final implementation.
43
46
  5. Use `persist` for stable reusable targets and extraction payloads.
44
47
  6. Use `exec` for SDK code and API experiments. Use `evaluate` only for page-context JavaScript.
45
48
  7. If `fetch()` fails with auth errors, inspect `state()`, `cookies()`, and `storage()` before changing transport.
46
- 8. Keep the final output simple. Prefer ordinary TypeScript with `Opensteer`, not extra abstraction unless the user asks for it.
47
- 9. Close the browser when you are done. Do not leave headed browser windows running on the user's machine. Use `opensteer browser delete --workspace <id>` or the matching SDK cleanup when the session does not need to stay open.
49
+ 8. Keep output simple. Prefer ordinary TypeScript with `Opensteer`, no extra abstraction.
50
+ 9. Close the browser when done. Do not leave headed browsers running. Use `opensteer browser delete --workspace <id>` or SDK cleanup when the session does not need to stay open.
48
51
 
49
52
  ## Choose A Path
50
53
 
51
- - Page interaction or extraction: use the DOM path.
52
- - API discovery or replay: use the network path.
53
- - Canvas, WebGL, or hard-to-target UI: use computer-use.
54
- - Browser profile, attach mode, or workspace management: use browser management.
55
- - Unsure: start by capturing network traffic.
54
+ ```
55
+ What does the task need?
56
+ ├─ Click, type, navigate, extract visible data → DOM path
57
+ ├─ Find or replay a site API Network path
58
+ ├─ Analyze, deobfuscate, or sandbox page JavaScript → Scripts analysis
59
+ ├─ Canvas, WebGL, or hard-to-target UI → Computer-use
60
+ ├─ Work with multiple tabs or popups → Tab management
61
+ ├─ Set up browser profile, clone, or attach → Browser sessions
62
+ ├─ Run browser in the cloud → Cloud mode
63
+ ├─ Watch what a headless browser is doing → Local view
64
+ └─ Unsure → start by capturing network traffic
65
+ ```
56
66
 
57
67
  ## DOM Path
58
68
 
@@ -106,8 +116,6 @@ Use `network detail --probe` to learn which transport works.
106
116
 
107
117
  ### Session state checks
108
118
 
109
- Use these when auth or browser state matters:
110
-
111
119
  ```bash
112
120
  opensteer state example.com --workspace demo
113
121
  ```
@@ -151,6 +159,54 @@ export async function search(keyword: string) {
151
159
 
152
160
  Use ordinary `fetch()` syntax. Only set `transport` explicitly if probing showed you need it.
153
161
 
162
+ ## Scripts Analysis
163
+
164
+ Use this when you need to understand what JavaScript a page is running — reverse-engineering obfuscated code, finding hidden API calls, or testing script behavior in isolation.
165
+
166
+ ### Capture scripts from the page
167
+
168
+ ```bash
169
+ opensteer scripts capture --workspace demo
170
+ opensteer scripts capture --workspace demo --url-filter "api" --external --dynamic
171
+ ```
172
+
173
+ Flags: `--inline`, `--external`, `--dynamic`, `--workers` to filter by source type. `--persist` to save as an artifact. `--url-filter <pattern>` to match script URLs.
174
+
175
+ ### Beautify and deobfuscate
176
+
177
+ ```bash
178
+ # Format minified code
179
+ opensteer scripts beautify <artifactId> --workspace demo --persist
180
+
181
+ # Deobfuscate packed/obfuscated code
182
+ opensteer scripts deobfuscate <artifactId> --workspace demo --persist
183
+ ```
184
+
185
+ ### Sandbox execution
186
+
187
+ Run captured JavaScript in isolation with controlled inputs:
188
+
189
+ ```bash
190
+ opensteer scripts sandbox <artifactId> --workspace demo \
191
+ --fidelity standard \
192
+ --timeout 5000 \
193
+ --cookies '{"session":"abc123"}' \
194
+ --globals '{"window.API_KEY":"test"}' \
195
+ --ajax-routes '[{"url":"*/api/*","response":{"data":[]}}]'
196
+ ```
197
+
198
+ Fidelity levels: `minimal` (fast, no DOM), `standard` (basic DOM), `full` (complete browser emulation).
199
+
200
+ ### Typical workflow
201
+
202
+ ```bash
203
+ opensteer scripts capture --workspace demo --persist --external
204
+ opensteer artifact read art_abc123 --workspace demo # inspect raw
205
+ opensteer scripts beautify art_abc123 --workspace demo --persist
206
+ opensteer scripts deobfuscate art_def456 --workspace demo --persist
207
+ opensteer scripts sandbox art_ghi789 --workspace demo
208
+ ```
209
+
154
210
  ## Computer-Use
155
211
 
156
212
  Use this only when DOM targeting is not enough.
@@ -170,54 +226,179 @@ await opensteer.computerExecute({
170
226
 
171
227
  After coordinate-based actions, switch back to normal extraction or request analysis as soon as possible.
172
228
 
173
- ## Browser Management
229
+ ## Tab Management
230
+
231
+ Use when handling OAuth popups, multi-page flows, or any task that opens new tabs.
232
+
233
+ ```bash
234
+ opensteer tab list --workspace demo # List all open tabs
235
+ opensteer tab new https://example.com --workspace demo # Open new tab
236
+ opensteer tab 2 --workspace demo # Switch to tab 2
237
+ opensteer tab close 3 --workspace demo # Close tab 3
238
+ opensteer tab close --workspace demo # Close current tab
239
+ ```
240
+
241
+ ```ts
242
+ const tabs = await opensteer.listPages();
243
+ await opensteer.newPage("https://example.com");
244
+ await opensteer.activatePage(2);
245
+ await opensteer.closePage(3);
246
+ ```
247
+
248
+ Re-snapshot after switching tabs — element numbers are per-page.
249
+
250
+ ## Browser Sessions
251
+
252
+ Each workspace has one browser. Three modes:
253
+
254
+ | Mode | What it does | Data persists? |
255
+ | ------------------------ | ----------------------------------------------- | --------------------------------------------------------------------- |
256
+ | **Persistent** (default) | Browser tied to workspace, survives restarts | Yes — cookies, localStorage, logins, history, extensions all retained |
257
+ | **Temporary** | Headless browser in `/tmp`, cleaned up on close | No |
258
+ | **Attach** | Connects to an already-running browser via CDP | Depends on that browser |
259
+
260
+ ### Headless vs headed
174
261
 
175
- Use these when the task is about browser setup rather than page logic.
262
+ Browsers launch headless by default. Use `--headless false` to see the browser window:
176
263
 
177
264
  ```bash
265
+ opensteer open https://example.com --workspace demo --headless false
266
+ ```
267
+
268
+ Use headed mode for debugging or when the user wants to watch. For hands-free automation, keep headless and use `opensteer view` if a human needs to observe.
269
+
270
+ ### Persistent sessions
271
+
272
+ When you `opensteer open` with a workspace, the browser's full Chrome user-data directory lives at `~/.opensteer/workspaces/<id>/browser/user-data/`. Everything Chrome normally persists (cookies, localStorage, IndexedDB, history, extensions) survives between runs.
273
+
274
+ Re-running `opensteer open --workspace demo` reconnects to the existing browser if it's still alive, or launches a fresh one with the same profile if it died.
275
+
276
+ ### Profile cloning
277
+
278
+ Clone a real user's Chrome profile to start a workspace with their logins already active:
279
+
280
+ ```bash
281
+ # Discover available local browsers and profiles
178
282
  opensteer browser discover
179
- opensteer browser inspect --attach-endpoint http://localhost:9222
283
+
284
+ # Clone a profile into a workspace
180
285
  opensteer browser clone --workspace demo \
181
286
  --source-user-data-dir "$HOME/Library/Application Support/Google/Chrome" \
182
287
  --source-profile-directory Default
183
- opensteer browser status --workspace demo
184
- opensteer browser reset --workspace demo
185
- opensteer browser delete --workspace demo
186
288
  ```
187
289
 
188
- Attach to an existing browser:
290
+ This copies cookies, localStorage, extensions, and settings from the source browser. Caches and lock files are skipped. The source browser does not need to be closed — cloning while running is safe.
291
+
292
+ ### Attach to an existing browser
189
293
 
190
294
  ```bash
191
295
  opensteer open https://example.com --workspace demo --attach-endpoint http://localhost:9222
192
296
  ```
193
297
 
194
- Cloud mode:
298
+ ### Workspace lifecycle
299
+
300
+ ```bash
301
+ opensteer browser status --workspace demo # Check if browser is running
302
+ opensteer browser reset --workspace demo # Wipe browser data, keep workspace
303
+ opensteer browser delete --workspace demo # Delete workspace entirely
304
+ ```
305
+
306
+ ## Cloud Mode
195
307
 
196
- - Use `--provider cloud` on CLI when needed.
197
- - Common env vars are `OPENSTEER_BASE_URL`, `OPENSTEER_API_KEY`, and `OPENSTEER_CLOUD_APP_BASE_URL`.
308
+ Run the browser on Opensteer's cloud infrastructure instead of locally. Use cloud mode when you need browsers that run 24/7, or when the local machine should not run Chromium.
198
309
 
199
- ## Useful SDK Surface
310
+ ### Setup
311
+
312
+ ```bash
313
+ export OPENSTEER_API_KEY=osk_your_key_here # Required
314
+ export OPENSTEER_PROVIDER=cloud # Or use --provider cloud per command
315
+ ```
316
+
317
+ ### Usage
318
+
319
+ All CLI commands work the same with `--provider cloud`:
320
+
321
+ ```bash
322
+ opensteer open https://example.com --workspace demo --provider cloud
323
+ opensteer snapshot action --workspace demo
324
+ opensteer click 5 --workspace demo
325
+ ```
326
+
327
+ ### Export local browser profile to cloud
328
+
329
+ Sync a local browser's cookies to a cloud browser profile so the cloud session starts logged in:
200
330
 
201
- Use these often:
331
+ ```bash
332
+ # Reads cookies from local Chrome, decrypts them, uploads to cloud
333
+ opensteer browser clone --workspace demo \
334
+ --source-user-data-dir "$HOME/Library/Application Support/Google/Chrome" \
335
+ --source-profile-directory Default \
336
+ --provider cloud
337
+ ```
338
+
339
+ The cookies are extracted from the local SQLite database, decrypted, packaged into a portable format, and uploaded. The cloud browser then starts with those cookies applied.
340
+
341
+ ## Local View
342
+
343
+ When Opensteer runs headless, a human cannot see what the browser is doing. Local view streams live screenshots from headless sessions to a browser-based viewer.
344
+
345
+ ```bash
346
+ opensteer view # Start viewer service, print URL
347
+ opensteer view stop # Stop the viewer service
348
+ opensteer view --auto # Auto-start viewer on every browser launch
349
+ opensteer view --no-auto # Only start viewer when manually requested
350
+ ```
351
+
352
+ The viewer is a local web UI that shows:
353
+
354
+ - Live JPEG stream of the active browser tab
355
+ - Tab bar with switching
356
+ - Navigation controls (back, forward, reload, URL bar)
357
+
358
+ Local view is a passive observer — it connects to the browser's existing CDP endpoint. Starting or stopping it has zero impact on running browser sessions.
359
+
360
+ ## Interaction Capture & Replay
361
+
362
+ Record a trace of browser interactions (clicks, typing, network, DOM changes) and replay them deterministically. Useful for building repeatable test flows or comparing behavior across runs.
363
+
364
+ ```bash
365
+ opensteer interaction capture --workspace demo --key "login-flow" --duration 30000
366
+ opensteer interaction get <traceId> --workspace demo
367
+ opensteer interaction replay <traceId> --workspace demo
368
+ opensteer interaction diff <traceA> <traceB> --workspace demo
369
+ ```
370
+
371
+ ## Artifacts
372
+
373
+ Commands that use `--persist` save artifacts to the workspace. Read them back with:
374
+
375
+ ```bash
376
+ opensteer artifact read <artifactId> --workspace demo
377
+ ```
378
+
379
+ Artifacts are created by `extract --persist`, `scripts capture --persist`, `scripts beautify --persist`, and other persist-enabled commands.
380
+
381
+ ## Useful SDK Surface
202
382
 
203
- - `open(url)`
204
- - `goto(url, { captureNetwork? })`
383
+ - `open(url)`, `goto(url, { captureNetwork? })`, `close()`
205
384
  - `snapshot("action" | "extraction")`
206
385
  - `click()`, `hover()`, `input()`, `scroll()`
207
386
  - `extract()`
208
- - `network.query()`
209
- - `network.detail()`
387
+ - `listPages()`, `newPage()`, `activatePage()`, `closePage()`
388
+ - `network.query()`, `network.detail()`
210
389
  - `waitForNetwork()`, `waitForResponse()`, `waitForPage()`
211
390
  - `cookies()`, `storage()`, `state()`
212
391
  - `fetch()`
392
+ - `evaluate()`, `addInitScript()`
393
+ - `route()` — intercept and modify network requests
213
394
  - `computerExecute()`
214
- - `addInitScript()`
215
395
  - `browser.status()`, `browser.clone()`, `browser.reset()`, `browser.delete()`
216
396
 
217
397
  ## Guardrails
218
398
 
219
- - Snapshot before using element numbers.
220
- - Snapshot again after UI changes.
221
- - Do not use `evaluate` for API work.
399
+ - Snapshot before using element numbers. Snapshot again after UI changes.
400
+ - Do not use `evaluate` for API work — use `exec` or `fetch`.
222
401
  - Do not keep the result as a manual-only workflow if the user needs reusable automation.
223
402
  - Prefer a small final script over a large framework.
403
+ - Close browsers when done. Do not leave headed browser windows open.
404
+ - When cloning profiles, verify the source path exists with `opensteer browser discover` first.