Flue integration

Use a @alineo-labs/sandbox SandboxHandle as a Flue session environment with @alineo-labs/flue.

@alineo-labs/flue adapts a SandboxHandle (from @alineo-labs/sandbox) to Flue's SandboxFactory interface, so these sandboxes can back any Flue agent that needs a container to exec commands and manage files.

Install

bun add @alineo-labs/flue @flue/runtime

Usage

Flue loads sandbox adapters from a file at <source-dir>/sandboxes/<name>.ts. Create src/sandboxes/alineo.ts in your Flue project:

// src/sandboxes/alineo.ts
import { alineo } from "@alineo-labs/flue";
import { Sandbox } from "@alineo-labs/sandbox";
import { SQLiteAdapter } from "@alineo-labs/sqlite";

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

export default alineo(
  await client.sandbox({
    image: "node:22",
    resources: { cpu: "500m", memory: "512Mi" },
  }),
);

Then reference it in your agent definition:

import { defineAgent } from "@flue/runtime";
import alineoSandbox from "./sandboxes/alineo.ts";

export default defineAgent({
  sandbox: alineoSandbox,
  // ...
});

API

function alineo(sandbox: SandboxHandle, opts?: { cwd?: string }): SandboxFactory;
ParameterTypeDescription
sandboxSandboxHandleAn already-created sandbox instance. Lifecycle is the caller's responsibility — the adapter never calls sb.close().
opts.cwdstringWorking directory passed to createSandboxSessionEnv. Defaults to "/".

SandboxApi coverage

The adapter implements all nine SandboxApi methods:

MethodBacked by
exec(cmd, opts?)sb.exec() with strict: false. Timeout via Promise.race + Math.ceil.
readFile(path)sb.readFile()
readFileBuffer(path)sb.exec("base64 -w0 <path>") → decoded Uint8Array
writeFile(path, string)sb.writeFile()
writeFile(path, Uint8Array)Base64-encoded, piped through base64 -d in the container
stat(path)sb.exec("stat -c '%F|%s|%Y' <path>") → parsed FileStat
readdir(path)sb.listDirectory(path, { depth: 1 }) → entry names
exists(path)sb.exec("test -e <path>") → exit code check
mkdir(path, opts?)sb.exec("mkdir [-p] <path>")
rm(path, opts?)sb.exec("rm [-r] [-f] <path>")

Known limits

Binary write cap — writeFile with a Uint8Array base64-encodes the content and passes it through the shell. This relies on the Linux ARG_MAX limit (~2 MB), so binary writes are capped at roughly 1.5 MB. String writes (writeFile(path, string)) use the native file upload API and have no size restriction.

No mid-flight cancellation — exec honors timeoutMs via Promise.race, but AbortSignal is ignored. The underlying SandboxHandle.exec() does not expose signal propagation to the HTTP layer.

Sandbox lifecycle

The adapter never calls sb.close(). You are responsible for closing the sandbox when your agent session ends:

const sb = await client.sandbox({ image: "node:22", resources: { cpu: "500m", memory: "512Mi" } });
try {
  const factory = alineo(sb);
  // pass factory to Flue agent...
} finally {
  await sb.close();
}