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 CHANGED
@@ -28,17 +28,14 @@ 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
+ # Add Libretto to your project. Requires Node.js and npm.
35
32
  npm install libretto
36
33
 
37
34
  # First-time onboarding: install skills and download Chromium
38
- libretto setup
35
+ npx libretto setup
39
36
 
40
37
  # Check workspace readiness at any time
41
- libretto status
38
+ npx libretto status
42
39
  ```
43
40
 
44
41
  `setup` creates the `.libretto/` directory, installs agent skills, and downloads Chromium unless you pass `--skip-browsers`.
@@ -76,17 +73,17 @@ Agents can use Libretto to reproduce the failure, pause the workflow at any poin
76
73
  You can also use Libretto directly from the command line. All commands accept `--session <name>` to target a specific session.
77
74
 
78
75
  ```bash
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
76
+ npx libretto open <url> # launch browser and open a URL
77
+ npx libretto run ./integration.ts --headless # run a workflow and close on success
78
+ npx libretto run ./integration.ts --headless --stay-open-on-success # keep a successful run inspectable
79
+ npx libretto snapshot --session <name> # capture a screenshot and compact accessibility tree
80
+ npx libretto exec "<code>" # execute Playwright TypeScript against the open page
81
+ npx libretto close # close the browser
85
82
  ```
86
83
 
87
84
  `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.
88
85
 
89
- Run `libretto help` for the full list of commands.
86
+ Run `npx libretto help` for the full list of commands.
90
87
 
91
88
  ## Configuration
92
89
 
@@ -26,17 +26,14 @@ 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
+ # Add Libretto to your project. Requires Node.js and npm.
33
30
  npm install libretto
34
31
 
35
32
  # First-time onboarding: install skills and download Chromium
36
- libretto setup
33
+ npx libretto setup
37
34
 
38
35
  # Check workspace readiness at any time
39
- libretto status
36
+ npx libretto status
40
37
  ```
41
38
 
42
39
  `setup` creates the `.libretto/` directory, installs agent skills, and downloads Chromium unless you pass `--skip-browsers`.
@@ -74,17 +71,17 @@ Agents can use Libretto to reproduce the failure, pause the workflow at any poin
74
71
  You can also use Libretto directly from the command line. All commands accept `--session <name>` to target a specific session.
75
72
 
76
73
  ```bash
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
74
+ npx libretto open <url> # launch browser and open a URL
75
+ npx libretto run ./integration.ts --headless # run a workflow and close on success
76
+ npx libretto run ./integration.ts --headless --stay-open-on-success # keep a successful run inspectable
77
+ npx libretto snapshot --session <name> # capture a screenshot and compact accessibility tree
78
+ npx libretto exec "<code>" # execute Playwright TypeScript against the open page
79
+ npx libretto close # close the browser
83
80
  ```
84
81
 
85
82
  `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.
86
83
 
87
- Run `libretto help` for the full list of commands.
84
+ Run `npx libretto help` for the full list of commands.
88
85
 
89
86
  ## Configuration
90
87
 
