> ## Documentation Index
> Fetch the complete documentation index at: https://docs.celesto.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Create and manage sandboxed computers

> Use the Celesto Computers API to create sandboxed computers, select templates, run shell commands, publish ports, and manage the VM lifecycle.

The Computers API gives your code or agent an isolated Linux computer. You create a computer, run work inside it, and then stop, start, or delete it when your workflow is done.

## Computer lifecycle

<Steps>
  <Step title="Create">
    Create a new computer from `scratch` or a template such as `coding-agent`.
  </Step>

  <Step title="Run">
    Execute shell commands, inspect output, and publish ports when a service needs a public URL.
  </Step>

  <Step title="Manage">
    Get details, list computers, stop and start long-lived computers, or delete temporary computers.
  </Step>
</Steps>

<Note>
  Celesto accepts Nano (1 vCPU, 512 MB), Small (1 vCPU, 1 GB), Standard (2 vCPU, 4 GB), and Large (4 vCPU, 12 GB). Free and Nano plans can create Nano computers. Builder and Growth can create every named size. See [Choose computer size](/celesto-sdk/features/resources).
</Note>

<View title="Python" icon="python">
  ## Create a computer

  Use `Computer()` with no arguments to get a fresh `scratch` computer, which is a minimal Ubuntu sandbox.

  ```python create_computer.py theme={null}
  from celesto import Computer


  computer = Computer()
  print(computer.name)
  print(computer.id)
  print(computer.status)

  computer.delete()
  ```

  Override CPU, memory, or disk size when the default size is too small:

  ```python create_sized_computer.py theme={null}
  from celesto import Computer


  computer = Computer(cpus=2, memory=4096, disk="15gb")
  print(f"{computer.name} has {computer.vcpus} vCPUs")
  print(f"{computer.ram_mb} MB RAM")

  computer.delete()
  ```

  <ParamField body="cpus" type="integer">
    CPU value from a named size. Use together with the matching `memory` value. Defaults to the template's value.
  </ParamField>

  <ParamField body="memory" type="integer">
    Memory in MB from a named size. Use together with the matching `cpus` value. Defaults to the template's value.
  </ParamField>

  <ParamField body="disk" type="integer | string">
    Disk size as MB or a string such as `"15gb"`. Alias for `disk_size_mb`.
  </ParamField>

  <ParamField body="disk_size_mb" type="integer">
    Disk size in MB. Range: 512-20480. Defaults to the template's value.
  </ParamField>

  <ParamField body="template_id" type="string" default="scratch">
    Sandbox template ID. Pass `coding-agent` when your agent needs common coding tools already installed.
  </ParamField>

  <ParamField body="template_version" type="string">
    Optional immutable template version. Pin this when you want repeatable builds as a template changes over time.
  </ParamField>

  ## Use a template

  A template is a ready-made computer image with extra tools and default CPU, memory, and disk settings. Use `coding-agent` for common coding workflows:

  ```python create_from_template.py theme={null}
  from celesto import Computer


  computer = Computer(template_id="coding-agent")
  result = computer.run("python3 --version")
  print(result["stdout"].strip())

  computer.delete()
  ```

  ## List templates

  ```python list_templates.py theme={null}
  from celesto import Computer


  templates = Computer.list_templates()
  for template in templates:
      print(
          f"{template['id']}: {template['display_name']} "
          f"({template['default_vcpus']} vCPU, {template['default_ram_mb']} MB RAM)"
      )
  ```

  ## Run commands

  ```python run_command.py theme={null}
  from celesto import Computer


  computer = Computer(template_id="scratch")
  result = computer.run("ls -la /home", timeout=60)
  print(result["stdout"])
  print(result["exit_code"])

  computer.delete()
  ```

  <ResponseField name="exit_code" type="integer" required>
    Exit code of the command. `0` means success.
  </ResponseField>

  <ResponseField name="stdout" type="string" required>
    Standard output from the command.
  </ResponseField>

  <ResponseField name="stderr" type="string" required>
    Standard error output from the command.
  </ResponseField>

  ## Stream command output

  Use `run_stream()` when you want to see output as it is produced instead of waiting for the command to finish. It returns an iterator of event dicts that arrive over a server-sent events stream, which is useful for long-running builds, test suites, or agent tool calls that print progress.

  ```python stream_command.py theme={null}
  from celesto import Computer


  computer = Computer(template_id="coding-agent")

  for event in computer.run_stream("pytest -q", timeout=300):
      if event["type"] in ("stdout", "stderr"):
          print(event["data"], end="")
      elif event["type"] == "exit":
          print(f"\nexit code: {event['exit_code']}")

  computer.delete()
  ```

  Each event has a `type` field:

  * `started` — the command has been accepted. Includes `command_id` and `timeout_seconds`.
  * `stdout` and `stderr` — a chunk of output. The bytes are in `data`.
  * `exit` — the command finished. Includes `exit_code`, `duration_ms`, and `timed_out`.

  The stream always ends with an `exit` event, even when the command times out or is interrupted. Break out of the loop early to stop consuming; the remote command still runs to completion.

  ## Get and list computers

  ```python list_computers.py theme={null}
  from celesto import Computer


  computers = Computer.list(status="running")
  for computer in computers:
      info = Computer.get(computer["id"])
      print(f"{info.name}: {info.status}")

  print(f"Total: {len(computers)}")
  ```

  ## Publish a port

  Publishing a port gives your computer a public HTTPS URL. Use it when an app, API server, or notebook running inside the computer needs to be reachable from outside the sandbox.

  ```python publish_port.py theme={null}
  from celesto import Computer


  computer = Computer(template_id="coding-agent")
  computer.run("cd /tmp && python3 -m http.server 8000 > /tmp/server.log 2>&1 &")

  url = computer.publish_port(8000)
  print(url)

  for port in computer.list_published_ports():
      print(f"{port['port']} -> {port['url']} ({port['status']})")

  computer.unpublish_port(8000)
  computer.delete()
  ```

  ## Open a terminal connection

  Use `create_terminal_session()` when your application needs an interactive shell against a running computer, for example to power a web terminal in your own product. It calls `POST /computers/{id}/terminals` and returns a short-lived, direct connection to Celesto's fast terminal gateway. Your account must have write access to the computer.

  ```python terminal.py theme={null}
  import websockets
  import asyncio
  from celesto import Computer


  async def main():
      computer = Computer.get("einstein")
      session = computer.create_terminal_session()

      async with websockets.connect(session["url"]) as ws:
          await ws.send("echo hello\n")
          print(await ws.recv())


  asyncio.run(main())
  ```

  <Warning>
    `session["url"]` embeds a short-lived terminal token. Treat it as a secret: pass it straight to your WebSocket client, do not log it, and do not send it to a browser you do not control. Use `session["expires_at"]` to know when to request a new session.
  </Warning>

  <ResponseField name="terminal_id" type="string" required>
    Durable terminal session ID.
  </ResponseField>

  <ResponseField name="gateway_url" type="string" required>
    Base gateway URL without credentials. Combine with `token` yourself only when you cannot use `url` directly.
  </ResponseField>

  <ResponseField name="token" type="string" required>
    Short-lived terminal token. Treat this as a secret.
  </ResponseField>

  <ResponseField name="expires_at" type="string" required>
    ISO 8601 timestamp after which the token stops working. Create a new session before this time.
  </ResponseField>

  <ResponseField name="url" type="string" required>
    Authenticated `wss://` URL ready to pass to a WebSocket client.
  </ResponseField>

  ## Stop, start, and delete

  ```python lifecycle.py theme={null}
  from celesto import Computer


  computer = Computer()
  computer.stop()
  computer.start()
  computer.delete()
  ```
