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.
- package/LICENSE +21 -0
- package/package.json +12 -12
- package/skill/SKILL.md +20 -18
- package/skill/code-generation-rules.md +3 -3
- package/skill/integration-approach-selection.md +3 -3
- package/dist/cli/cli.js +0 -209
- package/dist/cli/commands/ai.js +0 -21
- package/dist/cli/commands/browser.js +0 -82
- package/dist/cli/commands/execution.js +0 -461
- package/dist/cli/commands/init.js +0 -95
- package/dist/cli/commands/logs.js +0 -93
- package/dist/cli/commands/snapshot.js +0 -106
- package/dist/cli/core/ai-config.js +0 -149
- package/dist/cli/core/browser.js +0 -648
- package/dist/cli/core/context.js +0 -118
- package/dist/cli/core/pause-signals.js +0 -29
- package/dist/cli/core/session-telemetry.js +0 -491
- package/dist/cli/core/session.js +0 -183
- package/dist/cli/core/snapshot-analyzer.js +0 -492
- package/dist/cli/core/telemetry.js +0 -362
- package/dist/cli/index.js +0 -13
- package/dist/cli/workers/run-integration-runtime.js +0 -227
- package/dist/cli/workers/run-integration-worker-protocol.js +0 -12
- package/dist/cli/workers/run-integration-worker.js +0 -66
- package/dist/index.cjs +0 -116
- package/dist/index.d.cts +0 -21
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -97
- package/dist/runtime/download/download.cjs +0 -70
- package/dist/runtime/download/download.d.cts +0 -35
- package/dist/runtime/download/download.d.ts +0 -35
- package/dist/runtime/download/download.js +0 -45
- package/dist/runtime/download/index.cjs +0 -30
- package/dist/runtime/download/index.d.cts +0 -3
- package/dist/runtime/download/index.d.ts +0 -3
- package/dist/runtime/download/index.js +0 -8
- package/dist/runtime/extract/extract.cjs +0 -88
- package/dist/runtime/extract/extract.d.cts +0 -23
- package/dist/runtime/extract/extract.d.ts +0 -23
- package/dist/runtime/extract/extract.js +0 -64
- package/dist/runtime/extract/index.cjs +0 -28
- package/dist/runtime/extract/index.d.cts +0 -5
- package/dist/runtime/extract/index.d.ts +0 -5
- package/dist/runtime/extract/index.js +0 -4
- package/dist/runtime/network/index.cjs +0 -28
- package/dist/runtime/network/index.d.cts +0 -4
- package/dist/runtime/network/index.d.ts +0 -4
- package/dist/runtime/network/index.js +0 -6
- package/dist/runtime/network/network.cjs +0 -91
- package/dist/runtime/network/network.d.cts +0 -28
- package/dist/runtime/network/network.d.ts +0 -28
- package/dist/runtime/network/network.js +0 -67
- package/dist/runtime/recovery/agent.cjs +0 -223
- package/dist/runtime/recovery/agent.d.cts +0 -13
- package/dist/runtime/recovery/agent.d.ts +0 -13
- package/dist/runtime/recovery/agent.js +0 -199
- package/dist/runtime/recovery/errors.cjs +0 -124
- package/dist/runtime/recovery/errors.d.cts +0 -31
- package/dist/runtime/recovery/errors.d.ts +0 -31
- package/dist/runtime/recovery/errors.js +0 -100
- package/dist/runtime/recovery/index.cjs +0 -34
- package/dist/runtime/recovery/index.d.cts +0 -7
- package/dist/runtime/recovery/index.d.ts +0 -7
- package/dist/runtime/recovery/index.js +0 -10
- package/dist/runtime/recovery/recovery.cjs +0 -55
- package/dist/runtime/recovery/recovery.d.cts +0 -12
- package/dist/runtime/recovery/recovery.d.ts +0 -12
- package/dist/runtime/recovery/recovery.js +0 -31
- package/dist/shared/config/config.cjs +0 -44
- package/dist/shared/config/config.d.cts +0 -10
- package/dist/shared/config/config.d.ts +0 -10
- package/dist/shared/config/config.js +0 -18
- package/dist/shared/config/index.cjs +0 -32
- package/dist/shared/config/index.d.cts +0 -1
- package/dist/shared/config/index.d.ts +0 -1
- package/dist/shared/config/index.js +0 -10
- package/dist/shared/debug/index.cjs +0 -30
- package/dist/shared/debug/index.d.cts +0 -1
- package/dist/shared/debug/index.d.ts +0 -1
- package/dist/shared/debug/index.js +0 -5
- package/dist/shared/debug/pause.cjs +0 -90
- package/dist/shared/debug/pause.d.cts +0 -16
- package/dist/shared/debug/pause.d.ts +0 -16
- package/dist/shared/debug/pause.js +0 -55
- package/dist/shared/instrumentation/errors.cjs +0 -81
- package/dist/shared/instrumentation/errors.d.cts +0 -12
- package/dist/shared/instrumentation/errors.d.ts +0 -12
- package/dist/shared/instrumentation/errors.js +0 -57
- package/dist/shared/instrumentation/index.cjs +0 -35
- package/dist/shared/instrumentation/index.d.cts +0 -6
- package/dist/shared/instrumentation/index.d.ts +0 -6
- package/dist/shared/instrumentation/index.js +0 -12
- package/dist/shared/instrumentation/instrument.cjs +0 -206
- package/dist/shared/instrumentation/instrument.d.cts +0 -32
- package/dist/shared/instrumentation/instrument.d.ts +0 -32
- package/dist/shared/instrumentation/instrument.js +0 -190
- package/dist/shared/llm/ai-sdk-adapter.cjs +0 -67
- package/dist/shared/llm/ai-sdk-adapter.d.cts +0 -22
- package/dist/shared/llm/ai-sdk-adapter.d.ts +0 -22
- package/dist/shared/llm/ai-sdk-adapter.js +0 -43
- package/dist/shared/llm/client.cjs +0 -139
- package/dist/shared/llm/client.d.cts +0 -6
- package/dist/shared/llm/client.d.ts +0 -6
- package/dist/shared/llm/client.js +0 -115
- package/dist/shared/llm/index.cjs +0 -31
- package/dist/shared/llm/index.d.cts +0 -5
- package/dist/shared/llm/index.d.ts +0 -5
- package/dist/shared/llm/index.js +0 -6
- package/dist/shared/llm/types.cjs +0 -16
- package/dist/shared/llm/types.d.cts +0 -66
- package/dist/shared/llm/types.d.ts +0 -66
- package/dist/shared/llm/types.js +0 -0
- package/dist/shared/logger/index.cjs +0 -37
- package/dist/shared/logger/index.d.cts +0 -2
- package/dist/shared/logger/index.d.ts +0 -2
- package/dist/shared/logger/index.js +0 -13
- package/dist/shared/logger/logger.cjs +0 -213
- package/dist/shared/logger/logger.d.cts +0 -82
- package/dist/shared/logger/logger.d.ts +0 -82
- package/dist/shared/logger/logger.js +0 -188
- package/dist/shared/logger/sinks.cjs +0 -160
- package/dist/shared/logger/sinks.d.cts +0 -9
- package/dist/shared/logger/sinks.d.ts +0 -9
- package/dist/shared/logger/sinks.js +0 -124
- package/dist/shared/paths/paths.cjs +0 -104
- package/dist/shared/paths/paths.d.cts +0 -10
- package/dist/shared/paths/paths.d.ts +0 -10
- package/dist/shared/paths/paths.js +0 -73
- package/dist/shared/run/api.cjs +0 -28
- package/dist/shared/run/api.d.cts +0 -2
- package/dist/shared/run/api.d.ts +0 -2
- package/dist/shared/run/api.js +0 -4
- package/dist/shared/run/browser.cjs +0 -98
- package/dist/shared/run/browser.d.cts +0 -22
- package/dist/shared/run/browser.d.ts +0 -22
- package/dist/shared/run/browser.js +0 -74
- package/dist/shared/state/index.cjs +0 -38
- package/dist/shared/state/index.d.cts +0 -2
- package/dist/shared/state/index.d.ts +0 -2
- package/dist/shared/state/index.js +0 -16
- package/dist/shared/state/session-state.cjs +0 -85
- package/dist/shared/state/session-state.d.cts +0 -34
- package/dist/shared/state/session-state.d.ts +0 -34
- package/dist/shared/state/session-state.js +0 -56
- package/dist/shared/visualization/ghost-cursor.cjs +0 -174
- package/dist/shared/visualization/ghost-cursor.d.cts +0 -37
- package/dist/shared/visualization/ghost-cursor.d.ts +0 -37
- package/dist/shared/visualization/ghost-cursor.js +0 -145
- package/dist/shared/visualization/highlight.cjs +0 -134
- package/dist/shared/visualization/highlight.d.cts +0 -22
- package/dist/shared/visualization/highlight.d.ts +0 -22
- package/dist/shared/visualization/highlight.js +0 -108
- package/dist/shared/visualization/index.cjs +0 -45
- package/dist/shared/visualization/index.d.cts +0 -3
- package/dist/shared/visualization/index.d.ts +0 -3
- package/dist/shared/visualization/index.js +0 -24
- package/dist/shared/workflow/workflow.cjs +0 -47
- package/dist/shared/workflow/workflow.d.cts +0 -21
- package/dist/shared/workflow/workflow.d.ts +0 -21
- 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.
|
|
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.
|
|
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
|
-
##
|
|
14
|
+
## Session Access
|
|
15
15
|
|
|
16
|
-
Libretto sessions are **full-access by default
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
|
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.
|
|
142
|
-
| Bot protection detected, fetch not patched | **B** (`page.
|
|
143
|
-
| Bot protection detected, fetch IS patched | **B** (`page.
|
|
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
|
-
};
|
package/dist/cli/commands/ai.js
DELETED
|
@@ -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
|
-
};
|