libretto 0.2.6 → 0.3.0

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 (160) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +12 -12
  3. package/skill/SKILL.md +20 -18
  4. package/skill/code-generation-rules.md +3 -3
  5. package/skill/integration-approach-selection.md +3 -3
  6. package/dist/cli/cli.js +0 -209
  7. package/dist/cli/commands/ai.js +0 -21
  8. package/dist/cli/commands/browser.js +0 -82
  9. package/dist/cli/commands/execution.js +0 -461
  10. package/dist/cli/commands/init.js +0 -95
  11. package/dist/cli/commands/logs.js +0 -93
  12. package/dist/cli/commands/snapshot.js +0 -106
  13. package/dist/cli/core/ai-config.js +0 -149
  14. package/dist/cli/core/browser.js +0 -648
  15. package/dist/cli/core/context.js +0 -118
  16. package/dist/cli/core/pause-signals.js +0 -29
  17. package/dist/cli/core/session-telemetry.js +0 -491
  18. package/dist/cli/core/session.js +0 -183
  19. package/dist/cli/core/snapshot-analyzer.js +0 -492
  20. package/dist/cli/core/telemetry.js +0 -362
  21. package/dist/cli/index.js +0 -13
  22. package/dist/cli/workers/run-integration-runtime.js +0 -227
  23. package/dist/cli/workers/run-integration-worker-protocol.js +0 -12
  24. package/dist/cli/workers/run-integration-worker.js +0 -66
  25. package/dist/index.cjs +0 -116
  26. package/dist/index.d.cts +0 -21
  27. package/dist/index.d.ts +0 -21
  28. package/dist/index.js +0 -97
  29. package/dist/runtime/download/download.cjs +0 -70
  30. package/dist/runtime/download/download.d.cts +0 -35
  31. package/dist/runtime/download/download.d.ts +0 -35
  32. package/dist/runtime/download/download.js +0 -45
  33. package/dist/runtime/download/index.cjs +0 -30
  34. package/dist/runtime/download/index.d.cts +0 -3
  35. package/dist/runtime/download/index.d.ts +0 -3
  36. package/dist/runtime/download/index.js +0 -8
  37. package/dist/runtime/extract/extract.cjs +0 -88
  38. package/dist/runtime/extract/extract.d.cts +0 -23
  39. package/dist/runtime/extract/extract.d.ts +0 -23
  40. package/dist/runtime/extract/extract.js +0 -64
  41. package/dist/runtime/extract/index.cjs +0 -28
  42. package/dist/runtime/extract/index.d.cts +0 -5
  43. package/dist/runtime/extract/index.d.ts +0 -5
  44. package/dist/runtime/extract/index.js +0 -4
  45. package/dist/runtime/network/index.cjs +0 -28
  46. package/dist/runtime/network/index.d.cts +0 -4
  47. package/dist/runtime/network/index.d.ts +0 -4
  48. package/dist/runtime/network/index.js +0 -6
  49. package/dist/runtime/network/network.cjs +0 -91
  50. package/dist/runtime/network/network.d.cts +0 -28
  51. package/dist/runtime/network/network.d.ts +0 -28
  52. package/dist/runtime/network/network.js +0 -67
  53. package/dist/runtime/recovery/agent.cjs +0 -223
  54. package/dist/runtime/recovery/agent.d.cts +0 -13
  55. package/dist/runtime/recovery/agent.d.ts +0 -13
  56. package/dist/runtime/recovery/agent.js +0 -199
  57. package/dist/runtime/recovery/errors.cjs +0 -124
  58. package/dist/runtime/recovery/errors.d.cts +0 -31
  59. package/dist/runtime/recovery/errors.d.ts +0 -31
  60. package/dist/runtime/recovery/errors.js +0 -100
  61. package/dist/runtime/recovery/index.cjs +0 -34
  62. package/dist/runtime/recovery/index.d.cts +0 -7
  63. package/dist/runtime/recovery/index.d.ts +0 -7
  64. package/dist/runtime/recovery/index.js +0 -10
  65. package/dist/runtime/recovery/recovery.cjs +0 -55
  66. package/dist/runtime/recovery/recovery.d.cts +0 -12
  67. package/dist/runtime/recovery/recovery.d.ts +0 -12
  68. package/dist/runtime/recovery/recovery.js +0 -31
  69. package/dist/shared/config/config.cjs +0 -44
  70. package/dist/shared/config/config.d.cts +0 -10
  71. package/dist/shared/config/config.d.ts +0 -10
  72. package/dist/shared/config/config.js +0 -18
  73. package/dist/shared/config/index.cjs +0 -32
  74. package/dist/shared/config/index.d.cts +0 -1
  75. package/dist/shared/config/index.d.ts +0 -1
  76. package/dist/shared/config/index.js +0 -10
  77. package/dist/shared/debug/index.cjs +0 -30
  78. package/dist/shared/debug/index.d.cts +0 -1
  79. package/dist/shared/debug/index.d.ts +0 -1
  80. package/dist/shared/debug/index.js +0 -5
  81. package/dist/shared/debug/pause.cjs +0 -90
  82. package/dist/shared/debug/pause.d.cts +0 -16
  83. package/dist/shared/debug/pause.d.ts +0 -16
  84. package/dist/shared/debug/pause.js +0 -55
  85. package/dist/shared/instrumentation/errors.cjs +0 -81
  86. package/dist/shared/instrumentation/errors.d.cts +0 -12
  87. package/dist/shared/instrumentation/errors.d.ts +0 -12
  88. package/dist/shared/instrumentation/errors.js +0 -57
  89. package/dist/shared/instrumentation/index.cjs +0 -35
  90. package/dist/shared/instrumentation/index.d.cts +0 -6
  91. package/dist/shared/instrumentation/index.d.ts +0 -6
  92. package/dist/shared/instrumentation/index.js +0 -12
  93. package/dist/shared/instrumentation/instrument.cjs +0 -206
  94. package/dist/shared/instrumentation/instrument.d.cts +0 -32
  95. package/dist/shared/instrumentation/instrument.d.ts +0 -32
  96. package/dist/shared/instrumentation/instrument.js +0 -190
  97. package/dist/shared/llm/ai-sdk-adapter.cjs +0 -67
  98. package/dist/shared/llm/ai-sdk-adapter.d.cts +0 -22
  99. package/dist/shared/llm/ai-sdk-adapter.d.ts +0 -22
  100. package/dist/shared/llm/ai-sdk-adapter.js +0 -43
  101. package/dist/shared/llm/client.cjs +0 -139
  102. package/dist/shared/llm/client.d.cts +0 -6
  103. package/dist/shared/llm/client.d.ts +0 -6
  104. package/dist/shared/llm/client.js +0 -115
  105. package/dist/shared/llm/index.cjs +0 -31
  106. package/dist/shared/llm/index.d.cts +0 -5
  107. package/dist/shared/llm/index.d.ts +0 -5
  108. package/dist/shared/llm/index.js +0 -6
  109. package/dist/shared/llm/types.cjs +0 -16
  110. package/dist/shared/llm/types.d.cts +0 -66
  111. package/dist/shared/llm/types.d.ts +0 -66
  112. package/dist/shared/llm/types.js +0 -0
  113. package/dist/shared/logger/index.cjs +0 -37
  114. package/dist/shared/logger/index.d.cts +0 -2
  115. package/dist/shared/logger/index.d.ts +0 -2
  116. package/dist/shared/logger/index.js +0 -13
  117. package/dist/shared/logger/logger.cjs +0 -213
  118. package/dist/shared/logger/logger.d.cts +0 -82
  119. package/dist/shared/logger/logger.d.ts +0 -82
  120. package/dist/shared/logger/logger.js +0 -188
  121. package/dist/shared/logger/sinks.cjs +0 -160
  122. package/dist/shared/logger/sinks.d.cts +0 -9
  123. package/dist/shared/logger/sinks.d.ts +0 -9
  124. package/dist/shared/logger/sinks.js +0 -124
  125. package/dist/shared/paths/paths.cjs +0 -104
  126. package/dist/shared/paths/paths.d.cts +0 -10
  127. package/dist/shared/paths/paths.d.ts +0 -10
  128. package/dist/shared/paths/paths.js +0 -73
  129. package/dist/shared/run/api.cjs +0 -28
  130. package/dist/shared/run/api.d.cts +0 -2
  131. package/dist/shared/run/api.d.ts +0 -2
  132. package/dist/shared/run/api.js +0 -4
  133. package/dist/shared/run/browser.cjs +0 -98
  134. package/dist/shared/run/browser.d.cts +0 -22
  135. package/dist/shared/run/browser.d.ts +0 -22
  136. package/dist/shared/run/browser.js +0 -74
  137. package/dist/shared/state/index.cjs +0 -38
  138. package/dist/shared/state/index.d.cts +0 -2
  139. package/dist/shared/state/index.d.ts +0 -2
  140. package/dist/shared/state/index.js +0 -16
  141. package/dist/shared/state/session-state.cjs +0 -85
  142. package/dist/shared/state/session-state.d.cts +0 -34
  143. package/dist/shared/state/session-state.d.ts +0 -34
  144. package/dist/shared/state/session-state.js +0 -56
  145. package/dist/shared/visualization/ghost-cursor.cjs +0 -174
  146. package/dist/shared/visualization/ghost-cursor.d.cts +0 -37
  147. package/dist/shared/visualization/ghost-cursor.d.ts +0 -37
  148. package/dist/shared/visualization/ghost-cursor.js +0 -145
  149. package/dist/shared/visualization/highlight.cjs +0 -134
  150. package/dist/shared/visualization/highlight.d.cts +0 -22
  151. package/dist/shared/visualization/highlight.d.ts +0 -22
  152. package/dist/shared/visualization/highlight.js +0 -108
  153. package/dist/shared/visualization/index.cjs +0 -45
  154. package/dist/shared/visualization/index.d.cts +0 -3
  155. package/dist/shared/visualization/index.d.ts +0 -3
  156. package/dist/shared/visualization/index.js +0 -24
  157. package/dist/shared/workflow/workflow.cjs +0 -47
  158. package/dist/shared/workflow/workflow.d.cts +0 -21
  159. package/dist/shared/workflow/workflow.d.ts +0 -21
  160. package/dist/shared/workflow/workflow.js +0 -21
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Libretto contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libretto",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "description": "AI-powered browser automation library and CLI built on Playwright",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -88,16 +88,6 @@
88
88
  "require": "./dist/shared/run/api.cjs"