</View>

<View title="TypeScript" icon="js">
  ## Create a computer

  Use `Computer.create()` with no arguments to get a fresh `scratch` computer, which is a minimal Ubuntu sandbox.

  ```ts create-computer.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const computer = await Computer.create();
  console.log(computer.name);
  console.log(computer.id);
  console.log(computer.status);

  await computer.delete();
  ```

  Override CPU, memory, or disk size when the default size is too small:

  ```ts create-sized-computer.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const computer = await Computer.create({
    cpus: 2,
    memory: 4096,
    disk: "15gb",
  });

  console.log(`${computer.name} has ${computer.vcpus} vCPUs`);
  console.log(`${computer.ramMb} MB RAM`);

  await computer.delete();
  ```

  <ParamField body="cpus" type="integer">
    CPU value from a named size. Use together with the matching `memory` value. Defaults to the template's value.
  </ParamField>

  <ParamField body="memory" type="integer">
    Memory in MB from a named size. Use together with the matching `cpus` value. Defaults to the template's value.
  </ParamField>

  <ParamField body="disk" type="integer | string">
    Disk size as MB or a string such as `"15gb"`. Alias for `diskSizeMb`.
  </ParamField>

  <ParamField body="diskSizeMb" type="integer">
    Disk size in MB. Range: 512-20480. Defaults to the template's value.
  </ParamField>

  <ParamField body="templateId" type="string" default="scratch">
    Sandbox template ID. Pass `coding-agent` when your agent needs common coding tools already installed.
  </ParamField>

  <ParamField body="templateVersion" type="string">
    Optional immutable template version. Pin this when you want repeatable builds as a template changes over time.
  </ParamField>

  ## Use a template

  A template is a ready-made computer image with extra tools and default CPU, memory, and disk settings. Use `coding-agent` for common coding workflows:

  ```ts create-from-template.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const computer = await Computer.create({ templateId: "coding-agent" });
  const result = await computer.run("python3 --version");
  console.log(result.stdout.trim());

  await computer.delete();
  ```

  ## List templates

  ```ts list-templates.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const templates = await Computer.listTemplates();

  for (const template of templates) {
    console.log(
      `${template.id}: ${template.displayName} ` +
        `(${template.defaultVcpus} vCPU, ${template.defaultRamMb} MB RAM)`,
    );
  }
  ```

  ## Run commands

  ```ts run-command.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const computer = await Computer.create({ templateId: "scratch" });
  const result = await computer.run("ls -la /home", { timeout: 60 });
  console.log(result.stdout);
  console.log(result.exitCode);

  await computer.delete();
  ```

  <ResponseField name="exitCode" type="integer" required>
    Exit code of the command. `0` means success.
  </ResponseField>

  <ResponseField name="stdout" type="string" required>
    Standard output from the command.
  </ResponseField>

  <ResponseField name="stderr" type="string" required>
    Standard error output from the command.
  </ResponseField>

  ## Stream command output

  Use `runStream()` when you want to display command output as it is produced instead of waiting for the command to finish. It returns an `AsyncGenerator` that yields events over a server-sent events stream, so `for await` reads them one at a time. This is a good fit for long builds, test runs, or an agent tool call that wants to show progress to a user. `execStream()` is an alias.

  ```ts stream-command.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const computer = await Computer.create({ templateId: "coding-agent" });

  for await (const event of computer.runStream("pytest -q", { timeout: 300 })) {
    if (event.type === "stdout" || event.type === "stderr") {
      process.stdout.write(event.data);
    } else if (event.type === "exit") {
      console.log(`\nexit code: ${event.exitCode}`);
    }
  }

  await computer.delete();
  ```

  Each event has a `type` field:

  * `started` — the command has been accepted. Includes `commandId`, `startedAtUnixMs`, and `timeoutSeconds`.
  * `stdout` and `stderr` — a chunk of output. The bytes are in `data`.
  * `exit` — the command finished. Includes `exitCode`, `durationMs`, and `timedOut`.

  Pass an `AbortSignal` to cancel a stream from your own code. Aborting also stops the remote command:

  ```ts stream-cancel.ts theme={null}
  const controller = new AbortController();
  setTimeout(() => controller.abort(), 5000);

  for await (const event of computer.runStream("sleep 60 && echo done", {
    timeout: 120,
    signal: controller.signal,
  })) {
    // ...
  }
  ```

  ## List recent commands

  Retrieve metadata for commands recently executed on a computer, including their exit codes, durations, and command IDs. This is useful for auditing what an agent has run.

  ```ts list-command-history.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const computer = await Computer.get("einstein");
  const history = await computer.listCommandHistory({ limit: 20 });

  for (const entry of history.commands) {
    console.log(entry.commandId, entry.status, entry.exitCode);
  }
  ```

  ## Get and list computers

  ```ts list-computers.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const computers = await Computer.list({ status: "running" });

  for (const computer of computers) {
    const info = await Computer.get(computer.id);
    console.log(`${info.name}: ${info.status}`);
  }

  console.log(`Total: ${computers.length}`);
  ```

  ## Publish a port

  Publishing a port gives your computer a public HTTPS URL. Use it when an app, API server, or notebook running inside the computer needs to be reachable from outside the sandbox.

  ```ts publish-port.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const computer = await Computer.create({ templateId: "coding-agent" });
  await computer.run("cd /tmp && python3 -m http.server 8000 > /tmp/server.log 2>&1 &");

  const url = await computer.publishPort(8000);
  console.log(url);

  for (const port of await computer.listPublishedPorts()) {
    console.log(`${port.port} -> ${port.url} (${port.status})`);
  }

  await computer.unpublishPort(8000);
  await computer.delete();
  ```

  ## Stop, start, and delete

  ```ts lifecycle.ts theme={null}
  import { Computer } from "@celestoai/sdk";

  const computer = await Computer.create();

  await computer.stop();
  await computer.start();
  await computer.delete();
  ```

  ## Open a terminal connection

  Use `createTerminalSession()` when you are building your own interactive terminal against a Celesto computer, for example to power a web terminal in your own product. It calls `POST /computers/{id}/terminals` and returns a short-lived, direct connection to Celesto's fast terminal gateway. Your account must have write access to the computer.

  ```ts terminal.ts theme={null}
  import WebSocket from "ws";
  import { Computer } from "@celestoai/sdk";

  const computer = await Computer.get("einstein");
  const session = await computer.createTerminalSession();

  const ws = new WebSocket(session.url);
  ws.on("message", (data) => process.stdout.write(data));
  ws.on("open", () => ws.send("echo hello\n"));
  ```

  <Warning>
    `session.url` embeds a short-lived terminal token. Treat it as a secret: pass it straight to your WebSocket client, do not log it, and do not send it to a browser you do not control. Use `session.expiresAt` to know when to create a new session.
  </Warning>

  <ResponseField name="terminalId" type="string" required>
    Durable terminal session ID.
  </ResponseField>

  <ResponseField name="gatewayUrl" type="string" required>
    Base gateway URL without credentials. Combine with `token` yourself only when you cannot use `url` directly.
  </ResponseField>

  <ResponseField name="token" type="string" required>
    Short-lived terminal token. Treat this as a secret.
  </ResponseField>

  <ResponseField name="expiresAt" type="string" required>
    ISO 8601 timestamp after which the token stops working. Create a new session before this time.
  </ResponseField>

  <ResponseField name="url" type="string" required>
    Authenticated `wss://` URL ready to pass to a `WebSocket` constructor.
  </ResponseField>

  <Info>
    `getTerminalConnection()` is retained as a deprecated alias for `createTerminalSession()` and calls the same fast terminal gateway endpoint. Update existing code to `createTerminalSession()` at your convenience.
  </Info>
