New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@scalebrowser/sdk

Package Overview
Dependencies
Maintainers
2
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@scalebrowser/sdk

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

latest
Source
npmnpm
Version
1.2.0
Version published
Maintainers
2
Created
Source

@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

Keywords

scalebrowser

FAQs

Package last updated on 22 Oct 2026

Related posts