Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,35 @@ uv run pytest
- Avoid placeholders like `# ... rest of code ...`.
- Prefer clear, explicit error messages.
- Maintain backwards compatibility where possible (public SDK/CLI).

## CLI Design

- New CLI commands should follow a **NOUN-VERB** structure: `celesto <noun> <verb>`.
- The noun names the resource, such as a computer, deployment, connection, or template.
- The verb names the action, such as `create`, `list`, `start`, `stop`, or `delete`.
- When adding a new resource, register it as a top-level subcommand and put its actions underneath instead of overloading a global verb.
- Keep backwards compatibility for existing public CLI commands. Known exception: `celesto computer templates` is accepted for listing computer templates.

## User-Facing Writing Principles

- Follow progressive disclosure of complexity.
- Lead with outcomes, not implementation details.
- The first paragraph of every documentation page should be plain English with no jargon.
- Assume the reader may be a beginner engineer or a non-developer.
- Do not assume prior knowledge.
- Explain what the user can do and why it matters before explaining how it works.
- Do not introduce a new concept unless the page truly needs it.
- If a technical term is necessary, explain it immediately in simple language.
- Prefer short, concrete sentences over dense explanations.

## User-Facing Errors and Warnings

Error and warning messages are UX, not stack traces. Every user-facing message
in CLI output, panels, JSON `error` payloads, and JSON `warnings` entries should:

- State the fact in plain English.
- Avoid internal vocabulary when possible, even if that vocabulary appears in flag names.
- Name the recovery with the exact command or API call the user can run.
- Include the actual resource name or ID when available.
- Stay short: one sentence is best, two sentences is acceptable.
- Skip consequences that cannot be guaranteed in the current state.
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ from celesto import Celesto

client = Celesto()

computer = client.computers.create(cpus=2, memory=2048)
computer = client.computers.create(template_id="coding-agent")
print(f"Computer ready: {computer['name']}")

result = client.computers.exec(computer["id"], "uname -a")
Expand All @@ -38,7 +38,7 @@ import { Celesto } from "@celestoai/sdk";

const celesto = new Celesto({ token: process.env.CELESTO_API_KEY });

const computer = await celesto.computers.create({ cpus: 2, memory: 2048 });
const computer = await celesto.computers.create({ templateId: "coding-agent" });
console.log(`Computer ready: ${computer.name}`);

const result = await celesto.computers.exec(computer.id, "uname -a");
Expand All @@ -52,7 +52,7 @@ await celesto.computers.delete(computer.id);
```bash
export CELESTO_API_KEY="your-api-key"

celesto computer create --cpus 2 --memory 2048
celesto computer create --template coding-agent
celesto computer run einstein "ls -la"
celesto computer ssh einstein # interactive shell
celesto computer delete einstein
Expand Down Expand Up @@ -117,7 +117,9 @@ agent = SandboxAgent(
)

client = CelestoSandboxClient()
session = await client.create(options=CelestoSandboxClientOptions(cpus=2, memory=2048))
session = await client.create(
options=CelestoSandboxClientOptions(template_id="coding-agent")
)
try:
async with session:
result = await Runner.run(
Expand All @@ -135,13 +137,29 @@ when you want the same agent flow to run on a local SmolVM sandbox.

## Computers API

Templates are ready-made computer setups. Choose one when you want a computer
that already has the tools your agent needs.

### Create

```python
computer = client.computers.create(cpus=2, memory=2048)
computer = client.computers.create(
template_id="coding-agent",
cpus=2,
memory=2048,
disk_size_mb=15360,
)
print(computer["name"]) # e.g., "einstein"
```

Omit CPU, memory, or disk fields to use the selected template defaults.

```python
templates = client.computers.list_templates()
for template in templates:
print(template["id"], template["default_ram_mb"])
```

### Execute commands

```python
Expand Down Expand Up @@ -170,7 +188,8 @@ for vm in result["computers"]:

| Command | Description |
|---|---|
| `celesto computer create [--cpus N] [--memory MB]` | Create a computer |
| `celesto computer create [--template ID] [--cpus N] [--memory MB] [--disk-size-mb MB]` | Create a computer |
| `celesto computer templates` | List computer templates |
| `celesto computer list` | List all computers |
| `celesto computer run <name> "command"` | Execute a command |
| `celesto computer ssh <name>` | Interactive shell |
Expand All @@ -184,7 +203,8 @@ All commands support `--json` for machine-readable output:

