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

@scalebrowser/sdk

v1.2.0

Published

Official Node/TypeScript SDK for the Scalebrowser daemon: typed REST client plus a direct-CDP driver plane (nodriver-style, not Playwright).

Readme

@scalebrowser/sdk

Official Node / TypeScript SDK for the Scalebrowser daemon: a typed REST client plus a direct-CDP driver plane.

The driver plane is deliberately direct-CDP (nodriver-style), not Playwright / Puppeteer: anti-bot stacks block the Playwright control plane independently of how good the browser patches are, so this SDK speaks the Chrome DevTools Protocol over a raw websocket and never calls Runtime.enable.

Install

npm install @scalebrowser/sdk

Ships dual ESM + CJS with full type declarations, and needs Node ≥ 18. The SDK is MIT-licensed, and its source is public at blackdiamond-labs/scalebrowser-node: npm builds every release there and publishes it with a provenance attestation. The daemon it talks to is a separate, licensed product.

Quickstart

import { ScalebrowserClient } from '@scalebrowser/sdk';

const sb = new ScalebrowserClient({
  baseUrl: 'http://127.0.0.1:8787', // daemon native API (default)
  token: process.env.SCALEBROWSER_TOKEN,
});

// Create a coherent, per-seed-unlinkable profile.
const profile = await sb.createProfile({ name: 'acct-01' });

// Start it + open a direct-CDP session. `await using` stops the profile and
// closes CDP when the block exits.
await using session = await sb.launch(profile.id, { headless: true });

await session.cdp.navigate('https://example.com');
const title = await session.cdp.evaluate<string>('document.title');

// Humanized, trusted input is routed through the daemon (real OS/CDP events).
await session.cdp.humanizeMove(120, 220);
await session.cdp.humanizeClick(120, 220);

Without await using (or for explicit control):

const { cdp_ws } = await sb.startProfile(profile.id, { headless: true });
const cdp = await sb.connectCdp(cdp_ws, profile.id);
try {
  await cdp.navigate('https://example.com');
} finally {
  cdp.close();
  await sb.stopProfile(profile.id);
}

REST surface