89
89
  }
90
90
  },
91
- "scripts": {
92
- "postinstall": "node ./scripts/postinstall.mjs; npx playwright install chromium",
93
- "build": "pnpm run build:runtime && pnpm run build:cli",
94
- "build:runtime": "tsup --config tsup.config.ts",
95
- "build:cli": "tsup --config tsup.cli.config.ts",
96
- "type-check": "tsc --noEmit",
97
- "dev": "tsx src/cli/index.ts",
98
- "test": "pnpm run build && vitest run",
99
- "test:watch": "vitest"
100
- },
101
91
  "peerDependencies": {
102
92
  "@ai-sdk/anthropic": "^3.0.0",
103
93
  "@ai-sdk/google-vertex": "^4.0.0",
@@ -132,5 +122,15 @@
132
122
  "ai": "^6.0.110",
133
123
  "playwright": "^1.52.0",
134
124
  "yargs": "^17.7.2"
125
+ },
126
+ "scripts": {
127
+ "postinstall": "node ./scripts/postinstall.mjs; npx playwright install chromium",
128
+ "build": "pnpm run build:runtime && pnpm run build:cli",
129
+ "build:runtime": "tsup --config tsup.config.ts",
130
+ "build:cli": "tsup --config tsup.cli.config.ts",
131
+ "type-check": "tsc --noEmit",
132
+ "dev": "tsx src/cli/index.ts",
133
+ "test": "pnpm run build && vitest run",
134
+ "test:watch": "vitest"
135
135
  }