```bash
celesto computer list --json
celesto computer create --cpus 2 --memory 2048 --json
celesto computer templates --json
celesto computer create --template coding-agent --disk-size-mb 15360 --json
celesto computer run einstein "uname -a" --json
```

Expand Down
22 changes: 19 additions & 3 deletions js/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# @celestoai/sdk

Node-only TypeScript SDK for the [Celesto](https://celesto.ai) platform. Covers:
Use this package to create Celesto computers from Node.js apps. Your code can
start a computer, run commands in it, and delete it when the work is done.

It includes:

- **Computers** (`/v1/computers`) — create, manage, and interact with sandboxed virtual machines
- **Gatekeeper** (`/v1/gatekeeper`) — delegated access to user resources
Expand All @@ -22,21 +25,25 @@ const celesto = new Celesto({
});

// Computers
const computer = await celesto.computers.create({ cpus: 2, memory: 2048 });
const computer = await celesto.computers.create({ templateId: "coding-agent" });
const result = await celesto.computers.exec(computer.id, "uname -a");
console.log(result.stdout);
await celesto.computers.delete(computer.id);
```

## Computers

Templates are ready-made computer setups. Choose one when you want a computer
that already has the tools your agent needs.

### Lifecycle

```ts
const computer = await celesto.computers.create({
templateId: "coding-agent",
cpus: 2,
memory: 2048,
image: "ubuntu-desktop-24.04",
diskSizeMb: 15360,
});

await celesto.computers.stop(computer.id);
Expand All @@ -46,6 +53,15 @@ await celesto.computers.delete(computer.id);
const { computers, count } = await celesto.computers.list();
```

Omit CPU, memory, or disk fields to use the selected template defaults.

```ts
const templates = await celesto.computers.listTemplates();
for (const template of templates) {
console.log(template.id, template.defaultRamMb);
}
```

### Running commands

```ts
Expand Down
79 changes: 73 additions & 6 deletions js/src/computers/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ComputerStatus,
CreateComputerParams,
ExecParams,
SandboxTemplateInfo,
TerminalConnectionInfo,
} from "./types";

