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

orkestr

v0.1.4

Published

EU-hosted sandbox VMs for AI agents - official JavaScript / TypeScript SDK

Readme

orkestr JavaScript SDK

EU-hosted sandbox VMs for AI agents. TypeScript-first client for the api.orkestr.eu/v1/sandboxes REST API. Ships ESM and CJS dual builds.

Status: 0.1.0 - first stable release. The client surface may still have breaking changes within the 0.x line; 1.0.0 will signal API stability.

Install

npm install orkestr
# or
pnpm add orkestr

Node 18+ or any modern browser-grade runtime (Bun, Deno via npm: prefix). Works in serverless edge runtimes that ship fetch.

Authenticate

Mint an API token in the orkestr console with the sandboxes:write scope.

export ORKESTR_API_KEY="ork_..."

The SDK picks the key up from ORKESTR_API_KEY or you pass it explicitly.

Quick start

One-shot execution inside a fresh sandbox. withTemp ensures the sandbox is terminated when the callback returns, even if it throws.

import { Sandbox } from "orkestr";

await Sandbox.withTemp({ template: "python-3.12" }, async (sbx) => {
  await sbx.files.write("/workspace/main.py", "print(sum(range(1_000_000)))");
  const result = await sbx.exec("python /workspace/main.py");
  console.log(result.stdout);     // 499999500000
  console.log(result.durationMs); // ~120
});

API

Create a sandbox

const sbx = await Sandbox.create({
  template: "python-3.12",            // one of the templates below
  size: "small",                      // "small" | "medium" | "large" (plan-capped)
  network: "off",                     // "off" | "restricted" | "open"
  timeoutSeconds: 600,                // auto-terminate after this many seconds
  env: { OPENAI_API_KEY: "sk-..." },
  metadata: { agentRun: "r_123" },
  region: "fsn1",                     // "fsn1" | "hel1" | undefined for auto
  apiKey: process.env.ORKESTR_API_KEY,
});
console.log(sbx.id);     // "sbx_01HXYZ..."
console.log(sbx.status); // "running"

Templates

| Template | Description | |---------------------|------------------------------------------| | python-3.12 | CPython 3.12 with pip and common libs | | python-3.12-bare | CPython 3.12 only, faster start | | node-22 | Node 22 with npm | | ubuntu-24.04 | Minimal Ubuntu shell environment |

Sizes

size picks from a fixed menu. Allowed sizes are plan-capped.

| Size | vCPU | RAM | Plans | |----------|------|--------|------------------------| | small | 0.5 | 512 MB | free, pro, team | | medium | 2 | 4 GB | pro, team | | large | 4 | 8 GB | team |

Check your plan limits

Sandbox.limits() reports the sizes and caps available to your API key's plan — pick a size up front instead of discovering the limit from a PlanLimitError. Handy when the same code runs under keys on different plans (e.g. a reseller provisioning per customer).

const limits = await Sandbox.limits();
limits.plan;                            // "free"
limits.allowedSizes;                    // ["small"]
limits.allowedSizes.includes("medium"); // false
limits.maxConcurrent;                   // 1
limits.usageGbHoursUsed;                // 2.5  (memory consumed this month)
limits.usageGbHoursIncluded;            // 10.0 (memory budget for the month)
limits.usageCpuHoursUsed;               // 0.42 (CPU consumed this month)
limits.usageCpuHoursIncluded;           // 2.0  (CPU budget for the month)
limits.usageResetsAt;                   // Date - 1st of next month UTC
for (const s of limits.sizes) {         // full menu, each with .allowed
  console.log(s.size, s.cpu, s.memoryMb, s.allowed);
}

When usageGbHoursUsed >= usageGbHoursIncluded, Sandbox.create() throws PlanLimitError with code="usage_exhausted" until the counter resets. The metering rolls up mem_mb_seconds from the host's per-second compute samples, so an idle sandbox still accrues toward the budget until it is terminated.

Live metrics

