Skip to content

Latest commit

 

History

History

README.md

@screenpipe/sdk

Commercial screen recording SDK for Electron, Swift, Tauri, and Node apps.

The SDK exposes the capture primitives from the native screenpipe stack: record an MP4, grab JPEG preview snapshots, read a mic level for preflight UI, and inspect the focused app.

SDK Surfaces

Surface Source Example
Node index.js, index.d.ts examples/record-10s.mjs
Electron electron, session examples/electron-app
Swift Package.swift, Sources/Screenpipe examples/swift-app
Tauri tauri examples/tauri-app

Detailed embed notes live in docs/integration.md.

Example Apps

Electron Swift Tauri
Electron example app Swift example app Tauri example app
examples/electron-app examples/swift-app examples/tauri-app

See examples/README.md for run commands and smoke checks for all three apps.

Install

npm install @screenpipe/sdk
# or
bun add @screenpipe/sdk

This package is source-available under the Screenpipe Commercial License, the same license as the root repository. See LICENSE.md at the repository root (bundled into the npm package at publish time).

Node Quick Start

import { Recorder, requestPermissions } from "@screenpipe/sdk";

const permissions = await requestPermissions();
if (!permissions.screen) {
  throw new Error("Screen Recording permission is required");
}

const recorder = new Recorder({
  output: "/tmp/session.mp4",
  // Optional privacy filters — recording pauses (hard cut in the MP4)
  // while a matching window/URL is focused.
  // Plain strings match anywhere; `App::Title` scopes to one window of one app.
  ignoredWindows: ["1password", "private", "Slack::#hr"],
  ignoredUrls: ["wellsfargo.com", "chase"],
});
await recorder.start();

// ... user does stuff ...

await recorder.stop();

Core API

new Recorder(options)

  • options.output (string, required): path where the MP4 is written.

  • options.monitorId (number, optional): display id; defaults to the primary display.

  • options.microphone (boolean, optional): accepted for forward compatibility.

  • options.systemAudio (boolean, optional): accepted for forward compatibility.

  • options.ignoredWindows (string[], optional): substring patterns matched case-insensitively against the focused app name and window title. While a matching window is in focus, the recorder skips writing frames — the MP4 contains a hard cut over the filtered period. Mirrors the engine's --ignored-windows CLI flag.

    Each pattern may use an optional App::Title scope: "Slack::#hr" skips only the #hr window inside Slack and leaves other Slack channels recording. "::Confidential" matches any app whose title contains "Confidential". Plain "Slack" keeps the legacy "app OR title contains" behavior.

  • options.includedWindows (string[], optional): substring whitelist. If non-empty, frames are written ONLY while a matching window is focused. Scoped entries ("Greenhouse::Candidates") create a per-app whitelist — other apps stay unaffected. Unscoped entries keep the legacy "must match app or title" global semantics. Mirrors --included-windows.

  • options.ignoredUrls (string[], optional): URL patterns to skip (case-insensitive, domain-aware match — chase matches chase.com and online.chase.com but not purchase.com). When the focused browser is on a matching URL, the recorder skips writing frames. Mirrors --ignored-urls.

Filtering uses the macOS Accessibility API; without that permission the filter fails open (records everything). Without any filter list set, the recorder stays on the zero-overhead fast path — no a11y polling is done.

Methods

Method Purpose
start() Start screen capture and write frames into the MP4.
stop() Stop capture, flush the MP4 trailer, and close the file. Safe to call more than once.
snapshot() Capture the recorder's monitor as a JPEG preview.
framesWritten() Return frames written since start().
audioLevel() Return a smoothed microphone RMS level in [0, 1] for preflight UI.
focusedApp() Return best-effort focused-window metadata; requires Accessibility permission on macOS.
filterStatus() Return { paused, reason } for the window/URL filter. Poll, or subscribe via the session wrapper's paused/resumed events.
setFilters(patch) Replace the active filter lists at runtime — { ignoredWindows?, includedWindows?, ignoredUrls? }. Takes effect within ≤ 1 s.
requestPermissions() Trigger or check supported OS permissions.