136
- }
136
+ }
package/skill/SKILL.md CHANGED
@@ -4,18 +4,19 @@ description: "Browser automation CLI for building integrations, with a network-f
4
4
  license: MIT
5
5
  metadata:
6
6
  author: saffron-health
7
- version: "0.2.2"
7
+ version: "0.2.4"
8
8
  ---
9
9
 
10
10
  # Browser Integration with Libretto CLI
11
11
 
12
12
  Use the `npx libretto` CLI to automate web interactions, debug browser agent jobs, and prototype fixes.
13
13
 
14
- ## CRITICAL: Session Access
14
+ ## Session Access
15
15
 
16
- Libretto sessions are **full-access by default**. You can use `exec` and `run` immediately after opening a session.
16
+ Libretto sessions are **full-access by default** (no approval prompts). You can use `exec` immediately after opening a session. `run` starts its own workflow browser process and requires the target session to be available.
17
17
 
18
18
  **Rules:**
19
+
19
20
  - Always announce which session you opened and what page you are on.
20
21
  - Use `snapshot`, `network`, and `actions` first when debugging unknown page state.
21
22
  - Before any potentially mutating action (submit/save/delete, or non-idempotent API calls), describe what you are about to do and wait for explicit user confirmation.
@@ -27,14 +28,15 @@ If it's not obvious which element to click or what value to enter, **ask the use
27
28
  ## Commands
28
29
 
29
30
  ```bash
30
- npx libretto open <url> [--headless] # Launch browser and navigate (headed by default)
31
+ npx libretto open <url> [--headed|--headless] # Launch browser and navigate (headed by default)
31
32
  npx libretto exec <code> [--visualize] # Execute Playwright TypeScript code (--visualize enables ghost cursor + highlight)
32
- npx libretto run <integrationFile> <integrationExport> # Execute integration actions
33
+ npx libretto run <integrationFile> <integrationExport> [--params <json> | --params-file <path>] [--auth-profile <domain>] [--headed|--headless] # Execute integration actions
33
34
  npx libretto resume # Resume a paused workflow for the current session
34
35
  npx libretto snapshot --objective "<what to find>" [--context "<situational info>"]
35
36
  npx libretto save <url|domain> # Save session (cookies, localStorage) to .libretto/profiles/
36
37
  npx libretto network # Show last 20 captured network requests
37
38
  npx libretto actions # Show last 20 captured user/agent actions
39
+ npx libretto pages # List open pages in the session
38
40
  npx libretto close # Close the browser
39
41
  ```
40
42
 
