Workflow SDK
@alineo-labs/workflow adds a lazy, declarative layer over the Core SDK — describe a pipeline, then run it with one await, with sandbox lifecycles managed for you.
@alineo-labs/workflow lets you describe what should happen in a sandbox, then run all of it with a single await
at the end:
import { workflow } from "@alineo-labs/workflow";
await workflow(client)
.sandbox({ image: "node:22", resources: { cpu: "500m", memory: "512Mi" } }, (sb) => {
sb.exec("npm ci");
sb.retry(3, (sb) => sb.exec("npm test"), { backoff: "exponential" });
})
.pipe(process.stdout);client is an ordinary Core SDK Sandbox client. The callback runs synchronously and only queues
operations; nothing reaches a container until .pipe() or .result() is awaited. Then the sandbox is created, the
queue is flushed in order, and the sandbox is closed — no try/finally. Quickstart →
Control flow without awaits
sb.exec("test -f /tmp/cache.tar.gz", { strict: false });
sb.when(
(ctx) => ctx.exitCode === 0,
(sb) => sb.exec("tar -xzf /tmp/cache.tar.gz"),
(sb) => sb.exec("npm ci"),
);
sb.forEach(["lint", "test", "build"], (sb, task) => sb.exec(`npm run ${task}`));retry, when and forEach take a callback that queues more operations, so a pipeline with branches and loops is
still one synchronous description — control flow.
Many containers, one call
await workflow(client)
.parallel(
[
{ image: "node:20", resources: { cpu: "500m", memory: "512Mi" } },
{ image: "node:22", resources: { cpu: "500m", memory: "512Mi" } },
],
(sb) => sb.exec("npm test"),
)
.pipe(process.stdout);.parallel() runs the same operations in several containers at once; .sequence() runs different ones in order,
each step able to use what the last one produced — parallel and sequence.
Values come back as files
Queued operations don't return anything, so a value leaves the sandbox the way a shell script would hand one over:
write it to a file, then readFile(path, as). After the workflow resolves it is in vars[as] —
capturing values.
When to use it
Core SDK (@alineo-labs/sandbox) | Workflow SDK (@alineo-labs/workflow) | |
|---|---|---|
| Style | Imperative — hold objects, call methods | Declarative — describe ops, flush once |
| Await | Per operation | Once at the end |
| Lifecycle | Manual try/finally sb.close() | Managed automatically |
| Many sandboxes | Several variables | .parallel(), .sequence() |
| Streaming | ExecHandle.pipe() | .pipe(writable) on the workflow |
| Values | await sb.exec(...) | readFile(path, as) → vars |
Reach for the Core SDK directly when you need each exec's result mid-flow, or are fitting sandbox work into
imperative code you already have. Everything here runs on the same SandboxHandle underneath, and writes the same
ledger.