Bash
Run shell commands and arbitrary argv operations through the agentOS execution lifecycle.
Run bash inside the VM. Shell commands are the simplest agentOS execution surface, and share the same execution lifecycle as JavaScript, TypeScript, Python, and package workflows.
process.exec(): Runs a bash command line. Use for pipes, redirects, globs.process.execFile(): Injection-safe; args are never parsed by a shell.- In-VM only: Commands run inside the VM, never in the host shell.
For multi-step work, consider letting the agent write JavaScript or Python instead — one round trip and real data structures rather than a chain of shell calls.
Run commands
import { AgentOs } from "@rivet-dev/agentos";
const runtime = await AgentOs.create();
try {
// No capture: you get the outcome and exit code only.
await runtime.process.exec("mkdir -p /workspace/demo");
// Capture "stderr" for diagnostics, "all" for both streams.
const listing = await runtime.process.exec("ls -la /workspace", {
output: { capture: "all" },
});
console.log(listing.outcome === "succeeded" ? listing.stdout : listing.error);
// Pipes, redirects, and globs work — it's a real shell.
const piped = await runtime.process.exec("echo 'hello world' | tr a-z A-Z", {
output: { capture: "all" },
});
console.log(piped.stdout?.trim()); // "HELLO WORLD"
// execFile sends each arg verbatim — no shell parses it, so no injection.
const raw = await runtime.process.execFile("echo", ["$(whoami)"], {
output: { capture: "all" },
});
console.log(raw.stdout?.trim()); // "$(whoami)" — printed literally, never run
} finally {
await runtime.dispose();
}
Background and interactive work
spawn starts a long-lived process and returns a pid. From there you get
stdin, a PTY, output replay, signals, and waiting.
import { AgentOs } from "@rivet-dev/agentos";
const runtime = await AgentOs.create();
try {
const shell = await runtime.process.spawn("sh", [], {
output: { retainEvents: true }, // buffer output so it can be replayed
});
await runtime.process.writeStdin(shell.pid, "echo from the background\n");
await runtime.process.resizePty(shell.pid, { cols: 120, rows: 40 });
await runtime.process.closeStdin(shell.pid);
const exit = await runtime.process.wait(shell.pid);
const replay = await runtime.process.readOutput(shell.pid);
console.log(exit.exitCode, replay.events.length); // 0 1
} finally {
await runtime.dispose();
}
Files and software
Shell commands see the persistent filesystem shared by agents, JavaScript, and Python. Common POSIX commands ship by default; more is projected through the software registry.
import { AgentOs } from "@rivet-dev/agentos";
const runtime = await AgentOs.create();
try {
// Files written by the shell are the same files agents, JavaScript, and
// Python see — one persistent VM filesystem.
await runtime.process.exec("printf 'a\\nb\\nc\\n' > /workspace/out.txt");
const lines = await runtime.process.exec("cat /workspace/out.txt | wc -l", {
output: { capture: "all" },
});
console.log(lines.stdout?.trim()); // "3"
} finally {
await runtime.dispose();
}
Bindings
Bindings appear as commands, so pipelines use trusted host capabilities without putting credentials inside the VM.
import { AgentOs, type Bindings } from "@rivet-dev/agentos";
import { z } from "zod";
// The handler runs on the host, so the API key never enters the VM.
const weather: Bindings = {
name: "weather",
description: "Weather data bindings",
bindings: {
forecast: {
description: "Get the weather forecast for a city",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }: { city: string }) => {
const res = await fetch(
`https://api.weather.example/forecast?city=${city}&key=${process.env.WEATHER_API_KEY}`,
);
return res.json();
},
},
},
};
// The collection is projected into the VM as an `agentos-weather` command, so
// it composes with pipes and redirects like any other program.
const runtime = await AgentOs.create({ bindings: [weather] });
try {
const result = await runtime.process.exec(
"agentos-weather forecast --city Paris > /workspace/forecast.json && wc -c < /workspace/forecast.json",
{ output: { capture: "all" } },
);
console.log(result.outcome === "succeeded" ? result.stdout : result.error);
} finally {
await runtime.dispose();
}
Permissions, limits, and timeouts
Every command inherits the VM permission policy and resource limits.
import { AgentOs } from "@rivet-dev/agentos";
const runtime = await AgentOs.create();
try {
// timeoutMs is a wall-clock deadline. It expires into a result rather than
// throwing; a denied operation gets the normal POSIX error.
const result = await runtime.process.exec("sleep 10", { timeoutMs: 1_000 });
console.log(result.outcome); // "timed_out"
} finally {
await runtime.dispose();
}