# Processes & Shell

Run commands with one-shot `exec`, spawn long-running processes with streaming stdout/stderr and stdin, manage their lifecycle (stop, kill, wait, inspect), open interactive PTY-backed shells, and inspect the process tree across all VM runtimes.

## One-shot execution

Use `exec` to run a command and wait for completion. Returns stdout, stderr, and exit code.

examples/processes/exec.ts:

```ts
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";

const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });

const result = await client.vm
	.getOrCreate("my-agent")
	.process.exec("echo hello && ls /home/agentos");
console.log("stdout:", result.stdout);
console.log("stderr:", result.stderr);
console.log("exit code:", result.exitCode);
```

examples/processes/server.ts:

```ts
import { agentOS, setup } from "@rivet-dev/agentos";
import pi from "@agentos-software/pi";

const vm = agentOS({
  software: [pi],
});

export const registry = setup({ use: { vm } });
registry.start();
```

## Spawn a long-running process

Use `spawn` for processes that run in the background. Call `connect()` and subscribe to native `processOutput` and `processExit` events, filtering their `pid` in application code.

examples/processes/spawn.ts:

```ts
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";

const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });
const agent = client.vm.getOrCreate("my-agent");
const conn = agent.connect();

// Spawn a dev server
const { pid } = await agent.process.spawn("node", ["/home/agentos/server.js"]);

// Subscribe to process output
conn.on("processOutput", (data) => {
	if (data.pid !== pid) return;
  const text = new TextDecoder().decode(data.data);
  console.log(`[pid ${data.pid}] ${data.stream}: ${text}`);
});

conn.on("processExit", (data) => {
	if (data.pid !== pid) return;
  console.log(`[pid ${data.pid}] exited with code ${data.exitCode}`);
});

console.log("Started process:", pid);
```

examples/processes/server.ts:

```ts
import { agentOS, setup } from "@rivet-dev/agentos";
import pi from "@agentos-software/pi";

const vm = agentOS({
  software: [pi],
});

export const registry = setup({ use: { vm } });
registry.start();
```

## Write to stdin

Send input to a running process.

examples/processes/stdin.ts:

```ts
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";

const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });
const agent = client.vm.getOrCreate("my-agent");

const { pid } = await agent.process.spawn("cat", []);

// Write to stdin
await agent.process.writeStdin(pid, "hello from stdin\n");

// Close stdin when done
await agent.process.closeStdin(pid);

// Wait for the process to exit
const exitCode = await agent.process.wait(pid);
console.log("exit code:", exitCode);
```

examples/processes/server.ts:

```ts
import { agentOS, setup } from "@rivet-dev/agentos";
import pi from "@agentos-software/pi";

const vm = agentOS({
  software: [pi],
});

export const registry = setup({ use: { vm } });
registry.start();
```

## Process lifecycle

examples/processes/lifecycle.ts:

```ts
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";

const client = createClient<typeof registry>({
	endpoint: "http://localhost:6420",
});
const agent = client.vm.getOrCreate("my-agent");

const { pid } = await agent.process.spawn("node", ["/home/agentos/server.js"]);

const processStatus = (process: {
	running: boolean;
	exitCode?: number | null;
}) => (process.running ? "running" : `exited ${process.exitCode ?? ""}`.trim());

// List all processes tracked by the VM
const processes = await agent.process.list();
for (const p of processes) {
	console.log(p.pid, p.command, p.args.join(" "), processStatus(p));
}

// Inspect a specific process by pid
const info = await agent.process.get(pid);
console.log(processStatus(info), info.exitCode);

// Graceful stop (SIGTERM)
await agent.process.signal(pid, "SIGTERM");

// Force kill (SIGKILL)
await agent.process.kill(pid);
```

examples/processes/server.ts:

```ts
import { agentOS, setup } from "@rivet-dev/agentos";
import pi from "@agentos-software/pi";

const vm = agentOS({
  software: [pi],
});

export const registry = setup({ use: { vm } });
registry.start();
```

## Interactive shells

Open an interactive shell with PTY support. Subscribe to native `shellData`, `shellStderr`, and `shellExit` events, filtering their `shellId` in application code.

examples/processes/shell.ts:

```ts
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";

const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });
const agent = client.vm.getOrCreate("my-agent");
const conn = agent.connect();

// Spawn an interactive shell process
const { pid } = await agent.process.spawn("sh", []);

// Stream this process's output as it is produced
conn.on("processOutput", (data) => {
	if (data.pid !== pid) return;
  const text = new TextDecoder().decode(data.data);
  process.stdout.write(text);
});

// Drive it by writing commands to stdin
await agent.process.writeStdin(pid, "ls -la /home/agentos\n");

// Close stdin to let the shell exit, then wait for it
await agent.process.closeStdin(pid);
await agent.process.wait(pid);
```

examples/processes/server.ts:

```ts
import { agentOS, setup } from "@rivet-dev/agentos";
import pi from "@agentos-software/pi";

const vm = agentOS({
  software: [pi],
});

export const registry = setup({ use: { vm } });
registry.start();
```