package/dist/cli/cli.js CHANGED
@@ -11,19 +11,6 @@ function renderVersion() {
11
11
  function printSetupAudit() {
12
12
  warnIfLibrettoVersionsDiffer();
13
13
  }
14
- function isPackageManagerExec(env = process.env) {
15
- return env.npm_command === "exec";
16
- }
17
- function warnIfPackageManagerExec() {
18
- if (!isPackageManagerExec()) return;
19
- console.error(
20
- [
21
- "Warning: running Libretto through a package manager is deprecated and will be removed in a future release.",
22
- "Install the native command instead:",
23
- " curl -fsSL https://libretto.sh/install.sh | bash"
24
- ].join("\n")
25
- );
26
- }
27
14
  function isRootHelpRequest(rawArgs) {
28
15
  if (rawArgs.length === 0) return true;
29
16
  return rawArgs[0] === "help" && rawArgs.length === 1;
@@ -42,7 +29,6 @@ async function runLibrettoCLI() {
42
29
  const rawArgs = process.argv.slice(2);
43
30
  let exitCode = 0;
44
31
  loadEnv();
45
- warnIfPackageManagerExec();
46
32
  ensureLibrettoSetup();
47
33
  const app = createCLIApp();
48
34
  try {
@@ -93,6 +93,10 @@ const deployInput = SimpleCLI.input({
93
93
  name: "entry-point",
94
94
  help: "Entry point file (default: index.ts)"
95
95
  }),
96
+ autoRepair: SimpleCLI.flag({
97
+ name: "auto-repair",
98
+ help: "Route failed jobs for this deployment to autofix"
99
+ }),
96
100
  external: SimpleCLI.option(
97
101
  z.string().optional().transform(
98
102
  (value) => value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? []
@@ -120,6 +124,7 @@ const deployCommand = SimpleCLI.command({
120
124
  entry_point: entryPoint
121
125
  };
122
126
  if (input.description) createPayload.description = input.description;
127
+ if (input.autoRepair) createPayload.auto_repair = true;
123
128
  console.log("Uploading deployment...");
124
129
  const body = await orpcCall({
125
130
  apiUrl,
@@ -1,8 +1,16 @@
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
- const UPDATE_COMMAND = "curl -fsSL https://libretto.sh/install.sh | bash";
6
+ import { REPO_ROOT } from "../core/context.js";
7
+ import {
8
+ detectProjectPackageManager,
9
+ installCommand
10
+ } from "../../shared/package-manager.js";
11
+ function packageInstallCommand(packageManager, packageSpec) {
12
+ return `${installCommand(packageManager)} ${packageSpec}`;
13
+ }
6
14
  function readCurrentCliVersion() {
7
15
  const packageJsonPath = fileURLToPath(
8
16
  new URL("../../../package.json", import.meta.url)
@@ -17,6 +25,21 @@ function readCurrentCliVersion() {
17
25
  }
18
26
  return manifest.version;
19
27
  }
28
+ function readPackageVersion(packageJsonPath) {
29
+ try {
30
+ const manifest = JSON.parse(
31
+ readFileSync(packageJsonPath, "utf8")
32
+ );
33
+ return manifest.version?.trim() || null;
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+ function readLocalPackageVersion() {
39
+ return readPackageVersion(
40
+ join(REPO_ROOT, "node_modules", "libretto", "package.json")
41
+ );
42
+ }
20
43
  function readLatestNpmVersion() {
21
44
  const result = spawnSync("npm", ["view", "libretto@latest", "version"], {
22
45
  encoding: "utf8"
@@ -65,55 +88,61 @@ const updateInput = SimpleCLI.input({
65
88
  })
66
89
  }
67
90
  });
68
- function formatUpdateFailure(status, signal) {
69
- const knownState = status === null ? `installer was interrupted${signal ? ` by ${signal}` : ""}.` : `installer exited with status ${status}.`;
91
+ function formatUpdateFailure(status, signal, updateCommand2) {
92
+ const knownState = status === null ? `package update was interrupted${signal ? ` by ${signal}` : ""}.` : `package update exited with status ${status}.`;
70
93
  return [
71
94
  "Error: failed to update Libretto to the latest version.",
72
95
  `Known state: ${knownState}`,
73
- `Try: ${UPDATE_COMMAND}`,
96
+ `Try: ${updateCommand2}`,
74
97
  "Help: libretto help update"
75
98
  ].join("\n");
76
99
  }
77
100
  const updateCommand = SimpleCLI.command({
78
101
  description: "Update Libretto to the latest version"
79
102
  }).input(updateInput).handle(async ({ input }) => {
103
+ const packageManager = detectProjectPackageManager();
104
+ const updateCommand2 = packageInstallCommand(packageManager, "libretto@latest");
80
105
  if (input.dryRun) {
81
106
  console.log("Update command:");
82
- console.log(` ${UPDATE_COMMAND}`);
107
+ console.log(` ${updateCommand2}`);
83
108
  console.log("No changes made.");
84
109
  return;
85
110
  }
86
111
  const currentVersion = readCurrentCliVersion();
112
+ const localPackageVersion = readLocalPackageVersion();
113
+ const installedVersion = localPackageVersion ?? currentVersion;
87
114
  const latestVersion = readLatestNpmVersion();
88
- console.log(`Current version: ${currentVersion}`);
115
+ console.log(`Current version: ${installedVersion}`);
89
116
  console.log(`Latest version: ${latestVersion}`);
90
- if (currentVersion === latestVersion) {
91
- console.log(`Libretto is already up to date (${currentVersion}).`);
117
+ if (localPackageVersion && installedVersion === latestVersion) {
118
+ console.log(`Libretto is already up to date (${installedVersion}).`);
92
119
  console.log("No further action required.");
93
120
  return;
94
121
  }
95
- console.log("Updating Libretto to latest...");
96
- const result = spawnSync("bash", ["-lc", UPDATE_COMMAND], {
122
+ if (!localPackageVersion) {
123
+ console.log("Local package: not installed");
124
+ }
125
+ console.log("Updating local Libretto package to latest...");
126
+ const result = spawnSync(updateCommand2, {
97
127
  stdio: "inherit",
98
- env: {
99
- ...process.env,
100
- LIBRETTO_VERSION: "latest"
101
- }
128
+ shell: true
102
129
  });
103
130
  if (result.error) {
104
131
  throw new Error(
105
132
  [
106
- "Error: failed to start the Libretto installer.",
133
+ "Error: failed to start the Libretto package update.",
107
134
  `Known state: ${result.error.message}`,
108
- `Try: ${UPDATE_COMMAND}`,
135
+ `Try: ${updateCommand2}`,
109
136
  "Help: libretto help update"
110
137
  ].join("\n")
111
138
  );
112
139
  }
113
140
  if (result.status !== 0) {
114
- throw new Error(formatUpdateFailure(result.status, result.signal));
141
+ throw new Error(
142
+ formatUpdateFailure(result.status, result.signal, updateCommand2)
143
+ );
115
144
  }
116
- console.log("Libretto updated to latest.");
145
+ console.log("Local Libretto package updated to latest.");
117
146
  console.log("No further action required.");
118
147
  });
119
148
  export {
@@ -47,6 +47,7 @@ import {
47
47
  loadDefaultWorkflow
48
48
  } from "../workflow-runtime.js";
49
49
  import { WorkflowController } from "../workflow-runner/runner.js";
50
+ import { validateWorkflowInput } from "../../../shared/workflow/workflow.js";
50
51
  function isOperationalPage(page) {
51
52
  const url = page.url();
52
53
  return !url.startsWith("devtools://") && !url.startsWith("chrome-error://");
@@ -682,6 +683,7 @@ async function main() {
682
683
  loadedWorkflow = await loadDefaultWorkflow(
683
684
  getAbsoluteIntegrationPath(config.workflow.integrationPath)
684
685
  );
686
+ validateWorkflowInput(loadedWorkflow, config.workflow.params ?? {});
685
687
  } catch (error) {
686
688
  throw new UserFacingStartupError(
687
689
  error instanceof Error ? error.message : String(error)
@@ -64,8 +64,11 @@ function createLibrettoCloudProvider() {
64
64
  };
65
65
  },
66
66
  async closeSession(sessionId) {
67
- const json = await closeCloudSession(endpoint, apiKey, sessionId);
68
- return { replayUrl: json.replay_url ?? void 0 };
67
+ await closeCloudSession(endpoint, apiKey, sessionId);
68
+ const replayUrl = await getCloudRecordingUrl(endpoint, apiKey, sessionId).catch(
69
+ () => void 0
70
+ );
71
+ return { replayUrl };
69
72
  }
70
73
  };
71
74
  }
@@ -153,8 +156,24 @@ async function closeCloudSession(endpoint, apiKey, sessionId) {
153
156
  `Libretto Cloud API error closing session ${sessionId} (${resp.status}): ${body}`
154
157
  );
155
158
  }
159
+ }
160
+ async function getCloudRecordingUrl(endpoint, apiKey, sessionId) {
161
+ const resp = await fetch(`${endpoint}/v1/recordings/get`, {
162
+ method: "POST",
163
+ headers: {
164
+ "x-api-key": apiKey,
165
+ "Content-Type": "application/json"
166
+ },
167
+ body: JSON.stringify({ json: { session_id: sessionId } })
168
+ });
169
+ if (!resp.ok) {
170
+ const body = await resp.text();
171
+ throw new Error(
172
+ `Libretto Cloud API error reading recording for session ${sessionId} (${resp.status}): ${body}`
173
+ );
174
+ }
156
175
  const { json } = await resp.json();
157
- return json;
176
+ return json.recording_url ?? void 0;
158
177
  }
159
178
  function createStartupSessionCleanup(endpoint, apiKey, sessionId) {
160
179
  let cancelled = false;
@@ -68,115 +68,28 @@ function readInstalledSkillVersions() {
68
68
  }
69
69
  return [...versions];
70
70
  }
71
- function parseVersion(version) {
72
- const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/);
73
- if (!match) {
74
- return null;
75
- }
76
- return {
77
- major: Number(match[1]),
78
- minor: Number(match[2]),
79
- patch: Number(match[3]),
80
- prerelease: match[4] ?? null
81
- };
82
- }
83
- function compareVersions(left, right) {
84
- const parsedLeft = parseVersion(left);
85
- const parsedRight = parseVersion(right);
86
- if (!parsedLeft || !parsedRight) {
87
- return left.localeCompare(right);
88
- }
89
- for (const key of ["major", "minor", "patch"]) {
90
- const diff = parsedLeft[key] - parsedRight[key];
91
- if (diff !== 0) {
92
- return diff;
93
- }
94
- }
95
- if (parsedLeft.prerelease === parsedRight.prerelease) {
96
- return 0;
97
- }
98
- if (parsedLeft.prerelease === null) {
99
- return 1;
100
- }
101
- if (parsedRight.prerelease === null) {
102
- return -1;
103
- }
104
- return parsedLeft.prerelease.localeCompare(parsedRight.prerelease);
105
- }
106
- function selectTargetVersion(versions) {
107
- const counts = /* @__PURE__ */ new Map();
108
- for (const version of versions) {
109
- counts.set(version, (counts.get(version) ?? 0) + 1);
110
- }
111
- const byCountThenVersion = [...counts.entries()].sort(
112
- ([leftVersion, leftCount], [rightVersion, rightCount]) => rightCount - leftCount || compareVersions(rightVersion, leftVersion)
113
- );
114
- return byCountThenVersion[0]?.[0] ?? versions[0] ?? "latest";
115
- }
116
- function formatVersion(version, targetVersion) {
117
- return version === targetVersion ? version : `${version} (out of date)`;
118
- }
119
- function formatSkillVersions(versions, targetVersion) {
71
+ function formatSkillVersions(versions) {
120
72
  if (versions.length === 0) {
121
73
  return "not installed";
122
74
  }
123
- return versions.map((version) => formatVersion(version, targetVersion)).join(", ");
124
- }
125
- function formatUpdateInstructions(components) {
126
- const instructions = [];
127
- if (components.cliVersion !== components.targetVersion) {
128
- instructions.push(
129
- ` global CLI: curl -fsSL https://libretto.sh/install.sh | LIBRETTO_VERSION=${components.targetVersion} bash`
130
- );
131
- }
132
- if (components.localPackageVersion && components.localPackageVersion !== components.targetVersion) {
133
- instructions.push(
134
- ` local package: npm install libretto@${components.targetVersion}`
135
- );
136
- }
137
- if (components.skillVersions.length > 0 && components.skillVersions.some(
138
- (skillVersion) => skillVersion !== components.targetVersion
139
- )) {
140
- instructions.push(" agent skill: libretto setup");
141
- }
142
- return instructions;
75
+ return versions.join(", ");
143
76
  }
144
77
  function formatVersionWarning(components) {
145
- const targetVersion = selectTargetVersion([
146
- components.cliVersion,
147
- ...components.localPackageVersion ? [components.localPackageVersion] : [],
148
- ...components.skillVersions
149
- ]);
150
78
  const skillLabel = components.skillVersions.length > 1 ? "agent skills" : "agent skill";
151
- const updateInstructions = formatUpdateInstructions({
152
- ...components,
153
- targetVersion
154
- });
155
79
  return [
156
- "WARNING: Libretto version mismatch detected.",
157
- "",
158
- ` global CLI: ${formatVersion(components.cliVersion, targetVersion)}`,
159
- ` local package: ${components.localPackageVersion ? formatVersion(components.localPackageVersion, targetVersion) : "not installed"}`,
160
- ` ${skillLabel}: ${formatSkillVersions(
161
- components.skillVersions,
162
- targetVersion
163
- )}`,
164
- "",
165
- "How to update:",
166
- ...updateInstructions
80
+ "WARNING: Libretto skill version does not match the local package.",
81
+ ` local package: ${components.localPackageVersion ?? `${components.cliVersion} (current command)`}`,
82
+ ` ${skillLabel}: ${formatSkillVersions(components.skillVersions)}`,
83
+ "Fix: run libretto setup"
167
84
  ].join("\n");
168
85
  }
169
86
  function warnIfLibrettoVersionsDiffer() {
170
87
  try {
171
88
  const cliVersion = readCurrentCliVersion();
172
89
  const localPackageVersion = readLocalPackageVersion();
90
+ const packageVersion = localPackageVersion ?? cliVersion;
173
91
  const skillVersions = readInstalledSkillVersions();
174
- const observedVersions = /* @__PURE__ */ new Set([
175
- cliVersion,
176
- ...localPackageVersion ? [localPackageVersion] : [],
177
- ...skillVersions
178
- ]);
179
- if (observedVersions.size <= 1) {
92
+ if (skillVersions.length === 0 || skillVersions.every((skillVersion) => skillVersion === packageVersion)) {
180
93
  return;
181
94
  }
182
95
  console.error(
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ export { InstrumentationOptions, InstrumentedPage, installInstrumentation, instr
12
12
  export { GhostCursorOptions, ensureGhostCursor, ghostClick, hideGhostCursor, moveGhostCursor } from './shared/visualization/ghost-cursor.js';
13
13
  export { HighlightOptions, clearHighlights, ensureHighlightLayer, showHighlight } from './shared/visualization/highlight.js';
14
14
  export { BrowserSession, LaunchBrowserArgs, launchBrowser } from './shared/run/browser.js';
15
- export { ExportedLibrettoWorkflow, LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, LibrettoWorkflowInputError, LibrettoWorkflowSchemas, getDefaultWorkflowFromModuleExports, getWorkflowFromModuleExports, getWorkflowsFromModuleExports, isLibrettoWorkflow, workflow } from './shared/workflow/workflow.js';
15
+ export { ExportedLibrettoWorkflow, LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, LibrettoWorkflowInputError, LibrettoWorkflowSchemas, WorkflowInputValidator, getDefaultWorkflowFromModuleExports, getWorkflowFromModuleExports, getWorkflowsFromModuleExports, isLibrettoWorkflow, validateWorkflowInput, workflow } from './shared/workflow/workflow.js';
16
16
  import 'zod';
17
17
  import 'playwright';
18
18
  import 'ai';
package/dist/index.js CHANGED
@@ -59,6 +59,7 @@ import {
59
59
  LibrettoWorkflow,
60
60
  LibrettoWorkflowInputError,
61
61
  LIBRETTO_WORKFLOW_BRAND,
62
+ validateWorkflowInput,
62
63
  workflow
63
64
  } from "./shared/workflow/workflow.js";
64
65
  const isDirectExecution = () => {
@@ -113,5 +114,6 @@ export {
113
114
  prettyConsoleSink,
114
115
  serializeSessionState,
115
116
  showHighlight,
117
+ validateWorkflowInput,
116
118
  workflow
117
119
  };
@@ -16,6 +16,11 @@ declare class LibrettoWorkflowInputError extends Error {
16
16
  readonly zodError: z.ZodError;
17
17
  constructor(workflowName: string, zodError: z.ZodError);
18
18
  }
19
+ type WorkflowInputValidator = {
20
+ readonly name: string;
21
+ readonly inputSchema?: z.ZodType;
22
+ };
23
+ declare function validateWorkflowInput(workflow: WorkflowInputValidator, input: unknown): void;
19
24
  declare class LibrettoWorkflow<InputSchema extends z.ZodType = z.ZodType<unknown>, OutputSchema extends z.ZodType = z.ZodType<unknown>> {
20
25
  readonly [LIBRETTO_WORKFLOW_BRAND] = true;
21
26
  readonly name: string;
@@ -40,4 +45,4 @@ declare function getWorkflowFromModuleExports(moduleExports: WorkflowModuleExpor
40
45
  declare function workflow<InputSchema extends z.ZodType, OutputSchema extends z.ZodType>(name: string, schemas: LibrettoWorkflowSchemas<InputSchema, OutputSchema>, handler: LibrettoWorkflowHandler<z.infer<InputSchema>, z.infer<OutputSchema>>): LibrettoWorkflow<InputSchema, OutputSchema>;
41
46
  declare function workflow<Input = unknown, Output = unknown>(name: string, handler: LibrettoWorkflowHandler<Input, Output>): LibrettoWorkflow<z.ZodType<Input>, z.ZodType<Output>>;
42
47
 
43
- export { type ExportedLibrettoWorkflow, LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, type LibrettoWorkflowContext, type LibrettoWorkflowHandler, LibrettoWorkflowInputError, type LibrettoWorkflowSchemas, getDefaultWorkflowFromModuleExports, getWorkflowFromModuleExports, getWorkflowsFromModuleExports, isLibrettoWorkflow, workflow };
48
+ export { type ExportedLibrettoWorkflow, LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, type LibrettoWorkflowContext, type LibrettoWorkflowHandler, LibrettoWorkflowInputError, type LibrettoWorkflowSchemas, type WorkflowInputValidator, getDefaultWorkflowFromModuleExports, getWorkflowFromModuleExports, getWorkflowsFromModuleExports, isLibrettoWorkflow, validateWorkflowInput, workflow };
@@ -19,6 +19,17 @@ function formatZodErrorMessage(workflowName, zodError) {
19
19
  ...lines
20
20
  ].join("\n");
21
21
  }
22
+ function parseWorkflowInput(workflowName, inputSchema, input) {
23
+ if (!inputSchema) return input;
24
+ const result = inputSchema.safeParse(input);
25
+ if (!result.success) {
26
+ throw new LibrettoWorkflowInputError(workflowName, result.error);
27
+ }
28
+ return result.data;
29
+ }
30
+ function validateWorkflowInput(workflow2, input) {
31
+ parseWorkflowInput(workflow2.name, workflow2.inputSchema, input);
32
+ }
22
33
  class LibrettoWorkflow {
23
34
  [LIBRETTO_WORKFLOW_BRAND] = true;
24
35
  name;
@@ -38,16 +49,7 @@ class LibrettoWorkflow {
38
49
  this.handler = handler;
39
50
  }
40
51
  async run(ctx, input) {
41
- let parsed;
42
- if (this.inputSchema) {
43
- const result = this.inputSchema.safeParse(input);
44
- if (!result.success) {
45
- throw new LibrettoWorkflowInputError(this.name, result.error);
46
- }
47
- parsed = result.data;
48
- } else {
49
- parsed = input;
50
- }
52
+ const parsed = parseWorkflowInput(this.name, this.inputSchema, input);
51
53
  return this.handler(ctx, parsed);
52
54
  }
53
55
  }
@@ -118,5 +120,6 @@ export {
118
120
  getWorkflowFromModuleExports,
119
121
  getWorkflowsFromModuleExports,
120
122
  isLibrettoWorkflow,
123
+ validateWorkflowInput,
121
124
  workflow
122
125
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libretto",
3
- "version": "0.6.19",
3
+ "version": "0.6.21",
4
4
  "description": "AI-powered browser automation library and CLI built on Playwright",
5
5
  "license": "MIT",
6
6
  "homepage": "https://libretto.sh",