@@ -65,13 +67,13 @@ Workflows pause by calling `await pause()` (imported from `"libretto"`). In prod
65
67
 
66
68
  ## Globals Available in `exec`
67
69
 
68
- `page`, `context`, `state`, `browser`, `networkLog({ last?, filter?, method? })`, `actionLog({ last?, filter?, action?, source? })`, `console`, `fetch`, `Buffer`, `URL`, `setTimeout`
70
+ `page`, `context`, `state`, `browser`, `networkLog({ last?, filter?, method? })`, `actionLog({ last?, filter?, action?, source? })`, `console`, `fetch`, `Buffer`, `URL`, `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`
69
71
 
70
- The `state` object persists across `exec` calls within the same session use it to carry values between commands.
72
+ The `state` object is scoped to a single `exec` invocation and resets on the next call.
71
73
 
72
74
  ## CRITICAL: No try/catch in exec
73
75
 
74
- **Never use try/catch or .catch() in exec code.** Let errors throw so they surface as exec failures. When an exec fails, you get the full error message (e.g., "intercepts pointer events", "Timeout 30000ms exceeded") — use that to diagnose the problemand write a corrected exec.
76
+ **Never use try/catch or .catch() in exec code.** Let errors throw so they surface as exec failures. When an exec fails, you get the full error message (e.g., "intercepts pointer events", "Timeout 30000ms exceeded") — use that to diagnose the problem and write a corrected exec.
75
77
 
76
78
  **Why:** A try/catch inside exec hides failures from you. A click that times out takes 30 seconds — if you retry it in a loop with try/catch, you'll silently burn minutes on the same broken selector with no way to recover. Without try/catch, the error comes back immediately and you can reason about what went wrong.
77
79
 
@@ -174,9 +176,9 @@ npx libretto exec --session browser-agent "await page.locator('.dropdown-trigger
174
176
 
175
177
  ## Snapshot — The Primary Observation Tool
176
178
 
177
- The `snapshot` command captures a PNG screenshot + HTML, sends both to a vision model (Gemini Flash), and returns an analysis with Playwright-ready selectors. `--objective` is required for analysis, and `--context` is optional (but recommended for better results). This is the single way to understand what's on the page — use it any time you need to inspect page structure, find elements, or debug what's happening.
179
+ The `snapshot` command captures a PNG screenshot + HTML and (when `--objective` is provided) runs analysis through the configured AI runtime (`codex`, `claude`, or `gemini` via `npx libretto ai configure ...`). `--context` is optional (but recommended for better results). This is the single way to understand what's on the page — use it any time you need to inspect page structure, find elements, or debug what's happening.
178
180
 
179
- **Never use `page.screenshot()` via `exec` to understand the page.** Use the `snapshot` command instead — it captures the screenshot, HTML, and sends both to a vision model that returns actionable selectors. Raw screenshots give you an image with no analysis; `snapshot` gives you the answer.
181
+ **Never use `page.screenshot()` via `exec` to understand the page.** Use the `snapshot` command instead — it captures the screenshot, HTML, and runs analysis with selectors. Raw screenshots give you an image with no analysis; `snapshot` gives you the answer.
180
182
 
181
183
  ### What to Put in `--objective`
182
184
 
@@ -224,7 +226,7 @@ When the snapshot doesn't give you enough detail — why an element is hidden, w
224
226
 
225
227
  ## Tips
226
228
 
227
- - **Never use `page.screenshot()` via `exec`.** Use `npx libretto snapshot` instead — it captures the viewport, sends the screenshot + HTML to a vision model, and returns actionable selectors. The `fullPage` option is especially dangerous — it scrolls the entire page to stitch a screenshot, which can crash JavaScript-heavy pages (especially EMR portals like eClinicalWorks).
229
+ - **Never use `page.screenshot()` via `exec`.** Use `npx libretto snapshot` instead — it captures the viewport plus HTML and returns analyzed output with actionable selectors. The `fullPage` option is especially dangerous — it scrolls the entire page to stitch a screenshot, which can crash JavaScript-heavy pages (especially EMR portals like eClinicalWorks).
228
230
  - **Never run `exec` commands in parallel.** Always wait for one `exec` to finish before starting the next. Do not use `run_in_background` for `exec` calls. Running simultaneous `exec` calls opens multiple CDP connections to the same page, which corrupts the page state and kills the browser.
229
231
  - `open` requires an available session. If the session is already active, Libretto fails fast and asks you to close the existing session or use a different `--session`.
230
232
  - `run` also requires an available session, except for the specific case of a prior failed `run` in the same session; in that case Libretto releases the failed worker and allows rerun.
@@ -234,7 +236,7 @@ When the snapshot doesn't give you enough detail — why an element is hidden, w
234
236
 
235
237
  ## Network Logging
236
238
 
