Skip to main content
The PI Plugin is a lightweight HTTP client that communicates with a running Smithers server. Use it to start and resume workflow runs, stream lifecycle events, manage approvals, and query run status from any TypeScript process.

Import

import {
  runWorkflow,
  resume,
  approve,
  deny,
  streamEvents,
  getStatus,
  getFrames,
  cancel,
  listRuns,
} from "smithers-orchestrator/pi-plugin";

Default Configuration

All functions accept optional baseUrl and apiKey parameters. When omitted:
  • baseUrl defaults to http://127.0.0.1:7331
  • apiKey defaults to undefined (no authentication header sent)
If an apiKey is provided, it is sent as a Bearer token in the Authorization header.

Functions

runWorkflow

Start a new workflow run on the server.
async function runWorkflow(args: {
  workflowPath: string;
  input: unknown;
  runId?: string;
  baseUrl?: string;
  apiKey?: string;
}): Promise<{ runId: string }>
Parameters:
ParameterTypeRequiredDescription
workflowPathstringYesPath to the .tsx workflow file on the server
inputunknownYesInput data passed to the workflow
runIdstringNoCustom run ID. If omitted, the server generates one.
baseUrlstringNoServer URL (default: http://127.0.0.1:7331)
apiKeystringNoAuthentication token
Example:
const run = await runWorkflow({
  workflowPath: "./workflows/bugfix.tsx",
  input: { description: "Auth tokens expire silently" },
  apiKey: "sk-my-token",
});

console.log(run.runId); // "smi_abc123"

resume

Resume a previously paused or failed workflow run.
async function resume(args: {
  workflowPath: string;
  runId: string;
  baseUrl?: string;
  apiKey?: string;
}): Promise<{ runId: string }>
Parameters:
ParameterTypeRequiredDescription
workflowPathstringYesPath to the .tsx workflow file
runIdstringYesThe run ID to resume
baseUrlstringNoServer URL
apiKeystringNoAuthentication token
Example:
await resume({
  workflowPath: "./workflows/bugfix.tsx",
  runId: "smi_abc123",
});
Note: Internally, this calls POST /v1/runs with resume: true set in the body.

approve

Approve a node that is waiting for human approval.
async function approve(args: {
  runId: string;
  nodeId: string;
  iteration?: number;
  note?: string;
  baseUrl?: string;
  apiKey?: string;
}): Promise<{ runId: string }>
Parameters:
ParameterTypeRequiredDescription
runIdstringYesThe workflow run ID
nodeIdstringYesThe node ID to approve
iterationnumberNoRalph loop iteration (default: 0)
notestringNoOptional note explaining the approval
baseUrlstringNoServer URL
apiKeystringNoAuthentication token
Example:
await approve({
  runId: "smi_abc123",
  nodeId: "deploy",
  note: "Changes look good, approved for production.",
});

deny

Deny a node that is waiting for human approval.
async function deny(args: {
  runId: string;
  nodeId: string;
  iteration?: number;
  note?: string;
  baseUrl?: string;
  apiKey?: string;
}): Promise<{ runId: string }>
Parameters:
ParameterTypeRequiredDescription
runIdstringYesThe workflow run ID
nodeIdstringYesThe node ID to deny
iterationnumberNoRalph loop iteration (default: 0)
notestringNoOptional note explaining the denial
baseUrlstringNoServer URL
apiKeystringNoAuthentication token
Example:
await deny({
  runId: "smi_abc123",
  nodeId: "deploy",
  note: "Tests are failing, please fix before deploying.",
});

streamEvents

Stream lifecycle events from a run via Server-Sent Events. Returns an AsyncIterable of SmithersEvent objects.
async function* streamEvents(args: {
  runId: string;
  baseUrl?: string;
  apiKey?: string;
}): AsyncIterable<SmithersEvent>
Parameters:
ParameterTypeRequiredDescription
runIdstringYesThe workflow run ID
baseUrlstringNoServer URL
apiKeystringNoAuthentication token
Example:
for await (const event of streamEvents({ runId: "smi_abc123" })) {
  console.log(`[${event.type}]`, event);

  if (event.type === "RunFinished") {
    console.log("Workflow completed successfully.");
    break;
  }

  if (event.type === "RunFailed") {
    console.error("Workflow failed:", event.error);
    break;
  }

  if (event.type === "NodeWaitingApproval") {
    console.log(`Node ${event.nodeId} needs approval.`);
  }
}
Behavior:
  • Connects to GET /v1/runs/:runId/events and parses the SSE stream.
  • Each data: line is parsed as JSON and yielded as a SmithersEvent.
  • Keep-alive comments are silently filtered out.
  • The generator completes when the SSE stream closes (typically when the run reaches a terminal state).

getStatus

Get the current status and summary of a run.
async function getStatus(args: {
  runId: string;
  baseUrl?: string;
  apiKey?: string;
}): Promise<{
  runId: string;
  workflowName: string;
  status: string;
  startedAtMs: number | null;
  finishedAtMs: number | null;
  summary: Record<string, number>;
}>
Parameters:
ParameterTypeRequiredDescription
runIdstringYesThe workflow run ID
baseUrlstringNoServer URL
apiKeystringNoAuthentication token
Example:
const status = await getStatus({ runId: "smi_abc123" });
console.log(status.status);  // "running" | "finished" | "failed" | ...
console.log(status.summary); // { finished: 3, pending: 2, "in-progress": 1 }

getFrames

List render frames for a run.
async function getFrames(args: {
  runId: string;
  tail?: number;
  baseUrl?: string;
  apiKey?: string;
}): Promise<any[]>
Parameters:
ParameterTypeRequiredDescription
runIdstringYesThe workflow run ID
tailnumberNoMaximum number of frames to return (default: 20)
baseUrlstringNoServer URL
apiKeystringNoAuthentication token
Example:
const frames = await getFrames({ runId: "smi_abc123", tail: 5 });
console.log(`Last ${frames.length} frames:`);
for (const frame of frames) {
  console.log(`  Frame ${frame.frameNo}`);
}

cancel

Cancel a running workflow.
async function cancel(args: {
  runId: string;
  baseUrl?: string;
  apiKey?: string;
}): Promise<{ runId: string }>
Parameters:
ParameterTypeRequiredDescription
runIdstringYesThe workflow run ID
baseUrlstringNoServer URL
apiKeystringNoAuthentication token
Example:
await cancel({ runId: "smi_abc123" });

listRuns

List all runs tracked by the server. Requires the server to have a db configured.
async function listRuns(args?: {
  limit?: number;
  status?: string;
  baseUrl?: string;
  apiKey?: string;
}): Promise<any[]>
Parameters:
ParameterTypeRequiredDescription
limitnumberNoMaximum number of runs to return (default: server’s default of 50)
statusstringNoFilter by status (e.g., "running", "finished", "failed")
baseUrlstringNoServer URL
apiKeystringNoAuthentication token
Example:
// List the 10 most recent runs
const runs = await listRuns({ limit: 10 });

// List only failed runs
const failed = await listRuns({ status: "failed" });

Complete Example

import {
  runWorkflow,
  streamEvents,
  approve,
  getStatus,
  cancel,
} from "smithers-orchestrator/pi-plugin";

const apiKey = process.env.SMITHERS_API_KEY;

// Start a workflow
const run = await runWorkflow({
  workflowPath: "./workflows/deploy.tsx",
  input: { branch: "main", environment: "staging" },
  apiKey,
});

console.log(`Started run: ${run.runId}`);

// Stream events and handle approvals
for await (const event of streamEvents({ runId: run.runId, apiKey })) {
  switch (event.type) {
    case "NodeStarted":
      console.log(`Task ${event.nodeId} started (attempt ${event.attempt})`);
      break;

    case "NodeFinished":
      console.log(`Task ${event.nodeId} completed`);
      break;

    case "NodeWaitingApproval":
      console.log(`Task ${event.nodeId} needs approval`);
      // Auto-approve in staging
      await approve({
        runId: run.runId,
        nodeId: event.nodeId,
        note: "Auto-approved for staging",
        apiKey,
      });
      break;

    case "RunFinished":
      console.log("Deployment complete.");
      break;

    case "RunFailed":
      console.error("Deployment failed:", event.error);
      break;
  }
}

// Check final status
const status = await getStatus({ runId: run.runId, apiKey });
console.log(`Final status: ${status.status}`);