NodeOps
UK

Helpers & low-level transport

These utilities back the waitUntil* lifecycle methods on Sandbox, the logging-middleware redaction helpers you wire into onRequest/onResponse hooks, runtime environment tagging in User-Agent / X-Fc-Runtime, and the raw HTTP escape hatch exposed as client.http.


At a glance

  • Package: @nodeops-createos/sandbox (npm)
  • Import: import { createClient } from "@nodeops-createos/sandbox"
  • Base URL: https://api.sb.createos.sh — override with CREATEOS_SANDBOX_BASE_URL
  • Auth: API key via the apiKey option or CREATEOS_SANDBOX_API_KEY

Polling

TypeScript
1import { pollUntil, sleep } from "@nodeops-createos/sandbox";

pollUntil<T>(options: PollOptions<T>): Promise<T>

Polls options.poll() with adaptive backoff until options.done() returns true.

The interval starts at 250 ms and stays there for the first 5 s of wall time. After 5 s it ramps by ×1.25 per iteration, capped at 2 s — fast lifecycle transitions (sandbox ready, template built) resolve quickly without hammering the API, while slow operations don't busyloop.

Throws CreateosSandboxError when options.failed() returns a message, or CreateosSandboxTimeoutError when options.timeoutMs is exhausted.

PollOptions<T>

FieldTypeRequiredDescription
poll() => Promise<T>yesFetches the current state. Called on every iteration.
done(value: T) => booleanyesReturns true when the desired state is reached.
failed(value: T) => string | undefinednoReturns an error message on a terminal failure state; undefined means keep polling.
timeoutMsnumberyesOverall deadline in milliseconds.
signalAbortSignalnoAborts the loop early; throws CreateosSandboxError("Wait aborted.").

Example — wait for a custom condition:

TypeScript
1import { pollUntil } from "@nodeops-createos/sandbox";
2
3const result = await pollUntil({
4 poll: () => client.getSandbox(sandboxId),
5 done: (s) => s.status === "running",
6 failed: (s) => s.status === "failed" ? `Sandbox failed: ${s.status}` : undefined,
7 timeoutMs: 120_000,
8});

The built-in sandbox.waitUntilReady() / sandbox.waitUntilStopped() / sandbox.waitUntilDeleted() are thin wrappers over pollUntil. Reach for those directly unless you need a custom predicate.


sleep(ms: number, signal?: AbortSignal): Promise<void>

Resolves after ms milliseconds, or immediately if ms ≤ 0. If signal is already aborted or fires before the timer expires, resolves early — never rejects. Safe to use anywhere you need a cancellation-aware delay.

TypeScript
1import { sleep } from "@nodeops-createos/sandbox";
2
3await sleep(500); // plain delay
4await sleep(500, controller.signal); // abortable delay

Runtime detection

TypeScript
1import { detectRuntime, runtimeTag } from "@nodeops-createos/sandbox";
2import type { Runtime } from "@nodeops-createos/sandbox";

The SDK calls these automatically at construction time to stamp the User-Agent and X-Fc-Runtime request headers. They are exported so you can branch on the runtime yourself or include the tag in your own logging.

Runtime type

TypeScript
1type Runtime =
2 | "node"
3 | "bun"
4 | "deno"
5 | "workerd"
6 | "edge-light"
7 | "browser"
8 | "react-native"
9 | "unknown";

detectRuntime(): Runtime

Inspects globalThis via typeof guards that never throw. Detection order:

  1. Bun.version"bun"
  2. Deno.version.deno"deno"
  3. process.env.NEXT_RUNTIME === "edge""edge-light"
  4. typeof WebSocketPair !== "undefined""workerd"
  5. navigator.product === "ReactNative""react-native"
  6. process.versions.node"node"
  7. window + document defined → "browser"
  8. Falls back to "unknown".

runtimeTag(): string

Returns "<runtime>-<version>" when a version string is available (e.g. "node-22.10.0", "bun-1.1.0"), or just "<runtime>" for runtimes without an accessible version ("workerd", "edge-light", "browser", "react-native", "unknown").

TypeScript
1import { detectRuntime, runtimeTag } from "@nodeops-createos/sandbox";
2
3console.log(detectRuntime()); // e.g. "node"
4console.log(runtimeTag()); // e.g. "node-22.10.0"

Redaction

TypeScript
1import {
2 redactHeaders,
3 redactQuery,
4 redactUrl,
5 SENSITIVE_HEADER_NAMES,
6 SENSITIVE_QUERY_PARAMS,
7} from "@nodeops-createos/sandbox";

