libretto 0.5.3-experimental.5 → 0.5.3
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/README.md +114 -37
- package/README.template.md +160 -0
- package/dist/cli/cli.js +22 -97
- package/dist/cli/commands/browser.js +86 -59
- package/dist/cli/commands/deploy.js +148 -0
- package/dist/cli/commands/execution.js +218 -96
- package/dist/cli/commands/init.js +34 -29
- package/dist/cli/commands/logs.js +4 -5
- package/dist/cli/commands/shared.js +30 -29
- package/dist/cli/commands/snapshot.js +26 -39
- package/dist/cli/core/ai-config.js +21 -4
- package/dist/cli/core/api-snapshot-analyzer.js +15 -5
- package/dist/cli/core/browser.js +207 -37
- package/dist/cli/core/context.js +4 -1
- package/dist/cli/core/deploy-artifact.js +687 -0
- package/dist/cli/core/session-telemetry.js +434 -174
- package/dist/cli/core/session.js +21 -8
- package/dist/cli/core/snapshot-analyzer.js +14 -31
- package/dist/cli/core/snapshot-api-config.js +2 -6
- package/dist/cli/core/telemetry.js +20 -4
- package/dist/cli/framework/simple-cli.js +144 -43
- package/dist/cli/router.js +16 -21
- package/dist/cli/workers/run-integration-runtime.js +25 -45
- package/dist/cli/workers/run-integration-worker-protocol.js +3 -2
- package/dist/cli/workers/run-integration-worker.js +1 -4
- package/dist/index.d.ts +1 -2
- package/dist/index.js +13 -10
- package/dist/runtime/download/download.js +5 -1
- package/dist/runtime/extract/extract.js +11 -2
- package/dist/runtime/network/network.js +8 -1
- package/dist/runtime/recovery/agent.js +6 -2
- package/dist/runtime/recovery/errors.js +3 -1
- package/dist/runtime/recovery/recovery.js +3 -1
- package/dist/shared/condense-dom/condense-dom.js +17 -69
- package/dist/shared/config/config.d.ts +1 -9
- package/dist/shared/config/config.js +0 -18
- package/dist/shared/config/index.d.ts +2 -1
- package/dist/shared/config/index.js +0 -10
- package/dist/shared/debug/pause.js +9 -3
- package/dist/shared/dom-semantics.d.ts +8 -0
- package/dist/shared/dom-semantics.js +69 -0
- package/dist/shared/instrumentation/instrument.js +101 -5
- package/dist/shared/llm/ai-sdk-adapter.js +3 -1
- package/dist/shared/llm/client.js +3 -1
- package/dist/shared/logger/index.js +4 -1
- package/dist/shared/run/api.js +3 -1
- package/dist/shared/run/browser.js +47 -3
- package/dist/shared/state/session-state.d.ts +2 -1
- package/dist/shared/state/session-state.js +5 -2
- package/dist/shared/visualization/ghost-cursor.js +36 -14
- package/dist/shared/visualization/highlight.js +9 -6
- package/dist/shared/workflow/workflow.d.ts +18 -10
- package/dist/shared/workflow/workflow.js +50 -5
- package/package.json +14 -6
- package/scripts/generate-changelog.ts +132 -0
- package/scripts/postinstall.mjs +4 -3
- package/scripts/skills-libretto.mjs +2 -88
- package/scripts/summarize-evals.mjs +32 -10
- package/skills/libretto/SKILL.md +132 -62
- package/skills/libretto/references/action-logs.md +101 -0
- package/skills/libretto/references/auth-profiles.md +1 -2
- package/skills/libretto/references/code-generation-rules.md +176 -0
- package/skills/libretto/references/configuration-file-reference.md +53 -0
- package/skills/libretto/references/pages-and-page-targeting.md +1 -1
- package/skills/libretto/references/site-security-review.md +143 -0
- package/src/cli/cli.ts +23 -110
- package/src/cli/commands/browser.ts +94 -70
- package/src/cli/commands/deploy.ts +198 -0
- package/src/cli/commands/execution.ts +251 -111
- package/src/cli/commands/init.ts +37 -33
- package/src/cli/commands/logs.ts +7 -7
- package/src/cli/commands/shared.ts +36 -37
- package/src/cli/commands/snapshot.ts +44 -59
- package/src/cli/core/ai-config.ts +24 -4
- package/src/cli/core/api-snapshot-analyzer.ts +17 -6
- package/src/cli/core/browser.ts +260 -49
- package/src/cli/core/context.ts +7 -2
- package/src/cli/core/deploy-artifact.ts +938 -0
- package/src/cli/core/session-telemetry.ts +449 -197
- package/src/cli/core/session.ts +21 -7
- package/src/cli/core/snapshot-analyzer.ts +26 -46
- package/src/cli/core/snapshot-api-config.ts +170 -175
- package/src/cli/core/telemetry.ts +39 -4
- package/src/cli/framework/simple-cli.ts +281 -98
- package/src/cli/router.ts +15 -21
- package/src/cli/workers/run-integration-runtime.ts +35 -57
- package/src/cli/workers/run-integration-worker-protocol.ts +2 -1
- package/src/cli/workers/run-integration-worker.ts +1 -4
- package/src/index.ts +77 -67
- package/src/runtime/download/download.ts +62 -58
- package/src/runtime/download/index.ts +5 -5
- package/src/runtime/extract/extract.ts +71 -61
- package/src/runtime/network/index.ts +3 -3
- package/src/runtime/network/network.ts +99 -93
- package/src/runtime/recovery/agent.ts +217 -212
- package/src/runtime/recovery/errors.ts +107 -104
- package/src/runtime/recovery/index.ts +3 -3
- package/src/runtime/recovery/recovery.ts +38 -35
- package/src/shared/condense-dom/condense-dom.ts +27 -82
- package/src/shared/config/config.ts +0 -19
- package/src/shared/config/index.ts +0 -5
- package/src/shared/debug/pause.ts +57 -51
- package/src/shared/dom-semantics.ts +68 -0
- package/src/shared/instrumentation/errors.ts +64 -62
- package/src/shared/instrumentation/index.ts +5 -5
- package/src/shared/instrumentation/instrument.ts +339 -209
- package/src/shared/llm/ai-sdk-adapter.ts +58 -55
- package/src/shared/llm/client.ts +181 -174
- package/src/shared/llm/types.ts +39 -39
- package/src/shared/logger/index.ts +11 -4
- package/src/shared/logger/logger.ts +312 -306
- package/src/shared/logger/sinks.ts +118 -114
- package/src/shared/paths/paths.ts +50 -49
- package/src/shared/paths/repo-root.ts +17 -17
- package/src/shared/run/api.ts +5 -1
- package/src/shared/run/browser.ts +65 -3
- package/src/shared/state/index.ts +9 -9
- package/src/shared/state/session-state.ts +46 -43
- package/src/shared/visualization/ghost-cursor.ts +180 -149
- package/src/shared/visualization/highlight.ts +89 -86
- package/src/shared/visualization/index.ts +13 -13
- package/src/shared/workflow/workflow.ts +107 -30
- package/scripts/check-skills-sync.mjs +0 -23
- package/scripts/prepare-release.sh +0 -97
- package/skills/libretto/references/reverse-engineering-network-requests.md +0 -75
- package/skills/libretto/references/user-action-log.md +0 -31
|
@@ -5,7 +5,9 @@ import { basename, join, resolve } from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
function usage() {
|
|
8
|
-
console.error(
|
|
8
|
+
console.error(
|
|
9
|
+
"Usage: node scripts/summarize-evals.mjs <score-dir> <summary-json-path>",
|
|
10
|
+
);
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
function normalizeFailureRecord(failure) {
|
|
@@ -18,8 +20,11 @@ function normalizeFailureRecord(failure) {
|
|
|
18
20
|
function normalizeRecord(record) {
|
|
19
21
|
const failures = Array.isArray(record?.failures)
|
|
20
22
|
? record.failures
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
.map(normalizeFailureRecord)
|
|
24
|
+
.filter(
|
|
25
|
+
(failure) =>
|
|
26
|
+
failure.criterion.length > 0 && failure.reason.length > 0,
|
|
27
|
+
)
|
|
23
28
|
: [];
|
|
24
29
|
|
|
25
30
|
return {
|
|
@@ -35,14 +40,22 @@ export function loadScoreRecords(scoreDirArg) {
|
|
|
35
40
|
const scoreDir = resolve(scoreDirArg);
|
|
36
41
|
return readdirSync(scoreDir, { withFileTypes: true })
|
|
37
42
|
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
38
|
-
.map((entry) =>
|
|
43
|
+
.map((entry) =>
|
|
44
|
+
JSON.parse(readFileSync(join(scoreDir, entry.name), "utf8")),
|
|
45
|
+
)
|
|
39
46
|
.map(normalizeRecord)
|
|
40
47
|
.sort((a, b) => String(a.name).localeCompare(String(b.name)));
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
export function buildSummary(records) {
|
|
44
|
-
const passed = records.reduce(
|
|
45
|
-
|
|
51
|
+
const passed = records.reduce(
|
|
52
|
+
(sum, record) => sum + Number(record.passed || 0),
|
|
53
|
+
0,
|
|
54
|
+
);
|
|
55
|
+
const total = records.reduce(
|
|
56
|
+
(sum, record) => sum + Number(record.total || 0),
|
|
57
|
+
0,
|
|
58
|
+
);
|
|
46
59
|
const percent = total > 0 ? Number(((passed / total) * 100).toFixed(2)) : 0;
|
|
47
60
|
const failingRecords = records.filter((record) => record.failures.length > 0);
|
|
48
61
|
|
|
@@ -72,16 +85,22 @@ export function buildMarkdown(summary, summaryPathArg) {
|
|
|
72
85
|
lines.push("", "## Breakdown", "");
|
|
73
86
|
for (const record of summary.records) {
|
|
74
87
|
const status = record.failures.length > 0 ? "fail" : "pass";
|
|
75
|
-
lines.push(
|
|
88
|
+
lines.push(
|
|
89
|
+
`- ${status} \`${record.name}\`: \`${record.percent}%\` (${record.passed}/${record.total})`,
|
|
90
|
+
);
|
|
76
91
|
}
|
|
77
92
|
}
|
|
78
93
|
|
|
79
94
|
if (summary.failingRecordCount > 0) {
|
|
80
95
|
lines.push("", "## Failed Evals", "");
|
|
81
|
-
for (const record of summary.records.filter(
|
|
96
|
+
for (const record of summary.records.filter(
|
|
97
|
+
(candidate) => candidate.failures.length > 0,
|
|
98
|
+
)) {
|
|
82
99
|
lines.push(`### \`${record.name}\``);
|
|
83
100
|
lines.push("");
|
|
84
|
-
lines.push(
|
|
101
|
+
lines.push(
|
|
102
|
+
`- Score: \`${record.percent}%\` (${record.passed}/${record.total})`,
|
|
103
|
+
);
|
|
85
104
|
for (const failure of record.failures) {
|
|
86
105
|
lines.push(`- ${failure.criterion}: ${failure.reason}`);
|
|
87
106
|
}
|
|
@@ -108,6 +127,9 @@ function main(argv) {
|
|
|
108
127
|
process.stdout.write(buildMarkdown(summary, summaryPath));
|
|
109
128
|
}
|
|
110
129
|
|
|
111
|
-
if (
|
|
130
|
+
if (
|
|
131
|
+
process.argv[1] &&
|
|
132
|
+
resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
133
|
+
) {
|
|
112
134
|
main(process.argv);
|
|
113
135
|
}
|
package/skills/libretto/SKILL.md
CHANGED
|
@@ -4,41 +4,37 @@ description: "Browser automation CLI for building, maintaining, and running brow
|
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: saffron-health
|
|
7
|
-
version: "0.
|
|
7
|
+
version: "0.5.3"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## How Libretto Works
|
|
11
11
|
|
|
12
|
-
Libretto is a CLI for
|
|
13
|
-
Use
|
|
12
|
+
- Libretto is a CLI for exploring live websites and building or debugging reusable browser automation scripts.
|
|
13
|
+
- Use Libretto commands to inspect the site and open pages, observe state, inspect requests, and prototype interactions.
|
|
14
|
+
- Libretto work must end in script changes. Create or edit the workflow file instead of stopping at interactive exploration.
|
|
14
15
|
|
|
15
|
-
##
|
|
16
|
+
## Default Integration Approach
|
|
16
17
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- If the user asks for a new automation or scrape and no workflow file exists yet, create one in the workspace instead of stopping at interactive exploration.
|
|
21
|
-
- For a new automation, make the workflow file a required deliverable before you finish the task, even if you inspect the site first.
|
|
22
|
-
- If the user does not provide a workflow path, choose a reasonable filename in the current workspace and create it yourself.
|
|
23
|
-
- When building a new integration, prefer reverse-engineering network requests first. Fall back to browser automation when the request path is unclear, too fragile, or blocked by anti-bot systems.
|
|
18
|
+
- Prefer network requests first for new integrations unless the user explicitly asks for Playwright or UI automation, then do not use the site's internal API.
|
|
19
|
+
- Read `references/site-security-review.md` before committing to a network-first approach on a new site.
|
|
20
|
+
- Fall back to passive interception or Playwright-driven UI automation when the security review rules network requests out, the request path is not workable, or the user explicitly asks for Playwright.
|
|
24
21
|
|
|
25
22
|
## Setup
|
|
26
23
|
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
- If they already have credentials, `npx libretto ai configure openai|anthropic|gemini|vertex` is enough.
|
|
24
|
+
- Use `npx libretto init` for first-time workspace setup (sets up config file and snapshot command).
|
|
25
|
+
- If credentials are already available, `npx libretto ai configure openai|anthropic|gemini|vertex` is usually enough.
|
|
30
26
|
|
|
31
|
-
## Rules
|
|
27
|
+
## Working Rules
|
|
32
28
|
|
|
33
29
|
- Announce which session you are using and what page you are on.
|
|
34
|
-
- When the task is to build or change an automation, create or update the workflow file and use Libretto commands to gather the information needed for that code change.
|
|
35
|
-
- For a new automation, you may use `open`, `snapshot`, or `exec` first to learn the page, but do not finish or reply as if the task is complete until the workflow file exists.
|
|
36
|
-
- Treat scrape and integration requests as requests for reusable automation code by default, not as requests to manually collect the final data in the live session.
|
|
37
30
|
- Ask instead of guessing when it is unclear what to click, type, or submit.
|
|
38
|
-
-
|
|
31
|
+
- Do not treat visibility as interactivity. If an element will not act, inspect blockers before retrying.
|
|
32
|
+
- Defer repo/code review until you begin generating code, unless the user explicitly asks for it earlier.
|
|
33
|
+
- Read and follow guidelines in `references/code-generation-rules.md` before generating or editing production workflow code.
|
|
34
|
+
- Validation requires a successful clean `run --headless` with confirmation of the actual returned output, not just process success. If the user wants to watch the finished workflow, do a final headed `run` after headless validation succeeds.
|
|
35
|
+
- Treat exploration sessions as disposable unless the user explicitly wants one kept open.
|
|
39
36
|
- Get explicit user confirmation before mutating actions or replaying network requests that may have side effects.
|
|
40
37
|
- Never run multiple `exec` commands at the same time.
|
|
41
|
-
- Keep the browser session open until the user says the session is done.
|
|
42
38
|
|
|
43
39
|
## Commands
|
|
44
40
|
|
|
@@ -53,49 +49,135 @@ npx libretto open https://example.com --headed
|
|
|
53
49
|
npx libretto open https://example.com --headless --session debug-example
|
|
54
50
|
```
|
|
55
51
|
|
|
56
|
-
### `
|
|
52
|
+
### `connect`
|
|
57
53
|
|
|
58
|
-
- Use `
|
|
59
|
-
-
|
|
60
|
-
-
|
|
54
|
+
- Use `connect` to attach to any existing Chrome DevTools Protocol (CDP) endpoint — a browser started with `--remote-debugging-port`, an Electron app, or any other CDP-compatible target.
|
|
55
|
+
- After connecting, `exec`, `snapshot`, `pages`, and all other session commands work normally.
|
|
56
|
+
- Libretto does not manage the connected process's lifecycle. `close` clears the session but does not terminate the remote process.
|
|
61
57
|
|
|
62
58
|
```bash
|
|
63
|
-
npx libretto
|
|
64
|
-
npx libretto
|
|
65
|
-
npx libretto exec --visualize "await page.locator('button:has-text(\"Continue\")').click()"
|
|
59
|
+
npx libretto connect http://127.0.0.1:9222 --session my-session
|
|
60
|
+
npx libretto connect http://127.0.0.1:9223 --session another-session
|
|
66
61
|
```
|
|
67
62
|
|
|
68
63
|
### `snapshot`
|
|
69
64
|
|
|
70
65
|
- Use `snapshot` as the primary page observation tool.
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
- When
|
|
66
|
+
- Always provide both `--objective` and `--context`.
|
|
67
|
+
- A single snapshot objective can include multiple questions or analysis tasks.
|
|
68
|
+
- Use it before guessing at selectors, after workflow failures, and whenever the visible page state is unclear.
|
|
69
|
+
- When analysis is involved, expect it to take time. Use a timeout of at least 2 minutes for shell-wrapped calls.
|
|
75
70
|
|
|
76
71
|
```bash
|
|
77
|
-
npx libretto snapshot
|
|
78
72
|
npx libretto snapshot \
|
|
79
73
|
--objective "Find the sign-in form and submit button" \
|
|
80
74
|
--context "I just opened the login page and need the email field, password field, and submit button."
|
|
81
75
|
npx libretto snapshot \
|
|
76
|
+
--session debug-example \
|
|
77
|
+
--page <page-id> \
|
|
82
78
|
--objective "Explain why the table is empty" \
|
|
83
|
-
--context "I opened the referrals page and expected rows
|
|
79
|
+
--context "I opened the referrals page, applied filters, and expected rows to appear."
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `exec`
|
|
83
|
+
|
|
84
|
+
- Use `exec` for focused inspection and short-lived interaction experiments.
|
|
85
|
+
- Use `exec` to validate selectors, inspect data, or prototype a step before you encode it in the workflow file.
|
|
86
|
+
- Use `exec -` to run multi-line scripts from stdin, especially when the code is too long or complex for a command line argument.
|
|
87
|
+
- Available globals: `page`, `context`, `browser`, `state`, `fetch`, `Buffer`.
|
|
88
|
+
- Let failures throw. Do not hide `exec` failures with `try/catch` or `.catch()`.
|
|
89
|
+
- Do not run multiple `exec` commands in parallel.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx libretto exec "return await page.url()"
|
|
93
|
+
npx libretto exec "return await page.locator('button').count()"
|
|
94
|
+
npx libretto exec "await page.locator('button:has-text(\"Continue\")').click()"
|
|
95
|
+
echo "return await page.url()" | npx libretto exec - --session debug-example
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `pages`
|
|
99
|
+
|
|
100
|
+
- Use `pages` when a popup, new tab, or second page appears.
|
|
101
|
+
- If `exec`, `snapshot`, `network`, or `actions` complains about multiple pages, list page ids first and then pass `--page`.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npx libretto pages --session debug-example
|
|
105
|
+
npx libretto exec --session debug-example --page <page-id> "return await page.url()"
|
|
84
106
|
```
|
|
85
107
|
|
|
86
108
|
### `run`
|
|
87
109
|
|
|
88
|
-
- Use `run` to verify a workflow file after creating it or editing it.
|
|
110
|
+
- Use `run` to verify a workflow file after creating it or editing it, preferring `run --headless` for the normal fix/verify loop.
|
|
111
|
+
- Plain `run` defaults to headed mode.
|
|
89
112
|
- If the workflow fails, Libretto keeps the browser open. Inspect the failed state with `snapshot` and `exec` before editing code.
|
|
113
|
+
- Insert `await pause(session)` statements in the workflow file when you need to stop at specific states for interactive debugging, like breakpoints in the browser flow.
|
|
90
114
|
- If the workflow pauses, resume it with `npx libretto resume --session <name>`.
|
|
91
115
|
- Re-run the same workflow after each fix to verify the browser behavior end to end.
|
|
92
116
|
|
|
93
117
|
```bash
|
|
94
|
-
npx libretto run ./integration.ts
|
|
95
|
-
npx libretto run ./integration.ts
|
|
96
|
-
|
|
118
|
+
npx libretto run ./integration.ts workflowName --headless --params '{"status":"open"}'
|
|
119
|
+
npx libretto run ./integration.ts workflowName --auth-profile app.example.com
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `resume`
|
|
123
|
+
|
|
124
|
+
- Workflows pause by calling `await pause("session-name")` in the workflow file. Import `pause` from `"libretto"`.
|
|
125
|
+
- `pause(session)` is a no-op when `NODE_ENV === "production"`.
|
|
126
|
+
- Use `resume` when a workflow hit a `pause()` call.
|
|
127
|
+
- Keep resuming the same session until the workflow completes or pauses again.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx libretto resume --session debug-example
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### `save`
|
|
134
|
+
|
|
135
|
+
- Use `save` only when the user explicitly asks to save or reuse authenticated browser state.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx libretto save app.example.com
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `close`
|
|
142
|
+
|
|
143
|
+
- Use `close` when the user is done with the session or an exploration session is no longer helping progress (unless the user asked to keep watching that browser).
|
|
144
|
+
- `close --all` is available for workspace cleanup.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npx libretto close --session debug-example
|
|
148
|
+
npx libretto close --all
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Session Logs
|
|
152
|
+
|
|
153
|
+
Session state is stored in `.libretto/sessions/<session>/state.json`.
|
|
154
|
+
|
|
155
|
+
Session logs are JSONL files at `.libretto/sessions/<session>/`:
|
|
156
|
+
|
|
157
|
+
- CLI logs are in `.libretto/sessions/<session>/logs.jsonl`.
|
|
158
|
+
- Action logs are in `.libretto/sessions/<session>/actions.jsonl`.
|
|
159
|
+
- Network logs are in `.libretto/sessions/<session>/network.jsonl`.
|
|
160
|
+
|
|
161
|
+
Use `jq` to query jsonl logs directly — for any filtering, slicing, or inspection task.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Last 20 action entries
|
|
165
|
+
tail -n 20 .libretto/sessions/<session>/actions.jsonl | jq .
|
|
166
|
+
|
|
167
|
+
# POST requests only
|
|
168
|
+
jq 'select(.method == "POST")' .libretto/sessions/<session>/network.jsonl
|
|
97
169
|
```
|
|
98
170
|
|
|
171
|
+
### Action log (`actions.jsonl`)
|
|
172
|
+
|
|
173
|
+
Key fields: `ts` (ISO timestamp), `source` (`user` or `agent`), `action` (`click`, `fill`, `goto`, etc.), `selector` (locator used by the agent), `bestSemanticSelector` (canonical selector for user DOM events), `success` (boolean), `url` (navigation target), `value` (typed or submitted value), `error` (message on failure).
|
|
174
|
+
|
|
175
|
+
Read `references/action-logs.md` for full field descriptions and user-vs-agent entry semantics.
|
|
176
|
+
|
|
177
|
+
### Network log (`network.jsonl`)
|
|
178
|
+
|
|
179
|
+
Key fields: `ts` (ISO timestamp), `method` (HTTP method, e.g. `GET`, `POST`), `url` (request URL), `status` (HTTP status code), `contentType` (response content type), `responseBody` (response body string, may be null).
|
|
180
|
+
|
|
99
181
|
## Examples
|
|
100
182
|
|
|
101
183
|
### Building new browser automation workflows
|
|
@@ -105,31 +187,16 @@ npx libretto run ./integration.ts main --auth-profile app.example.com --headed
|
|
|
105
187
|
```text
|
|
106
188
|
<example>
|
|
107
189
|
[Context: The user wants to build a new browser workflow and does not yet know the page structure]
|
|
108
|
-
Assistant: I'll inspect the real site first if needed, but before I finish I'll create
|
|
190
|
+
Assistant: I'll inspect the real site first if needed, but before I finish I'll create `target-workflow.ts` so the task produces reusable automation code.
|
|
109
191
|
Assistant: [Runs `npx libretto open https://target.example.com --headed`]
|
|
110
|
-
Assistant:
|
|
111
|
-
Assistant: [Runs `npx libretto snapshot --objective "Find the next required action" --context "We are starting the workflow from the landing page."`]
|
|
112
|
-
Assistant:
|
|
113
|
-
Assistant: [
|
|
192
|
+
Assistant: [Reads `references/site-security-review.md` before choosing between passive network inspection, direct browser fetch calls, and Playwright-first automation]
|
|
193
|
+
Assistant: [Runs `npx libretto snapshot --objective "Find the next required action" --context "We are starting the workflow from the landing page and need the first meaningful step."`]
|
|
194
|
+
Assistant: [Uses `network`, `snapshot`, and `exec` as needed to understand the site and decide the implementation path]
|
|
195
|
+
Assistant: [Reads `references/code-generation-rules.md` before writing production workflow code]
|
|
114
196
|
Assistant: I found the working path. I'll now update the workflow file outside Libretto and verify it with `npx libretto run ...`.
|
|
115
197
|
</example>
|
|
116
198
|
```
|
|
117
199
|
|
|
118
|
-
#### Network requests
|
|
119
|
-
|
|
120
|
-
```text
|
|
121
|
-
<example>
|
|
122
|
-
[Context: The user wants to build an integration using network requests]
|
|
123
|
-
Assistant: [Reads `references/reverse-engineering-network-requests.md`]
|
|
124
|
-
Assistant: Let me open the page in headed mode. Perform the workflow and I'll use the network log to recreate it.
|
|
125
|
-
Assistant: [Runs `npx libretto open https://target.example.com --headed`]
|
|
126
|
-
[User performs workflow]
|
|
127
|
-
User: I've completed the workflow
|
|
128
|
-
Assistant: [Runs `npx libretto network --method POST --last 20`]
|
|
129
|
-
Assistant: I found the relevant requests. I'll recreate the workflow from those requests, then test the resulting script with `npx libretto run ...`.
|
|
130
|
-
</example>
|
|
131
|
-
```
|
|
132
|
-
|
|
133
200
|
### Debugging existing workflows
|
|
134
201
|
|
|
135
202
|
```text
|
|
@@ -140,13 +207,16 @@ Assistant: [Runs `npx libretto run ./integration.ts main --session debug-flow --
|
|
|
140
207
|
Assistant: The workflow failed and Libretto kept the browser open. I'll inspect the page state before changing code.
|
|
141
208
|
Assistant: [Runs `npx libretto snapshot --session debug-flow --objective "Find the blocking error or broken selector target" --context "The workflow just failed after trying to continue from the review step, and I need to identify the visible blocker on the current page."`]
|
|
142
209
|
Assistant: [Runs `npx libretto exec --session debug-flow "...focused inspection or prototype..."`]
|
|
210
|
+
Assistant: [Reads `references/code-generation-rules.md` before patching the workflow file]
|
|
143
211
|
Assistant: I found the issue. I'll patch the workflow code, then rerun `npx libretto run ...` to verify the fix.
|
|
144
212
|
</example>
|
|
145
213
|
```
|
|
146
214
|
|
|
147
215
|
## References
|
|
148
216
|
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
217
|
+
- Read `references/configuration-file-reference.md` when you need to inspect or change `.libretto/config.json` for snapshot model selection or viewport defaults.
|
|
218
|
+
- Read `references/site-security-review.md` before reviewing the site's security posture and deciding whether to lead with network requests, passive interception, or Playwright DOM automation on a new site.
|
|
219
|
+
- Read `references/code-generation-rules.md` before writing or editing production workflow files.
|
|
220
|
+
- Read `references/auth-profiles.md` when auth-profile behavior is relevant.
|
|
221
|
+
- Read `references/pages-and-page-targeting.md` when a session has multiple open pages or you need `--page`.
|
|
222
|
+
- Read `references/action-logs.md` for full action log field descriptions and user-vs-agent event semantics.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Action Logs
|
|
2
|
+
|
|
3
|
+
- Stored at `.libretto/sessions/<session>/actions.jsonl`.
|
|
4
|
+
- One JSON object per line.
|
|
5
|
+
- Query the file directly with `jq`, for example `tail -n 20 .libretto/sessions/<session>/actions.jsonl | jq .`.
|
|
6
|
+
- This is an orientation log, not a replay trace.
|
|
7
|
+
|
|
8
|
+
## User vs Agent
|
|
9
|
+
|
|
10
|
+
- `agent` entries log the Playwright action Libretto observed, usually as `action` plus `selector`, and sometimes `value`, `url`, `duration`, or `error`.
|
|
11
|
+
- `user` entries log the browser DOM event Libretto captured, so they can include `bestSemanticSelector`, `targetSelector`, `ancestorSelectors`, `nearbyText`, `composedPath`, and `coordinates`.
|
|
12
|
+
- This is why agent entries usually describe what Playwright tried to do, while user entries can describe what element was actually interacted with in the page.
|
|
13
|
+
|
|
14
|
+
## Fields
|
|
15
|
+
|
|
16
|
+
- `ts`
|
|
17
|
+
ISO timestamp.
|
|
18
|
+
|
|
19
|
+
- `pageId`
|
|
20
|
+
Playwright target id for the page that produced the entry.
|
|
21
|
+
|
|
22
|
+
- `action`
|
|
23
|
+
Logged action name, such as `click`, `dblclick`, `fill`, `goto`, or `reload`.
|
|
24
|
+
|
|
25
|
+
- `source`
|
|
26
|
+
`user` for captured DOM events, `agent` for logged Playwright calls.
|
|
27
|
+
|
|
28
|
+
- `success`
|
|
29
|
+
`true` if the action completed, `false` if Libretto logged a failure.
|
|
30
|
+
|
|
31
|
+
- `selector`
|
|
32
|
+
Selector or locator hint for agent entries.
|
|
33
|
+
|
|
34
|
+
- `bestSemanticSelector`
|
|
35
|
+
Canonical selector for user DOM events.
|
|
36
|
+
|
|
37
|
+
- `targetSelector`
|
|
38
|
+
Selector for the raw DOM event target. Usually only present for user DOM events.
|
|
39
|
+
|
|
40
|
+
- `ancestorSelectors`
|
|
41
|
+
Meaningful ancestor selector candidates for a user DOM event. Ordered from closest meaningful ancestor to farthest meaningful ancestor.
|
|
42
|
+
|
|
43
|
+
- `nearbyText`
|
|
44
|
+
Short visible text near the event target, used as human context.
|
|
45
|
+
|
|
46
|
+
- `composedPath`
|
|
47
|
+
Compact event-path summaries. Ordered from the raw event target to farthest ancestor.
|
|
48
|
+
|
|
49
|
+
- `coordinates`
|
|
50
|
+
Rounded `clientX` and `clientY` for pointer-style events:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{ "x": 42, "y": 24 }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- `value`
|
|
57
|
+
Typed, selected, or submitted value when the action had one.
|
|
58
|
+
|
|
59
|
+
- `url`
|
|
60
|
+
Navigation target or recorded page URL for navigation-style actions.
|
|
61
|
+
|
|
62
|
+
- `duration`
|
|
63
|
+
Elapsed time in milliseconds when Libretto recorded it.
|
|
64
|
+
|
|
65
|
+
- `error`
|
|
66
|
+
Error message when the action failed.
|
|
67
|
+
|
|
68
|
+
## User Example
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"ts": "2026-03-20T22:34:56.123Z",
|
|
73
|
+
"pageId": "A1B2C3D4E5F6",
|
|
74
|
+
"action": "dblclick",
|
|
75
|
+
"source": "user",
|
|
76
|
+
"bestSemanticSelector": "button#saveBtn",
|
|
77
|
+
"targetSelector": "span",
|
|
78
|
+
"ancestorSelectors": ["button#saveBtn", "form[action=\"/save\"]"],
|
|
79
|
+
"nearbyText": "Save",
|
|
80
|
+
"composedPath": ["span [text=\"Save\"]", "button#saveBtn [text=\"Save\"]"],
|
|
81
|
+
"coordinates": {
|
|
82
|
+
"x": 42,
|
|
83
|
+
"y": 24
|
|
84
|
+
},
|
|
85
|
+
"success": true
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Agent Example
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"ts": "2026-03-20T22:35:10.456Z",
|
|
94
|
+
"pageId": "A1B2C3D4E5F6",
|
|
95
|
+
"action": "click",
|
|
96
|
+
"source": "agent",
|
|
97
|
+
"selector": "page.getByRole(\"button\", {\"name\":\"Save\"})",
|
|
98
|
+
"duration": 187,
|
|
99
|
+
"success": true
|
|
100
|
+
}
|
|
101
|
+
```
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# Auth Profiles
|
|
2
2
|
|
|
3
|
-
Use this reference when the
|
|
3
|
+
Use this reference only when the user explicitly asks to save or reuse local authenticated browser state.
|
|
4
4
|
|
|
5
5
|
## When to Use This
|
|
6
6
|
|
|
7
7
|
- The site requires manual login.
|
|
8
8
|
- The user is running workflows locally.
|
|
9
|
-
- Reusing a saved session is simpler than building credential-handling logic into the workflow.
|
|
10
9
|
|
|
11
10
|
## Workflow
|
|
12
11
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Code Generation Rules
|
|
2
|
+
|
|
3
|
+
These rules apply when generating production TypeScript files from interactive browser sessions. Read this file before writing any production code.
|
|
4
|
+
|
|
5
|
+
Follow the user's existing codebase conventions, abstractions, and patterns whenever possible. Do not introduce a new style unless the codebase does not already have a suitable one.
|
|
6
|
+
|
|
7
|
+
## Workflow File Structure
|
|
8
|
+
|
|
9
|
+
Generated files must export a `workflow()` instance so they can be run via `npx libretto run <file> <workflowName>`. Import `workflow` and its types from `"libretto"`:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { workflow, pause, type LibrettoWorkflowContext } from "libretto";
|
|
13
|
+
|
|
14
|
+
type Input = {
|
|
15
|
+
// Define the expected input shape — passed via --params JSON
|
|
16
|
+
query: string;
|
|
17
|
+
maxResults?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type Output = {
|
|
21
|
+
// Define what the workflow returns
|
|
22
|
+
results: Array<{ name: string; value: string }>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const myWorkflow = workflow<Input, Output>(
|
|
26
|
+
"myWorkflow",
|
|
27
|
+
async (ctx: LibrettoWorkflowContext, input): Promise<Output> => {
|
|
28
|
+
const { session, page, logger } = ctx;
|
|
29
|
+
|
|
30
|
+
logger.info("workflow-start", { session, query: input.query });
|
|
31
|
+
await page.goto("https://example.com");
|
|
32
|
+
await pause(session);
|
|
33
|
+
|
|
34
|
+
return { results: [] };
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Key points:
|
|
40
|
+
|
|
41
|
+
- `workflow(name, handler)` takes a unique workflow name and returns the workflow object that Libretto can run.
|
|
42
|
+
- `npx libretto run ./file.ts myWorkflow` resolves `myWorkflow` from the workflows exported by `./file.ts`, so export or re-export the workflow from that file directly or through a `workflows` object, and make sure the run argument matches the name passed to `workflow("myWorkflow", ...)`.
|
|
43
|
+
- `ctx` provides `session`, `page`, and `logger`
|
|
44
|
+
- `input` comes from `--params '{"query":"foo"}'` or `--params-file params.json` on the CLI
|
|
45
|
+
- Use `await pause(ctx.session)` (or `await pause(session)`) to pause the workflow for debugging. It is a no-op in production.
|
|
46
|
+
- After validation is complete and the workflow is confirmed working end to end, remove all `pause()` calls and pause-only workflow params unless the user explicitly says to keep them.
|
|
47
|
+
- The browser is launched and closed automatically by the CLI. Do not launch or close it in the handler.
|
|
48
|
+
|
|
49
|
+
## Playwright DOM Interaction Rules
|
|
50
|
+
|
|
51
|
+
Generated code must use Playwright locator APIs for all DOM interactions. Do not use `page.evaluate()` with `document.querySelector`, `querySelectorAll`, `textContent`, `click()`, or other DOM APIs when a Playwright locator can do the same thing.
|
|
52
|
+
|
|
53
|
+
During the interactive `exec` phase, `page.evaluate` is fine for quick prototyping. In generated production code, translate those patterns into Playwright locators.
|
|
54
|
+
|
|
55
|
+
Before extracting data (for example text, rows, or field values), wait for the target content itself to be ready, not just its container.
|
|
56
|
+
|
|
57
|
+
### Anti-Patterns
|
|
58
|
+
|
|
59
|
+
These patterns come up frequently during interactive sessions and should not carry over into production code:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// DON'T — batch-read via evaluate string
|
|
63
|
+
const data = await page.evaluate(`(() => {
|
|
64
|
+
const posts = document.querySelectorAll('.post');
|
|
65
|
+
return Array.from(posts).map(p => ({
|
|
66
|
+
name: p.querySelector('.name')?.textContent,
|
|
67
|
+
content: p.querySelector('.content')?.textContent,
|
|
68
|
+
}));
|
|
69
|
+
})()`);
|
|
70
|
+
|
|
71
|
+
// DO — Playwright locators with a loop
|
|
72
|
+
const posts = await page.locator(".post").all();
|
|
73
|
+
for (const post of posts) {
|
|
74
|
+
const name = await post.locator(".name").textContent();
|
|
75
|
+
const content = await post.locator(".content").textContent();
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// DON'T — evaluate to count elements
|
|
81
|
+
const count = await el.evaluate(`(el) => el.querySelectorAll('.item').length`);
|
|
82
|
+
|
|
83
|
+
// DO
|
|
84
|
+
const count = await el.locator(".item").count();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// DON'T — evaluate to read scoped text
|
|
89
|
+
const text = await post.evaluate(
|
|
90
|
+
`(el) => el.querySelector('[data-view-name="foo"]')?.textContent`,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// DO
|
|
94
|
+
const text = await post.locator('[data-view-name="foo"]').textContent();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### When `page.evaluate()` Is Acceptable
|
|
98
|
+
|
|
99
|
+
Use `page.evaluate()` only for operations that have no Playwright locator equivalent:
|
|
100
|
+
|
|
101
|
+
1. Browser-native APIs: `getComputedStyle()`, `window.*` globals, `document.cookie`, scroll position
|
|
102
|
+
2. In-browser `fetch()` calls: making HTTP requests from the browser context
|
|
103
|
+
3. Parsing operations: using `DOMParser` to parse HTML/XML strings inside the browser
|
|
104
|
+
|
|
105
|
+
A quick test: if the evaluate body contains `querySelector`, `querySelectorAll`, `textContent`, `click()`, `getAttribute()`, or iterates DOM elements, it should be rewritten with Playwright locators.
|
|
106
|
+
|
|
107
|
+
When `page.evaluate()` is used for the acceptable cases above, keep the logic self-contained and return JSON-serializable values:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const data = (await page.evaluate(`(() => {
|
|
111
|
+
const style = getComputedStyle(document.documentElement);
|
|
112
|
+
return style.getPropertyValue('--brand-color');
|
|
113
|
+
})()`)) as string;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Do not rely on broad DOM querying inside `page.evaluate()` for production flows when Playwright locators can express the same interaction.
|
|
117
|
+
|
|
118
|
+
## Network Request Methods
|
|
119
|
+
|
|
120
|
+
When codifying network-based data extraction or form submissions, wrap `page.evaluate(() => fetch(...))` calls in typed methods on a shared API client class:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
class ApiClient {
|
|
124
|
+
constructor(private page: Page) {}
|
|
125
|
+
|
|
126
|
+
private async apiFetch(
|
|
127
|
+
url: string,
|
|
128
|
+
options?: { method?: string; body?: string },
|
|
129
|
+
): Promise<string> {
|
|
130
|
+
return await this.page.evaluate(
|
|
131
|
+
async ({ url, method, body }) => {
|
|
132
|
+
const init: RequestInit = { method: method ?? "GET" };
|
|
133
|
+
if (body) {
|
|
134
|
+
init.headers = {
|
|
135
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
136
|
+
};
|
|
137
|
+
init.body = body;
|
|
138
|
+
}
|
|
139
|
+
const response = await fetch(url, init);
|
|
140
|
+
if (!response.ok) throw new Error(`${response.status} for ${url}`);
|
|
141
|
+
return await response.text();
|
|
142
|
+
},
|
|
143
|
+
{ url, method: options?.method, body: options?.body },
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async fetchReferralList(status: string): Promise<Referral[]> {
|
|
148
|
+
const raw = await this.apiFetch(`/api/referrals?status=${status}`);
|
|
149
|
+
// parse and return typed data
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
One method per endpoint. No try/catch in API methods. Let errors propagate to the orchestrator. Parse XML/HTML inside `page.evaluate()` with `DOMParser`. Use string expressions for `page.evaluate()` to avoid DOM type errors.
|
|
155
|
+
|
|
156
|
+
## Comments
|
|
157
|
+
|
|
158
|
+
Add comments throughout generated code to explain what each logical block is doing. Comments should describe intent, not restate the code. Group related actions under a single comment rather than commenting every line.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// Log in with credentials
|
|
162
|
+
await page.locator("#username").fill(user);
|
|
163
|
+
await page.locator("#password").fill(pass);
|
|
164
|
+
await page.locator("#login").click();
|
|
165
|
+
|
|
166
|
+
// Extract author and content from each feed post
|
|
167
|
+
const posts = await page.locator(".post").all();
|
|
168
|
+
for (const post of posts) {
|
|
169
|
+
const name = await post.locator(".name").textContent();
|
|
170
|
+
const content = await post.locator(".content").textContent();
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Type Checking
|
|
175
|
+
|
|
176
|
+
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.
|