workflows/approval.tsx
Ghost doc — Real script from workflows/approval.tsx. Demonstrates needsApproval for human-in-the-loop workflows.
Source
/** @jsxImportSource smithers-orchestrator */
// workflows/approval.tsx
import { createSmithers } from "smithers-orchestrator";
import { z } from "zod";
const { Workflow, Sequence, Task, smithers, outputs } = createSmithers({
input: z.object({
name: z.string(),
}),
output: z.object({
message: z.string(),
length: z.number(),
}),
});
export default smithers((ctx) => (
<Workflow name="approval">
<Sequence>
<Task id="approve" output={outputs.output} needsApproval>
{{
message: `Approved: ${ctx.input.name}`,
length: ctx.input.name.length,
}}
</Task>
<Task id="final" output={outputs.output}>
{{
message: `Done: ${ctx.input.name}`,
length: ctx.input.name.length,
}}
</Task>
</Sequence>
</Workflow>
));
Running
bunx smithers-orchestrator up workflows/approval.tsx --input '{"name": "Deploy v2"}'
The workflow pauses at the approval gate:
[approval] Starting run mno345
[approve] Waiting for approval...
[approval] Paused — run `bunx smithers-orchestrator approve` or `bunx smithers-orchestrator deny` to continue.
Approve and resume:
bunx smithers-orchestrator approve mno345 --node approve
bunx smithers-orchestrator up workflows/approval.tsx --run-id mno345 --resume true
[approve] Approved. Running...
[approve] Done -> { message: "Approved: Deploy v2", length: 9 }
[final] Done -> { message: "Done: Deploy v2", length: 9 }
[approval] Completed
Notes
needsApproval — Pauses execution until a human approves. Core human-in-the-loop primitive.
- Resumable — Workflow state is persisted to SQLite. Approval is recorded durably;
--resume true continues from the gate.