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 withCREATEOS_SANDBOX_BASE_URL - Auth: API key via the
apiKeyoption orCREATEOS_SANDBOX_API_KEY
Polling
TypeScript1import { 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>
| Field | Type | Required | Description |
|---|---|---|---|
poll | () => Promise<T> | yes | Fetches the current state. Called on every iteration. |
done | (value: T) => boolean | yes | Returns true when the desired state is reached. |
failed | (value: T) => string | undefined | no | Returns an error message on a terminal failure state; undefined means keep polling. |
timeoutMs | number | yes | Overall deadline in milliseconds. |
signal | AbortSignal | no | Aborts the loop early; throws CreateosSandboxError("Wait aborted."). |
Example — wait for a custom condition:
TypeScript1import { pollUntil } from "@nodeops-createos/sandbox";23const 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 overpollUntil. 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.
TypeScript1import { sleep } from "@nodeops-createos/sandbox";23await sleep(500); // plain delay4await sleep(500, controller.signal); // abortable delay
Runtime detection
TypeScript1import { 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
TypeScript1type 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:
Bun.version→"bun"Deno.version.deno→"deno"process.env.NEXT_RUNTIME === "edge"→"edge-light"typeof WebSocketPair !== "undefined"→"workerd"navigator.product === "ReactNative"→"react-native"process.versions.node→"node"window+documentdefined →"browser"- 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").
TypeScript1import { detectRuntime, runtimeTag } from "@nodeops-createos/sandbox";23console.log(detectRuntime()); // e.g. "node"4console.log(runtimeTag()); // e.g. "node-22.10.0"
Redaction
TypeScript1import {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:
TypeScript1import { createClient, redactHeaders, redactUrl } from "@nodeops-createos/sandbox";23const 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
TypeScript1import { 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.
TypeScript1import { VERSION } from "@nodeops-createos/sandbox";23console.log(VERSION); // "0.6.0"
CreateosSandboxHttp (escape hatch)
TypeScript1import { 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
CreateosSandboxClientandSandboxfirst. Drop toclient.httponly 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
TypeScript1import type { ExecStreamFrame } from "@nodeops-createos/sandbox";23// 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);910for 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.