Expand All @@ -22,7 +23,10 @@ interface ComputerInfoWire {
status: ComputerStatus;
vcpus: number;
ram_mb: number;
disk_size_mb: number;
image: string;
template_id: string;
template_version?: string | null;
connection?: ComputerConnectionInfoWire | null;
last_error?: string | null;
created_at: string;
Expand All @@ -40,6 +44,17 @@ interface ComputerExecResponseWire {
stderr: string;
}

interface SandboxTemplateInfoWire {
id: string;
display_name: string;
description: string;
default_vcpus: number;
default_ram_mb: number;
default_disk_size_mb: number;
version?: string | null;
experimental: boolean;
}

const toConnection = (
payload: ComputerConnectionInfoWire | null | undefined,
): ComputerConnectionInfo | undefined => {
Expand All @@ -62,7 +77,10 @@ const toComputerInfo = (payload: ComputerInfoWire): ComputerInfo => ({
status: payload.status,
vcpus: payload.vcpus,
ramMb: payload.ram_mb,
diskSizeMb: payload.disk_size_mb,
image: payload.image,
templateId: payload.template_id,
templateVersion: payload.template_version ?? null,
connection: toConnection(payload.connection),
lastError: payload.last_error ?? null,
createdAt: payload.created_at,
Expand All @@ -75,6 +93,49 @@ const toExecResponse = (payload: ComputerExecResponseWire): ComputerExecResponse
stderr: payload.stderr,
});

const toSandboxTemplateInfo = (payload: SandboxTemplateInfoWire): SandboxTemplateInfo => ({
id: payload.id,
displayName: payload.display_name,
description: payload.description,
defaultVcpus: payload.default_vcpus,
defaultRamMb: payload.default_ram_mb,
defaultDiskSizeMb: payload.default_disk_size_mb,
version: payload.version ?? null,
experimental: payload.experimental,
});

const buildCreateComputerBody = (params: CreateComputerParams): Record<string, unknown> => {
if (params.cpus !== undefined && params.vcpus !== undefined && params.cpus !== params.vcpus) {
throw new Error("cpus and vcpus must have the same value when both are provided.");
}
if (params.memory !== undefined && params.ramMb !== undefined && params.memory !== params.ramMb) {
throw new Error("memory and ramMb must have the same value when both are provided.");
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const body: Record<string, unknown> = {};
const vcpus = params.vcpus ?? params.cpus;
const ramMb = params.ramMb ?? params.memory;
if (vcpus !== undefined) {
body.vcpus = vcpus;
}
if (ramMb !== undefined) {
body.ram_mb = ramMb;
}
if (params.diskSizeMb !== undefined) {
body.disk_size_mb = params.diskSizeMb;
}
if (params.image !== undefined) {
body.image = params.image;
}
if (params.templateId !== undefined) {
body.template_id = params.templateId;
}
if (params.templateVersion !== undefined) {
body.template_version = params.templateVersion;
}
return body;
};

const computersPath = (path: string): string => `/v1/computers${path}`;

const pickOverrides = (options?: RequestOverrides): RequestOverrides => ({
Expand All @@ -92,7 +153,7 @@ const pickOverrides = (options?: RequestOverrides): RequestOverrides => ({
* @example
* ```ts
* const celesto = new Celesto({ token: process.env.CELESTO_API_KEY });
* const computer = await celesto.computers.create({ cpus: 2, memory: 2048 });
* const computer = await celesto.computers.create({ templateId: "coding-agent" });
* const result = await celesto.computers.exec(computer.id, "uname -a");
* console.log(result.stdout);
* await celesto.computers.delete(computer.id);
Expand All @@ -110,16 +171,22 @@ export class ComputersClient {
const data = await request<ComputerInfoWire>(ctx, {
method: "POST",
path: computersPath(""),
body: {
vcpus: params.cpus ?? 1,
ram_mb: params.memory ?? 1024,
image: params.image ?? "ubuntu-desktop-24.04",
},
body: buildCreateComputerBody(params),
...pickOverrides(options),
});
return toComputerInfo(data);
}

async listTemplates(options?: RequestOverrides): Promise<SandboxTemplateInfo[]> {
const ctx = buildRequestContext(this.config);
const data = await request<SandboxTemplateInfoWire[]>(ctx, {
method: "GET",
path: computersPath("/templates"),
...pickOverrides(options),
});
return data.map(toSandboxTemplateInfo);
}

async list(options?: RequestOverrides): Promise<ComputerListResponse> {
const ctx = buildRequestContext(this.config);
const data = await request<ComputerListResponseWire>(ctx, {
Expand Down
1 change: 1 addition & 0 deletions js/src/computers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export type {
ComputerStatus,
CreateComputerParams,
ExecParams,
SandboxTemplateInfo,
TerminalConnectionInfo,
} from "./types";
32 changes: 29 additions & 3 deletions js/src/computers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export type ComputerStatus =
| "stopping"
| "stopped"
| "starting"
| "restoring"
| "restorable"
| "deleting"
| "deleted"
| "error";
Expand All @@ -19,7 +21,10 @@ export interface ComputerInfo {
status: ComputerStatus;
vcpus: number;
ramMb: number;
diskSizeMb: number;
image: string;
templateId: string;
templateVersion?: string | null;
connection?: ComputerConnectionInfo;
lastError?: string | null;
createdAt: string;
Expand All @@ -37,13 +42,34 @@ export interface ComputerExecResponse {
stderr: string;
}

export interface SandboxTemplateInfo {
id: string;
displayName: string;
description: string;
defaultVcpus: number;
defaultRamMb: number;
defaultDiskSizeMb: number;
version?: string | null;
experimental: boolean;
}

export interface CreateComputerParams {
/** Number of virtual CPUs (1-16). Defaults to 1. */
/** Number of virtual CPUs (1-16). Alias for vcpus. */
cpus?: number;
/** Memory in MB (512-32768). Defaults to 1024. */
/** Number of virtual CPUs (1-16). */
vcpus?: number;
/** Memory in MB (512-32768). Alias for ramMb. */
memory?: number;
/** OS image name. Defaults to "ubuntu-desktop-24.04". */
/** Memory in MB (512-32768). */
ramMb?: number;
/** Disk size in MB (512-51200). */
diskSizeMb?: number;
/** Legacy OS image selector. */
image?: string;
/** Sandbox template id, such as "scratch" or "coding-agent". */
templateId?: string;
/** Optional immutable template version. */
templateVersion?: string;
}

export interface ExecParams {
Expand Down
1 change: 1 addition & 0 deletions js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type {
ComputerStatus,
CreateComputerParams,
ExecParams,
SandboxTemplateInfo,
TerminalConnectionInfo,
} from "./computers";
export type { ClientConfig, RequestOverrides } from "./core/config";
Expand Down
Loading
Loading