# Quickstart
URL: /docs/workflow/quickstart

Run your first workflow — install, configure, and run a multi-step pipeline in one await.



## Install [#install]

```bash
npm install @alineo-labs/sandbox @alineo-labs/workflow
```

## Configure [#configure]

```ts
import { Sandbox } from "@alineo-labs/sandbox";
import { SQLiteAdapter } from "@alineo-labs/sqlite";
import { workflow } from "@alineo-labs/workflow";

const client = new Sandbox({
  baseUrl: "http://localhost:8080",
  adapter: new SQLiteAdapter("./ledger.db"),
});
```

No `connect()` call needed — the adapter initializes lazily on first use.

## Run a workflow [#run-a-workflow]

```ts
await workflow(client)
  .sandbox({ image: "node:22", resources: { cpu: "500m", memory: "512Mi" } }, (sb) => {
    sb.exec("node --version");
    sb.exec("npm --version");
  })
  .pipe(process.stdout);
```

No `close()` call needed either — `Sandbox` has no such method; the adapter closes itself automatically when the process exits.

The callback receives a `SandboxBuilder`. All calls inside it queue operations synchronously — nothing runs until `.pipe()` or `.result()` is awaited. The sandbox is created, all ops are flushed in order, then the sandbox is closed automatically.

## Capture output [#capture-output]

Use `.result()` instead of `.pipe()` to get the combined stdout and any captured file values:

```ts
const { stdout, vars } = await workflow(client)
  .sandbox({ image: "node:22", resources: { cpu: "500m", memory: "512Mi" } }, (sb) => {
    sb.exec("node --version > /tmp/ver.txt");
    sb.readFile("/tmp/ver.txt", "nodeVersion");
  })
  .result();

console.log(vars.nodeVersion); // "v22.x.x\n"
```

## Next steps [#next-steps]

* [SandboxBuilder operations](/docs/workflow/concepts/sandbox-builder) — the full set of queued ops
* [Control flow](/docs/workflow/concepts/control-flow) — retry, when, forEach
* [Parallel & sequence](/docs/workflow/concepts/parallel-sequence) — multi-sandbox patterns
