Docs

Server (Embedding)

@cuitty/scrape/server is the backend half. It exposes createScrapeServer(config), which returns an object with health(), run(), handleRequest(req), and dispose().

Server Config

createScrapeServer(config) accepts:

OptionTypePurpose
bridgeScrapeAiBridgePlanner, extractor, actor, and healer. Defaults to the mock bridge.
sessionFactory() => BrowserSessionCreates one browser session per run. Defaults to the mock session.
credentialsCredentialProviderResolves Safe refs and session storage state. Defaults to the mock provider.
clock() => stringOptional deterministic ISO timestamp source.
idgen(prefix: string) => stringOptional deterministic ID generator.

Starting a Server

The current API does not provide server.start(port). Wrap handleRequest(req) with your runtime's HTTP server.

import { createScrapeServer } from "@cuitty/scrape/server";

const scrape = createScrapeServer();

Bun.serve({
  port: 4340,
  fetch(request) {
    return scrape.handleRequest(request);
  },
});

console.log("Scrape API on http://127.0.0.1:4340/api/scrape");

In-Process Runs

Use server.run(request, interactionHandler) when you do not need HTTP. This path blocks until completion or until the supplied interaction handler resolves a human-in-the-loop request.

import { createScrapeServer } from "@cuitty/scrape/server";

const server = createScrapeServer();
const result = await server.run(
  { url: "https://example.com", intent: "Confirm the page has content" },
  async (request) => ({ id: request.id, outcome: "aborted" }),
);

console.log(result.status, result.answer);

Middleware Shape

handleRequest(req) implements the /api/scrape/* routes. In Bun, Express adapters, Hono, Astro endpoints, or edge runtimes, route matching should forward only the Scrape prefix to this handler.

import { createScrapeServer } from "@cuitty/scrape/server";

const scrape = createScrapeServer();

export async function handleApiRequest(request: Request): Promise<Response> {
  const url = new URL(request.url);
  if (url.pathname.startsWith("/api/scrape/")) {
    return scrape.handleRequest(request);
  }
  return new Response("not found", { status: 404 });
}

Live Server

@cuitty/scrape/live composes the real browser session with the Claude bridge when ANTHROPIC_API_KEY is available. Without a key, it still uses Playwright but falls back to the mock bridge.

import { createLiveScrapeServer } from "@cuitty/scrape/live";

const server = createLiveScrapeServer({
  playwright: { headless: true, channel: "chrome" },
  forceMockBridge: !process.env.ANTHROPIC_API_KEY,
});

Bun.serve({ port: 4340, fetch: (request) => server.handleRequest(request) });

Interaction Pause and Resume

The async HTTP run manager parks a run in needs-interaction when the engine raises an interaction request. The client or caller responds through POST /api/scrape/runs/:runId/interactions/:interactionId; the server resumes the parked promise and continues the run.