Filter events (session wrapper)

createScreenpipeSession emits paused and resumed events whenever the filter verdict flips. Payload: { paused: boolean, reason: string | null } where reason is one of "ignored_window", "included_window_mismatch", "ignored_url", "incognito", "excluded_app".

session.on("paused", ({ reason }) => {
  showBanner(`recording paused — ${reason}`);
});
session.on("resumed", () => {
  hideBanner();
});

// Runtime toggle (e.g. user flips "Pause on banking" in your settings UI):
await session.setFilters({ ignoredUrls: ["chase", "wellsfargo.com"] });

Audio is not muxed into the MP4 in v0.1.0.

Telemetry & user identification

The session wrapper reports a small, PII-scrubbed set of crash and usage events to screenpipe so we can keep the SDK healthy in the wild. Pass a userId and that identifier is attached to every event, so a specific end user of your app shows up in screenpipe's Sentry (crashes) and PostHog (usage) dashboards.

import { createScreenpipeSession } from "@screenpipe/sdk/session";

const session = createScreenpipeSession({
  userId: currentUser.id, // identifies this user in screenpipe's dashboards
  appName: "acme-recorder", // optional segmentation tag
});
  • What is sent. Crashes/errors go to Sentry (with the error message). Usage goes to PostHog: recording_started, recording_stopped (frame/byte/duration counts only), recording_paused/recording_resumed (with the enum reason), permissions_changed, plus one session_initialized ping. Window titles, app names, URLs and output file paths are never sent to PostHog.
  • What is NOT sent. No screen content, no audio, no clipboard, no app_switched stream, no frames_progress ticks.
  • Opt out. Set telemetry: false in the options, or set the env var SCREENPIPE_SDK_TELEMETRY=0 (also honors DO_NOT_TRACK=1 and SCREENPIPE_DISABLE_ANALYTICS=1). When off, the SDK makes no network calls.

Without a userId, events fall back to a per-session anonymous id, so set userId if you want stable identification across sessions.

Per-surface usage

All four surfaces accept the same userId / appName / telemetry knobs.

Electron (via the session passed to registerScreenpipeIpc):

registerScreenpipeIpc({ ipcMain, app, sessionOptions: { userId: currentUser.id } });

Swift (forwarded to the bundled Node bridge as env vars):

let config = ScreenpipeClient.Configuration(
  sdkRoot: sdkRoot,
  userId: currentUser.id,        // identifies this user in screenpipe's dashboards
  appName: "acme-recorder",      // optional
  telemetryEnabled: true         // set false to disable
)
let client = try ScreenpipeClient(configuration: config)

Tauri — reporting happens natively in the Rust plugin (no webview fetch, so no Content-Security-Policy to configure). The JS client forwards the identity to the plugin via screenpipe_identify on creation:

const client = createScreenpipeTauriClient({ userId: currentUser.id });

You can also set it (or a Rust-side default) when registering the plugin:

tauri::Builder::default()
    .plugin(screenpipe_tauri::init(
        screenpipe_tauri::ScreenpipeConfig::default().user_id("user-123"),
    ))

Development

bun install
bun run build:debug
node --test --test-concurrency=1 "__test__/**/*.test.mjs"
swift test

Example app smoke checks:

npm --prefix examples/electron-app run smoke
npm --prefix examples/tauri-app run smoke
SCREENPIPE_SWIFT_EXAMPLE_SMOKE=1 swift run --package-path examples/swift-app ScreenpipeExample

Run the optional native Tauri example compile with:

SCREENPIPE_RUN_NATIVE_EXAMPLE_BUILDS=1 node --test --test-concurrency=1 __test__/examples_e2e.test.mjs

Before publishing:

cargo test --lib
bun run build
bun run prepublishOnly
npm pack --dry-run

Publish generated platform packages first, then publish the root @screenpipe/sdk package.

Platforms

OS Architecture Status
macOS Apple Silicon Supported
macOS Intel Supported
Windows x64 Builds in CI; runtime validation required before public launch
Windows ARM64 Builds in CI; runtime validation required before public launch
Linux - Not supported in v0.1.0