These are pure helpers for your own logging middleware. They are not wired into the SDK transport. To use them, pass hooks.onRequest / hooks.onResponse to the client — see Observability. All functions are non-mutating.

Constants

SENSITIVE_HEADER_NAMES: ReadonlySet<string>

Header names (lowercase) whose values are always redacted: authorization, cookie, set-cookie, x-api-key, x-access-token, x-auth-token, x-csrf-token, proxy-authorization. Any header whose lowercased name ends in "-token" or "-key" is also redacted even if not in the set.

SENSITIVE_QUERY_PARAMS: ReadonlySet<string>

Query-string keys (lowercased) whose values are redacted: token, api_key, apikey, access_token, auth_token, password, secret.

redactHeaders(headers: Headers | HeadersInit): Record<string, string>

Returns a plain Record<string, string> with sensitive header values replaced by "redacted". Does not mutate the input.

redactQuery(query: URLSearchParams): URLSearchParams

Returns a new URLSearchParams with sensitive param values replaced by "redacted".

redactUrl(url: string): string

Strips the URL userinfo (username:password) and redacts sensitive query params. Returns the original string unchanged if url does not parse as a URL.

Example — safe request logging in an onRequest hook:

TypeScript
1import { createClient, redactHeaders, redactUrl } from "@nodeops-createos/sandbox";
2
3const client = createClient({
4 apiKey: process.env.CREATEOS_SANDBOX_API_KEY,
5 hooks: {
6 onRequest({ method, url, headers }) {
7 console.log(method, redactUrl(url), redactHeaders(headers));
8 },
9 },
10});

VERSION

TypeScript
1import { VERSION } from "@nodeops-createos/sandbox";

VERSION: string — the current SDK version string (e.g. "0.6.0"), stamped into the User-Agent header as createos-sandbox-sdk/<VERSION> <runtimeTag>. Keep in sync with package.json when cutting a release.

TypeScript
1import { VERSION } from "@nodeops-createos/sandbox";
2
3console.log(VERSION); // "0.6.0"

CreateosSandboxHttp (escape hatch)

TypeScript
1import { CreateosSandboxHttp } from "@nodeops-createos/sandbox";

The HTTP transport is exposed as client.http — a CreateosSandboxHttp instance. This is the escape hatch for calling control-plane endpoints that the high-level client does not yet model, or for advanced use cases that need raw frame access.

Retries, timeouts, JSend envelope unwrapping, and AbortSignal composition all live in CreateosSandboxHttp. You do not need to handle these yourself when going through its methods.

Reach for CreateosSandboxClient and Sandbox first. Drop to client.http only when you have no other option.

Methods

request<T>(method, path, options?): Promise<T>

Issues a request, unwraps the JSend { status, data } envelope, and returns the data value typed as T. Retried according to the retry policy for idempotent methods or on 429/503 for non-idempotent ones.

requestRaw(method, path, options?): Promise<Response>

Like request, but returns the raw Response without unwrapping JSend. Use for binary or plain-text endpoints (content-type: application/octet-stream, file downloads, etc.).

stream<T>(method, path, options?): AsyncGenerator<T>

Streams an NDJSON response as an async iterator, yielding one parsed frame per line. Not retried. Throws CreateosSandboxError if the response body is empty. See Streaming for patterns.

fetchAllPages<T>(method, path, options?): Promise<T[]>

Fetches all pages of a paginated list endpoint and returns a flat array. Handles the doubly-nested envelope { data: { data: [...], pagination: { total, limit, offset, count } } }, the legacy { <key>: [...] } wrapper, and bare arrays. The server clamps limit to 500; paging is driven by the reported total and actual item count.

Raw-frame streaming example

TypeScript
1import type { ExecStreamFrame } from "@nodeops-createos/sandbox";
2
3// See Types reference for ExecStreamFrame shape.
4const frames = client.http.stream<ExecStreamFrame>(
5 "POST",
6 `/v1/sandboxes/${sandbox.id}/exec`,
7 { body: { command: ["bash", "-c", "echo hello"] } },
8);
9
10for await (const frame of frames) {
11 process.stdout.write(frame.output ?? "");
12}

For higher-level command execution with built-in output handling, see sandbox.runCommand() / sandbox.streamCommand() in Sandbox and the Streaming guide.

For ExecStreamFrame and all other wire types, see Types.

100,000+ Builders. One Workspace.

Get product updates, builder stories, and early access to features that help you ship faster.

CreateOS is a unified intelligent workspace where ideas move seamlessly from concept to live deployment, eliminating context-switching across tools, infrastructure, and workflows with the opportunity to monetize ideas immediately on the CreateOS Marketplace.