CrowNest
Quickstart

TypeScript SDK quickstart

Create a sandbox, run a command, work with files, export an artifact, and expose a preview with the @crownest/sdk package.

Walk the full sandbox lifecycle with the TypeScript SDK: create a sandbox, run a command, write and read a file, export an artifact, expose a preview, and clean up. The result is a complete script you can drop into your own project.

For archive-based repo/test work, use client.workspaceRuns instead of creating a sandbox directly. Workspace Runs upload an existing .tar.gz or .tgz, stream execution events, and persist durable evidence:

import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";

const archive = await readFile("repo.tgz");
const sha256 = createHash("sha256").update(archive).digest("hex");

const run = await client.workspaceRuns.runArchive({
  archive: {
    body: archive,
    sha256,
    sizeBytes: archive.byteLength,
  },
  template: "python-node",
  command: "pnpm test",
});

See Workspace Runs for streaming events, staged transfers, cancelation, and evidence reads.

Prerequisites

Warning

The raw API key is shown once, at creation time. Copy it immediately and store it somewhere safe — you can't view it again later.

Set up

Follow these steps to install the SDK and authenticate.

  1. Install the package.

    Terminal
    pnpm add @crownest/sdk

    npm and yarn work too: npm install @crownest/sdk or yarn add @crownest/sdk.

  2. Export your API key. The client reads CROWNEST_API_KEY from the environment when you don't pass apiKey explicitly; if both are missing, creating the client throws.

    Terminal
    export CROWNEST_API_KEY="cn_live_..."
  3. Create a client.

    main.ts
    import { createCrowNestClient } from "@crownest/sdk";
    
    const client = createCrowNestClient();

    You can also pass options: createCrowNestClient({ apiKey, baseUrl, fetch }). The baseUrl defaults to https://api.crownest.dev.

Walk through the lifecycle

Each step below builds on the previous one inside the same script.

  1. Create a sandbox. create returns a SandboxHandle — the sandbox resource plus bound helpers (sandbox.commands, sandbox.code, sandbox.files, sandbox.artifacts, sandbox.previews, and sandbox.kill()), so you don't repeat the sandbox ID on every call.

    const sandbox = await client.sandboxes.create({ template: "python-node" });

    python-node is the default broad repository runner. Use node for a lean JavaScript and TypeScript environment. You can also set ttlMs to request a lifetime and metadata to attach small string labels — see Sandboxes.

  2. Run a command. run waits for the process to exit and returns the full command record, including exitCode, stdout, and stderr.

    const result = await sandbox.commands.run("python3 -c 'print(40 + 2)'");
    console.log(result.exitCode, result.stdout);

    Important

    A non-zero exit code does not throw. The command completed; your code inside it failed. Always check result.exitCode when the outcome matters. CrowNestApiError is reserved for API-level failures.

    Stream output as it happens. Use callbacks when you want live stdout and stderr while still getting the final Command record from run.

    await sandbox.commands.run(
      `python3 -c "import time; [print('step', i, flush=True) or time.sleep(0.2) for i in range(3)]"`,
      {
        onStdout: (chunk) => process.stdout.write(chunk),
        onStderr: (chunk) => process.stderr.write(chunk),
      },
    );

    Run code, not just commands. Use Code Runs for notebook-style snippets and rich interpreter outputs. See Code for stateful Code Contexts, streaming Code Run events, and Artifact promotion.

    const codeResult = await sandbox.code.run({ code: "print(40 + 2)" });
    console.log(codeResult.stdout);
  3. Write and read a file. All file paths are inside /workspace, the sandbox's working filesystem area.

    await sandbox.files.write("notes.txt", "hello from crownest");
    const content = await sandbox.files.read("notes.txt");
    console.log(content);

    Note

    File APIs are confined to /workspace. Paths that resolve outside it through .. segments are rejected with the path_outside_workspace error code.

  4. Export an artifact. The workspace disappears with the sandbox, so copy anything you want to keep to durable storage with an explicit export.

    const artifact = await sandbox.artifacts.create({ path: "notes.txt" });
    console.log(artifact.id);

    You can download it later — even after the sandbox is gone — with client.artifacts.download(artifact.id), which returns a Uint8Array.

  5. Expose a preview. When your sandbox hosts an HTTP service, create a preview to get an authenticated URL like https://p-a1b2c3.crownest.dev.

    await sandbox.commands.start("python3 -m http.server 8000");
    const { preview } = await sandbox.previews.create({ port: 8000 });
    console.log(preview.url);

    start launches the server without waiting for it to exit. Previews require authentication in v1. Use authMode: "token" when you need a browser-openable link; the one-time previewToken is returned only from the create response. See Previews.

  6. Kill the sandbox. This stops billing and releases the environment. Sandboxes also expire automatically at their TTL.

    await sandbox.kill();

Handle errors

API failures throw CrowNestApiError, which carries the HTTP status, a stable machine-readable code, a message, and optional details.

import { createCrowNestClient, CrowNestApiError } from "@crownest/sdk";

const client = createCrowNestClient();

try {
  await client.sandboxes.get("sbx_does_not_exist");
} catch (error) {
  if (error instanceof CrowNestApiError) {
    console.error(error.status, error.code, error.message, error.details);
  } else {
    throw error;
  }
}

See the error reference for the full list of codes.

Complete script

The end-to-end version of the steps above.

main.ts
import { createCrowNestClient } from "@crownest/sdk";

const client = createCrowNestClient();

const sandbox = await client.sandboxes.create({ template: "python-node" });

const result = await sandbox.commands.run("python3 -c 'print(40 + 2)'");
console.log(result.exitCode, result.stdout);

await sandbox.files.write("notes.txt", "hello from crownest");
const content = await sandbox.files.read("notes.txt");
console.log(content);

const artifact = await sandbox.artifacts.create({ path: "notes.txt" });
console.log("artifact:", artifact.id);

await sandbox.commands.start("python3 -m http.server 8000");
const { preview } = await sandbox.previews.create({ port: 8000 });
console.log("preview:", preview.url);

await sandbox.kill();

Next steps

On this page