npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@generativex/sandbox-sdk

v0.3.1

Published

TypeScript SDK for GX Sandbox — isolated microVM sandboxes, app serving, and managed databases via one typed client.

Readme

GX Sandbox — TypeScript SDK

npm License

Typed TypeScript/JavaScript client for GX Sandbox — spin up isolated microVM sandboxes, run commands and stream logs, read/write files, deploy app-serving services, and provision managed databases (MySQL / Valkey), all behind one client.

Ships ESM and CommonJS with full .d.ts types. Types and request/response shapes are derived from the server's OpenAPI schema (the single source of truth — see ADR 70-devex/sdk/000), so the SDK can't drift from the API.

Install

npm install @generativex/sandbox-sdk

Requires Node.js 18+ (uses the global fetch/AbortSignal).

Authentication

Every request authenticates with a project API key (sb_…) or a personal access token (sbp_…) as a Bearer token. Get one from the Console, the sandbox CLI, or the admin API (see authentication docs).

Pass it explicitly, or set environment variables and let the client pick them up:

export SANDBOX_API_KEY="sb_…"
export SANDBOX_API_URL="https://api.your-gx-sandbox.example"   # defaults to http://localhost:3000

Quick start

import { SandboxClient } from "@generativex/sandbox-sdk";

// apiKey / baseUrl fall back to SANDBOX_API_KEY / SANDBOX_API_URL
const client = new SandboxClient({
  apiKey: "sb_…",
  baseUrl: "https://api.your-gx-sandbox.example",
});

const sb = await client.sandboxes.create({ alias: "demo" }); // auto-starts
const out = await sb.run("echo hello");
console.log(out.stdout); // "hello\n"

// Files are base64-on-the-wire; the SDK encodes/decodes for you.
// write() accepts a string or Uint8Array; read() returns bytes (Uint8Array).
await sb.files.write("/tmp/note.txt", "hi there");
const bytes = await sb.files.read("/tmp/note.txt");
console.log(new TextDecoder().decode(bytes)); // "hi there"

// Stream logs as an async iterable.
for await (const line of sb.logs({ follow: true })) {
  console.log(line.line);
}

await sb.destroy();

Ergonomics

await using — never leak a microVM. The sandbox is destroyed when it leaves scope, even if your code throws:

{
  await using sb = await client.sandboxes.create({ alias: "ci" });
  await sb.run("pytest");
} // sb.destroy() runs here automatically

Stream output live and still get the exit code. Pass onStdout/onStderr to run() — output is delivered as it's produced, and the resolved result (exit code included) is returned at the end. Ideal for agent loops:

const { exitCode } = await sb.run("npm test", {
  onStdout: (line) => process.stdout.write(line + "\n"),
  onStderr: (line) => process.stderr.write(line + "\n"),
});

Prefer an async iterable? for await (const line of sb.stream("npm test")) ….

Background processes return a handle — poll, wait, or kill without juggling PIDs:

const proc = await sb.runBackground("python -m http.server 8000");
const result = await proc.wait();   // polls until exit; result.exitCode
await proc.kill();

Read files in the shape you want; write many at once:

const text = await sb.files.read("/app/log.txt", { format: "text" });
await sb.files.writeMany([
  { path: "/app/main.py", content: "print('hi')" },
  { path: "/app/data.bin", content: bytes },
]);

Interactive terminal (PTY) over WebSocket — no BYO WS client:

await using term = await sb.terminal();
term.onData((bytes) => process.stdout.write(bytes));
term.write("ls -la\n");
term.resize(120, 40);
// or stream output: for await (const chunk of term) process.stdout.write(chunk);

Run the OpenAI Codex agent inside the sandbox in one call. sb.codex() starts the codex app-server in the VM and opens an initialize-d JSON-RPC session over the passthrough WebSocket:

await using codex = await sb.codex({ version: "0.125.0", apiKey: process.env.OPENAI_API_KEY });
for await (const ev of codex.turn("Refactor src/parse.ts and add tests")) {
  if (ev.method === "item/completed") console.log(ev.params);
}

