Skip to main content
Define and run reusable browser automation workflows.
The workflow() factory is the entry point for every Libretto automation. You wrap your Playwright logic in a typed handler, export the result, and the CLI (or your own runner) takes care of launching a browser, wiring up context, and invoking your handler.

workflow()

Creates a named LibrettoWorkflow from a handler function.
function workflow<Input = unknown, Output = unknown>(
  name: string,
  handler: LibrettoWorkflowHandler<Input, Output>,
): LibrettoWorkflow<Input, Output>;
The handler receives a LibrettoWorkflowContext and typed input, and must return a Promise<Output>.
type LibrettoWorkflowHandler<Input, Output> = (
  ctx: LibrettoWorkflowContext,
  input: Input,
) => Promise<Output>;

LibrettoWorkflowContext

session
string
The session identifier for this workflow run. Use it to correlate logs and state files.
page
Page
A Playwright Page instance ready for navigation and interaction.

Full example

import { workflow, launchBrowser, pause } from "libretto";

export default workflow<{ query: string }, string[]>(
  "main",
  async (ctx, input) => {
    const { page } = ctx;

    await page.goto("https://example.com/search");
    await page.fill("#query", input.query);
    await page.click("#submit");
    await page.waitForSelector(".results");

    const results = await page.$$eval(".result-item", (els) =>
      els.map((el) => el.textContent ?? ""),
    );

    return results;
  },
);

Running with the CLI

Pass the file path to libretto run. The file must have a default-exported workflow():
npx libretto run ./my-workflow.ts
The CLI compiles the file with tsx, launches a Chromium browser, and calls your handler with the session context it constructs.

launchBrowser()

Launches a Playwright Chromium browser and returns a ready-to-use BrowserSession. Use this when you want to run workflows programmatically outside the CLI, or when you need direct access to the Browser or BrowserContext objects.
async function launchBrowser(args: LaunchBrowserArgs): Promise<BrowserSession>;

LaunchBrowserArgs

sessionName
string
required
A unique identifier for this browser session. Used to name the session state file written under .libretto/sessions/.
headless
boolean
Whether to run Chromium in headless mode. Defaults to false (visible window).
viewport
{ width: number; height: number }
The browser viewport size. Defaults to { width: 1366, height: 768 }.
storageStatePath
string
Path to a Playwright storage state JSON file (cookies, localStorage). Useful for resuming authenticated sessions.

BrowserSession return value

browser
Browser
The underlying Playwright Browser instance.
context
BrowserContext
The Playwright BrowserContext created for this session.
page
Page
The initial Page opened in the context. Pass this to your workflow handler.
debugPort
number
The remote debugging port Chromium is listening on.
metadataPath
string
Absolute path to the session state JSON file written by launchBrowser.
close
() => Promise<void>
Closes the browser and releases all resources.

Programmatic usage

import { launchBrowser } from "libretto";
import main from "./my-workflow";

const session = await launchBrowser({
  sessionName: "my-run",
  headless: true,
});

try {
  const result = await main.run(
    {
      session: "my-run",
      page: session.page,
    },
    { query: "hello world" },
  );
  console.log(result);
} finally {
  await session.close();
}

pause()

Pauses a running workflow so you can inspect browser state interactively, then resume from the CLI.
async function pause(session: string): Promise<void>;
session
string
required
The session identifier. Must match the session name used when the workflow was started.
pause() is a no-op when NODE_ENV === "production". It is safe to leave pause() calls in your code without worrying about them blocking production runs.
When called in a non-production environment, pause() writes a .paused signal file and polls for a .resume signal. Use the Libretto CLI to send the resume signal:
npx libretto resume --session <session-name>

Example

import { workflow, pause } from "libretto";

export default workflow<{ id: string }, void>(
  "main",
  async (ctx, input) => {
    const { page, session } = ctx;

    await page.goto(`https://example.com/items/${input.id}`);

    // Pause here to inspect the page before continuing
    await pause(session);

    await page.click("#confirm");
  },
);