</View>

## Computer response fields

<ResponseField name="id" type="string" required>
  Unique identifier for the computer, such as `cmp_abc123`.
</ResponseField>

<ResponseField name="name" type="string" required>
  Auto-generated display name for the computer, such as `einstein`.
</ResponseField>

<ResponseField name="status" type="string" required>
  Current status. One of: `creating`, `running`, `stopping`, `stopped`, `starting`, `restoring`, `restorable`, `deleting`, `deleted`, or `error`.
</ResponseField>

<ResponseField name="vcpus" type="integer" required>
  Number of virtual CPUs allocated.
</ResponseField>

<ResponseField name="ram_mb" type="integer" required>
  Memory allocated in MB. TypeScript exposes this as `ramMb`.
</ResponseField>

<ResponseField name="disk_size_mb" type="integer" required>
  Disk allocated in MB. TypeScript exposes this as `diskSizeMb`.
</ResponseField>

<ResponseField name="template_id" type="string" required>
  Sandbox template the computer was created from. TypeScript exposes this as `templateId`.
</ResponseField>

<ResponseField name="template_version" type="string">
  Pinned template version, if one was provided at create time. TypeScript exposes this as `templateVersion`.
</ResponseField>

<ResponseField name="connection" type="object">
  Connection details for accessing a running computer.

  <Expandable title="Connection properties">
    <ResponseField name="ssh" type="string">
      SSH connection string for terminal access.
    </ResponseField>

    <ResponseField name="access_url" type="string">
      URL for browser-based access to the computer.
    </ResponseField>
  </Expandable>
</ResponseField>
