libretto 0.6.19 → 0.6.21
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 +10 -13
- package/README.template.md +10 -13
- package/dist/cli/cli.js +0 -14
- package/dist/cli/commands/deploy.js +5 -0
- package/dist/cli/commands/update.js +47 -18
- package/dist/cli/core/daemon/daemon.js +2 -0
- package/dist/cli/core/providers/libretto-cloud.js +22 -3
- package/dist/cli/core/skill-version.js +8 -95
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -0
- package/dist/shared/workflow/workflow.d.ts +6 -1
- package/dist/shared/workflow/workflow.js +13 -10
- package/package.json +1 -1
- package/skills/libretto/SKILL.md +36 -35
- package/skills/libretto/references/auth-profiles.md +3 -3
- package/skills/libretto/references/code-generation-rules.md +2 -2
- package/skills/libretto/references/configuration-file-reference.md +10 -10
- package/skills/libretto/references/pages-and-page-targeting.md +3 -3
- package/skills/libretto-readonly/SKILL.md +1 -1
- package/src/cli/cli.ts +0 -17
- package/src/cli/commands/deploy.ts +5 -0
- package/src/cli/commands/update.ts +57 -19
- package/src/cli/core/daemon/daemon.ts +2 -0
- package/src/cli/core/providers/libretto-cloud.ts +29 -6
- package/src/cli/core/skill-version.ts +10 -131
- package/src/index.ts +2 -0
- package/src/shared/workflow/workflow.ts +27 -10
package/skills/libretto/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ 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.6.
|
|
7
|
+
version: "0.6.21"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## How Libretto Works
|
|
@@ -39,12 +39,13 @@ Prefer to enter sites at a user-facing URL (homepage, login, etc.) on the first
|
|
|
39
39
|
|
|
40
40
|
## Setup
|
|
41
41
|
|
|
42
|
-
- Use
|
|
43
|
-
- Use `libretto
|
|
42
|
+
- Use the package manager convention for the target project. The examples use `npx libretto`; pnpm, yarn, and bun projects should use their equivalent package-manager execution form.
|
|
43
|
+
- Use `npx libretto setup` for first-time workspace onboarding. It installs Chromium and syncs skills.
|
|
44
|
+
- Use `npx libretto status` to inspect open sessions without triggering setup.
|
|
44
45
|
|
|
45
46
|
## Experiments
|
|
46
47
|
|
|
47
|
-
- Use `libretto experiments` to list internal feature flags and `libretto experiments describe <name>` for usage notes when an experiment is enabled.
|
|
48
|
+
- Use `npx libretto experiments` to list internal feature flags and `npx libretto experiments describe <name>` for usage notes when an experiment is enabled.
|
|
48
49
|
|
|
49
50
|
## Working Rules
|
|
50
51
|
|
|
@@ -83,9 +84,9 @@ npx libretto open https://example.com --session debug-example
|
|
|
83
84
|
- Pass `--read-only` if the connected session must stay inspection-only from the start.
|
|
84
85
|
|
|
85
86
|
```bash
|
|
86
|
-
libretto connect http://127.0.0.1:9222 --session my-session
|
|
87
|
-
libretto connect http://127.0.0.1:9222 --read-only --session readonly-session
|
|
88
|
-
libretto connect http://127.0.0.1:9223 --session another-session
|
|
87
|
+
npx libretto connect http://127.0.0.1:9222 --session my-session
|
|
88
|
+
npx libretto connect http://127.0.0.1:9222 --read-only --session readonly-session
|
|
89
|
+
npx libretto connect http://127.0.0.1:9223 --session another-session
|
|
89
90
|
```
|
|
90
91
|
|
|
91
92
|
### `session-mode`
|
|
@@ -96,7 +97,7 @@ libretto connect http://127.0.0.1:9223 --session another-session
|
|
|
96
97
|
- Pass `--read-only` or `--write-access` to override the config default for a single command.
|
|
97
98
|
|
|
98
99
|
```bash
|
|
99
|
-
libretto session-mode --session my-session
|
|
100
|
+
npx libretto session-mode --session my-session
|
|
100
101
|
```
|
|
101
102
|
|
|
102
103
|
### `snapshot`
|
|
@@ -108,9 +109,9 @@ libretto session-mode --session my-session
|
|
|
108
109
|
- Use it before guessing at selectors, after workflow failures, and whenever the visible page state is unclear.
|
|
109
110
|
|
|
110
111
|
```bash
|
|
111
|
-
libretto snapshot --session debug-example
|
|
112
|
-
libretto snapshot <ref> --session debug-example
|
|
113
|
-
libretto snapshot --session debug-example --page <page-id>
|
|
112
|
+
npx libretto snapshot --session debug-example
|
|
113
|
+
npx libretto snapshot <ref> --session debug-example
|
|
114
|
+
npx libretto snapshot --session debug-example --page <page-id>
|
|
114
115
|
```
|
|
115
116
|
|
|
116
117
|
### `exec`
|
|
@@ -126,10 +127,10 @@ libretto snapshot --session debug-example --page <page-id>
|
|
|
126
127
|
- After successful mutations, `exec` prints page-change diffs from compact snapshots.
|
|
127
128
|
|
|
128
129
|
```bash
|
|
129
|
-
libretto exec "await page.url()"
|
|
130
|
-
libretto exec "await page.locator('button:has-text(\"Continue\")').click()"
|
|
131
|
-
echo "async function textOf(selector) { return await page.locator(selector).textContent(); }" | libretto exec - --session debug-example
|
|
132
|
-
libretto exec --session debug-example "await textOf('h1')"
|
|
130
|
+
npx libretto exec "await page.url()"
|
|
131
|
+
npx libretto exec "await page.locator('button:has-text(\"Continue\")').click()"
|
|
132
|
+
echo "async function textOf(selector) { return await page.locator(selector).textContent(); }" | npx libretto exec - --session debug-example
|
|
133
|
+
npx libretto exec --session debug-example "await textOf('h1')"
|
|
133
134
|
```
|
|
134
135
|
|
|
135
136
|
### `pages`
|
|
@@ -138,8 +139,8 @@ libretto exec --session debug-example "await textOf('h1')"
|
|
|
138
139
|
- If `exec` or `snapshot` complains about multiple pages, list page ids first and then pass `--page`.
|
|
139
140
|
|
|
140
141
|
```bash
|
|
141
|
-
libretto pages --session debug-example
|
|
142
|
-
libretto exec --session debug-example --page <page-id> "await page.url()"
|
|
142
|
+
npx libretto pages --session debug-example
|
|
143
|
+
npx libretto exec --session debug-example --page <page-id> "await page.url()"
|
|
143
144
|
```
|
|
144
145
|
|
|
145
146
|
### `run`
|
|
@@ -150,14 +151,14 @@ libretto exec --session debug-example --page <page-id> "await page.url()"
|
|
|
150
151
|
- Pass `--read-only` if the preserved session should come back locked for follow-up terminal inspection after the workflow run.
|
|
151
152
|
- If the workflow fails, Libretto keeps the browser open. Inspect the failed state with `snapshot` and `exec` before editing code.
|
|
152
153
|
- 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.
|
|
153
|
-
- If the workflow pauses, resume it with `libretto resume --session <name>`.
|
|
154
|
+
- If the workflow pauses, resume it with `npx libretto resume --session <name>`.
|
|
154
155
|
- Re-run the same workflow after each fix to verify the browser behavior end to end.
|
|
155
156
|
|
|
156
157
|
```bash
|
|
157
|
-
libretto run ./integration.ts --params '{"status":"open"}'
|
|
158
|
-
libretto run ./integration.ts --read-only
|
|
159
|
-
libretto run ./integration.ts --stay-open-on-success
|
|
160
|
-
libretto run ./integration.ts --auth-profile app.example.com
|
|
158
|
+
npx libretto run ./integration.ts --params '{"status":"open"}'
|
|
159
|
+
npx libretto run ./integration.ts --read-only
|
|
160
|
+
npx libretto run ./integration.ts --stay-open-on-success
|
|
161
|
+
npx libretto run ./integration.ts --auth-profile app.example.com
|
|
161
162
|
```
|
|
162
163
|
|
|
163
164
|
### `resume`
|
|
@@ -168,7 +169,7 @@ libretto run ./integration.ts --auth-profile app.example.com
|
|
|
168
169
|
- Keep resuming the same session until the workflow completes or pauses again.
|
|
169
170
|
|
|
170
171
|
```bash
|
|
171
|
-
libretto resume --session debug-example
|
|
172
|
+
npx libretto resume --session debug-example
|
|
172
173
|
```
|
|
173
174
|
|
|
174
175
|
### `save`
|
|
@@ -176,7 +177,7 @@ libretto resume --session debug-example
|
|
|
176
177
|
- Use `save` only when the user explicitly asks to save or reuse authenticated browser state.
|
|
177
178
|
|
|
178
179
|
```bash
|
|
179
|
-
libretto save app.example.com
|
|
180
|
+
npx libretto save app.example.com
|
|
180
181
|
```
|
|
181
182
|
|
|
182
183
|
### `close`
|
|
@@ -185,8 +186,8 @@ libretto save app.example.com
|
|
|
185
186
|
- `close --all` is available for workspace cleanup.
|
|
186
187
|
|
|
187
188
|
```bash
|
|
188
|
-
libretto close --session debug-example
|
|
189
|
-
libretto close --all
|
|
189
|
+
npx libretto close --session debug-example
|
|
190
|
+
npx libretto close --all
|
|
190
191
|
```
|
|
191
192
|
|
|
192
193
|
## Session Logs
|
|
@@ -234,17 +235,17 @@ Key fields: `id` (incrementing request id), `ts` (ISO timestamp), `pageId` (page
|
|
|
234
235
|
<example>
|
|
235
236
|
[Context: The user wants to build a new browser workflow and does not yet know the page structure]
|
|
236
237
|
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.
|
|
237
|
-
Assistant: [Runs `libretto open https://target.example.com --headed`]
|
|
238
|
+
Assistant: [Runs `npx libretto open https://target.example.com --headed`]
|
|
238
239
|
Assistant: [Reads `references/site-security-review.md` before choosing between passive network inspection, direct browser fetch calls, and Playwright-first automation]
|
|
239
|
-
Assistant: [Runs `libretto snapshot --session <session>`]
|
|
240
|
+
Assistant: [Runs `npx libretto snapshot --session <session>`]
|
|
240
241
|
Assistant: [Uses `snapshot` and `exec` as needed to understand the site and decide the implementation path]
|
|
241
242
|
Assistant: [Reads `references/code-generation-rules.md` before writing production workflow code]
|
|
242
243
|
Assistant: I found the working path. I'll now create the workflow file and verify it.
|
|
243
244
|
Assistant: [Creates or edits `target-workflow.ts` following `references/code-generation-rules.md`]
|
|
244
|
-
Assistant: [Runs `libretto run ./target-workflow.ts --params '{"status":"open"}'` to validate]
|
|
245
|
+
Assistant: [Runs `npx libretto run ./target-workflow.ts --params '{"status":"open"}'` to validate]
|
|
245
246
|
Assistant: Validation passed. Here are the results:
|
|
246
247
|
[Shows the output/results from the validation run]
|
|
247
|
-
To run it again, use: libretto run ./target-workflow.ts --params '{"status":"open"}'
|
|
248
|
+
To run it again, use: npx libretto run ./target-workflow.ts --params '{"status":"open"}'
|
|
248
249
|
</example>
|
|
249
250
|
```
|
|
250
251
|
|
|
@@ -254,17 +255,17 @@ To run it again, use: libretto run ./target-workflow.ts --params '{"status":"ope
|
|
|
254
255
|
<example>
|
|
255
256
|
[Context: The user has an existing Libretto workflow that is failing]
|
|
256
257
|
Assistant: I'll reproduce the failure first so we can inspect the exact browser state it leaves behind.
|
|
257
|
-
Assistant: [Runs `libretto run ./integration.ts --session debug-flow`]
|
|
258
|
+
Assistant: [Runs `npx libretto run ./integration.ts --session debug-flow`]
|
|
258
259
|
Assistant: The workflow failed and Libretto kept the browser open. I'll inspect the page state before changing code.
|
|
259
|
-
Assistant: [Runs `libretto snapshot --session debug-flow`]
|
|
260
|
-
Assistant: [Runs `libretto exec --session debug-flow "...focused inspection or prototype..."`]
|
|
260
|
+
Assistant: [Runs `npx libretto snapshot --session debug-flow`]
|
|
261
|
+
Assistant: [Runs `npx libretto exec --session debug-flow "...focused inspection or prototype..."`]
|
|
261
262
|
Assistant: [Reads `references/code-generation-rules.md` before patching the workflow file]
|
|
262
263
|
Assistant: I found the issue. I'll patch the workflow code and verify.
|
|
263
264
|
Assistant: [Edits `integration.ts` following `references/code-generation-rules.md`]
|
|
264
|
-
Assistant: [Runs `libretto run ./integration.ts` to validate the fix]
|
|
265
|
+
Assistant: [Runs `npx libretto run ./integration.ts` to validate the fix]
|
|
265
266
|
Assistant: Fix verified. Here are the results:
|
|
266
267
|
[Shows the output/results from the validation run]
|
|
267
|
-
To run it again, use: libretto run ./integration.ts
|
|
268
|
+
To run it again, use: npx libretto run ./integration.ts
|
|
268
269
|
</example>
|
|
269
270
|
```
|
|
270
271
|
|
|
@@ -17,9 +17,9 @@ Use this reference only when the user explicitly asks to save or reuse local aut
|
|
|
17
17
|
## Commands
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
libretto open https://app.example.com --headed
|
|
21
|
-
libretto save app.example.com
|
|
22
|
-
libretto run ./integration.ts --auth-profile app.example.com
|
|
20
|
+
npx libretto open https://app.example.com --headed
|
|
21
|
+
npx libretto save app.example.com
|
|
22
|
+
npx libretto run ./integration.ts --auth-profile app.example.com
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Notes
|
|
@@ -6,7 +6,7 @@ Follow the user's existing codebase conventions, abstractions, and patterns when
|
|
|
6
6
|
|
|
7
7
|
## Workflow File Structure
|
|
8
8
|
|
|
9
|
-
Generated files must default-export a `workflow()` instance so they can be run via `libretto run <file>`. Workflows declare their input and output shapes as Zod schemas, which both type the handler and validate runtime input.
|
|
9
|
+
Generated files must default-export a `workflow()` instance so they can be run via `npx libretto run <file>`. Workflows declare their input and output shapes as Zod schemas, which both type the handler and validate runtime input.
|
|
10
10
|
|
|
11
11
|
Add `zod` (`^4.0.0`) to the workflow's `package.json` dependencies. Then import `workflow` from `"libretto"` and `z` from `"zod"`:
|
|
12
12
|
|
|
@@ -42,7 +42,7 @@ Key points:
|
|
|
42
42
|
|
|
43
43
|
- `workflow(name, { input, output }, handler)` takes a unique workflow name, a pair of Zod schemas describing input and output, and the async handler. The handler's `input` parameter is inferred from the input schema — do not redeclare it with a separate `type Input = ...`.
|
|
44
44
|
- At run time, Libretto validates `input` against `inputSchema` before calling the handler. Invalid input throws a clear error listing each failing field; the workflow handler never sees malformed input.
|
|
45
|
-
- `libretto run ./file.ts` executes the file's default-exported workflow, so always use `export default workflow(...)`.
|
|
45
|
+
- `npx libretto run ./file.ts` executes the file's default-exported workflow, so always use `export default workflow(...)`.
|
|
46
46
|
- `ctx` provides `session` and `page`. Use `console.log`/`console.warn`/`console.error` for logging — the runtime wraps these with structured metadata automatically.
|
|
47
47
|
- `input` comes from `--params '{"query":"foo"}'` or `--params-file params.json` on the CLI, then gets parsed through `inputSchema`.
|
|
48
48
|
- Use `await pause(ctx.session)` (or `await pause(session)`) to pause the workflow for debugging. It is a no-op in production.
|
|
@@ -12,8 +12,8 @@ Use this reference when you need to inspect or change workspace configuration fo
|
|
|
12
12
|
|
|
13
13
|
Libretto reads workspace config from `.libretto/config.json`.
|
|
14
14
|
|
|
15
|
-
- The file is created by `libretto setup` during first-time onboarding.
|
|
16
|
-
- Use `libretto status` to inspect open sessions without changing anything.
|
|
15
|
+
- The file is created by `npx libretto setup` during first-time onboarding.
|
|
16
|
+
- Use `npx libretto status` to inspect open sessions without changing anything.
|
|
17
17
|
- For first-time setup instructions, follow the main `SKILL.md` flow instead of expanding this reference.
|
|
18
18
|
|
|
19
19
|
## Supported Settings
|
|
@@ -42,17 +42,17 @@ Example:
|
|
|
42
42
|
## Common Commands
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
libretto setup # first-time onboarding
|
|
46
|
-
libretto status # inspect open sessions
|
|
47
|
-
libretto open https://example.com --provider kernel
|
|
48
|
-
libretto run ./integration.ts --provider browserbase
|
|
49
|
-
libretto open https://example.com --provider steel
|
|
50
|
-
libretto open https://example.com --viewport 1440x900
|
|
51
|
-
libretto run ./integration.ts --viewport 1440x900
|
|
45
|
+
npx libretto setup # first-time onboarding
|
|
46
|
+
npx libretto status # inspect open sessions
|
|
47
|
+
npx libretto open https://example.com --provider kernel
|
|
48
|
+
npx libretto run ./integration.ts --provider browserbase
|
|
49
|
+
npx libretto open https://example.com --provider steel
|
|
50
|
+
npx libretto open https://example.com --viewport 1440x900
|
|
51
|
+
npx libretto run ./integration.ts --viewport 1440x900
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
## Notes
|
|
55
55
|
|
|
56
56
|
- If you want a persistent default provider for the workspace, add `provider` to `.libretto/config.json` instead of repeating `--provider` on every command.
|
|
57
57
|
- If you want a persistent default viewport for the workspace, add `viewport` to `.libretto/config.json` instead of repeating `--viewport` on every command.
|
|
58
|
-
- Run `libretto status` at any time to check open sessions.
|
|
58
|
+
- Run `npx libretto status` at any time to check open sessions.
|
|
@@ -17,9 +17,9 @@ Use this reference when a Libretto session has multiple open pages and you need
|
|
|
17
17
|
## Commands
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
libretto pages --session debug-flow
|
|
21
|
-
libretto exec --session debug-flow --page <page-id> "return await page.url()"
|
|
22
|
-
libretto snapshot --session debug-flow --page <page-id>
|
|
20
|
+
npx libretto pages --session debug-flow
|
|
21
|
+
npx libretto exec --session debug-flow --page <page-id> "return await page.url()"
|
|
22
|
+
npx libretto snapshot --session debug-flow --page <page-id>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Notes
|
package/src/cli/cli.ts
CHANGED
|
@@ -14,22 +14,6 @@ function printSetupAudit(): void {
|
|
|
14
14
|
warnIfLibrettoVersionsDiffer();
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function isPackageManagerExec(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
18
|
-
return env.npm_command === "exec";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function warnIfPackageManagerExec(): void {
|
|
22
|
-
if (!isPackageManagerExec()) return;
|
|
23
|
-
|
|
24
|
-
console.error(
|
|
25
|
-
[
|
|
26
|
-
"Warning: running Libretto through a package manager is deprecated and will be removed in a future release.",
|
|
27
|
-
"Install the native command instead:",
|
|
28
|
-
" curl -fsSL https://libretto.sh/install.sh | bash",
|
|
29
|
-
].join("\n"),
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
17
|
function isRootHelpRequest(rawArgs: readonly string[]): boolean {
|
|
34
18
|
if (rawArgs.length === 0) return true;
|
|
35
19
|
return rawArgs[0] === "help" && rawArgs.length === 1;
|
|
@@ -55,7 +39,6 @@ export async function runLibrettoCLI(): Promise<void> {
|
|
|
55
39
|
const rawArgs = process.argv.slice(2);
|
|
56
40
|
let exitCode = 0;
|
|
57
41
|
loadEnv();
|
|
58
|
-
warnIfPackageManagerExec();
|
|
59
42
|
ensureLibrettoSetup();
|
|
60
43
|
const app = createCLIApp();
|
|
61
44
|
|
|
@@ -125,6 +125,10 @@ export const deployInput = SimpleCLI.input({
|
|
|
125
125
|
name: "entry-point",
|
|
126
126
|
help: "Entry point file (default: index.ts)",
|
|
127
127
|
}),
|
|
128
|
+
autoRepair: SimpleCLI.flag({
|
|
129
|
+
name: "auto-repair",
|
|
130
|
+
help: "Route failed jobs for this deployment to autofix",
|
|
131
|
+
}),
|
|
128
132
|
external: SimpleCLI.option(
|
|
129
133
|
z
|
|
130
134
|
.string()
|
|
@@ -167,6 +171,7 @@ export const deployCommand = SimpleCLI.command({
|
|
|
167
171
|
entry_point: entryPoint,
|
|
168
172
|
};
|
|
169
173
|
if (input.description) createPayload.description = input.description;
|
|
174
|
+
if (input.autoRepair) createPayload.auto_repair = true;
|
|
170
175
|
|
|
171
176
|
console.log("Uploading deployment...");
|
|
172
177
|
const body = await orpcCall<DeploymentResponse["json"]>({
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { SimpleCLI } from "affordance";
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
import { REPO_ROOT } from "../core/context.js";
|
|
7
|
+
import {
|
|
8
|
+
detectProjectPackageManager,
|
|
9
|
+
installCommand,
|
|
10
|
+
type PackageManager,
|
|
11
|
+
} from "../../shared/package-manager.js";
|
|
7
12
|
|
|
8
13
|
type PackageManifest = {
|
|
9
14
|
version?: string;
|
|
10
15
|
};
|
|
11
16
|
|
|
17
|
+
function packageInstallCommand(
|
|
18
|
+
packageManager: PackageManager,
|
|
19
|
+
packageSpec: string,
|
|
20
|
+
): string {
|
|
21
|
+
return `${installCommand(packageManager)} ${packageSpec}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
12
24
|
function readCurrentCliVersion(): string {
|
|
13
25
|
const packageJsonPath = fileURLToPath(
|
|
14
26
|
new URL("../../../package.json", import.meta.url),
|
|
@@ -26,6 +38,23 @@ function readCurrentCliVersion(): string {
|
|
|
26
38
|
return manifest.version;
|
|
27
39
|
}
|
|
28
40
|
|
|
41
|
+
function readPackageVersion(packageJsonPath: string): string | null {
|
|
42
|
+
try {
|
|
43
|
+
const manifest = JSON.parse(
|
|
44
|
+
readFileSync(packageJsonPath, "utf8"),
|
|
45
|
+
) as PackageManifest;
|
|
46
|
+
return manifest.version?.trim() || null;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function readLocalPackageVersion(): string | null {
|
|
53
|
+
return readPackageVersion(
|
|
54
|
+
join(REPO_ROOT, "node_modules", "libretto", "package.json"),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
29
58
|
function readLatestNpmVersion(): string {
|
|
30
59
|
const result = spawnSync("npm", ["view", "libretto@latest", "version"], {
|
|
31
60
|
encoding: "utf8",
|
|
@@ -83,16 +112,17 @@ export const updateInput = SimpleCLI.input({
|
|
|
83
112
|
function formatUpdateFailure(
|
|
84
113
|
status: number | null,
|
|
85
114
|
signal: string | null,
|
|
115
|
+
updateCommand: string,
|
|
86
116
|
): string {
|
|
87
117
|
const knownState =
|
|
88
118
|
status === null
|
|
89
|
-
? `
|
|
90
|
-
: `
|
|
119
|
+
? `package update was interrupted${signal ? ` by ${signal}` : ""}.`
|
|
120
|
+
: `package update exited with status ${status}.`;
|
|
91
121
|
|
|
92
122
|
return [
|
|
93
123
|
"Error: failed to update Libretto to the latest version.",
|
|
94
124
|
`Known state: ${knownState}`,
|
|
95
|
-
`Try: ${
|
|
125
|
+
`Try: ${updateCommand}`,
|
|
96
126
|
"Help: libretto help update",
|
|
97
127
|
].join("\n");
|
|
98
128
|
}
|
|
@@ -102,48 +132,56 @@ export const updateCommand = SimpleCLI.command({
|
|
|
102
132
|
})
|
|
103
133
|
.input(updateInput)
|
|
104
134
|
.handle(async ({ input }) => {
|
|
135
|
+
const packageManager = detectProjectPackageManager();
|
|
136
|
+
const updateCommand = packageInstallCommand(packageManager, "libretto@latest");
|
|
137
|
+
|
|
105
138
|
if (input.dryRun) {
|
|
106
139
|
console.log("Update command:");
|
|
107
|
-
console.log(` ${
|
|
140
|
+
console.log(` ${updateCommand}`);
|
|
108
141
|
console.log("No changes made.");
|
|
109
142
|
return;
|
|
110
143
|
}
|
|
111
144
|
|
|
112
145
|
const currentVersion = readCurrentCliVersion();
|
|
146
|
+
const localPackageVersion = readLocalPackageVersion();
|
|
147
|
+
const installedVersion = localPackageVersion ?? currentVersion;
|
|
113
148
|
const latestVersion = readLatestNpmVersion();
|
|
114
|
-
console.log(`Current version: ${
|
|
149
|
+
console.log(`Current version: ${installedVersion}`);
|
|
115
150
|
console.log(`Latest version: ${latestVersion}`);
|
|
116
151
|
|
|
117
|
-
if (
|
|
118
|
-
console.log(`Libretto is already up to date (${
|
|
152
|
+
if (localPackageVersion && installedVersion === latestVersion) {
|
|
153
|
+
console.log(`Libretto is already up to date (${installedVersion}).`);
|
|
119
154
|
console.log("No further action required.");
|
|
120
155
|
return;
|
|
121
156
|
}
|
|
122
157
|
|
|
123
|
-
|
|
124
|
-
|
|
158
|
+
if (!localPackageVersion) {
|
|
159
|
+
console.log("Local package: not installed");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log("Updating local Libretto package to latest...");
|
|
163
|
+
const result = spawnSync(updateCommand, {
|
|
125
164
|
stdio: "inherit",
|
|
126
|
-
|
|
127
|
-
...process.env,
|
|
128
|
-
LIBRETTO_VERSION: "latest",
|
|
129
|
-
},
|
|
165
|
+
shell: true,
|
|
130
166
|
});
|
|
131
167
|
|
|
132
168
|
if (result.error) {
|
|
133
169
|
throw new Error(
|
|
134
170
|
[
|
|
135
|
-
"Error: failed to start the Libretto
|
|
171
|
+
"Error: failed to start the Libretto package update.",
|
|
136
172
|
`Known state: ${result.error.message}`,
|
|
137
|
-
`Try: ${
|
|
173
|
+
`Try: ${updateCommand}`,
|
|
138
174
|
"Help: libretto help update",
|
|
139
175
|
].join("\n"),
|
|
140
176
|
);
|
|
141
177
|
}
|
|
142
178
|
|
|
143
179
|
if (result.status !== 0) {
|
|
144
|
-
throw new Error(
|
|
180
|
+
throw new Error(
|
|
181
|
+
formatUpdateFailure(result.status, result.signal, updateCommand),
|
|
182
|
+
);
|
|
145
183
|
}
|
|
146
184
|
|
|
147
|
-
console.log("Libretto updated to latest.");
|
|
185
|
+
console.log("Local Libretto package updated to latest.");
|
|
148
186
|
console.log("No further action required.");
|
|
149
187
|
});
|
|
@@ -89,6 +89,7 @@ import {
|
|
|
89
89
|
loadDefaultWorkflow,
|
|
90
90
|
} from "../workflow-runtime.js";
|
|
91
91
|
import { WorkflowController } from "../workflow-runner/runner.js";
|
|
92
|
+
import { validateWorkflowInput } from "../../../shared/workflow/workflow.js";
|
|
92
93
|
|
|
93
94
|
function isOperationalPage(page: Page): boolean {
|
|
94
95
|
const url = page.url();
|
|
@@ -941,6 +942,7 @@ async function main(): Promise<void> {
|
|
|
941
942
|
loadedWorkflow = await loadDefaultWorkflow(
|
|
942
943
|
getAbsoluteIntegrationPath(config.workflow.integrationPath),
|
|
943
944
|
);
|
|
945
|
+
validateWorkflowInput(loadedWorkflow, config.workflow.params ?? {});
|
|
944
946
|
} catch (error) {
|
|
945
947
|
throw new UserFacingStartupError(
|
|
946
948
|
error instanceof Error ? error.message : String(error),
|
|
@@ -6,7 +6,6 @@ type CloudSessionResponse = {
|
|
|
6
6
|
status: string;
|
|
7
7
|
cdp_url: string | null;
|
|
8
8
|
live_view_url: string | null;
|
|
9
|
-
recording_url: string | null;
|
|
10
9
|
};
|
|
11
10
|
|
|
12
11
|
const DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
@@ -77,8 +76,11 @@ export function createLibrettoCloudProvider(): ProviderApi {
|
|
|
77
76
|
};
|
|
78
77
|
},
|
|
79
78
|
async closeSession(sessionId) {
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
await closeCloudSession(endpoint, apiKey, sessionId);
|
|
80
|
+
const replayUrl = await getCloudRecordingUrl(endpoint, apiKey, sessionId).catch(
|
|
81
|
+
() => undefined,
|
|
82
|
+
);
|
|
83
|
+
return { replayUrl };
|
|
82
84
|
},
|
|
83
85
|
};
|
|
84
86
|
}
|
|
@@ -172,7 +174,7 @@ async function closeCloudSession(
|
|
|
172
174
|
endpoint: string,
|
|
173
175
|
apiKey: string,
|
|
174
176
|
sessionId: string,
|
|
175
|
-
): Promise<
|
|
177
|
+
): Promise<void> {
|
|
176
178
|
const resp = await fetch(`${endpoint}/v1/sessions/close`, {
|
|
177
179
|
method: "POST",
|
|
178
180
|
headers: {
|
|
@@ -187,10 +189,31 @@ async function closeCloudSession(
|
|
|
187
189
|
`Libretto Cloud API error closing session ${sessionId} (${resp.status}): ${body}`,
|
|
188
190
|
);
|
|
189
191
|
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function getCloudRecordingUrl(
|
|
195
|
+
endpoint: string,
|
|
196
|
+
apiKey: string,
|
|
197
|
+
sessionId: string,
|
|
198
|
+
): Promise<string | undefined> {
|
|
199
|
+
const resp = await fetch(`${endpoint}/v1/recordings/get`, {
|
|
200
|
+
method: "POST",
|
|
201
|
+
headers: {
|
|
202
|
+
"x-api-key": apiKey,
|
|
203
|
+
"Content-Type": "application/json",
|
|
204
|
+
},
|
|
205
|
+
body: JSON.stringify({ json: { session_id: sessionId } }),
|
|
206
|
+
});
|
|
207
|
+
if (!resp.ok) {
|
|
208
|
+
const body = await resp.text();
|
|
209
|
+
throw new Error(
|
|
210
|
+
`Libretto Cloud API error reading recording for session ${sessionId} (${resp.status}): ${body}`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
190
213
|
const { json } = (await resp.json()) as {
|
|
191
|
-
json: {
|
|
214
|
+
json: { recording_url: string | null };
|
|
192
215
|
};
|
|
193
|
-
return json;
|
|
216
|
+
return json.recording_url ?? undefined;
|
|
194
217
|
}
|
|
195
218
|
|
|
196
219
|
function createStartupSessionCleanup(
|