Builder API
workflow(), WorkflowBuilder, and SandboxBuilder — the @alineo-labs/workflow lazy orchestration layer.
import { workflow, WorkflowBuilder, SandboxBuilder } from "@alineo-labs/workflow";workflow()
workflow(client: Sandbox): WorkflowBuilderCreates a WorkflowBuilder attached to a Sandbox client.
import { workflow } from "@alineo-labs/workflow";
await workflow(client)
.sandbox({ image: "node:22", resources: { cpu: "500m", memory: "512Mi" } }, (sb) => {
sb.exec("npm ci");
sb.exec("npm test");
})
.pipe(process.stdout);WorkflowBuilder
Collects stages and executes them all when .pipe() or .result() is awaited. Sandbox lifecycle (create + close) is managed automatically.
.sandbox()
.sandbox(opts: SandboxOptions, fn: (sb: SandboxBuilder) => void): thisAdd a sandbox stage. The fn callback receives a SandboxBuilder to queue operations synchronously. Multiple .sandbox() calls run sequentially.
.parallel()
.parallel(configs: SandboxOptions[], fn: (sb: SandboxBuilder) => void): thisRun the same operation across multiple containers simultaneously. All configs receive the same fn. Results are merged.
await workflow(client)
.parallel([{ image: "node:20" }, { image: "node:22" }, { image: "node:24" }], (sb) =>
sb.exec("npm test"),
)
.pipe(process.stdout);.sequence()
.sequence(steps: SequenceStep[]): thisRun sandboxes one after another. Each step's callback receives the previous step's WorkflowResult as prev:
await workflow(client)
.sequence([
{
image: "node:22",
name: "build",
resources: { cpu: "1", memory: "512Mi" },
run: (sb) => sb.exec("npm run build"),
},
{
image: "ubuntu:22.04",
name: "deploy",
resources: { cpu: "500m", memory: "256Mi" },
run: (sb, prev) => sb.exec("./deploy.sh"),
},
])
.pipe(process.stdout);.pipe()
await workflow.pipe(writable: { write(chunk: string): unknown }): Promise<void>Execute the workflow and stream stdout to a writable. Flushes all queued stages.
.result()
await workflow.result(): Promise<WorkflowResult>Execute the workflow and return the combined result:
interface WorkflowResult {
stdout: string; // concatenated stdout from all sandboxes
vars: Record<string, unknown>; // values captured by readFile(path, as)
}SandboxBuilder
Queues operations synchronously. All methods return this for chaining. Nothing runs until the WorkflowBuilder is flushed.
.exec()
sb.exec(cmd: string, opts?: ExecOptions): thisQueue a shell command.
.execCode()
sb.execCode(code: string, opts?: ExecCodeOptions): thisQueue a code execution via the interpreter.
.writeFile()
sb.writeFile(path: string, content: string): thisQueue a file write into the sandbox.
.readFile()
sb.readFile(path: string, as: string): thisQueue a file read. The content is stored in vars[as] and available via WorkflowResult.vars.
.moveFile()
sb.moveFile(from: string, to: string): thisQueue a file move inside the sandbox.
.deleteFile()
sb.deleteFile(path: string): thisQueue a file deletion.
.checkpoint()
sb.checkpoint(name?: string): thisQueue a container snapshot.
.retry()
sb.retry(
maxAttempts: number,
fn: (sb: SandboxBuilder) => void,
opts?: RetryOptions,
): thisQueue a retry block. Runs up to maxAttempts total attempts (the first run plus retries), waiting delayMs between each on failure.
interface RetryOptions {
delayMs?: number; // default: 1000
backoff?: "fixed" | "exponential"; // default: "fixed"
}.when()
sb.when(
pred: (ctx: { stdout: string; exitCode: number; vars: Record<string, unknown> }) => boolean,
then: (sb: SandboxBuilder) => void,
otherwise?: (sb: SandboxBuilder) => void,
): thisQueue a conditional branch. pred is evaluated at flush time with the current execution context.
.forEach()
sb.forEach(
items: unknown[],
fn: (sb: SandboxBuilder, item: unknown, index: number) => void,
opts?: ForEachOptions,
): thisQueue iteration over a list of items.
interface ForEachOptions {
concurrency?: number; // default: 1 (sequential)
}