Every /v1 endpoint is a typed method on ScalebrowserClient:

  • Profiles: listProfiles, listProfileIds, countProfiles, getProfile, createProfile, updateProfile, deleteProfile, startProfile, stopProfile. releaseProfile drops an agent's lease without its handle (the operator's way out), and takeoverProfile asks for a profile that is open on another machine of your account
  • Bulk: bulkCreateProfiles, bulkStart, bulkStop, bulkDelete, bulkAssignProxy, bulkAssignExtensions
  • Groups / Presets: listGroups/createGroup/getGroup/updateGroup/deleteGroup, listPresets/createPreset/getPreset/updatePreset/deletePreset, getPersonaConstraints. A preset is config (what the profiles do: geo_mode, proxy_id, …) plus constraints (what they are: country, which pins the persona's language, timezone and voices). Both are typed and the daemon refuses an unknown key with a 400, so read the valid regions from getPersonaConstraints() rather than hardcoding them.
  • Proxies: listProxies, createProxy, getProxy, updateProxy, deleteProxy, checkProxy, checkProxyConfig (probe a config before you save it; pass id to reuse an existing proxy's stored credentials). Credentials are write-only, never returned
  • Extensions: listExtensions, attachExtension, detachExtension, plus the daemon-wide library (listLibraryExtensions, uploadExtension, getLibraryExtension, deleteLibraryExtension, deleteLibraryExtensionVersion). An attached package IS loaded into the browser at launch, under the canonical Web-Store id its own key derives
  • Credentials: listCredentials, putCredential, deleteCredential, revealCredential (needs the vault password), exportCredentials, importCredentials. The vault password itself: vaultStatus, setVaultPassword
  • Secret layer: getSecretLayer, setSecretLayer, listSecrets, updateSecret, deleteSecret. scrubText filters a text you hold against a profile's values, and runWithSecrets runs a command with named values in its environment, so no value crosses a socket
  • Cookies: revealCookies, the one route a cookie VALUE leaves through, behind the same vault password
  • Recorded requests: revealRequest, the one route what a request carried (query, header values, body) leaves through, behind the same vault password
  • Sessions: exportSession, importSession
  • Mailboxes: listInboxes, createInbox, updateInbox, deleteInbox, getInboxBindings, bindInbox, unbindInbox, which is where a profile's confirmation codes arrive
  • Passkeys: listPasskeys, deletePasskey. Metadata only: the private key has no field and no endpoint
  • Agent runs: listRuns, getRun, listRunSteps, getRunShot, getActivity. Read-only, all of it
  • Interruptions: listInterruptionLocks, setInterruptionLock, listInterruptionRules, setInterruptionRule, deleteInterruptionRule: who may answer when the browser asks something
  • Artifacts: putArtifact (hand the daemon a file to upload later), getArtifact (fetch a screenshot, download or saved PDF as bytes)
  • Tasks and memory: listTasks, createTasks, cancelTask, deleteTask, plus a profile's standing instructions for agents, its PROFILE.md: getProfileMemory, putProfileMemory, deleteProfileMemory
  • Videos: listVideos, getVideo, renameVideo, deleteVideo, cancelVideo, rerenderVideo, videoAccess, downloadVideoFile, videosStorage; recordings with listRecordings, getRecording, deleteRecordingRaw, renderRecording; public links with share, shareStatus, revokeShare
  • Video presets: listVideoPresets, getVideoPreset, createVideoPreset, updateVideoPreset, duplicateVideoPreset, deleteVideoPreset, and a look at an unsaved config with previewVideoPreset, previewVideoPresetClip, downloadVideoPresetClip. The files a preset points at: listMedia, uploadMedia, deleteMedia, mediaFile. Narration keys: listTtsProviders, putTtsProvider, verifyTtsProvider, deleteTtsProvider. Encoders: getCodecs, setCodecEnabled
  • Fingerprint audit: startAudit drives the profile's running browser through the external detector pages, getAudit reads the state and the last report
  • Exit addresses: getExitStatus (which addresses a profile owns), and the machine's rule that one address belongs to one profile: getExitRule, setExitRule
  • Machine settings: getRemoteAccess, setRemoteAccess (may a chat service reach this machine's browsers), getAgentTools, setAgentToolGroup (which optional MCP tool groups agents see), getConnection (which installation answered: its data directory and its binary)
  • Input: sendInput
  • Metrics / Account / Health: getMetrics, getAccount, health, ready. The first two answer 404 on a daemon without them, which the SDK treats as information rather than as an error: metrics are then derived from profile state, and the account reads licensed: false, which is what "self-hosted, no control plane" means

setToken swaps the bearer token on a live client.

Errors map to typed exceptions carrying the daemon's numeric code (4001–4013):

import { ApiError, ErrorCode } from '@scalebrowser/sdk';

try {
  await sb.startProfile(id);
} catch (err) {
  if (err instanceof ApiError && err.code === ErrorCode.Capacity) {
    // 4003: capacity exceeded
  }
}

Lifecycle events (SSE)

// callback form
const unsubscribe = sb.subscribeEvents({
  onEvent: (e) => {
    if (e.type === 'profile_crashed') console.warn(e.profile_id, e.reason);
  },
});

// or async-iterator form
for await (const e of sb.events()) {
  if (e.type === 'profile_started') console.log(e.cdp_ws);
}

Direct-CDP driver

CdpSession (returned by connectCdp / launch) gives you:

  • send<T>(method, params?): raw CDP command, awaited by id (concurrent-safe)
  • navigate(url, { wait, timeout }), evaluate<T>(expr, { isolated }). The wait for the load reads from an isolated world, so a page that watches its own document.readyState sees no foreign read; isolated: true asks for the world of the document that is loaded now, on every call
  • createIsolatedWorld(frameId?, worldName?)
  • attachToPage({ targetId?, create?, url? })
  • on(method, cb) / events(): CDP event subscription scoped to the session
  • humanizeMove / humanizeClick / humanizeType / humanizeScroll
  • close()

Security

  • Bearer token is sent on every request and the SSE/CDP handshakes; it is never logged (the client's inspect output masks it).
  • Proxy credentials are injected by the daemon and never travel over CDP.
  • The CDP endpoint is guarded by the OS user, not by that token. The engine's DevTools port has no authentication of its own: it answers a bogus bearer with 200, measured, so the engine drops any connection whose peer process runs as a different user. Nothing to configure and nothing to pass: your process is the one that started the profile, so it is on the allowed side. A helper running as another account will not get in, by design.

Develop

npm install
npm run typecheck   # tsc --noEmit (strict)
npm run build       # tsup → dist/ (ESM + CJS + .d.ts)
npm test            # vitest (unit; e2e skipped unless SCALEBROWSER_E2E=1)

The env-gated end-to-end test runs the full path against a real daemon + mock engine:

SCALEBROWSER_E2E=1 SCALEBROWSER_BASE_URL=http://127.0.0.1:8787 \
  SCALEBROWSER_TOKEN=… npm test