Execute a workflow file and resume paused workflows.
run command executes the default export from a TypeScript workflow file against a live browser. Use it to verify a workflow after creating or editing it.12npx libretto run ./integration.ts npx libretto run ./integration.ts --headless
1npx libretto run <file> [flags]
<file>: path to the TypeScript workflow file. The file must have a default-exported workflow().path --headlessbooleanpath --headedbooleanpath --sessionstringexec, snapshot, or resume after a failure or
pause.path --paramsstringschemas.input before the handler runs. Example:
--params '{"status":"open"}'.path --params-filestring--params. When the workflow uses Zod schemas, the file contents are
validated against schemas.input.path --viewportstringWIDTHxHEIGHT format, for example 1920x1080. Falls back to
.libretto/config.json, then 1366x768.path --tsconfigstringtsconfig.json file for module resolution during workflow
compilation.path --no-visualizebooleanpath --providerstringlocal, kernel, browserbase,
steel, or libretto-cloud. Overrides LIBRETTO_PROVIDER and the provider setting
in .libretto/config.json. Cannot be combined with --cdp.path --cdpstring--remote-debugging-port. Cannot be combined with --provider, --headed,
--headless, or --viewport. Libretto attaches to the selected page without
navigating to workflow startUrl, so the existing URL and authentication
state stay intact. Closing the Libretto session disconnects from CDP and does
not terminate the remote browser.path --pagestring--cdp, target a specific discovered page id such as page-0 or
page-1. Omit to use the default (last operational page). Requires --cdp.provider in .libretto/config.json. See Alternative
providers for Kernel, Browserbase, Steel, AWS, and
GCP setup.--cdp when the browser already exists (Electron, remote debugging Chrome, CI browser). Use interactive connect to explore with exec / snapshot; use run --cdp to execute a workflow file against that same endpoint. Unlike a normal run, --cdp does not open workflow startUrl — put the browser on the page you want before the run, or navigate inside the handler.12345# Chrome or Electron started with --remote-debugging-port=9222 npx libretto run ./integration.ts --cdp http://127.0.0.1:9222 # Target a specific window/page (ids from `libretto pages` on a connect session) npx libretto run ./integration.ts --cdp http://127.0.0.1:9222 --page page-1
snapshot and exec to inspect the live page state before editing the workflow code.1234567891011# Workflow fails, browser stays open npx libretto run ./integration.ts --session debug-flow --headed # Inspect the failure state npx libretto snapshot --session debug-flow # Prototype a fix npx libretto exec --session debug-flow "await page.locator('.error-message').textContent()" # Re-run after fixing the code npx libretto run ./integration.ts --headless
run --headless for the normal fix-and-verify loop. When the headless run passes, do a final headed run if you or the user wants to watch the finished workflow:12345# Fix/verify loop, fast, no visible browser npx libretto run ./integration.ts --headless # Final confirmation run, shows the browser npx libretto run ./integration.ts --headed
1234567891011121314# Basic run npx libretto run ./integration.ts # Headless run with inline params npx libretto run ./integration.ts --headless --params '{"status":"open"}' # Run with a params file npx libretto run ./integration.ts --params-file ./params.json # Run with explicit session name for post-failure inspection npx libretto run ./integration.ts --session debug-flow --headed # Run against an external CDP / Electron browser npx libretto run ./integration.ts --cdp http://127.0.0.1:9222
resume command unpauses a workflow that has stopped at an await pause(session) call. Use it repeatedly until the workflow completes or pauses again at the next breakpoint.1npx libretto resume --session debug-example
path --sessionstringrequiredpause() APIawait pause(session) calls in your workflow file to create interactive breakpoints, similar to debugger breakpoints in the browser flow. The workflow stops at each pause() call and waits for resume before continuing.123456789101112131415161718192021222324252627import { workflow, pause } from "libretto"; import { z } from "zod"; export default workflow( "myWorkflow", { input: z.object({}), output: z.object({ done: z.boolean(), }), }, async (ctx) => { const { session, page } = ctx; await page.goto("https://linkedin.com"); // Pause here for inspection await pause(session); await page.locator("#submit").click(); // Pause again before the final step await pause(session); return { done: true }; }, );
pause() is a no-op when NODE_ENV === "production". You can leave
pause() calls in your workflow code during development and they will be
silently skipped in production.1npx libretto run ./integration.ts --session debug-flow --headed
pause(), it prints Workflow paused. and returns. The browser stays open.123npx libretto snapshot --session debug-flow npx libretto exec --session debug-flow "await page.url()"
1npx libretto resume --session debug-flow
resume until the workflow prints Integration completed. or fails with an error.1npx libretto run ./integration.ts --headless