turn() is the opinionated convenience (thread/startturn/start → stream to turn/completed). For full control, every codex RPC is typed: codex.request autocompletes the method name and types params per method (over codex's ClientRequest union), notifications stream as the typed ServerNotification union, and the approvals/elicitations codex raises mid-turn are answered via onServerRequest:

codex.onServerRequest((req) => {
  if (req.method === "item/commandExecution/requestApproval") return { decision: "accept" };
  if (req.method === "item/fileChange/requestApproval") return { decision: "accept" };
  return { decision: "decline" };
});
await codex.request("model/list", {});        // typed method + params
for await (const n of codex) console.log(n.method); // typed ServerNotification

The codex message types are owned by codex — committed under src/generated/codex/ and regenerated with npm run generate:codex (runs codex app-server generate-ts) to match the codex version your platform runs.

Codex config is fully, type-safely controllable. Pass a typed CodexConfig (codex's own config.toml schema — enum-enforced) and the SDK serializes it to TOML and sends it verbatim, so what you write is exactly codex's config:

await using codex = await sb.codex({
  version: "0.139.0",
  apiKey: process.env.OPENAI_API_KEY,
  config: {
    model_provider: "openai",
    approval_policy: "untrusted",   // typed as AskForApproval
    sandbox_mode: "read-only",      // typed as SandboxMode
    model_reasoning_effort: "high", // typed as ReasoningEffort
  },
});

Every event the agent emits is captured — reasoning ("thinking"), tool/command execution, file changes, agent messages — as typed ServerNotifications (item/started/item/completed carry a typed item), and approvals arrive via onServerRequest. (Verified live against codex 0.139.)

Control the idle auto-stop window (the server-side setTimeout analog — in seconds), or pin a sandbox up:

await sb.setIdleTimeout(900);  // auto-stop after 15 min of inactivity
await sb.setAlwaysOn();        // disable idle auto-stop entirely

Reconnect to a sandbox by id/alias from anywhere (the client is stateless):

const sb = await client.sandboxes.connect(sandboxId); // alias for .get()

Correlate failures with server logs. Every API error carries the server request id:

catch (err) {
  if (err instanceof SandboxHttpStatusError) {
    console.error(err.code, err.requestId); // → scripts/logs.sh request <id>
  }
}

What you can do

| Namespace | What it covers | |---|---| | client.sandboxes | Create / list / search / get / handle; lifecycle (start/stop/destroy/waitFor); run ({ timeoutSecs }), stream, background processes (runBackground.follow() incremental output, .wait()); files (read/write/writeMany/patch/list/upload/download/stream); ports, forwards & exposures ({ signal }); snapshots; commit({ metadata, requireClean })/clone; env, runtime secrets & metadata; resize; idle policy; desktop (desktopInfo/resizeDesktop/clipboard) & terminal/VNC; startVscode/vscodeStatus; logs (SSE); egress audit | | client.services | App-serving (Cloud Run-like): deploy (image / build-from-git / source), scale, autoscale, rename, domains, secrets, volumes (+ backup/restore), instances, logs, deploy-events, builds, rollback — via services.handle(idOrName) | | client.mysql / client.valkey | Managed databases via handle(id): create/get/list; SQL console (query/databases/schema/tableDetail/browseData); users; backups; metrics; lifecycle | | client.apps | App manifests + ephemeral per-session app instances; TeamRun for multi-turn WebSocket sessions | | client.images | Read-only image catalog (catalog), named templates (templates), and image store (store, committed snapshots) | | client.networkPolicies | Project L3/L4 egress/ingress firewall policies | | client.secrets / client.principals | IAM: project secrets and first-class identities | | client.whoami() / client.egressProfiles() | Identify the calling credential; list egress profiles |

Errors

All API failures throw a typed error. Branch on the structured fields, not strings:

import {
  SandboxHttpStatusError,
  SandboxPayloadTooLargeError,
  isRetryable,
} from "@generativex/sandbox-sdk";

try {
  await sb.files.write("/tmp/big", huge);
} catch (err) {
  if (err instanceof SandboxPayloadTooLargeError) {
    console.error(err.body?.hint);
  } else if (err instanceof SandboxHttpStatusError) {
    console.error(err.status, err.body?.code, err.body?.hint);
  }
  if (isRetryable(err)) {
    // 429 / 5xx / network — the client already retried with backoff
  }
}

Pagination

List methods return a Paginated<T> that transparently follows cursors:

for await (const policy of client.networkPolicies.list()) {
  console.log(policy.name);
}

const all = await client.images.catalog().all(); // collect to an array

Configuration

new SandboxClient({
  apiKey: "sb_…",       // or SANDBOX_API_KEY
  baseUrl: "https://…",  // or SANDBOX_API_URL (default http://localhost:3000)
  timeout: 60_000,        // ms; unary calls only — streaming is never timed out
  maxRetries: 2,          // 429/5xx/network, exponential backoff + Retry-After
  autoStartOnStopped: true, // a data-plane op (run/files/…) on a stopped/idle
                            // sandbox transparently starts it, waits for running,
                            // and retries once — long-lived sandboxes "just work".
                            // Override per call: run(cmd, { autoStart: false }).
  headers: { "x-admin-key": "…" }, // non-Bearer auth (apiKey optional when set)
  fetch: customFetch,     // override the fetch implementation (proxy, polyfill)
});

Zero-round-trip handles. When you already hold an id, sandboxes.handle(id), mysql.handle(id), valkey.handle(id), and services.handle(idOrName) return a handle with no network call — call ops (run, query, deploy, …) directly.

Development

npm install
npm run typecheck
npm run build         # ESM + CJS + .d.ts via tsup
npm test              # vitest (offline; fetch is mocked)
npm run test:e2e      # live e2e (needs SANDBOX_API_URL + SANDBOX_API_KEY)
npm run generate      # regenerate src/generated/api.ts from ../../openapi.json

License

Apache-2.0. See LICENSE.