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

@giselles-ai/sandkit

v0.1.2

Published

Workspace state, session management, and durable command execution for Vercel Sandbox.

Readme

Sandkit

Sandkit makes Vercel Sandbox stateful.

Sandkit adds workspace state, session management, durable command execution, and resumable sandbox workflows to Vercel Sandbox.

It keeps two paths explicit:

  • workspace.sandbox.runCommand(...) for durable, one-command-at-a-time work
  • openSession() / attachSession() for a live leased sandbox when you need an interactive process

An active session is an exclusive workspace lease. While a live session is open, runCommand() is unavailable until you attach to that session or commit it.

Durable lock enforcement is currently in-process (packages/sandkit only). Concurrent durable commands for the same workspace are excluded while one is in flight in the same process.

Provider-specific behavior still matters, but the public API stays centered on workspaces, policies, and durable state.

Problem

Vercel Sandbox is ephemeral by design. It does not give you durable workspaces, session lifecycle, or a clear boundary between one-shot commands and live attached execution.

  • No built-in workspace identity or durable workspace state
  • No session management abstraction for live attach / resume
  • No durable command boundary for one-command-at-a-time work
  • Teams end up rebuilding the same sandbox state and lifecycle layer around jobs, agents, and recovery flows

Solution

Sandkit adds a workspace state layer on top of Vercel Sandbox:

  • Persistent workspaces for sandbox state management
  • Live session lifecycle with explicit attach / commit semantics
  • Durable command execution through runCommand(...) for committed work
  • Policy controls that stay part of workspace state
  • Resumable sandbox workflows for long-running apps and control planes

Positioning

| Tool | Responsibility | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | Vercel Sandbox | Ephemeral execution environment | | Sandkit | State and lifecycle layer for sandboxed execution: workspaces, session lifecycle, durable command boundaries, and resume | | Workflow engines / app control planes | Decide when and why work runs |

Primary Use Case: Persistent Workspaces for AI Coding Agents

Sandkit is especially useful when an agent or long-running sandbox app needs to keep a workspace alive across runs, attach to a live process, expose a public URL, and commit progress durably.

Install

npm install @giselles-ai/sandkit

With Drizzle:

npm install @giselles-ai/sandkit drizzle-orm

Quick Start

Migration note: sandkit(...) was renamed to createSandkit(...) and this package is not yet aliased. Callers must update imports and call sites from sandkit to createSandkit.

import { Database } from "bun:sqlite";

import { createSandkit } from "@giselles-ai/sandkit";
import { createBunSqliteAdapter } from "@giselles-ai/sandkit/adapters/sqlite-bun";
import { vercelSandbox } from "@giselles-ai/sandkit/integrations/vercel";

const database = new Database("./sandkit.sqlite");
const workspaceAdapter = createBunSqliteAdapter(database);

const sandkit = createSandkit({
  database: workspaceAdapter,
  sandbox: vercelSandbox({
    defaultTimeout: 60_000,
  }),
});

const workspace = await sandkit.createWorkspace({
  name: "hello-sandkit",
});

await workspace.sandbox.runCommand({
  command: "sh",
  args: ["-lc", "echo 'hello world' > ./hello.txt"],
});

const result = await workspace.sandbox.runCommand({
  command: "cat",
  args: ["./hello.txt"],
});

console.log(result.stdout.trim());

runCommand(...) (without detached) resolves to CommandResult only after the full unit-of-work is complete: process exit, snapshot/commit, and persist.

For detached execution, pass detached: true and you get a Command object. Observe logs and then await durable completion:

const command = await workspace.sandbox.runCommand({
  command: "sh",
  args: ["-lc", "echo 'start'; sleep 1; echo 'done'"],
  detached: true,
});
for await (const chunk of command.logs?.() ?? []) {
  console.log(`${chunk.stream}: ${chunk.chunk}`);
}

const commandResult = await command.wait();

Set VERCEL_OIDC_TOKEN for local runs or VERCEL_ACCESS_TOKEN in CI before creating a Vercel-backed sandbox.

Declare exposedPorts on createWorkspace({ sandbox: ... }) only when you need a live session URL. defaultTimeout is the provider-level lease default; override a specific live session with openSession({ timeoutMs }).

Setup bootstrap

setup is the shared bootstrap definition, not the materialized artifact. It is optional. When setup is provided, it is the shared bootstrap command, args, and required durable policy. This produces adapter-scoped shared bootstrap state keyed by adapter.id + setup definition fingerprint. Multiple Sandkit instances using the same adapter and setup definition can reuse the same shared bootstrap state.

sandkit.bootstrap() is an optional eager materialization step:

  • it creates shared bootstrap state if missing,
  • it leaves existing shared bootstrap state untouched,
  • it does not run a restore path to prove an existing shared bootstrap state is still usable.

Without bootstrap(), shared setup is still materialized lazily on first workspace use (first runCommand(...) or openSession(...) that needs it). Stale or unusable shared bootstrap artifacts are detected and rebuilt in those workspace flows, not by bootstrap() alone.

setup durability is adapter-backed. With a persistent adapter such as Bun SQLite or Drizzle, the shared bootstrap survives process restarts. With the default in-memory adapter, it does not.

import { createSandkit, allowAll } from "@giselles-ai/sandkit";
import { vercelSandbox } from "@giselles-ai/sandkit/integrations/vercel";

const sandkit = createSandkit({
  sandbox: vercelSandbox(),
  setup: {
    command: "sh",
    args: ["-lc", "npm ci"],
    policy: allowAll(),
  },
});

await sandkit.bootstrap();

// Optional: omit bootstrap() and let setup materialize on first workspace use.
const workspace = await sandkit.createWorkspace({
  name: "bootstrapped-workspace",
});

Configuration

Provide a sandbox provider explicitly (for example vercelSandbox(...)). If you do not pass database, Sandkit defaults to the in-memory adapter. That default is useful for local tests and internal development, but the primary published usage is an explicit Vercel provider plus a persistent adapter.

Policies

  • npm() allows the public npm registry host registry.npmjs.org
  • bun() allows Bun install/distribution hosts bun.sh and bun.com
  • codex() reads CODEX_API_KEY
  • gemini() reads GEMINI_API_KEY
  • github() reads GITHUB_TOKEN and maps it through Vercel Sandbox firewall transforms:
    • Authorization: Basic <base64(x-access-token:<token>)> on requests to github.com, intended for Git-over-HTTPS operations
    • Authorization: Bearer <token> on requests to api.github.com
    • no Authorization header for *.githubusercontent.com
  • aiGateway() reads AI_GATEWAY_API_KEY from host env and allows the hostname (plus wildcard) from AI_GATEWAY_BASE_URL. AI_GATEWAY_BASE_URL ports are ignored for allow-listing; only host/domain matches are used.

For JavaScript package bootstrap, prefer explicit service presets over allowAll():

import { allowServices, bun, npm } from "@giselles-ai/sandkit";

const policy = allowServices([bun(), npm()]);

Durable default policy belongs to the workspace: use createWorkspace({ policy: ... }) when you create it, or workspace.setPolicy(...) later. Pass policy to runCommand(...) for one-off overrides.

Schema Generation

npx @giselles-ai/sandkit generate --adapter drizzle --provider sqlite

If the project already has a Drizzle setup, provider discovery can infer the dialect:

npx @giselles-ai/sandkit generate

Examples

  • examples/workflow-hello-git
  • examples/sandbox-openclaw
  • smoke/drizzle-sample

Repository: github.com/giselles-ai/sandkit