sbx.metrics() returns this sandbox's live CPU and memory - the latest reading, a rolling ~60s sample window for sparklines, and its lifetime totals. Use it to watch a workload for saturation or memory pressure without instrumenting the workload itself.

const m = await sbx.metrics();
m.cpu.usagePercent;     // 47.0  (% of allocated cores; pegged 1-core = 100)
m.cpu.usageCores;       // 0.94  (cores in use of m.cpu.cores)
m.memory.usageBytes;    // 1879048192  (working set, excludes reclaimable cache)
m.memory.usagePercent;  // 43.7  (% of m.memory.limitBytes)
m.lifetime.cpuSeconds;  // 1284.31  (on-CPU seconds since the sandbox started)
for (const s of m.samples) {  // oldest first; s.t is a Date
  console.log(s.t, s.cpuPercent, s.memBytes);
}

It is telemetry, not a state change: a paused or terminated sandbox resolves with null live usage (check m.sandboxStatus) and an empty samples window - lifetime is still populated. Poll no faster than m.sampleIntervalSeconds; pass { since: <Date> } to fetch only samples newer than your last poll.

Run a command

const result = await sbx.exec("python /workspace/main.py", {
  timeoutSeconds: 60,
});
result.stdout;      // string
result.stderr;      // string
result.exitCode;    // number
result.durationMs;  // number

Stream a long command

for await (const chunk of sbx.execStream("python long_task.py")) {
  if (chunk.stream === "stdout") {
    process.stdout.write(chunk.data);
  } else {
    process.stderr.write(chunk.data);
  }
}

Files

await sbx.files.write("/workspace/data.json", '{"x": 1}');
await sbx.files.writeBytes("/workspace/blob.bin", new Uint8Array([0, 1, 2]));

const content = await sbx.files.read("/workspace/out.txt");      // string
const raw = await sbx.files.readBytes("/workspace/blob.bin");    // Uint8Array

for (const entry of await sbx.files.list("/workspace")) {
  console.log(entry.name, entry.isDir, entry.size);
}

await sbx.files.delete("/workspace/out.txt");

Pause + resume

Pausing snapshots the sandbox memory + disk and stops the compute meter. Resume restores it on the same or a different host. pause() returns the sandbox id; persist it across processes and pass to Sandbox.resume.

const sbx = await Sandbox.create({ template: "node-22", network: "restricted" });
const sandboxId = await sbx.pause();
// ... minutes or hours later, from any worker:
const resumed = await Sandbox.resume(sandboxId);

Snapshot retention is plan-capped (free: 1, pro: 3, team: 10). Calling pause() over the cap throws SnapshotCapReached.

Terminate

withTemp calls terminate() for you. Otherwise:

await sbx.terminate();

After terminate() the sandbox row stays in your account history but the VM and any in-memory state are gone.

List your sandboxes

for await (const sbx of Sandbox.list({ status: "running" })) {
  console.log(sbx.id, sbx.template, sbx.createdAt);
}

Errors

All SDK errors extend OrkestrError.

| Class | When | |------------------------|---------------------------------------------------| | AuthError | Missing / invalid / expired API key, or scope mismatch | | RateLimitError | Plan rate limit hit | | PlanLimitError | Sandbox limit, concurrent limit, snapshot cap | | SandboxNotFound | sandboxId doesn't exist or isn't yours | | SandboxNotReady | Operation called on a paused / terminated sandbox | | ExecTimeout | exec() exceeded timeoutSeconds | | NetworkPolicyError | Command tried to reach a host the policy blocks | | OrkestrError | Any other API-level error |

Field naming

The REST API uses snake_case. The JS SDK exposes camelCase to feel native; the client converts in both directions. The Python SDK uses snake_case end-to-end.

Versioning

Follows Semantic Versioning. The SDK targets the /v1/sandboxes API; bumps to v1 of the API are non-breaking for SDK callers. v2 of the API will require an SDK major.

Links