237
- Network requests are captured automatically when a browser is opened via `npx libretto open`. All non-static HTTP responses (excluding `.css`, `.js`, `.png`, `.jpg`, `.gif`, `.woff`, `.ico`, `.svg`, and `chrome-extension://` URLs) are logged to `.libretto/sessions/<session>/network.jsonl`.
239
+ Network requests are captured automatically for Libretto-managed browser sessions (for example from `npx libretto open` and `npx libretto run`). Non-static HTTP responses are logged to `.libretto/sessions/<session>/network.jsonl`.
238
240
 
239
241
  ### CLI: `npx libretto network`
240
242
 
@@ -256,11 +258,11 @@ npx libretto exec "return await networkLog({ method: 'POST' })"
256
258
 
257
259
  Returns an array of objects with: `ts`, `method`, `url`, `status`, `contentType`, `postData` (POST/PUT/PATCH only, first 2000 chars), `size`, `durationMs`.
258
260
 
259
- **Note:** Network logging only works for sessions opened via `npx libretto open`. It does not capture requests for external sessions like `--session browser-agent`.
261
+ **Note:** Network logging works for Libretto-managed sessions. It does not capture requests for external sessions like `--session browser-agent`.
260
262
 
261
263
  ## Action Logging
262
264
 
263
- Browser actions are captured automatically when a browser is opened via `npx libretto open`. Both user interactions (manual clicks, typing in the headed browser window) and agent actions (programmatic Playwright API calls via `exec`) are logged to `.libretto/sessions/<session>/actions.jsonl` with a `source` field of `'user'` or `'agent'` to distinguish the two.
265
+ Browser actions are captured automatically for Libretto-managed browser sessions (for example from `npx libretto open` and `npx libretto run`). Both user interactions (manual clicks, typing in the headed browser window) and agent actions (programmatic Playwright API calls via `exec`) are logged to `.libretto/sessions/<session>/actions.jsonl` with a `source` field of `'user'` or `'agent'` to distinguish the two.
264
266
 
265
267
  ### CLI: `npx libretto actions`
266
268
 
@@ -284,7 +286,7 @@ npx libretto exec "return await actionLog({ action: 'click' })"
284
286
 
285
287
  Returns an array of objects with: `ts`, `action`, `source` (`'user'` | `'agent'`), `selector`, `value`, `url`, `duration`, `success`, `error`.
286
288
 
287
- **Note:** Action logging only works for sessions opened via `npx libretto open`. It does not capture actions for external sessions like `--session browser-agent`.
289
+ **Note:** Action logging works for Libretto-managed sessions. It does not capture actions for external sessions like `--session browser-agent`.
288
290
 
289
291
  ## Workflow: Creating a New Integration
290
292
 
@@ -436,7 +438,7 @@ After completing interactive exploration, **always generate the TypeScript workf
436
438
  **STOP AND ASK BEFORE GENERATING CODE.** Once the interactive workflow is figured out, pause and ask:
437
439
 
438
440
  1. "Are there any existing files or patterns in the codebase you want me to reference?"
439
- 2. "Do you want me to incorporate any of your manual browser interactions from the actions log (`npx libretto actions --source user`) into the generated code?"
441
+ 2. Check the action log for user interactions by running `npx libretto actions --source user`. If there are any recorded user interactions, ask: "I see you performed some manual interactions in the browser (clicks, form fills, etc.). Would you like me to incorporate any of those into the generated code?" — and briefly list what you found. If there are no user interactions, skip this question entirely.
440
442
  3. "Any other guidance for how the production code should be structured?"
441
443
 
442
444
  Wait for the user's response before proceeding. Then:
@@ -446,6 +448,6 @@ Wait for the user's response before proceeding. Then:
446
448
 
447
449
  ## Patient Safety Warning
448
450
 
449
- Browser automation jobs process real patient health information. The `npx libretto` CLI executes arbitrary code with full page access. **Never** execute code that submits forms, sends referrals, deletes data, or modifies patient records.
451
+ Browser automation jobs process real patient health information. The `npx libretto` CLI executes arbitrary code with full page access. **Never execute mutating actions without explicit user confirmation first** (submits, sends, deletes, updates, or other side effects).
450
452
 
451
- See `apps/browser-agent/docs/interactive-debugging-workflow.md` for the complete debugging guide.
453
+ For debugging steps, see the "Workflow: Interactive Debugging" section in this file.
@@ -151,7 +151,7 @@ Use `page.evaluate()` only for operations that have no Playwright locator equiva
151
151
 
152
152
  A quick test: if the evaluate body contains `querySelector`, `querySelectorAll`, `textContent`, `click()`, `getAttribute()`, or iterates DOM elements, it should be rewritten with Playwright locators.
153
153
 
154
- When `page.evaluate()` is used for the acceptable cases above, use a string expression to avoid DOM type errors:
154
+ When `page.evaluate()` is used for the acceptable cases above, keep the logic self-contained and return JSON-serializable values:
155
155
 
