/** * Reference: agentOS VM as a sandbox backend for Flue. * * This script demonstrates the core idea behind a Flue sandbox adapter that * delegates file/shell operations to an agentOS VM. It does NOT wire into the * Flue agent yet — it proves the primitives work end-to-end so you can see * exactly how the adapter will map calls. * * Run: bun --env-file=../../.env packages/agents/scripts/agentos-sandbox-ref.ts * * Architecture: * * Flue agent (host) agentOS VM (isolated Wasm+V8) * ┌─────────────────────┐ ┌──────────────────────┐ * │ defineAgent(...) │ │ Linux-like sandbox │ * │ sandbox: agentOs()│── readFile ──► │ persistent FS │ * │ │── writeFile ─► │ shell (sh, coreutils)│ * │ │── exec ──────► │ ~6ms cold start │ * └─────────────────────┘ └──────────────────────┘ * * The Flue SandboxApi maps 1:1 to AgentOs methods: * * Flue SandboxApi AgentOs (core SDK) * ───────────────────────── ────────────────────────── * readFile(path) vm.readFile(path) * readFileBuffer(path) vm.readFile(path) * writeFile(path, content) vm.writeFile(path, content) * stat(path) vm.stat(path) * readdir(path) vm.readdir(path) * exists(path) vm.exists(path) * mkdir(path) vm.mkdir(path) * rm(path) vm.remove(path) * exec(command, opts) vm.exec(command, opts) */ import { AgentOs } from "@rivet-dev/agentos-core"; import type { VirtualStat } from "@rivet-dev/agentos-core"; const formatStat = (stat: VirtualStat): string => JSON.stringify({ isDirectory: stat.isDirectory, isSymbolicLink: stat.isSymbolicLink, mtime: new Date(stat.mtimeMs).toISOString(), size: stat.size, }); const main = async () => { console.log("=== Booting agentOS VM ===\n"); // AgentOs.create() boots a local VM with the default software bundle // (sh + coreutils). The VM sleeps when idle and wakes on the next action. const vm = await AgentOs.create(); console.log("VM booted.\n"); // ── File operations ────────────────────────────────────────────── console.log("=== writeFile ==="); await vm.writeFile("/workspace/hello.txt", "Hello from agentOS VM!"); console.log("wrote /workspace/hello.txt\n"); console.log("=== readFile ==="); const content = await vm.readFile("/workspace/hello.txt"); console.log("content:", new TextDecoder().decode(content), "\n"); console.log("=== stat ==="); const stat = await vm.stat("/workspace/hello.txt"); console.log("stat:", formatStat(stat), "\n"); console.log("=== exists ==="); console.log( "exists /workspace/hello.txt:", await vm.exists("/workspace/hello.txt") ); console.log( "exists /workspace/nope.txt:", await vm.exists("/workspace/nope.txt"), "\n" ); console.log("=== mkdir + readdir ==="); await vm.mkdir("/workspace/src", { recursive: true }); await vm.writeFile("/workspace/src/a.ts", "export const a = 1;"); await vm.writeFile("/workspace/src/b.ts", "export const b = 2;"); console.log( "readdir /workspace/src:", await vm.readdir("/workspace/src"), "\n" ); // ── Shell exec ─────────────────────────────────────────────────── console.log("=== exec: echo ==="); const echoResult = await vm.exec("echo 'shell works!'"); console.log( "stdout:", echoResult.stdout.trim(), `(exit ${echoResult.exitCode})\n` ); console.log("=== exec: pipe + coreutils ==="); const pipeResult = await vm.exec("echo 'hello world' | tr a-z A-Z | wc -w"); console.log( "stdout:", pipeResult.stdout.trim(), `(exit ${pipeResult.exitCode})\n` ); console.log("=== exec: with cwd + env ==="); await vm.mkdir("/workspace/project", { recursive: true }); await vm.writeFile("/workspace/project/file.txt", "data"); const cwdResult = await vm.exec("pwd && ls", { cwd: "/workspace/project", env: { CUSTOM_VAR: "from-env" }, }); console.log( "stdout:", cwdResult.stdout.trim(), `(exit ${cwdResult.exitCode})\n` ); // ── Remove ─────────────────────────────────────────────────────── console.log("=== remove ==="); await vm.remove("/workspace/src/b.ts"); console.log( "after remove, readdir:", await vm.readdir("/workspace/src"), "\n" ); // ── Cleanup ────────────────────────────────────────────────────── await vm.dispose(); console.log("=== VM disposed. All primitives work. ==="); }; try { await main(); } catch (error) { console.error(error); process.exit(1); }