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
- Node.js 18 or later.
- A CrowNest API key, created in the dashboard at https://crownest.dev. See API keys for scopes and presets.
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.
-
Install the package.
Terminal pnpm add @crownest/sdknpm and yarn work too:
npm install @crownest/sdkoryarn add @crownest/sdk. -
Export your API key. The client reads
CROWNEST_API_KEYfrom the environment when you don't passapiKeyexplicitly; if both are missing, creating the client throws.Terminal export CROWNEST_API_KEY="cn_live_..." -
Create a client.
main.ts import { createCrowNestClient } from "@crownest/sdk"; const client = createCrowNestClient();You can also pass options:
createCrowNestClient({ apiKey, baseUrl, fetch }). ThebaseUrldefaults tohttps://api.crownest.dev.
Walk through the lifecycle
Each step below builds on the previous one inside the same script.
-
Create a sandbox.
createreturns aSandboxHandle— the sandbox resource plus bound helpers (sandbox.commands,sandbox.code,sandbox.files,sandbox.artifacts,sandbox.previews, andsandbox.kill()), so you don't repeat the sandbox ID on every call.const sandbox = await client.sandboxes.create({ template: "python-node" });python-nodeis the default broad repository runner. Usenodefor a lean JavaScript and TypeScript environment. You can also setttlMsto request a lifetime andmetadatato attach small string labels — see Sandboxes. -
Run a command.
runwaits for the process to exit and returns the full command record, includingexitCode,stdout, andstderr.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.exitCodewhen the outcome matters.CrowNestApiErroris 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); -
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 thepath_outside_workspaceerror code. -
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 aUint8Array. -
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);startlaunches the server without waiting for it to exit. Previews require authentication in v1. UseauthMode: "token"when you need a browser-openable link; the one-timepreviewTokenis returned only from the create response. See Previews. -
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.
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
- Learn how lifetimes, templates, and statuses work in Sandboxes.
- Stream logs and collect outputs with Commands.
- Run archive-based workflows with Workspace Runs.
- Browse every method in the TypeScript SDK reference.
- Prefer the terminal? Try the CLI quickstart.