On this page

Files & Exec

The Files and Exec APIs are how you (or an AI agent) interact with a Sandbox. Together they’re the substrate for everything else: generating code, running builds, starting servers.

Files

Write

Parent directories are created as needed. Existing files are overwritten.

Read

const content = await sandbox.files.read("/workspace/package.json")
const parsed = JSON.parse(content)
curl https://api.miosa.ai/api/v1/sandboxes/$SBX/files/read?path=/workspace/package.json 
  -H "Authorization: Bearer $MIOSA_API_KEY"

List

const entries = await sandbox.files.list("/workspace")
for (const e of entries) {
  console.log(e.type, e.path, e.size)
}
curl https://api.miosa.ai/api/v1/sandboxes/$SBX/files/list?path=/workspace 
  -H "Authorization: Bearer $MIOSA_API_KEY"

Watch

Stream filesystem events:

for await (const event of sandbox.files.watch("/workspace")) {
  console.log(event.type, event.path)
  // event.type: "create" | "modify" | "delete"
}

Watch is implemented as Server-Sent Events. See Events for the SSE event shape.

Filesystem layout

PathPurpose
/workspaceWorking directory; this is where agents write app code
/home/sandboxUser home
/tmpTemporary scratch (cleared on sandbox destroy)
/opt/venvPre-installed Python virtualenv (miosa-sandbox and Python templates)
/usr/local/binSystem binaries

Generally, write inside /workspace and let your build tooling handle node_modules, virtualenvs, etc. Production publish freezes /workspace (excluding cache dirs) as the source snapshot — see Publishing.

Exec

Run commands inside the sandbox.

One-shot

Blocks until the command exits (or timeoutMs elapses). Returns exit_code, stdout, stderr, and duration_ms.

Detached (long-running servers)

await sandbox.exec({
  cmd: "node",
  args: ["server.js"],
  cwd: "/workspace",
  detached: true,
})

Returns immediately with a job_id. The server keeps running. Use Previews to expose its port.

Streaming

For real-time stdout/stderr (e.g. tail an agent’s compile output):

for await (const chunk of sandbox.exec.stream({
  cmd: "pnpm",
  args: ["dev"],
})) {
  if (chunk.type === "stdout") process.stdout.write(chunk.data)
  if (chunk.type === "stderr") process.stderr.write(chunk.data)
  if (chunk.type === "exit") console.log("exit:", chunk.exit_code)
}

Implemented as SSE. See Streaming Exec.

Terminal

Need an interactive PTY (for remote dev or AI agents driving CLIs)?

const terminal = await sandbox.terminal()
terminal.send("ls /workspace\n")
for await (const out of terminal.output) {
  process.stdout.write(out)
}

Terminals are WebSocket-based with xterm.js-compatible ANSI passthrough. See API Reference: Terminal for protocol details.

Process management

For detached exec jobs:

const job = await sandbox.exec({ cmd: "npm", args: ["start"], detached: true })

await sandbox.jobs.get(job.id)            // current state
await sandbox.jobs.stop(job.id)           // SIGTERM, then SIGKILL after grace
await sandbox.jobs.list()                 // all detached jobs

Detached jobs survive until you explicitly stop them or the sandbox is destroyed.

Idempotency

Exec is not idempotent — running npm install twice runs it twice. If you need idempotent retries, store a marker file:

const marker = "/workspace/.deps-installed"
const exists = await sandbox.files.exists(marker)
if (!exists) {
  await sandbox.exec({ cmd: "npm", args: ["ci"] })
  await sandbox.files.write(marker, new Date().toISOString())
}

File writes are idempotent — same path, same content, same result.

Limits

LimitDefaultTunable
Max file size per write100 MBYes (plan-dependent)
Max files per sandbox1,000,000Yes
Max exec stdout buffer10 MBStream mode bypasses this
Max exec durationtimeout_ms parameter, default 30sYes; detached has no limit
Max concurrent detached jobs100Yes

See also

Was this helpful?