libretto 0.6.16-experimental-zod.0 → 0.6.16
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 +13 -9
- package/README.template.md +13 -9
- package/dist/cli/cli.js +14 -0
- package/dist/cli/commands/browser.js +5 -6
- package/dist/cli/commands/execution.js +9 -10
- package/dist/cli/commands/experiments.js +4 -5
- package/dist/cli/commands/setup.js +1 -2
- package/dist/cli/commands/snapshot.js +2 -3
- package/dist/cli/core/browser.js +16 -17
- package/dist/cli/core/daemon/daemon.js +2 -3
- package/dist/cli/core/daemon/snapshot.js +1 -2
- package/dist/cli/core/session.js +4 -5
- package/dist/cli/core/skill-version.js +1 -2
- package/dist/cli/router.js +1 -2
- package/dist/shared/debug/pause.js +2 -3
- package/dist/shared/package-manager.d.ts +1 -2
- package/dist/shared/package-manager.js +1 -18
- package/docs/experiments.md +4 -4
- package/package.json +1 -1
- package/skills/libretto/SKILL.md +38 -38
- package/skills/libretto/references/auth-profiles.md +3 -3
- package/skills/libretto/references/code-generation-rules.md +0 -1
- package/skills/libretto/references/configuration-file-reference.md +9 -9
- package/skills/libretto/references/pages-and-page-targeting.md +3 -3
- package/skills/libretto-readonly/SKILL.md +8 -8
- package/src/cli/cli.ts +17 -0
- package/src/cli/commands/browser.ts +4 -5
- package/src/cli/commands/execution.ts +7 -8
- package/src/cli/commands/experiments.ts +4 -5
- package/src/cli/commands/setup.ts +1 -2
- package/src/cli/commands/snapshot.ts +2 -3
- package/src/cli/core/browser.ts +17 -18
- package/src/cli/core/daemon/daemon.ts +2 -3
- package/src/cli/core/daemon/snapshot.ts +1 -2
- package/src/cli/core/session.ts +4 -5
- package/src/cli/core/skill-version.ts +1 -2
- package/src/cli/router.ts +1 -2
- package/src/shared/debug/pause.ts +2 -3
- package/src/shared/package-manager.ts +0 -21
package/README.md
CHANGED
|
@@ -28,13 +28,17 @@ https://github.com/user-attachments/assets/9b9a0ab3-5133-4b20-b3be-459943349d18
|
|
|
28
28
|
## Installation
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
+
# Install the Libretto command once. Requires Node.js and npm.
|
|
32
|
+
curl -fsSL https://libretto.sh/install.sh | bash
|
|
33
|
+
|
|
34
|
+
# Add Libretto to your project
|
|
31
35
|
npm install libretto
|
|
32
36
|
|
|
33
37
|
# First-time onboarding: install skills and download Chromium
|
|
34
|
-
|
|
38
|
+
libretto setup
|
|
35
39
|
|
|
36
40
|
# Check workspace readiness at any time
|
|
37
|
-
|
|
41
|
+
libretto status
|
|
38
42
|
```
|
|
39
43
|
|
|
40
44
|
`setup` creates the `.libretto/` directory, installs agent skills, and downloads Chromium unless you pass `--skip-browsers`.
|
|
@@ -72,17 +76,17 @@ Agents can use Libretto to reproduce the failure, pause the workflow at any poin
|
|
|
72
76
|
You can also use Libretto directly from the command line. All commands accept `--session <name>` to target a specific session.
|
|
73
77
|
|
|
74
78
|
```bash
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
libretto open <url> # launch browser and open a URL
|
|
80
|
+
libretto run ./integration.ts --headless # run a workflow and close on success
|
|
81
|
+
libretto run ./integration.ts --headless --stay-open-on-success # keep a successful run inspectable
|
|
82
|
+
libretto snapshot --session <name> # capture a screenshot and compact accessibility tree
|
|
83
|
+
libretto exec "<code>" # execute Playwright TypeScript against the open page
|
|
84
|
+
libretto close # close the browser
|
|
81
85
|
```
|
|
82
86
|
|
|
83
87
|
`run` sessions are inspectable through the same daemon-backed commands as `open` sessions. Successful runs close the browser by default; pass `--stay-open-on-success` to keep the browser open for `pages`, `snapshot`, and `exec`. Failed or paused workflows keep the browser open so you can inspect the exact page state before fixing or resuming the workflow.
|
|
84
88
|
|
|
85
|
-
Run `
|
|
89
|
+
Run `libretto help` for the full list of commands.
|
|
86
90
|
|
|
87
91
|
## Configuration
|
|
88
92
|
|
package/README.template.md
CHANGED
|
@@ -26,13 +26,17 @@ https://github.com/user-attachments/assets/9b9a0ab3-5133-4b20-b3be-459943349d18
|
|
|
26
26
|
## Installation
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
+
# Install the Libretto command once. Requires Node.js and npm.
|
|
30
|
+
curl -fsSL https://libretto.sh/install.sh | bash
|
|
31
|
+
|
|
32
|
+
# Add Libretto to your project
|
|
29
33
|
npm install libretto
|
|
30
34
|
|
|
31
35
|
# First-time onboarding: install skills and download Chromium
|
|
32
|
-
|
|
36
|
+
libretto setup
|
|
33
37
|
|
|
34
38
|
# Check workspace readiness at any time
|
|
35
|
-
|
|
39
|
+
libretto status
|
|
36
40
|
```
|
|
37
41
|
|
|
38
42
|
`setup` creates the `.libretto/` directory, installs agent skills, and downloads Chromium unless you pass `--skip-browsers`.
|
|
@@ -70,17 +74,17 @@ Agents can use Libretto to reproduce the failure, pause the workflow at any poin
|
|
|
70
74
|
You can also use Libretto directly from the command line. All commands accept `--session <name>` to target a specific session.
|
|
71
75
|
|
|
72
76
|
```bash
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
libretto open <url> # launch browser and open a URL
|
|
78
|
+
libretto run ./integration.ts --headless # run a workflow and close on success
|
|
79
|
+
libretto run ./integration.ts --headless --stay-open-on-success # keep a successful run inspectable
|
|
80
|
+
libretto snapshot --session <name> # capture a screenshot and compact accessibility tree
|
|
81
|
+
libretto exec "<code>" # execute Playwright TypeScript against the open page
|
|
82
|
+
libretto close # close the browser
|
|
79
83
|
```
|
|
80
84
|
|
|
81
85
|
`run` sessions are inspectable through the same daemon-backed commands as `open` sessions. Successful runs close the browser by default; pass `--stay-open-on-success` to keep the browser open for `pages`, `snapshot`, and `exec`. Failed or paused workflows keep the browser open so you can inspect the exact page state before fixing or resuming the workflow.
|
|
82
86
|
|
|
83
|
-
Run `
|
|
87
|
+
Run `libretto help` for the full list of commands.
|
|
84
88
|
|
|
85
89
|
## Configuration
|
|
86
90
|
|
package/dist/cli/cli.js
CHANGED
|
@@ -14,6 +14,19 @@ Docs (agent-friendly): https://libretto.sh/docs
|
|
|
14
14
|
function printSetupAudit() {
|
|
15
15
|
warnIfInstalledSkillOutOfDate();
|
|
16
16
|
}
|
|
17
|
+
function isPackageManagerExec(env = process.env) {
|
|
18
|
+
return env.npm_command === "exec";
|
|
19
|
+
}
|
|
20
|
+
function warnIfPackageManagerExec() {
|
|
21
|
+
if (!isPackageManagerExec()) return;
|
|
22
|
+
console.error(
|
|
23
|
+
[
|
|
24
|
+
"Warning: running Libretto through a package manager is deprecated and will be removed in a future release.",
|
|
25
|
+
"Install the native command instead:",
|
|
26
|
+
" curl -fsSL https://libretto.sh/install.sh | bash"
|
|
27
|
+
].join("\n")
|
|
28
|
+
);
|
|
29
|
+
}
|
|
17
30
|
function isRootHelpRequest(rawArgs) {
|
|
18
31
|
if (rawArgs.length === 0) return true;
|
|
19
32
|
if (rawArgs[0] === "--help" || rawArgs[0] === "-h") return true;
|
|
@@ -23,6 +36,7 @@ async function runLibrettoCLI() {
|
|
|
23
36
|
const rawArgs = process.argv.slice(2);
|
|
24
37
|
let exitCode = 0;
|
|
25
38
|
loadEnv();
|
|
39
|
+
warnIfPackageManagerExec();
|
|
26
40
|
ensureLibrettoSetup();
|
|
27
41
|
const app = createCLIApp();
|
|
28
42
|
try {
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
import { resolveProviderName } from "../core/providers/index.js";
|
|
13
13
|
import { readLibrettoConfig } from "../core/config.js";
|
|
14
14
|
import { createLoggerForSession } from "../core/context.js";
|
|
15
|
-
import { librettoCommand } from "../../shared/package-manager.js";
|
|
16
15
|
import {
|
|
17
16
|
assertSessionAvailableForStart,
|
|
18
17
|
setSessionMode,
|
|
@@ -134,7 +133,7 @@ const connectInput = SimpleCLI.input({
|
|
|
134
133
|
}
|
|
135
134
|
}).refine(
|
|
136
135
|
(input) => Boolean(input.cdpUrl),
|
|
137
|
-
`Usage:
|
|
136
|
+
`Usage: libretto connect <cdp-url> [--read-only|--write-access] --session <name>`
|
|
138
137
|
).refine(
|
|
139
138
|
(input) => !(input.readOnly && input.writeAccess),
|
|
140
139
|
"Cannot pass both --read-only and --write-access."
|
|
@@ -162,7 +161,7 @@ const saveInput = SimpleCLI.input({
|
|
|
162
161
|
}
|
|
163
162
|
}).refine(
|
|
164
163
|
(input) => Boolean(input.urlOrDomain),
|
|
165
|
-
`Usage:
|
|
164
|
+
`Usage: libretto save <url|domain> --session <name>`
|
|
166
165
|
);
|
|
167
166
|
const saveCommand = SimpleCLI.command({
|
|
168
167
|
description: "Save current browser session"
|
|
@@ -217,14 +216,14 @@ const closeInput = SimpleCLI.input({
|
|
|
217
216
|
}
|
|
218
217
|
}).refine(
|
|
219
218
|
(input) => input.all || input.session,
|
|
220
|
-
`Usage:
|
|
221
|
-
Usage:
|
|
219
|
+
`Usage: libretto close <session>
|
|
220
|
+
Usage: libretto close --all [--force]`
|
|
222
221
|
);
|
|
223
222
|
const closeCommand = SimpleCLI.command({
|
|
224
223
|
description: "Close the browser"
|
|
225
224
|
}).input(closeInput).handle(async ({ input }) => {
|
|
226
225
|
if (input.force && !input.all) {
|
|
227
|
-
throw new Error(`Usage:
|
|
226
|
+
throw new Error(`Usage: libretto close --all [--force]`);
|
|
228
227
|
}
|
|
229
228
|
if (input.all) {
|
|
230
229
|
const logger2 = createLoggerForSession("cli");
|
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
} from "../core/session.js";
|
|
26
26
|
import { warnIfInstalledSkillOutOfDate } from "../core/skill-version.js";
|
|
27
27
|
import { readLibrettoConfig } from "../core/config.js";
|
|
28
|
-
import { librettoCommand } from "../../shared/package-manager.js";
|
|
29
28
|
import { renderSnapshotDiff } from "../../shared/snapshot/diff-snapshots.js";
|
|
30
29
|
import {
|
|
31
30
|
getProviderStartupTimeoutMs,
|
|
@@ -288,7 +287,7 @@ async function stopExistingFailedRunSession(session, logger) {
|
|
|
288
287
|
pid: existingState.pid
|
|
289
288
|
});
|
|
290
289
|
throw new Error(
|
|
291
|
-
`Existing failed workflow process for session "${session}" (pid ${existingState.pid}) is still running. Close it with:
|
|
290
|
+
`Existing failed workflow process for session "${session}" (pid ${existingState.pid}) is still running. Close it with: libretto close --session ${session}`
|
|
292
291
|
);
|
|
293
292
|
}
|
|
294
293
|
clearSessionState(session, logger);
|
|
@@ -373,7 +372,7 @@ async function runResume(session, logger, sessionState) {
|
|
|
373
372
|
const status = await client.getWorkflowStatus();
|
|
374
373
|
if (status.state !== "paused") {
|
|
375
374
|
throw new Error(
|
|
376
|
-
`Session "${session}" is not paused. Run "
|
|
375
|
+
`Session "${session}" is not paused. Run "libretto run ... --session ${session}" and call pause("${session}") first.`
|
|
377
376
|
);
|
|
378
377
|
}
|
|
379
378
|
await client.resumeWorkflow();
|
|
@@ -427,9 +426,9 @@ async function runIntegrationFromFile(args, logger) {
|
|
|
427
426
|
`Local auth profile not found for domain "${normalizedDomain}".`,
|
|
428
427
|
`Expected profile file: ${profilePath}`,
|
|
429
428
|
"To create it:",
|
|
430
|
-
` 1.
|
|
429
|
+
` 1. libretto open https://${normalizedDomain} --headed --session ${args.session}`,
|
|
431
430
|
" 2. Log in manually in the browser window.",
|
|
432
|
-
` 3.
|
|
431
|
+
` 3. libretto save ${normalizedDomain} --session ${args.session}`
|
|
433
432
|
].join("\n")
|
|
434
433
|
);
|
|
435
434
|
}
|
|
@@ -546,8 +545,8 @@ const execInput = SimpleCLI.input({
|
|
|
546
545
|
}
|
|
547
546
|
}).refine(
|
|
548
547
|
(input) => input.code !== void 0,
|
|
549
|
-
`Usage:
|
|
550
|
-
echo '<code>' |
|
|
548
|
+
`Usage: libretto exec <code|-> [--session <name>] [--visualize]
|
|
549
|
+
echo '<code>' | libretto exec - [--session <name>] [--visualize]`
|
|
551
550
|
);
|
|
552
551
|
const execCommand = SimpleCLI.command({
|
|
553
552
|
description: "Execute Playwright TypeScript code"
|
|
@@ -583,8 +582,8 @@ const readonlyExecInput = SimpleCLI.input({
|
|
|
583
582
|
}
|
|
584
583
|
}).refine(
|
|
585
584
|
(input) => input.code !== void 0,
|
|
586
|
-
`Usage:
|
|
587
|
-
echo '<code>' |
|
|
585
|
+
`Usage: libretto readonly-exec <code|-> [--session <name>] [--page <id>]
|
|
586
|
+
echo '<code>' | libretto readonly-exec - [--session <name>] [--page <id>]`
|
|
588
587
|
);
|
|
589
588
|
const readonlyExecCommand = SimpleCLI.command({
|
|
590
589
|
description: "Execute read-only Playwright inspection code"
|
|
@@ -601,7 +600,7 @@ const readonlyExecCommand = SimpleCLI.command({
|
|
|
601
600
|
mode: "readonly-exec"
|
|
602
601
|
});
|
|
603
602
|
});
|
|
604
|
-
const runUsage = `Usage:
|
|
603
|
+
const runUsage = `Usage: libretto run <integrationFile> [--params <json> | --params-file <path>] [--tsconfig <path>] [--headed|--headless] [--read-only|--write-access] [--no-visualize] [--stay-open-on-success] [--viewport WxH] [--provider <provider>]`;
|
|
605
604
|
const runInput = SimpleCLI.input({
|
|
606
605
|
positionals: [
|
|
607
606
|
SimpleCLI.positional("integrationFile", z.string().optional(), {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { librettoCommand } from "../../shared/package-manager.js";
|
|
3
2
|
import {
|
|
4
3
|
EXPERIMENTS,
|
|
5
4
|
isExperimentName,
|
|
@@ -10,10 +9,10 @@ import { SimpleCLI } from "affordance";
|
|
|
10
9
|
const experimentNames = Object.keys(EXPERIMENTS);
|
|
11
10
|
const experimentsUsage = [
|
|
12
11
|
"Usage:",
|
|
13
|
-
`
|
|
14
|
-
`
|
|
15
|
-
`
|
|
16
|
-
`
|
|
12
|
+
` libretto experiments`,
|
|
13
|
+
` libretto experiments describe <experiment>`,
|
|
14
|
+
` libretto experiments enable <experiment>`,
|
|
15
|
+
` libretto experiments disable <experiment>`
|
|
17
16
|
].join("\n");
|
|
18
17
|
const experimentsInput = SimpleCLI.input({
|
|
19
18
|
positionals: [
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
LIBRETTO_CONFIG_PATH,
|
|
8
8
|
REPO_ROOT
|
|
9
9
|
} from "../core/context.js";
|
|
10
|
-
import { librettoCommand } from "../../shared/package-manager.js";
|
|
11
10
|
import { SimpleCLI } from "affordance";
|
|
12
11
|
function installBrowsers() {
|
|
13
12
|
console.log("Installing Playwright Chromium...");
|
|
@@ -47,7 +46,7 @@ function copySkills() {
|
|
|
47
46
|
"\n\u26A0\uFE0F No .agents/ or .claude/ directory found. Libretto skills were not installed."
|
|
48
47
|
);
|
|
49
48
|
console.log(
|
|
50
|
-
` Create one of these directories in your repo root and rerun
|
|
49
|
+
` Create one of these directories in your repo root and rerun \`libretto setup\` to install skills:`
|
|
51
50
|
);
|
|
52
51
|
console.log(` mkdir ${join(REPO_ROOT, ".claude")}`);
|
|
53
52
|
return;
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
withRequiredSession
|
|
8
8
|
} from "./shared.js";
|
|
9
9
|
import { DaemonClient } from "../core/daemon/ipc.js";
|
|
10
|
-
import { librettoCommand } from "../../shared/package-manager.js";
|
|
11
10
|
import { renderSnapshot } from "../../shared/snapshot/render-snapshot.js";
|
|
12
11
|
const FALLBACK_SNAPSHOT_VIEWPORT = { width: 1280, height: 800 };
|
|
13
12
|
function isZeroViewport(value) {
|
|
@@ -67,7 +66,7 @@ async function forceSnapshotViewport(page, viewport, logger, session, pageId, re
|
|
|
67
66
|
async function runCompactSnapshot(args) {
|
|
68
67
|
if (!args.daemonSocketPath) {
|
|
69
68
|
throw new Error(
|
|
70
|
-
`Session "${args.session}" has no daemon socket. The browser daemon may have crashed. Close and reopen the session:
|
|
69
|
+
`Session "${args.session}" has no daemon socket. The browser daemon may have crashed. Close and reopen the session: libretto close --session ${args.session}`
|
|
71
70
|
);
|
|
72
71
|
}
|
|
73
72
|
args.logger.info("compact-snapshot-via-daemon", {
|
|
@@ -88,7 +87,7 @@ async function runCompactSnapshot(args) {
|
|
|
88
87
|
console.log(`Screenshot at ${result.pngPath}`);
|
|
89
88
|
console.log(renderSnapshot(result.snapshot, args.ref));
|
|
90
89
|
console.log(
|
|
91
|
-
`Hint: Use
|
|
90
|
+
`Hint: Use libretto snapshot <ref> --session ${args.session} to inspect a subtree.`
|
|
92
91
|
);
|
|
93
92
|
}
|
|
94
93
|
const snapshotInput = SimpleCLI.input({
|
package/dist/cli/core/browser.js
CHANGED
|
@@ -8,7 +8,6 @@ import { createServer } from "node:net";
|
|
|
8
8
|
import { isWindowsNamedPipePath } from "../../shared/ipc/socket-transport.js";
|
|
9
9
|
import { getSessionProviderClosePath, PROFILES_DIR } from "./context.js";
|
|
10
10
|
import { readLibrettoConfig } from "./config.js";
|
|
11
|
-
import { librettoCommand } from "../../shared/package-manager.js";
|
|
12
11
|
import {
|
|
13
12
|
getCloudProviderApi,
|
|
14
13
|
getProviderStartupTimeoutMs
|
|
@@ -161,13 +160,13 @@ async function connect(session, logger, timeoutMs = 1e4, options) {
|
|
|
161
160
|
});
|
|
162
161
|
if (state.provider) {
|
|
163
162
|
throw new Error(
|
|
164
|
-
`Could not connect to ${state.provider.name} session for "${session}" at ${endpoint}. The remote session may still be active. Try again, or close with:
|
|
163
|
+
`Could not connect to ${state.provider.name} session for "${session}" at ${endpoint}. The remote session may still be active. Try again, or close with: libretto close --session ${session}`
|
|
165
164
|
);
|
|
166
165
|
}
|
|
167
166
|
if (state.pid == null || !isPidRunning(state.pid)) {
|
|
168
167
|
clearSessionState(session, logger);
|
|
169
168
|
throw new Error(
|
|
170
|
-
`No browser running for session "${session}". Run '
|
|
169
|
+
`No browser running for session "${session}". Run 'libretto open <url> --session ${session}' first.`
|
|
171
170
|
);
|
|
172
171
|
}
|
|
173
172
|
throw new Error(
|
|
@@ -197,14 +196,14 @@ async function connect(session, logger, timeoutMs = 1e4, options) {
|
|
|
197
196
|
}
|
|
198
197
|
if (options?.requireSinglePage && !options.pageId && pages.length > 1) {
|
|
199
198
|
throw new Error(
|
|
200
|
-
`Multiple pages are open in session "${session}". Pass --page <id> to target a page (run "
|
|
199
|
+
`Multiple pages are open in session "${session}". Pass --page <id> to target a page (run "libretto pages --session ${session}" to list ids).`
|
|
201
200
|
);
|
|
202
201
|
}
|
|
203
202
|
const pageRefs = await resolvePageReferences(pages);
|
|
204
203
|
const pageRef = options?.pageId ? pageRefs.find((ref) => ref.id === options.pageId) ?? null : pageRefs[pageRefs.length - 1];
|
|
205
204
|
if (!pageRef) {
|
|
206
205
|
throw new Error(
|
|
207
|
-
`Page "${options?.pageId}" was not found in session "${session}". Run "
|
|
206
|
+
`Page "${options?.pageId}" was not found in session "${session}". Run "libretto pages --session ${session}" to list ids.`
|
|
208
207
|
);
|
|
209
208
|
}
|
|
210
209
|
const page = pageRef.page;
|
|
@@ -237,7 +236,7 @@ async function runPages(session, logger) {
|
|
|
237
236
|
let pageSummaries;
|
|
238
237
|
if (!state.daemonSocketPath) {
|
|
239
238
|
throw new Error(
|
|
240
|
-
`Session "${session}" has no daemon socket. The browser daemon may have crashed. Close and reopen the session:
|
|
239
|
+
`Session "${session}" has no daemon socket. The browser daemon may have crashed. Close and reopen the session: libretto close --session ${session}`
|
|
241
240
|
);
|
|
242
241
|
}
|
|
243
242
|
const client = await DaemonClient.connect(state.daemonSocketPath);
|
|
@@ -310,7 +309,7 @@ async function runOpen(rawUrl, headed, session, logger, options) {
|
|
|
310
309
|
const authProfilePath = getProfilePath(authDomain);
|
|
311
310
|
if (!existsSync(authProfilePath)) {
|
|
312
311
|
throw new Error(
|
|
313
|
-
`No saved auth profile for "${authDomain}". Save one first:
|
|
312
|
+
`No saved auth profile for "${authDomain}". Save one first: libretto open https://${authDomain} --headed --session <name>, log in, then run: libretto save ${authDomain} --session <name>`
|
|
314
313
|
);
|
|
315
314
|
}
|
|
316
315
|
}
|
|
@@ -565,7 +564,7 @@ async function runClose(session, logger) {
|
|
|
565
564
|
} else {
|
|
566
565
|
writeSessionState({ ...state, status: "cleanup-failed" }, logger);
|
|
567
566
|
throw new Error(
|
|
568
|
-
`Failed to confirm remote ${state.provider.name} session cleanup for session "${session}". State preserved with status "cleanup-failed". Retry with:
|
|
567
|
+
`Failed to confirm remote ${state.provider.name} session cleanup for session "${session}". State preserved with status "cleanup-failed". Retry with: libretto close --session ${session}`
|
|
569
568
|
);
|
|
570
569
|
}
|
|
571
570
|
} else {
|
|
@@ -623,7 +622,7 @@ function formatDaemonCloseFailure(session, providerName, error) {
|
|
|
623
622
|
const message = error instanceof Error ? error.message : String(error);
|
|
624
623
|
const cleanupWarning = providerName ? ` State preserved with status "cleanup-failed" because remote ${providerName} cleanup could not be confirmed.` : " State preserved so you can retry or inspect the session.";
|
|
625
624
|
return new Error(
|
|
626
|
-
`Failed to close session "${session}" gracefully over daemon IPC: ${message}.${cleanupWarning} Retry with:
|
|
625
|
+
`Failed to close session "${session}" gracefully over daemon IPC: ${message}.${cleanupWarning} Retry with: libretto close --session ${session}`
|
|
627
626
|
);
|
|
628
627
|
}
|
|
629
628
|
function waitForCloseSignalWindow(ms) {
|
|
@@ -667,7 +666,7 @@ async function closeProviderSessionDirectly(session, providerState, logger) {
|
|
|
667
666
|
error
|
|
668
667
|
});
|
|
669
668
|
throw new Error(
|
|
670
|
-
`Failed to close remote ${providerState.name} session "${providerState.sessionId}" for session "${session}". State preserved with status "cleanup-failed". Retry with:
|
|
669
|
+
`Failed to close remote ${providerState.name} session "${providerState.sessionId}" for session "${session}". State preserved with status "cleanup-failed". Retry with: libretto close --session ${session}`
|
|
671
670
|
);
|
|
672
671
|
}
|
|
673
672
|
}
|
|
@@ -841,7 +840,7 @@ async function runCloseAll(logger, options) {
|
|
|
841
840
|
[
|
|
842
841
|
`Failed to close ${failedSessions.length} session(s) gracefully: ${formatSessionList(failedSessions)}.`,
|
|
843
842
|
`Closed ${closed} session(s).`,
|
|
844
|
-
`Retry with:
|
|
843
|
+
`Retry with: libretto close --all --force`
|
|
845
844
|
].join("\n")
|
|
846
845
|
);
|
|
847
846
|
}
|
|
@@ -894,7 +893,7 @@ async function runCloseAll(logger, options) {
|
|
|
894
893
|
console.log(`Closed ${closedCount} session(s).`);
|
|
895
894
|
if (failedProviderSessions.size > 0) {
|
|
896
895
|
console.warn(
|
|
897
|
-
`Failed to confirm remote cleanup for ${failedProviderSessions.size} provider-backed session(s). State preserved with status "cleanup-failed". Retry with:
|
|
896
|
+
`Failed to confirm remote cleanup for ${failedProviderSessions.size} provider-backed session(s). State preserved with status "cleanup-failed". Retry with: libretto close --all`
|
|
898
897
|
);
|
|
899
898
|
}
|
|
900
899
|
for (const recording of replayUrls) {
|
|
@@ -918,11 +917,11 @@ async function runConnect(cdpUrl, session, logger, accessMode, experiments) {
|
|
|
918
917
|
`Invalid CDP URL: ${cdpUrl}`,
|
|
919
918
|
``,
|
|
920
919
|
`Expected an HTTP or WebSocket URL pointing to a Chrome DevTools Protocol endpoint, for example:`,
|
|
921
|
-
`
|
|
922
|
-
`
|
|
923
|
-
`
|
|
924
|
-
`
|
|
925
|
-
`
|
|
920
|
+
` libretto connect http://127.0.0.1:9222`,
|
|
921
|
+
` libretto connect http://remote-host:9222`,
|
|
922
|
+
` libretto connect http://remote-host:9222/devtools/browser/<id>`,
|
|
923
|
+
` libretto connect ws://remote-host:9222/devtools/browser/<id>`,
|
|
924
|
+
` libretto connect wss://remote-host/cdp-endpoint`
|
|
926
925
|
].join("\n")
|
|
927
926
|
);
|
|
928
927
|
}
|
|
@@ -33,7 +33,6 @@ import { handlePages } from "./pages.js";
|
|
|
33
33
|
import { handleExec, handleReadonlyExec } from "./exec.js";
|
|
34
34
|
import { DaemonExecRepl } from "./exec-repl.js";
|
|
35
35
|
import { handleCompactSnapshot } from "./snapshot.js";
|
|
36
|
-
import { librettoCommand } from "../../../shared/package-manager.js";
|
|
37
36
|
import { snapshot } from "../../../shared/snapshot/capture-snapshot.js";
|
|
38
37
|
import { diffSnapshots } from "../../../shared/snapshot/diff-snapshots.js";
|
|
39
38
|
import {
|
|
@@ -446,7 +445,7 @@ class BrowserDaemon {
|
|
|
446
445
|
const openPages = Array.from(this.pageById.values());
|
|
447
446
|
if (openPages.length === 1) return openPages[0];
|
|
448
447
|
throw new Error(
|
|
449
|
-
`The primary page for session "${this.session}" is closed. Run "
|
|
448
|
+
`The primary page for session "${this.session}" is closed. Run "libretto pages --session ${this.session}" to choose a page id.`
|
|
450
449
|
);
|
|
451
450
|
}
|
|
452
451
|
return this.page;
|
|
@@ -454,7 +453,7 @@ class BrowserDaemon {
|
|
|
454
453
|
const page = this.pageById.get(pageId);
|
|
455
454
|
if (!page) {
|
|
456
455
|
throw new Error(
|
|
457
|
-
`Page "${pageId}" was not found in session "${this.session}". Run "
|
|
456
|
+
`Page "${pageId}" was not found in session "${this.session}". Run "libretto pages --session ${this.session}" to list ids.`
|
|
458
457
|
);
|
|
459
458
|
}
|
|
460
459
|
return page;
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
snapshot
|
|
5
5
|
} from "../../../shared/snapshot/capture-snapshot.js";
|
|
6
6
|
import { waitForPageStable } from "../../../shared/snapshot/wait-for-page-stable.js";
|
|
7
|
-
import { librettoCommand } from "../../../shared/package-manager.js";
|
|
8
7
|
import {
|
|
9
8
|
resolveSnapshotViewport,
|
|
10
9
|
readSnapshotViewportMetrics,
|
|
@@ -17,7 +16,7 @@ async function handleCompactSnapshot(targetPage, session, logger, options = {})
|
|
|
17
16
|
if (options.useCachedSnapshot) {
|
|
18
17
|
if (!options.cachedSnapshot) {
|
|
19
18
|
throw new Error(
|
|
20
|
-
`No compact snapshot is cached for session "${session}". Run
|
|
19
|
+
`No compact snapshot is cached for session "${session}". Run libretto snapshot --session ${session} first.`
|
|
21
20
|
);
|
|
22
21
|
}
|
|
23
22
|
const screenshot2 = await captureSnapshotScreenshot(
|
package/dist/cli/core/session.js
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
parseSessionStateContent,
|
|
19
19
|
serializeSessionState
|
|
20
20
|
} from "../../shared/state/index.js";
|
|
21
|
-
import { librettoCommand } from "../../shared/package-manager.js";
|
|
22
21
|
const SESSION_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
23
22
|
const SESSION_DEV_SERVER = "dev-server";
|
|
24
23
|
const SESSION_BROWSER_AGENT = "browser-agent";
|
|
@@ -120,7 +119,7 @@ function throwSessionNotFoundError(session) {
|
|
|
120
119
|
}
|
|
121
120
|
lines.push("");
|
|
122
121
|
lines.push("Start one with:");
|
|
123
|
-
lines.push(`
|
|
122
|
+
lines.push(` libretto open <url> --session ${session}`);
|
|
124
123
|
throw new Error(lines.join("\n"));
|
|
125
124
|
}
|
|
126
125
|
function assertSessionStateExistsOrThrow(session) {
|
|
@@ -177,7 +176,7 @@ function assertSessionAllowsCommand(state, commandName, allowedModes) {
|
|
|
177
176
|
}
|
|
178
177
|
const supportedModes = [...allowedModes].join(", ");
|
|
179
178
|
throw new Error(
|
|
180
|
-
`Command "${commandName}" is blocked for session "${state.session}" because it is in ${mode} mode. Allowed modes for this command: ${supportedModes}. Run
|
|
179
|
+
`Command "${commandName}" is blocked for session "${state.session}" because it is in ${mode} mode. Allowed modes for this command: ${supportedModes}. Run \`libretto session-mode write-access --session ${state.session}\` to unlock the session.`
|
|
181
180
|
);
|
|
182
181
|
}
|
|
183
182
|
function clearSessionState(session, logger) {
|
|
@@ -214,7 +213,7 @@ function assertSessionAvailableForStart(session, logger) {
|
|
|
214
213
|
if (!existingState) return;
|
|
215
214
|
if (existingState.provider && existingState.cdpEndpoint) {
|
|
216
215
|
throw new Error(
|
|
217
|
-
`Session "${session}" is already open via ${existingState.provider.name} provider. Close it first with:
|
|
216
|
+
`Session "${session}" is already open via ${existingState.provider.name} provider. Close it first with: libretto close --session ${session}`
|
|
218
217
|
);
|
|
219
218
|
}
|
|
220
219
|
if (existingState.pid == null || !isPidRunning(existingState.pid)) {
|
|
@@ -223,7 +222,7 @@ function assertSessionAvailableForStart(session, logger) {
|
|
|
223
222
|
}
|
|
224
223
|
const endpoint = `http://127.0.0.1:${existingState.port}`;
|
|
225
224
|
throw new Error(
|
|
226
|
-
`Session "${session}" is already open and connected to ${endpoint} (pid ${existingState.pid}). Create a new session or close the current one with:
|
|
225
|
+
`Session "${session}" is already open and connected to ${endpoint} (pid ${existingState.pid}). Create a new session or close the current one with: libretto close --session ${session}`
|
|
227
226
|
);
|
|
228
227
|
}
|
|
229
228
|
export {
|
|
@@ -2,7 +2,6 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { REPO_ROOT } from "./context.js";
|
|
5
|
-
import { librettoCommand } from "../../shared/package-manager.js";
|
|
6
5
|
const INSTALLED_SKILL_PATHS = [
|
|
7
6
|
[".agents", "skills", "libretto", "SKILL.md"],
|
|
8
7
|
[".claude", "skills", "libretto", "SKILL.md"]
|
|
@@ -64,7 +63,7 @@ function warnIfInstalledSkillOutOfDate() {
|
|
|
64
63
|
return;
|
|
65
64
|
}
|
|
66
65
|
console.error(
|
|
67
|
-
`Warning: Your agent skill (${mismatch.installedVersion}) is out of date with your Libretto CLI (${mismatch.cliVersion}). Please run
|
|
66
|
+
`Warning: Your agent skill (${mismatch.installedVersion}) is out of date with your Libretto CLI (${mismatch.cliVersion}). Please run \`libretto setup\` to update your skills to the correct version.`
|
|
68
67
|
);
|
|
69
68
|
} catch {
|
|
70
69
|
}
|
package/dist/cli/router.js
CHANGED
|
@@ -7,7 +7,6 @@ import { experimentsCommand } from "./commands/experiments.js";
|
|
|
7
7
|
import { setupCommand } from "./commands/setup.js";
|
|
8
8
|
import { statusCommand } from "./commands/status.js";
|
|
9
9
|
import { snapshotCommand } from "./commands/snapshot.js";
|
|
10
|
-
import { librettoCommand } from "../shared/package-manager.js";
|
|
11
10
|
import { SimpleCLI } from "affordance";
|
|
12
11
|
const cliRoutes = {
|
|
13
12
|
...browserCommands,
|
|
@@ -26,7 +25,7 @@ const cliRoutes = {
|
|
|
26
25
|
snapshot: snapshotCommand
|
|
27
26
|
};
|
|
28
27
|
function createCLIApp() {
|
|
29
|
-
return SimpleCLI.define(
|
|
28
|
+
return SimpleCLI.define("libretto", cliRoutes);
|
|
30
29
|
}
|
|
31
30
|
export {
|
|
32
31
|
cliRoutes,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { getActivePauseHandler } from "./pause-handler.js";
|
|
2
|
-
import { librettoCommand } from "../package-manager.js";
|
|
3
2
|
function throwMissingSessionError() {
|
|
4
3
|
throw new Error(
|
|
5
|
-
`pause(session) requires a non-empty session ID. Pass ctx.session from inside your workflow: await pause(ctx.session). To list running sessions, run:
|
|
4
|
+
`pause(session) requires a non-empty session ID. Pass ctx.session from inside your workflow: await pause(ctx.session). To list running sessions, run: libretto status.`
|
|
6
5
|
);
|
|
7
6
|
}
|
|
8
7
|
async function pause(session) {
|
|
@@ -15,7 +14,7 @@ async function pause(session) {
|
|
|
15
14
|
const handler = getActivePauseHandler();
|
|
16
15
|
if (!handler) {
|
|
17
16
|
throw new Error(
|
|
18
|
-
`pause(session) can only suspend an active Libretto workflow. Run the workflow with
|
|
17
|
+
`pause(session) can only suspend an active Libretto workflow. Run the workflow with libretto run <integrationFile> and call pause(ctx.session) from inside the workflow.`
|
|
19
18
|
);
|
|
20
19
|
}
|
|
21
20
|
await handler({
|
|
@@ -2,6 +2,5 @@ type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
|
|
|
2
2
|
declare function detectPackageManager(root?: string, env?: NodeJS.ProcessEnv): PackageManager;
|
|
3
3
|
declare function detectProjectPackageManager(root?: string): PackageManager;
|
|
4
4
|
declare function installCommand(packageManager?: PackageManager): string;
|
|
5
|
-
declare function librettoCommand(args?: string, packageManager?: PackageManager): string;
|
|
6
5
|
|
|
7
|
-
export { type PackageManager, detectPackageManager, detectProjectPackageManager, installCommand
|
|
6
|
+
export { type PackageManager, detectPackageManager, detectProjectPackageManager, installCommand };
|
|
@@ -36,25 +36,8 @@ function installCommand(packageManager = detectProjectPackageManager()) {
|
|
|
36
36
|
return "npm install";
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
function librettoRunner(packageManager = detectPackageManager()) {
|
|
40
|
-
switch (packageManager) {
|
|
41
|
-
case "pnpm":
|
|
42
|
-
return "pnpm exec";
|
|
43
|
-
case "yarn":
|
|
44
|
-
return "yarn";
|
|
45
|
-
case "bun":
|
|
46
|
-
return "bunx";
|
|
47
|
-
default:
|
|
48
|
-
return "npx";
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function librettoCommand(args = "", packageManager = detectPackageManager()) {
|
|
52
|
-
const suffix = args.trim();
|
|
53
|
-
return `${librettoRunner(packageManager)} libretto${suffix ? ` ${suffix}` : ""}`;
|
|
54
|
-
}
|
|
55
39
|
export {
|
|
56
40
|
detectPackageManager,
|
|
57
41
|
detectProjectPackageManager,
|
|
58
|
-
installCommand
|
|
59
|
-
librettoCommand
|
|
42
|
+
installCommand
|
|
60
43
|
};
|
package/docs/experiments.md
CHANGED
|
@@ -35,10 +35,10 @@ Example workspace state:
|
|
|
35
35
|
`libretto experiments` is the CLI surface for inspecting and changing workspace experiment overrides.
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
libretto experiments
|
|
39
|
+
libretto experiments describe <experiment>
|
|
40
|
+
libretto experiments enable <experiment>
|
|
41
|
+
libretto experiments disable <experiment>
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
The command is implemented in `packages/libretto/src/cli/commands/experiments.ts` and registered as a top-level command. Listing prints registered experiments in registry order with their enabled/disabled state and description. `describe` prints the experiment status and full instructions. `enable` persists the override to `.libretto/config.json` and prints the full description with a prelude that the enabled experiment changes expected Libretto usage from the skill. `disable` persists the override and prints deterministic success text.
|