# VMs

`evaluate` and `execute` at the top level are conveniences: each call creates
a VM, runs your code, and disposes it. When something should outlast one call,
create the VM yourself.

secure-exec/examples/vm/src/index.ts:

```ts
import { createVm } from "secure-exec";

// A VM lives until you dispose it. It is an agentOS VM with Secure Exec's
// defaults, so files, installed packages, and processes persist across calls.
const vm = await createVm();
```

`createVm` takes the same VM options as a one-shot call: `permissions`,
`limits`, `mounts`, `hostFunctions`, and the rest.

## What a VM gives you

| On the VM | Purpose |
|---|---|
| `vm.javascript` | `execute`, `evaluate`, `executeFile`, `spawn`, `spawnFile` |
| `vm.typescript` | The same, plus `check` and `checkProject` |
| `vm.npm` | `install`, `runScript`, `runPackage` |
| `vm.filesystem` | Read and write guest files from the host |
| `vm.network` | Send requests to a server running in the VM |
| `vm.process` | Manage background processes |
| `vm.createContext()` | JavaScript state that survives between calls |
| `vm.dispose()` | Shut the VM down |

A Secure Exec VM **is** an [agentOS](https://rivet.dev/agentos) VM with Secure
Exec's defaults, so each namespace behaves exactly as the agentOS docs describe.

## Files across calls

Every call on a VM gets fresh JavaScript memory and the same filesystem.

secure-exec/examples/vm/src/index.ts:

```ts
// Hand the guest a file from the host.
await vm.filesystem.writeFile(
	"/workspace/orders.csv",
	"item,price\ncoffee,4\nbagel,3\n",
);

// Guest code uses ordinary `node:fs`. Each call gets fresh JavaScript memory
// and the same filesystem.
await vm.javascript.execute(`
	import { readFileSync, writeFileSync } from "node:fs";

	const rows = readFileSync("/workspace/orders.csv", "utf8").trim().split("\\n").slice(1);
	const total = rows.reduce((sum, row) => sum + Number(row.split(",")[1]), 0);
	writeFileSync("/workspace/report.json", JSON.stringify({ orders: rows.length, total }));
`);

// Read what it produced back on the host.
const report = await vm.filesystem.readFile("/workspace/report.json");
console.log(new TextDecoder().decode(report)); // {"orders":2,"total":7}
```

## Run files

secure-exec/examples/vm/src/index.ts:

```ts
// Run a file that is already in the VM, and type-check the project around it.
await vm.filesystem.writeFile(
	"/workspace/main.ts",
	'const total: number = 42;\nconsole.log("total", total);\n',
);
const ran = await vm.typescript.executeFile("/workspace/main.ts", {
	output: { capture: "all" },
});
console.log(ran.stdout?.trim()); // total 42
```

A file resolves its imports from its own directory, the same as in Node.js, so a
file under `/workspace` finds packages installed there.

## Dispose

A VM holds memory until you dispose it. Use `try` and `finally`, or
`await using` with TypeScript 5.2 or newer.

secure-exec/examples/vm/src/index.ts:

```ts
await vm.dispose();
```

## What you usually do not need

Most stateful work needs a VM and nothing else. Files, packages, and servers all
live in the VM. Reach for a [context](/secure-exec/docs/contexts) only when
JavaScript variables must survive between calls, as in a REPL or a notebook.
