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:
CROWNEST_WORKSPACE_RUN_E2E=1 \
CROWNEST_API_KEY=cn_live_... \
pnpm live:workspace-runsRun 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.
| Field | Type | Description |
|---|---|---|
command | string | Shell command to run after archive extraction. |
projectId | `prj_${string}` | Project to create the run in. |
template | string | CrowNest template slug. python-node is the launch default. |
templateVersionId | `tplv_${string}` | Specific CrowNest template version. |
sandboxId | `sbx_${string}` | Warm sandbox to reuse. |
keepSandbox | boolean | Keep the sandbox after the run. |
timeoutMs | number | Command timeout in milliseconds. |
metadata | Record<string,string> | Small labels for list/evidence. |
sourceMetadata | Record<string,string> | Caller source labels such as repo or commit. |
artifacts | { path: string; name?: string }[] | Explicit relative paths to collect after command completion. |
idempotencyKey | string | Retry 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
- API Workspace Runs — REST endpoints and event shapes.
- CLI Workspace Runs — terminal commands.
- Errors —
CrowNestApiErrorand retry guidance.
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.