156
156
  ```typescript
157
157
  const data = (await page.evaluate(`(() => {
@@ -160,7 +160,7 @@ const data = (await page.evaluate(`(() => {
160
160
  })()`)) as string;
161
161
  ```
162
162
 
163
- Do not use `/// <reference lib="dom" />` or add `"dom"` to the tsconfig lib this project's tsconfig intentionally excludes DOM types.
163
+ Do not rely on broad DOM querying inside `page.evaluate()` for production flows when Playwright locators can express the same interaction.
164
164
 
165
165
  ## Network Request Methods
166
166
 
@@ -220,4 +220,4 @@ for (const post of posts) {
220
220
 
221
221
  ## Type Checking
222
222
 
223
- The generated file must pass `npx tsc --noEmit` before it's considered done. If there are DOM type errors (`document`, `HTMLElement`, `getComputedStyle`), convert to locator APIs or string-expression `page.evaluate()`.
223
+ The generated file must pass `npx tsc --noEmit` before it's considered done. If there are type errors around DOM access, prefer locator APIs first, then use focused `page.evaluate()` only for browser-native APIs.
@@ -138,9 +138,9 @@ Extract data directly from the rendered page using selectors and `page.evaluate(
138
138
  | Site Profile | Primary Strategy | Supplement With |
139
139
  |---|---|---|
140
140
  | No bot protection, fetch not patched | **A** (`page.evaluate(fetch)`) | Playwright for navigation/auth |
141
- | No bot protection, fetch IS patched | **B** (`page.onResponse`) | Playwright for navigation; DOM extraction as fallback |
142
- | Bot protection detected, fetch not patched | **B** (`page.onResponse`) | Playwright for all navigation; cautious use of `page.evaluate(fetch)` only if needed |
143
- | Bot protection detected, fetch IS patched | **B** (`page.onResponse`) | Playwright for all navigation; DOM extraction as fallback |
141
+ | No bot protection, fetch IS patched | **B** (`page.on('response', ...)`) | Playwright for navigation; DOM extraction as fallback |
142
+ | Bot protection detected, fetch not patched | **B** (`page.on('response', ...)`) | Playwright for all navigation; cautious use of `page.evaluate(fetch)` only if needed |
143
+ | Bot protection detected, fetch IS patched | **B** (`page.on('response', ...)`) | Playwright for all navigation; DOM extraction as fallback |
144
144
  | Server-rendered content (no API calls) | **C** (DOM extraction) | Playwright for all interaction |
145
145
 
146
146
  ---
package/dist/cli/cli.js DELETED
@@ -1,209 +0,0 @@
1
- import yargs from "yargs";
2
- import { hideBin } from "yargs/helpers";
3
- import { registerAICommands } from "./commands/ai.js";
4
- import { registerBrowserCommands } from "./commands/browser.js";
5
- import { registerExecutionCommands } from "./commands/execution.js";
6
- import { registerLogCommands } from "./commands/logs.js";
7
- import { registerInitCommand } from "./commands/init.js";
8
- import { registerSnapshotCommands } from "./commands/snapshot.js";
9
- import {
10
- closeLogger,
11
- createLoggerForSession,
12
- ensureLibrettoSetup
13
- } from "./core/context.js";
14
- import {
15
- SESSION_DEFAULT,
16
- validateSessionName
17
- } from "./core/session.js";
18
- const CLI_COMMANDS = /* @__PURE__ */ new Set([
19
- "open",
20
- "run",
21
- "ai",
22
- "save",
23
- "exec",
24
- "snapshot",
25
- "network",
26
- "actions",
27
- "pages",
28
- "resume",
29
- "close",
30
- "init",
31
- "--help",
32
- "-h",
33
- "help"
34
- ]);
35
- function printUsage() {
36
- console.log(`Usage: libretto-cli <command> [--session <name>]
37
-
38
- Commands:
39
- init [--skip-browsers] Initialize libretto (copy skills, install browsers)
40
- open <url> [--headless] Launch browser and open URL (headed by default)
41
- Automatically loads saved profile if available
42
- run <integrationFile> <integrationExport> [--params <json> | --params-file <path>] [--headed|--headless] Run an exported Libretto workflow from a file
43
- ai configure [preset] [-- <command prefix...>] Configure AI runtime for analysis commands
44
- save <url|domain> Save current browser session (cookies, localStorage, etc.)
45
- exec <code> [--visualize] Execute Playwright typescript code (--visualize enables ghost cursor + highlight)
46
- snapshot [--objective <text> --context <text>] Capture PNG + HTML; analyze when objective is provided (context optional)
47
- network [--last N] [--filter regex] [--method M] [--clear] View captured network requests
48
- actions [--last N] [--filter regex] [--action TYPE] [--source SOURCE] [--clear] View captured actions
49
- pages List open pages in the active session
50
- resume Resume a paused workflow in the active session
51
- close [--all] [--force] Close the browser for the session, or all tracked sessions with --all
52
-
53
- Options:
54
- --session <name> Use a named session (default: "default")
55
- Built-in sessions: default, dev-server, browser-agent
56
-
57
- Examples:
58
- libretto-cli open https://linkedin.com
59
-
60
- # ... manually log in ...
61
- libretto-cli save linkedin.com
62
- # Next time you open linkedin.com, you'll be logged in automatically
63
-
64
- libretto-cli exec "await page.locator('button:has-text(\\"Sign in\\")').click()"
65
- libretto-cli exec "await page.fill('input[name=\\"email\\"]', 'test@example.com')"
66
- libretto-cli ai configure codex
67
- libretto-cli ai configure claude
68
- libretto-cli ai configure gemini
69
- libretto-cli ai configure <codex|claude|gemini> -- <command prefix...>
70
- libretto-cli snapshot
71
- libretto-cli snapshot --objective "Find the submit button" --context "Submitting a referral form, already filled in patient details"
72
- libretto-cli resume --session default
73
- libretto-cli close
74
- libretto-cli close --all
75
- libretto-cli close --all --force
76
-
77
- # Multiple sessions
78
- libretto-cli open https://site1.com --session test1
79
- libretto-cli open https://site2.com --session test2
80
- libretto-cli exec "return await page.title()" --session test1
81
-
82
- Available in exec:
83
- page, context, state, browser, networkLog, actionLog
84
-
85
- Profiles:
86
- Profiles are saved to .libretto/profiles/<domain>.json (git-ignored)
87
- They persist cookies, localStorage, and session data across browser launches.
88
- Local profiles are machine-local and are not shared with other users/environments.
89
- Sessions can expire; if run fails auth, log in again and re-save the profile.
90
-
91
- Sessions:
92
- Session state is stored in .libretto/sessions/<session>/state.json
93
- CLI logs are stored in .libretto/sessions/<session>/logs.jsonl
94
- Each session runs an isolated browser instance on a dynamic port.
95
- `);
96
- }
97
- function filterSessionArgs(args) {
98
- const result = [];
99
- for (let i = 0; i < args.length; i++) {
100
- if (args[i] === "--session") {
101
- i++;
102
- } else {
103
- result.push(args[i]);
104
- }
105
- }
106
- return result;
107
- }
108
- function parseSessionForLog(rawArgs) {
109
- const idx = rawArgs.indexOf("--session");
110
- if (idx < 0) return SESSION_DEFAULT;
111
- const value = rawArgs[idx + 1];
112
- if (!value || value.startsWith("--") || CLI_COMMANDS.has(value)) {
113
- return SESSION_DEFAULT;
114
- }
115
- try {
116
- validateSessionName(value);
117
- return value;
118
- } catch {
119
- return SESSION_DEFAULT;
120
- }
121
- }
122
- function validateLegacySessionArg(rawArgs) {
123
- const idx = rawArgs.indexOf("--session");
124
- if (idx < 0) return;
125
- const value = rawArgs[idx + 1];
126
- if (!value || value.startsWith("--") || CLI_COMMANDS.has(value)) {
127
- throw new Error(
128
- "Usage: libretto-cli <command> [--session <name>]\nMissing or invalid --session value."
129
- );
130
- }
131
- validateSessionName(value);
132
- }
133
- function initializeLogger(rawArgs) {
134
- const sessionForLog = parseSessionForLog(rawArgs);
135
- const logger = createLoggerForSession(sessionForLog);
136
- logger.info("cli-start", {
137
- args: rawArgs,
138
- cwd: process.cwd(),
139
- session: sessionForLog
140
- });
141
- return logger;
142
- }
143
- async function withCliLogger(rawArgs, run) {
144
- const logger = initializeLogger(rawArgs);
145
- try {
146
- return await run(logger);
147
- } finally {
148
- await closeLogger(logger);
149
- }
150
- }
151
- function createParser(logger) {
152
- let parser = yargs(hideBin(process.argv)).scriptName("libretto-cli").parserConfiguration({ "populate--": true }).option("session", {
153
- type: "string",
154
- default: SESSION_DEFAULT,
155
- describe: "Use a named session",
156
- global: true
157
- }).middleware((argv) => {
158
- validateSessionName(String(argv.session));
159
- }).exitProcess(false).help(false).version(false).fail((msg, err) => {
160
- if (err) throw err;
161
- throw new Error(msg || "Command failed");
162
- });
163
- parser = registerBrowserCommands(parser, logger);
164
- parser = registerExecutionCommands(parser, logger);
165
- parser = registerLogCommands(parser);
166
- parser = registerAICommands(parser);
167
- parser = registerSnapshotCommands(parser, logger);
168
- parser = registerInitCommand(parser);
169
- parser = parser.command("help", "Show usage", () => {
170
- }, () => {
171
- printUsage();
172
- });
173
- return parser;
174
- }
175
- async function runLibrettoCLI() {
176
- const rawArgs = process.argv.slice(2);
177
- let exitCode = 0;
178
- ensureLibrettoSetup();
179
- await withCliLogger(rawArgs, async (logger) => {
180
- try {
181
- validateLegacySessionArg(rawArgs);
182
- const args = filterSessionArgs(rawArgs);
183
- const command = args[0];
184
- if (!command || command === "--help" || command === "-h" || command === "help") {
185
- printUsage();
186
- return;
187
- }
188
- if (!CLI_COMMANDS.has(command)) {
189
- console.error(`Unknown command: ${command}
190
- `);
191
- printUsage();
192
- exitCode = 1;
193
- return;
194
- }
195
- const parser = createParser(logger);
196
- logger.info("cli-command", { command, args });
197
- await parser.parseAsync();
198
- } catch (err) {
199
- logger.error("cli-error", { error: err, args: rawArgs });
200
- const message = err instanceof Error ? err.message : String(err);
201
- console.error(message);
202
- exitCode = 1;
203
- }
204
- });
205
- process.exit(exitCode);
206
- }
207
- export {
208
- runLibrettoCLI
209
- };
@@ -1,21 +0,0 @@
1
- import { runAiConfigure } from "../core/ai-config.js";
2
- function registerAICommands(yargs) {
3
- return yargs.command(
4
- "ai configure [preset]",
5
- "Configure AI runtime",
6
- (cmd) => cmd.option("clear", { type: "boolean", default: false }),
7
- (argv) => {
8
- const customPrefix = Array.isArray(argv["--"]) ? argv["--"] : [];
9
- runAiConfigure({
10
- clear: Boolean(argv.clear),
11
- preset: argv.preset,
12
- customPrefix
13
- }, {
14
- configureCommandName: "libretto-cli ai configure"
15
- });
16
- }
17
- );
18
- }
19
- export {
20
- registerAICommands
21
- };
@@ -1,82 +0,0 @@
1
- import {
2
- runClose as runCloseWithLogger,
3
- runCloseAll as runCloseAllWithLogger,
4
- runOpen,
5
- runPages,
6
- runSave
7
- } from "../core/browser.js";
8
- import { withSessionLogger } from "../core/context.js";
9
- function registerBrowserCommands(yargs, logger) {
10
- return yargs.command(
11
- "open [url]",
12
- "Launch browser and open URL (headed by default)",
13
- (cmd) => cmd.option("headed", {
14
- type: "boolean",
15
- default: false
16
- }).option("headless", {
17
- type: "boolean",
18
- default: false
19
- }),
20
- async (argv) => {
21
- const hasHeadedFlag = Boolean(argv.headed);
22
- const hasHeadlessFlag = Boolean(argv.headless);
23
- if (hasHeadedFlag && hasHeadlessFlag) {
24
- throw new Error("Cannot pass both --headed and --headless.");
25
- }
26
- const headed = hasHeadedFlag || !hasHeadlessFlag;
27
- const url = argv.url;
28
- if (!url) {
29
- throw new Error(
30
- "Usage: libretto-cli open <url> [--headless] [--session <name>]"
31
- );
32
- }
33
- await runOpen(url, headed, String(argv.session), logger);
34
- }
35
- ).command(
36
- "save [urlOrDomain]",
37
- "Save current browser session",
38
- (cmd) => cmd,
39
- async (argv) => {
40
- const urlOrDomain = argv.urlOrDomain;
41
- if (!urlOrDomain) {
42
- throw new Error("Usage: libretto-cli save <url|domain> [--session <name>]");
43
- }
44
- await runSave(urlOrDomain, String(argv.session), logger);
45
- }
46
- ).command("pages", "List open pages in the session", (cmd) => cmd, async (argv) => {
47
- await runPages(String(argv.session), logger);
48
- }).command(
49
- "close",
50
- "Close the browser",
51
- (cmd) => cmd.option("all", {
52
- type: "boolean",
53
- default: false,
54
- describe: "Close all tracked sessions in this workspace"
55
- }).option("force", {
56
- type: "boolean",
57
- default: false,
58
- describe: "Force kill sessions that ignore SIGTERM (requires --all)"
59
- }),
60
- async (argv) => {
61
- const closeAll = Boolean(argv.all);
62
- const force = Boolean(argv.force);
63
- if (force && !closeAll) {
64
- throw new Error("Usage: libretto-cli close --all [--force]");
65
- }
66
- if (closeAll) {
67
- await runCloseAllWithLogger(logger, { force });
68
- return;
69
- }
70
- await runCloseWithLogger(String(argv.session), logger);
71
- }
72
- );
73
- }
74
- async function runClose(session) {
75
- await withSessionLogger(session, async (logger) => {
76
- await runCloseWithLogger(session, logger);
77
- });
78
- }
79
- export {
80
- registerBrowserCommands,
81
- runClose
82
- };