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

@timely-sandbox/client

v0.1.1

Published

믿을 수 없는 코드를 격리된 환경에서 실행하는 클라이언트. 샌드박스를 띄우고 명령을 실행하고 파일을 주고받습니다. 의존성 없음, Node 20+.

Readme

@timely-sandbox/client

믿을 수 없는 코드를 격리된 환경에서 돌려야 할 때 쓰는 클라이언트입니다. 샌드박스를 띄우고, 명령을 실행하고, 파일을 주고받습니다.

AI 에이전트가 생성한 코드를 실행하거나, 사용자가 올린 스크립트를 처리하거나, 빌드·변환 작업을 서버 밖에서 돌리고 싶을 때 쓰기 좋습니다.

의존성이 없고 Node 20 이상이면 바로 동작합니다.

설치

npm i @timely-sandbox/client

접속 정보는 운영자에게 요청하세요. 아래 세 가지를 받게 됩니다.

TSBX_URL=https://sbx.timelyai.io
TSBX_KEY_ID=...
TSBX_SECRET=...

빠른 시작

import { ControllerClient, sandboxClientFor } from "@timely-sandbox/client";

const tsbx = new ControllerClient({
  baseUrl: process.env.TSBX_URL!,
  keyId: process.env.TSBX_KEY_ID!,
  secret: process.env.TSBX_SECRET!,
});

// 샌드박스를 하나 만듭니다. labels 는 나중에 이 샌드박스를 다시 찾는 열쇠가 되니
// 서비스 쪽 식별자를 그대로 넣어 주세요.
const info = await tsbx.create({
  image: "python:3.12-slim",
  labels: { user: "u-42" },
});

// 준비될 때까지 기다립니다. 예비 풀에서 잡히면 즉시 반환되고,
// 새로 뜨는 경우에만 실제로 기다립니다.
const sbx = sandboxClientFor(info.url ? info : await tsbx.waitUntilReachable(info.id));

await sbx.writeFile("/workspace/hello.py", "print('hi')\n");

const run = await sbx.exec("python3 /workspace/hello.py");
console.log(run.stdout);   // hi
console.log(run.exitCode); // 0

await tsbx.delete(info.id);

알아 두면 좋은 것 두 가지

컨트롤러와 샌드박스는 서로 다른 곳입니다

샌드박스를 만들고 지우는 일은 컨트롤러(baseUrl)에 요청하고, 명령 실행과 파일 입출력은 샌드박스(info.url)에 직접 요청합니다. 큰 파일이 컨트롤러를 거치지 않게 하려는 구조라서, 두 요청은 서로 다른 키로 서명됩니다.

sandboxClientFor(info) 를 쓰면 이 부분은 알아서 처리되니 크게 신경 쓰지 않아도 됩니다. 다만 샌드박스 키(info.key)는 그 샌드박스 하나만 열 수 있는 키이고, 워크스페이스 키는 여러분의 모든 샌드박스를 다룰 수 있는 키입니다. 워크스페이스 키를 샌드박스 안으로 넘기는 일은 피해 주세요.

파일은 샌드박스보다 오래 남습니다

샌드박스는 일정 시간 요청이 없으면 자동으로 회수되지만, 작업 공간(볼륨)은 며칠 더 남아 있습니다. 사용자가 나중에 돌아왔을 때 파일이 그대로여야 한다면 라벨로 볼륨을 찾아서 다시 붙여 주세요.

async function open(userId: string) {
  const labels = { user: userId };

  // 아직 살아 있는 샌드박스가 있으면 그대로 씁니다.
  const alive = (await tsbx.list(labels)).find((s) => s.state !== "terminating");
  if (alive) return alive;

  // 없으면 같은 라벨의 볼륨을 찾아 이어 붙입니다.
  const [vol] = await tsbx.listVolumes(labels);
  const info = await tsbx.create({
    image: "python:3.12-slim",
    labels,
    ...(vol ? { volume: vol.name } : {}),
  });

  return info.url ? info : tsbx.waitUntilReachable(info.id);
}

볼륨 이름을 따로 저장해 둘 필요는 없습니다. 라벨만 있으면 언제든 찾을 수 있어요.

tsbx.delete(id) 는 볼륨을 남겨 둡니다. 사용자가 "전부 지워 달라"고 했을 때만 tsbx.delete(id, { purgeVolume: true }) 를 쓰세요.

주요 API

ControllerClient

| 메서드 | 설명 | |---|---| | create(spec) | 샌드박스 생성 (image, labels, volume, idleTtlSec, cpu, memory …) | | list(labels?) | 내 워크스페이스의 샌드박스 목록 | | get(id) / find(id) | 하나 조회 (find 는 없으면 null) | | waitUntilReachable(id) | 파드가 뜰 때까지 대기 | | patch(id, spec) | 유휴 시간 등 변경 | | delete(id, opts?) | 삭제 ({ purgeVolume: true } 면 볼륨까지) | | listVolumes(labels?) | 작업 공간 목록 | | deleteVolume(name) | 작업 공간 삭제 |

SandboxClient (sandboxClientFor(info) 로 만듭니다)

| 메서드 | 설명 | |---|---| | exec(cmd, opts?) | 명령 실행 후 { stdout, stderr, exitCode } | | execStream(cmd, opts?) | 출력을 실시간으로 받는 async iterator | | writeFile(path, data) / readFile(path) / readText(path) | 파일 입출력 | | list(path, opts?) / stat(path) / exists(path) | 파일 조회 | | makeDir(path) / remove(path, opts?) | 디렉터리 만들기·지우기 | | startProcess(opts) / stopProcess(name) | 오래 도는 프로세스 | | fetchPort(port, path, init?) | 샌드박스 안에서 열린 포트로 HTTP 요청 | | health() | 마지막 활동 시각, 메모리 감시 기록 |

CLI

패키지에 CLI 도 들어 있습니다. 환경 변수는 위와 같습니다.

npx tsbx ls                      # 샌드박스 목록
npx tsbx create python:3.12-slim --label user=u-42
npx tsbx exec <id> echo hello
npx tsbx rm <id>                 # 볼륨은 유지
npx tsbx rm <id> --purge-volume
npx tsbx --help

TypeScript

응답 타입은 모두 함께 내보냅니다. 별도 @types 패키지는 필요하지 않습니다.

import type { SandboxInfo, VolumeInfo, CreateSandboxRequest } from "@timely-sandbox/client";

다른 언어를 쓰신다면 요청 서명 방식만 옮기면 됩니다. HMAC-SHA256 한 번이고, 온보딩 문서에 파이썬과 curl 예제가 있습니다.

더 읽을 것

  • 온보딩 문서: 키 발급부터 되붙이기 패턴, 수명과 한도, 자주 보는 에러까지 담았습니다.
  • HTTP 계약: 엔드포인트 전체 목록입니다.

라이선스

MIT