Pilot

Pilot React SDK

React hooks and components for Cuitty Pilot — manage playbooks, runs, and real-time events in React apps.

Pilot React SDK

@cuitty/pilot-react provides React hooks and pre-built components for the Pilot API. It wraps @cuitty/pilot-sdk with idiomatic React patterns — context providers, data-fetching hooks, and SSE subscriptions that clean up automatically.

Install

sfw bun add @cuitty/pilot-react
# or
sfw npm install @cuitty/pilot-react

Peer dependencies: react>=18, @cuitty/pilot-sdk.

Quick start

import { PilotProvider, usePlaybooks, useRun, useRunEvents } from "@cuitty/pilot-react";

function App() {
  return (
    <PilotProvider baseUrl="http://localhost:4320" auth={{ apiKey: "pk_..." }}>
      <Dashboard />
    </PilotProvider>
  );
}

function Dashboard() {
  const { data: playbooks, loading } = usePlaybooks();
  const { run, start } = useRun();
  const events = useRunEvents(run?.id);

  if (loading) return <p>Loading...</p>;

  return (
    <div>
      {playbooks?.map((pb) => (
        <button key={pb.id} onClick={() => start({ playbookId: pb.id, mode: "autonomous" })}>
          {pb.title}
        </button>
      ))}
      {events.map((e, i) => (
        <div key={i}>{e.type} {e.stepId}</div>
      ))}
    </div>
  );
}

PilotProvider

Wraps your component tree with a PilotClient instance. Accepts all PilotClientConfig props plus children. The client is recreated when baseUrl or auth.token change, and closed on unmount.

import { PilotProvider } from "@cuitty/pilot-react";

<PilotProvider
  baseUrl="http://localhost:4320"
  auth={{ token: "ey..." }}
  timeout={15000}
>
  {children}
</PilotProvider>
PropTypeDescription
baseUrlstringPilot server URL
auth{ token?: string; cookie?: string; apiKey?: string }Authentication credentials
timeoutnumberPer-request timeout in ms (default 30000)
childrenReact.ReactNodeChild components

usePilot()

Returns the PilotClient from context. Throws if used outside <PilotProvider>.

import { usePilot } from "@cuitty/pilot-react";

const pilot = usePilot();
const health = await pilot.health();

Hooks

usePlaybooks(options?)

Fetches and caches the playbook list. Re-fetches when options change.

const { data, loading, error, refetch } = usePlaybooks();
const { data: cfOnly } = usePlaybooks({ target: "cloudflare" });
ReturnTypeDescription
dataPlaybookSummary[] | nullFetched playbooks, null until loaded
loadingbooleantrue while fetching
errorError | nullFetch error, if any
refetch() => Promise<void>Manually re-fetch the list

useRun()

Manages a single run lifecycle: start, abort, and refresh.

const { run, loading, error, start, abort, refresh } = useRun();

// Start a new run
const result = await start({
  playbookId: "cloudflare.dns.add-a-record",
  mode: "interactive",
  inputs: { zone: "example.com", record_name: "api", ip_address: "1.2.3.4" },
});

// Abort the active run
await abort();

// Refresh run state from the server
await refresh();
ReturnTypeDescription
runRun | nullCurrent run object
loadingbooleantrue while starting a run
errorError | nullError from the last operation
start(req)(StartRunRequest) => Promise<Run | null>Start a run and fetch its full details
abort()() => Promise<void>Abort the current run
refresh()() => Promise<void>Re-fetch current run state

useRunEvents(runId)

Subscribes to real-time SSE events for a run. Automatically unsubscribes on unmount or when runId changes. Returns a growing array of events.

const events = useRunEvents(run?.id);

// events: RunEvent[]
events.forEach((event) => {
  console.log(event.type, event.stepId);
});
ParamTypeDescription
runIdstring | null | undefinedRun ID to subscribe to; null/undefined pauses subscription

Returns RunEvent[] — accumulates all events received since subscription started.

Components

RunStatusBadge

Renders a colored pill badge for a run status.

import { RunStatusBadge } from "@cuitty/pilot-react";

<RunStatusBadge status="running" />
<RunStatusBadge status="completed" />
<RunStatusBadge status="failed" />

Supported statuses: queued, running, awaiting_approval, completed, failed, aborted.

RunTimeline

Renders a vertical timeline of run events with color-coded borders (blue for in-progress, green for complete, red for failures).

import { RunTimeline } from "@cuitty/pilot-react";

const events = useRunEvents(runId);
<RunTimeline events={events} />

PlaybookCard

Displays a playbook summary card with an optional “Run” button.

import { PlaybookCard } from "@cuitty/pilot-react";

<PlaybookCard
  playbook={playbook}
  onRun={(pb) => start({ playbookId: pb.id, mode: "autonomous" })}
/>
PropTypeDescription
playbookPlaybookSummaryPlaybook to display
onRun(playbook: PlaybookSummary) => voidOptional callback when “Run” is clicked

Error handling

Errors from hooks are captured in the error field rather than thrown. For direct PilotClient usage via usePilot(), catch PilotApiError and PilotConnectionError from @cuitty/pilot-sdk.

import { PilotApiError, PilotConnectionError } from "@cuitty/pilot-sdk";

const pilot = usePilot();
try {
  await pilot.runs.start({ playbookId: "nonexistent" });
} catch (err) {
  if (err instanceof PilotApiError) {
    console.error(`API ${err.status}: ${err.statusText}`, err.body);
  } else if (err instanceof PilotConnectionError) {
    console.error("Cannot reach Pilot:", err.message);
  }
}

See also