CrowNest
TypeScript SDK

Workspace Runs

Use client.workspaceRuns to upload an archive, run a command, stream events, and read evidence.

client.workspaceRuns exposes CrowNest Workspace Runs from the TypeScript SDK. Use it when you already have a .tar.gz or .tgz archive and want CrowNest to execute a command inside a curated template such as python-node.

The SDK does not pack local directories. Build the archive yourself or use a tool such as Crabbox for dirty checkout sync.

Launch support is gated on the python-node template. Release operators can run the proof with:

Terminal
CROWNEST_WORKSPACE_RUN_E2E=1 \
CROWNEST_API_KEY=cn_live_... \
pnpm live:workspace-runs

Run an archive

import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";
import { createCrowNestClient } from "@crownest/sdk";

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

const run = await client.workspaceRuns.create({
  template: "python-node",
  command: "pnpm test",
  timeoutMs: 120_000,
});

const transfer = await client.workspaceRuns.createArchiveTransfer(run.id, {
  sha256,
  sizeBytes: bytes.byteLength,
});

await client.workspaceRuns.uploadArchiveToTransfer(transfer, { body: bytes });

await client.workspaceRuns.finalizeArchive(run.id, {
  uploadId: transfer.id,
  sha256,
  sizeBytes: bytes.byteLength,
});

await client.workspaceRuns.start(run.id);

for await (const event of client.workspaceRuns.streamEvents(run.id)) {
  if (event.type === "stdout") process.stdout.write(event.data);
  if (event.type === "stderr") process.stderr.write(event.data);
  if (event.type === "error") throw new Error(event.message);
  if (event.type === "terminal") {
    process.exitCode = event.workspaceRun.exitCode ?? 0;
    break;
  }
}

const evidence = await client.workspaceRuns.evidence(run.id);
console.log(evidence.status, evidence.exitCode);

uploadArchiveToTransfer authenticates transfer URLs on the configured CrowNest API origin and strips the CrowNest bearer token for external upload targets. That keeps the helper compatible with today's Worker-backed upload route and future durable-storage upload URLs.

Implemented upload limit

Current archive uploads are capped at 8 MiB because both direct and staged uploads route through the API Worker today. The staged helper exists so callers can keep the same flow when storage-backed uploads replace the Worker-backed target.

Methods

create

create(input: CreateWorkspaceRunInput): Promise<WorkspaceRun>

Creates a run record in awaiting_archive.

FieldTypeDescription
commandstringShell command to run after archive extraction.
projectId`prj_${string}`Project to create the run in.
templatestringCrowNest template slug. python-node is the launch default.
templateVersionId`tplv_${string}`Specific CrowNest template version.
sandboxId`sbx_${string}`Warm sandbox to reuse.
keepSandboxbooleanKeep the sandbox after the run.
timeoutMsnumberCommand timeout in milliseconds.
metadataRecord<string,string>Small labels for list/evidence.
sourceMetadataRecord<string,string>Caller source labels such as repo or commit.
artifacts{ path: string; name?: string }[]Explicit relative paths to collect after command completion.
idempotencyKeystringRetry key for create.

Artifact collection requires the API key to include artifact:create and file:read.

uploadArchive

uploadArchive(
  workspaceRunId: `wsr_${string}`,
  input: { bytes: Uint8Array; sha256: string; sizeBytes: number; idempotencyKey?: string },
): Promise<{ archive: WorkspaceRunArchive; workspaceRun: WorkspaceRun }>

Uploads a small archive directly through the CrowNest API.

createArchiveTransfer

createArchiveTransfer(
  workspaceRunId: `wsr_${string}`,
  input: { sha256: string; sizeBytes: number; idempotencyKey?: string },
): Promise<WorkspaceRunArchiveTransfer>

Creates a staged upload target with uploadUrl, method, headers, expiresAt, and maxSizeBytes.

uploadArchiveToTransfer

uploadArchiveToTransfer(
  transfer: WorkspaceRunArchiveTransfer,
  input: { body: BodyInit },
): Promise<void>

Uploads bytes to the staged transfer target without CrowNest API authentication.

finalizeArchive

finalizeArchive(
  workspaceRunId: `wsr_${string}`,
  input: { uploadId: `upl_${string}`; sha256: string; sizeBytes: number; idempotencyKey?: string },
): Promise<{ archive: WorkspaceRunArchive; workspaceRun: WorkspaceRun }>

Verifies and attaches the staged upload to the run.

start

start(
  workspaceRunId: `wsr_${string}`,
  input?: { idempotencyKey?: string },
): Promise<WorkspaceRun>

Starts extraction and command execution.

streamEvents

streamEvents(
  workspaceRunId: `wsr_${string}`,
  input?: { afterSeq?: number; reconnect?: boolean },
): AsyncIterable<WorkspaceRunStreamEvent>

Streams status, archive_progress, stdout, stderr, artifact_collected, artifact_error, heartbeat, terminal, and error events. Use afterSeq to resume after the last sequence number you processed.

get, list, cancel, evidence

get(workspaceRunId: `wsr_${string}`): Promise<WorkspaceRun>

list(input?: {
  metadata?: Record<string, string>;
  projectId?: `prj_${string}`;
  status?: WorkspaceRunStatus;
}): Promise<readonly WorkspaceRun[]>

cancel(workspaceRunId: `wsr_${string}`): Promise<WorkspaceRun>

evidence(workspaceRunId: `wsr_${string}`): Promise<WorkspaceRunEvidenceBundle>

evidence returns durable run proof after the run is terminal and the Evidence Bundle has been persisted.

Dashboard inspection

The signed-in dashboard can inspect SDK-created Workspace Runs at /workspace-runs. It lists runs, links to a detail page, displays Evidence Bundle fields after terminal completion, and offers owner/admin cancellation for active runs.

The dashboard does not perform SDK actions such as archive upload, start, event streaming, or Template creation. Use client.workspaceRuns for those.

Next steps

The SDK surface does not expose provider selection, self-serve Dockerfile templates, E2B-backed execution, SSH/devbox access, desktop, GPU, pause/resume, browser automation, artifact globs, or full CI replacement.

On this page