libretto 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -36
- package/dist/cli/cli.js +22 -97
- package/dist/cli/commands/browser.js +86 -59
- package/dist/cli/commands/execution.js +199 -86
- package/dist/cli/commands/init.js +30 -8
- 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 +9 -2
- package/dist/cli/core/api-snapshot-analyzer.js +15 -5
- package/dist/cli/core/browser.js +132 -29
- package/dist/cli/core/context.js +4 -1
- package/dist/cli/core/session-telemetry.js +5 -2
- 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 +10 -2
- package/dist/cli/framework/simple-cli.js +45 -25
- package/dist/cli/router.js +14 -21
- package/dist/cli/workers/run-integration-runtime.js +24 -5
- package/dist/cli/workers/run-integration-worker-protocol.js +3 -1
- package/dist/cli/workers/run-integration-worker.js +1 -4
- package/dist/index.d.ts +1 -2
- package/dist/index.js +7 -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 +6 -13
- 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/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 +7 -2
- 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 +19 -10
- package/dist/shared/visualization/highlight.js +9 -6
- package/dist/shared/workflow/workflow.d.ts +4 -5
- package/dist/shared/workflow/workflow.js +3 -5
- package/package.json +6 -2
- package/scripts/check-skills-sync.mjs +25 -0
- package/scripts/compare-eval-summary.mjs +47 -0
- package/scripts/postinstall.mjs +15 -15
- package/scripts/prepare-release.sh +97 -0
- package/scripts/skills-libretto.mjs +103 -0
- package/scripts/summarize-evals.mjs +135 -0
- package/scripts/sync-skills.mjs +12 -0
- package/skills/libretto/SKILL.md +113 -49
- package/skills/libretto/references/code-generation-rules.md +208 -0
- package/skills/libretto/references/configuration-file-reference.md +53 -0
- 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/execution.ts +233 -102
- package/src/cli/commands/init.ts +32 -9
- 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 +12 -3
- package/src/cli/core/api-snapshot-analyzer.ts +17 -6
- package/src/cli/core/browser.ts +178 -41
- package/src/cli/core/context.ts +7 -2
- package/src/cli/core/session-telemetry.ts +19 -8
- 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 +16 -3
- package/src/cli/framework/simple-cli.ts +144 -77
- package/src/cli/router.ts +13 -21
- package/src/cli/workers/run-integration-runtime.ts +36 -9
- package/src/cli/workers/run-integration-worker-protocol.ts +2 -0
- package/src/cli/workers/run-integration-worker.ts +1 -4
- package/src/index.ts +73 -66
- 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 +15 -18
- 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/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 +12 -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 +161 -148
- package/src/shared/visualization/highlight.ts +89 -86
- package/src/shared/visualization/index.ts +13 -13
- package/src/shared/workflow/workflow.ts +19 -25
- package/skills/libretto/references/reverse-engineering-network-requests.md +0 -39
- package/skills/libretto/references/user-action-log.md +0 -31
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
cpSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
rmSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { relative, resolve, join } from "node:path";
|
|
12
|
+
|
|
13
|
+
export const SKILL_DIRS = [
|
|
14
|
+
"skills/libretto",
|
|
15
|
+
".agents/skills/libretto",
|
|
16
|
+
".claude/skills/libretto",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function walkFiles(dir, baseDir = dir) {
|
|
20
|
+
const entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) =>
|
|
21
|
+
a.name.localeCompare(b.name),
|
|
22
|
+
);
|
|
23
|
+
const files = [];
|
|
24
|
+
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
const fullPath = join(dir, entry.name);
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
files.push(...walkFiles(fullPath, baseDir));
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (entry.isFile()) files.push(relative(baseDir, fullPath));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return files;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function syncSkillDir(sourceDir, destDir) {
|
|
38
|
+
rmSync(destDir, { recursive: true, force: true });
|
|
39
|
+
mkdirSync(destDir, { recursive: true });
|
|
40
|
+
cpSync(sourceDir, destDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function syncRepoSkills(repoRoot) {
|
|
44
|
+
const sourceDir = resolve(repoRoot, "skills/libretto");
|
|
45
|
+
for (const dir of SKILL_DIRS.slice(1)) {
|
|
46
|
+
syncSkillDir(sourceDir, resolve(repoRoot, dir));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function compareSkillDirs(repoRoot) {
|
|
51
|
+
const roots = SKILL_DIRS.map((dir) => ({
|
|
52
|
+
label: dir,
|
|
53
|
+
absPath: resolve(repoRoot, dir),
|
|
54
|
+
}));
|
|
55
|
+
const missing = roots.filter(({ absPath }) => !existsSync(absPath));
|
|
56
|
+
const mismatches = [];
|
|
57
|
+
|
|
58
|
+
if (missing.length > 0) {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
issues: missing.map(({ label }) => `missing directory: ${label}`),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const expectedFiles = walkFiles(roots[0].absPath);
|
|
66
|
+
const expectedFileSet = new Set(expectedFiles);
|
|
67
|
+
|
|
68
|
+
for (const root of roots.slice(1)) {
|
|
69
|
+
const actualFiles = walkFiles(root.absPath);
|
|
70
|
+
const actualFileSet = new Set(actualFiles);
|
|
71
|
+
|
|
72
|
+
for (const file of expectedFiles) {
|
|
73
|
+
if (!actualFileSet.has(file)) {
|
|
74
|
+
mismatches.push(`${root.label} is missing file: ${file}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const file of actualFiles) {
|
|
79
|
+
if (!expectedFileSet.has(file)) {
|
|
80
|
+
mismatches.push(`${root.label} has unexpected file: ${file}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const file of expectedFiles) {
|
|
86
|
+
const expectedContent = readFileSync(join(roots[0].absPath, file));
|
|
87
|
+
for (const root of roots.slice(1)) {
|
|
88
|
+
const targetPath = join(root.absPath, file);
|
|
89
|
+
if (!existsSync(targetPath)) continue;
|
|
90
|
+
const actualContent = readFileSync(targetPath);
|
|
91
|
+
if (!expectedContent.equals(actualContent)) {
|
|
92
|
+
mismatches.push(
|
|
93
|
+
`${root.label} differs from ${roots[0].label}: ${file}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
ok: mismatches.length === 0,
|
|
101
|
+
issues: mismatches,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { basename, join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
function usage() {
|
|
8
|
+
console.error(
|
|
9
|
+
"Usage: node scripts/summarize-evals.mjs <score-dir> <summary-json-path>",
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeFailureRecord(failure) {
|
|
14
|
+
return {
|
|
15
|
+
criterion: String(failure?.criterion ?? "").trim(),
|
|
16
|
+
reason: String(failure?.reason ?? "").trim(),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizeRecord(record) {
|
|
21
|
+
const failures = Array.isArray(record?.failures)
|
|
22
|
+
? record.failures
|
|
23
|
+
.map(normalizeFailureRecord)
|
|
24
|
+
.filter(
|
|
25
|
+
(failure) =>
|
|
26
|
+
failure.criterion.length > 0 && failure.reason.length > 0,
|
|
27
|
+
)
|
|
28
|
+
: [];
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
name: String(record?.name ?? "").trim(),
|
|
32
|
+
passed: Number(record?.passed ?? 0),
|
|
33
|
+
total: Number(record?.total ?? 0),
|
|
34
|
+
percent: Number(record?.percent ?? 0),
|
|
35
|
+
failures,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function loadScoreRecords(scoreDirArg) {
|
|
40
|
+
const scoreDir = resolve(scoreDirArg);
|
|
41
|
+
return readdirSync(scoreDir, { withFileTypes: true })
|
|
42
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
43
|
+
.map((entry) =>
|
|
44
|
+
JSON.parse(readFileSync(join(scoreDir, entry.name), "utf8")),
|
|
45
|
+
)
|
|
46
|
+
.map(normalizeRecord)
|
|
47
|
+
.sort((a, b) => String(a.name).localeCompare(String(b.name)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildSummary(records) {
|
|
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
|
+
);
|
|
59
|
+
const percent = total > 0 ? Number(((passed / total) * 100).toFixed(2)) : 0;
|
|
60
|
+
const failingRecords = records.filter((record) => record.failures.length > 0);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
generatedAt: new Date().toISOString(),
|
|
64
|
+
recordCount: records.length,
|
|
65
|
+
passed,
|
|
66
|
+
total,
|
|
67
|
+
percent,
|
|
68
|
+
failingRecordCount: failingRecords.length,
|
|
69
|
+
records,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function buildMarkdown(summary, summaryPathArg) {
|
|
74
|
+
const lines = [
|
|
75
|
+
"# Eval Summary",
|
|
76
|
+
"",
|
|
77
|
+
`- Overall score: \`${summary.percent}%\``,
|
|
78
|
+
`- Passed criteria: \`${summary.passed}/${summary.total}\``,
|
|
79
|
+
`- Recorded score entries: \`${summary.recordCount}\``,
|
|
80
|
+
`- Failed evals: \`${summary.failingRecordCount}\``,
|
|
81
|
+
`- Summary file: \`${basename(summaryPathArg)}\``,
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
if (summary.records.length > 0) {
|
|
85
|
+
lines.push("", "## Breakdown", "");
|
|
86
|
+
for (const record of summary.records) {
|
|
87
|
+
const status = record.failures.length > 0 ? "fail" : "pass";
|
|
88
|
+
lines.push(
|
|
89
|
+
`- ${status} \`${record.name}\`: \`${record.percent}%\` (${record.passed}/${record.total})`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (summary.failingRecordCount > 0) {
|
|
95
|
+
lines.push("", "## Failed Evals", "");
|
|
96
|
+
for (const record of summary.records.filter(
|
|
97
|
+
(candidate) => candidate.failures.length > 0,
|
|
98
|
+
)) {
|
|
99
|
+
lines.push(`### \`${record.name}\``);
|
|
100
|
+
lines.push("");
|
|
101
|
+
lines.push(
|
|
102
|
+
`- Score: \`${record.percent}%\` (${record.passed}/${record.total})`,
|
|
103
|
+
);
|
|
104
|
+
for (const failure of record.failures) {
|
|
105
|
+
lines.push(`- ${failure.criterion}: ${failure.reason}`);
|
|
106
|
+
}
|
|
107
|
+
lines.push("");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return `${lines.join("\n").trimEnd()}\n`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function main(argv) {
|
|
115
|
+
const [, , scoreDirArg, summaryPathArg] = argv;
|
|
116
|
+
|
|
117
|
+
if (!scoreDirArg || !summaryPathArg) {
|
|
118
|
+
usage();
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const summaryPath = resolve(summaryPathArg);
|
|
123
|
+
const records = loadScoreRecords(scoreDirArg);
|
|
124
|
+
const summary = buildSummary(records);
|
|
125
|
+
|
|
126
|
+
writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}\n`, "utf8");
|
|
127
|
+
process.stdout.write(buildMarkdown(summary, summaryPath));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (
|
|
131
|
+
process.argv[1] &&
|
|
132
|
+
resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
133
|
+
) {
|
|
134
|
+
main(process.argv);
|
|
135
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import { SKILL_DIRS, syncRepoSkills } from "./skills-libretto.mjs";
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const repoRoot = join(__dirname, "..");
|
|
10
|
+
|
|
11
|
+
syncRepoSkills(repoRoot);
|
|
12
|
+
console.log(`libretto: synced skill mirrors across ${SKILL_DIRS.join(", ")}`);
|
package/skills/libretto/SKILL.md
CHANGED
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: libretto
|
|
3
|
-
description: "Browser automation CLI for
|
|
3
|
+
description: "Browser automation CLI for building, maintaining, and running browser automation workflows by inspecting live pages and prototyping interactions."
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: saffron-health
|
|
7
|
-
version: "0.4.
|
|
7
|
+
version: "0.4.2"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## How Libretto Works
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
- Libretto is a CLI for exploring live websites and building or debugging reusable browser automation scripts.
|
|
13
|
+
- Use Libretto to inspect the real site first: open pages, observe state, inspect requests, and prototype interactions before writing code.
|
|
14
|
+
- Libretto work must end in script changes. Create or edit the workflow file instead of stopping at interactive exploration.
|
|
13
15
|
|
|
14
|
-
##
|
|
16
|
+
## Default Integration Approach
|
|
15
17
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- 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.
|
|
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.
|
|
20
21
|
|
|
21
22
|
## Setup
|
|
22
23
|
|
|
23
24
|
- Ask the user to set up snapshot analysis before relying on `snapshot` for page understanding.
|
|
24
|
-
- Use `npx libretto init` for first-time setup.
|
|
25
|
-
- If
|
|
25
|
+
- Use `npx libretto init` for first-time workspace setup.
|
|
26
|
+
- If credentials are already available, `npx libretto ai configure openai|anthropic|gemini|vertex` is usually enough.
|
|
26
27
|
|
|
27
|
-
## Rules
|
|
28
|
+
## Working Rules
|
|
28
29
|
|
|
29
30
|
- Announce which session you are using and what page you are on.
|
|
30
31
|
- Ask instead of guessing when it is unclear what to click, type, or submit.
|
|
31
|
-
-
|
|
32
|
+
- Read and follow guidelines in `references/code-generation-rules.md` before generating or editing production workflow code.
|
|
33
|
+
- After interactive exploration and code generation, test key logic with `exec`, then verify the workflow file with `run --headless`.
|
|
32
34
|
- Get explicit user confirmation before mutating actions or replaying network requests that may have side effects.
|
|
33
35
|
- Never run multiple `exec` commands at the same time.
|
|
34
36
|
- Keep the browser session open until the user says the session is done.
|
|
@@ -38,6 +40,7 @@ Use `npx libretto` to inspect live browser state, prototype interactions, and ru
|
|
|
38
40
|
### `open`
|
|
39
41
|
|
|
40
42
|
- Open a page before using `exec` or `snapshot`.
|
|
43
|
+
- Use `open` at the start of script authoring when you need live page state to decide how the workflow should work.
|
|
41
44
|
- Use headed mode when the user needs to log in or watch the workflow.
|
|
42
45
|
|
|
43
46
|
```bash
|
|
@@ -45,37 +48,83 @@ npx libretto open https://example.com --headed
|
|
|
45
48
|
npx libretto open https://example.com --headless --session debug-example
|
|
46
49
|
```
|
|
47
50
|
|
|
48
|
-
### `
|
|
51
|
+
### `connect`
|
|
49
52
|
|
|
50
|
-
- Use `
|
|
51
|
-
-
|
|
53
|
+
- 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.
|
|
54
|
+
- After connecting, `exec`, `snapshot`, `pages`, and all other session commands work normally.
|
|
55
|
+
- Libretto does not manage the connected process's lifecycle. `close` clears the session but does not terminate the remote process.
|
|
52
56
|
|
|
53
57
|
```bash
|
|
54
|
-
npx libretto
|
|
55
|
-
npx libretto
|
|
56
|
-
npx libretto exec --visualize "await page.locator('button:has-text(\"Continue\")').click()"
|
|
58
|
+
npx libretto connect http://127.0.0.1:9222 --session my-session
|
|
59
|
+
npx libretto connect http://127.0.0.1:9223 --session another-session
|
|
57
60
|
```
|
|
58
61
|
|
|
59
62
|
### `snapshot`
|
|
60
63
|
|
|
61
64
|
- Use `snapshot` as the primary page observation tool.
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
- When
|
|
65
|
+
- Always provide both `--objective` and `--context`.
|
|
66
|
+
- Use it before guessing at selectors, after workflow failures, and whenever the visible page state is unclear.
|
|
67
|
+
- When analysis is involved, expect it to take time. Use a timeout of at least 2 minutes for shell-wrapped calls.
|
|
65
68
|
|
|
66
69
|
```bash
|
|
67
|
-
npx libretto snapshot
|
|
68
70
|
npx libretto snapshot \
|
|
69
71
|
--objective "Find the sign-in form and submit button" \
|
|
70
72
|
--context "I just opened the login page and need the email field, password field, and submit button."
|
|
71
73
|
npx libretto snapshot \
|
|
74
|
+
--session debug-example \
|
|
75
|
+
--page <page-id> \
|
|
72
76
|
--objective "Explain why the table is empty" \
|
|
73
|
-
--context "I opened the referrals page and expected rows
|
|
77
|
+
--context "I opened the referrals page, applied filters, and expected rows to appear."
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `exec`
|
|
81
|
+
|
|
82
|
+
- Use `exec` for focused inspection and short-lived interaction experiments.
|
|
83
|
+
- Use `exec` to validate selectors, inspect data, or prototype a step before you encode it in the workflow file.
|
|
84
|
+
- Let failures throw. Do not hide `exec` failures with `try/catch` or `.catch()`.
|
|
85
|
+
- Do not run multiple `exec` commands in parallel.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx libretto exec "return await page.url()"
|
|
89
|
+
npx libretto exec "return await page.locator('button').count()"
|
|
90
|
+
npx libretto exec --visualize "await page.locator('button:has-text(\"Continue\")').click()"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `pages`
|
|
94
|
+
|
|
95
|
+
- Use `pages` when a popup, new tab, or second page appears.
|
|
96
|
+
- If `exec`, `snapshot`, `network`, or `actions` complains about multiple pages, list page ids first and then pass `--page`.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npx libretto pages --session debug-example
|
|
100
|
+
npx libretto exec --session debug-example --page <page-id> "return await page.url()"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `network`
|
|
104
|
+
|
|
105
|
+
- Use `network` to inspect the requests the page already made.
|
|
106
|
+
- Prefer this when discovering how a site loads data or when validating whether a network-first approach is workable.
|
|
107
|
+
- Filter aggressively by method, URL pattern, and page when the log is noisy.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npx libretto network --session debug-example --last 20
|
|
111
|
+
npx libretto network --session debug-example --method POST --filter 'referral|patient'
|
|
112
|
+
npx libretto network --session debug-example --page <page-id>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `actions`
|
|
116
|
+
|
|
117
|
+
- Use `actions` when you need a quick record of recent user or agent interactions in the current session.
|
|
118
|
+
- Keep it lightweight. It is a helper for orientation, not the main integration-building workflow.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npx libretto actions --session debug-example --last 20
|
|
122
|
+
npx libretto actions --session debug-example --action click --source user
|
|
74
123
|
```
|
|
75
124
|
|
|
76
125
|
### `run`
|
|
77
126
|
|
|
78
|
-
- Use `run` to
|
|
127
|
+
- Use `run` to verify a workflow file after creating it or editing it.
|
|
79
128
|
- If the workflow fails, Libretto keeps the browser open. Inspect the failed state with `snapshot` and `exec` before editing code.
|
|
80
129
|
- If the workflow pauses, resume it with `npx libretto resume --session <name>`.
|
|
81
130
|
- Re-run the same workflow after each fix to verify the browser behavior end to end.
|
|
@@ -86,6 +135,34 @@ npx libretto run ./integration.ts main --params '{"status":"open"}'
|
|
|
86
135
|
npx libretto run ./integration.ts main --auth-profile app.example.com --headed
|
|
87
136
|
```
|
|
88
137
|
|
|
138
|
+
### `resume`
|
|
139
|
+
|
|
140
|
+
- Workflows pause by calling `await pause()` in the workflow file.
|
|
141
|
+
- Use `resume` when a workflow hit `await pause()`.
|
|
142
|
+
- Keep resuming the same session until the workflow completes or pauses again.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx libretto resume --session debug-example
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### `save`
|
|
149
|
+
|
|
150
|
+
- Use `save` when the user logs in manually and wants to reuse that authenticated browser state later.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npx libretto save app.example.com
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `close`
|
|
157
|
+
|
|
158
|
+
- Use `close` only when the user is done with the session.
|
|
159
|
+
- `close --all` is available for workspace cleanup.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npx libretto close --session debug-example
|
|
163
|
+
npx libretto close --all
|
|
164
|
+
```
|
|
165
|
+
|
|
89
166
|
## Examples
|
|
90
167
|
|
|
91
168
|
### Building new browser automation workflows
|
|
@@ -95,31 +172,16 @@ npx libretto run ./integration.ts main --auth-profile app.example.com --headed
|
|
|
95
172
|
```text
|
|
96
173
|
<example>
|
|
97
174
|
[Context: The user wants to build a new browser workflow and does not yet know the page structure]
|
|
98
|
-
Assistant:
|
|
175
|
+
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.
|
|
99
176
|
Assistant: [Runs `npx libretto open https://target.example.com --headed`]
|
|
100
|
-
Assistant:
|
|
101
|
-
Assistant: [Runs `npx libretto snapshot --objective "Find the next required action" --context "We are starting the workflow from the landing page."`]
|
|
102
|
-
Assistant:
|
|
103
|
-
Assistant: [
|
|
177
|
+
Assistant: [Reads `references/site-security-review.md` before choosing between passive network inspection, direct browser fetch calls, and Playwright-first automation]
|
|
178
|
+
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."`]
|
|
179
|
+
Assistant: [Uses `network`, `snapshot`, and `exec` as needed to understand the site and decide the implementation path]
|
|
180
|
+
Assistant: [Reads `references/code-generation-rules.md` before writing production workflow code]
|
|
104
181
|
Assistant: I found the working path. I'll now update the workflow file outside Libretto and verify it with `npx libretto run ...`.
|
|
105
182
|
</example>
|
|
106
183
|
```
|
|
107
184
|
|
|
108
|
-
#### Network requests
|
|
109
|
-
|
|
110
|
-
```text
|
|
111
|
-
<example>
|
|
112
|
-
[Context: The user wants to build an integration using network requests]
|
|
113
|
-
Assistant: [Reads `references/reverse-engineering-network-requests.md`]
|
|
114
|
-
Assistant: Let me open the page in headed mode. Perform the workflow and I'll use the network log to recreate it.
|
|
115
|
-
Assistant: [Runs `npx libretto open https://target.example.com --headed`]
|
|
116
|
-
[User performs workflow]
|
|
117
|
-
User: I've completed the workflow
|
|
118
|
-
Assistant: [Runs `npx libretto network --method POST --last 20`]
|
|
119
|
-
Assistant: I found the relevant requests. I'll recreate the workflow from those requests, then test the resulting script with `npx libretto run ...`.
|
|
120
|
-
</example>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
185
|
### Debugging existing workflows
|
|
124
186
|
|
|
125
187
|
```text
|
|
@@ -130,13 +192,15 @@ Assistant: [Runs `npx libretto run ./integration.ts main --session debug-flow --
|
|
|
130
192
|
Assistant: The workflow failed and Libretto kept the browser open. I'll inspect the page state before changing code.
|
|
131
193
|
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."`]
|
|
132
194
|
Assistant: [Runs `npx libretto exec --session debug-flow "...focused inspection or prototype..."`]
|
|
195
|
+
Assistant: [Reads `references/code-generation-rules.md` before patching the workflow file]
|
|
133
196
|
Assistant: I found the issue. I'll patch the workflow code, then rerun `npx libretto run ...` to verify the fix.
|
|
134
197
|
</example>
|
|
135
198
|
```
|
|
136
199
|
|
|
137
200
|
## References
|
|
138
201
|
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
202
|
+
- Read `references/configuration-file-reference.md` when you need to inspect or change `.libretto/config.json` for snapshot model selection or viewport defaults.
|
|
203
|
+
- 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.
|
|
204
|
+
- Read `references/code-generation-rules.md` before writing or editing production workflow files.
|
|
205
|
+
- Read `references/auth-profiles.md` when the site requires login and the simplest path is to save local browser state.
|
|
206
|
+
- Read `references/pages-and-page-targeting.md` when a session has multiple open pages or you need `--page`.
|