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

@umbra-sdk/core

v1.1.5

Published

UmbraBrowser SDK - Anti-detect browser automation

Readme

@umbra/core SDK 集成文档

概述

@umbra/core 是 UmbraBrowser 的 Node.js SDK,提供反指纹浏览器的完整管理能力。你的应用通过 SDK 创建 Profile、启动浏览器、管理代理和 Cookie,浏览器窗口独立弹出,可通过 Puppeteer/Playwright 完全控制页面内容。

安装

将我们提供的 SDK 包放到你的项目中,然后本地安装:

npm install ./umbra-sdk

你还需要安装 UmbraBrowser 桌面端(我们提供安装包),或者将 UmbraBrowser 二进制文件放到你的项目中。

支持平台:

  • macOS (Apple Silicon / Intel)
  • Windows (x64)

快速开始

import { createUmbra } from '@umbra/core'

// 1. 初始化(自动在后台启动 UmbraBrowser 进程)
const umbra = await createUmbra({
  licenseKey: 'uk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 我们提供的授权 key
  binaryPath: '/Applications/UmbraBrowser.app/Contents/MacOS/UmbraBrowser', // UmbraBrowser 二进制路径
})

// 2. 创建 Profile(自动生成完整指纹)
const { profile_id } = await umbra.createProfile({
  name: '账号-01',
  os: 'windows',          // 'windows' | 'macos' | 'linux'
})

// 3. 启动浏览器
const browser = await umbra.openBrowser(profile_id)
console.log(browser.wsEndpoint)   // ws://127.0.0.1:9222/devtools/browser/xxx
console.log(browser.debugPort)    // 9222

// 4. 用 Puppeteer 连接,操控页面
import puppeteer from 'puppeteer-core'
const page = await puppeteer.connect({
  browserWSEndpoint: browser.wsEndpoint,
})
const tab = (await page.pages())[0]
await tab.goto('https://www.xiaohongshu.com')

// 5. 关闭浏览器
await umbra.closeBrowser(profile_id)

// 6. 退出时关闭后台进程
await umbra.shutdown()

初始化选项

const umbra = await createUmbra({
  licenseKey: 'uk_xxx',              // 必填,授权 key
  binaryPath: '/path/to/UmbraBrowser', // 必填,UmbraBrowser 二进制路径
  port: 17200,                       // 可选,API 端口(默认 17200,多实例时需指定不同端口)
})

binaryPath 各平台参考

| 平台 | 路径示例 | |------|---------| | macOS(直接安装) | /Applications/UmbraBrowser.app/Contents/MacOS/UmbraBrowser | | Windows(直接安装) | C:\\Program Files\\UmbraBrowser\\UmbraBrowser.exe | | macOS(打包到你的应用内) | path.join(process.resourcesPath, 'umbra-engine', 'UmbraBrowser.app', 'Contents', 'MacOS', 'UmbraBrowser') | | Windows(打包到你的应用内) | path.join(process.resourcesPath, 'umbra-engine', 'UmbraBrowser.exe') |

API 参考

Profile 管理

// 创建 Profile
const { profile_id, name } = await umbra.createProfile({
  name: '账号名',           // 可选,默认自动生成
  os: 'windows',            // 可选,'windows' | 'macos' | 'linux',默认 'windows'
  os_version: '10.0',       // 可选
  group_id: 'xxx',          // 可选,分组 ID
  notes: '备注',            // 可选
})

// 列出 Profile
const result = await umbra.listProfiles({
  page: 1,
  page_size: 20,
  search: '关键词',         // 可选,按名称搜索
  status: 'closed',         // 可选,'closed' | 'open'
  group_id: 'xxx',          // 可选
})
// result = { total: 100, page: 1, pageSize: 20, items: [...] }

// 获取 Profile 详情(含指纹、代理、账号等完整信息)
const full = await umbra.getProfile(profile_id)

// 更新 Profile
await umbra.updateProfile({
  profile_id: 'xxx',
  name: '新名称',
})

// 删除 Profile
await umbra.deleteProfile(profile_id)

// 批量删除
await umbra.deleteProfiles(['id1', 'id2', 'id3'])

浏览器控制

// 启动浏览器(返回连接信息)
const browser = await umbra.openBrowser(profile_id)
// browser = {
//   profile_id: 'xxx',
//   wsEndpoint: 'ws://127.0.0.1:9222/devtools/browser/xxx',  ← Puppeteer/Playwright 连接地址
//   debugPort: 9222,
//   pid: 12345,
// }

// 关闭浏览器
await umbra.closeBrowser(profile_id)

// 查询浏览器状态
const status = await umbra.getBrowserStatus(profile_id)
// status.status = 'open' | 'closed' | 'starting' | 'crashed'

// 列出所有运行中的浏览器
const { browsers } = await umbra.getActiveBrowsers()

// 平铺排列所有浏览器窗口
await umbra.tileBrowsers()

代理管理

// 创建代理
const proxy = await umbra.createProxy({
  name: '美国代理-01',
  type: 'http',              // 'http' | 'socks5'
  host: '1.2.3.4',
  port: 8080,
  username: 'user',          // 可选
  password: 'pass',          // 可选
  rotate_url: 'http://...',  // 可选,轮换 IP 的 URL
})

// 分配代理到 Profile
await umbra.assignProxy(profile_id, proxy.id)

// 或者设置内联代理(不入库,直接绑定到 Profile)
await umbra.setInlineProxy(profile_id, {
  type: 'socks5',
  host: '1.2.3.4',
  port: 1080,
  username: 'user',
  password: 'pass',
})

// 测试代理
const testResult = await umbra.testProxy(proxy.id)
// testResult = { available: true, exitIp: '5.6.7.8', latencyMs: 120, region: 'US' }

// 也可以直接测试代理配置(不创建)
const testResult2 = await umbra.testProxy({
  type: 'http',
  host: '1.2.3.4',
  port: 8080,
})

// 轮换 IP
await umbra.rotateIP(proxy.id)

// 列出代理
const proxies = await umbra.listProxies({ search: '美国' })

// 删除代理
await umbra.deleteProxy(proxy.id)

Cookie 管理

// 获取 Profile 的 Cookie
const { cookies } = await umbra.getCookies(profile_id)
const { cookies: xhsCookies } = await umbra.getCookies(profile_id, '.xiaohongshu.com')

// 导入 Cookie(JSON 格式)
await umbra.importCookies(profile_id, JSON.stringify([
  {
    domain: '.xiaohongshu.com',
    name: 'session_id',
    value: 'abc123',
    path: '/',
    httpOnly: true,
    secure: true,
    sameSite: 'None',
    expires: 1735689600,
  }
]))

// 清空 Cookie
await umbra.clearCookies(profile_id)

分组管理

// 列出所有分组
const { groups } = await umbra.listGroups()

License 状态

// 查看当前授权状态和配额
const license = await umbra.getLicenseStatus()
// license = {
//   valid: true,
//   maxProfiles: 500,        ← 最多创建的 Profile 数
//   maxConcurrent: 50,       ← 最多同时打开的浏览器数
//   maxApiCalls: 100000,     ← 每月 API 调用次数上限
//   expiresAt: '2027-01-01', ← 授权到期时间
//   customerName: 'Acme',
// }

与 Puppeteer 集成

import { createUmbra } from '@umbra/core'
import puppeteer from 'puppeteer-core'

const umbra = await createUmbra({
  licenseKey: 'uk_xxx',
  binaryPath: '/Applications/UmbraBrowser.app/Contents/MacOS/UmbraBrowser',
})

// 创建 Profile + 设置代理
const { profile_id } = await umbra.createProfile({ name: '小红书-01', os: 'windows' })
await umbra.setInlineProxy(profile_id, {
  type: 'http', host: '1.2.3.4', port: 8080, username: 'user', password: 'pass',
})

// 启动浏览器
const { wsEndpoint } = await umbra.openBrowser(profile_id)

// Puppeteer 连接
const browser = await puppeteer.connect({ browserWSEndpoint: wsEndpoint })
const page = (await browser.pages())[0]

// 操作页面
await page.goto('https://www.xiaohongshu.com')
await page.waitForSelector('.note-item')
const title = await page.title()
console.log('Page title:', title)

// 截图
await page.screenshot({ path: 'screenshot.png' })

// 关闭
await browser.disconnect()
await umbra.closeBrowser(profile_id)
await umbra.shutdown()

与 Playwright 集成

import { createUmbra } from '@umbra/core'
import { chromium } from 'playwright-core'

const umbra = await createUmbra({
  licenseKey: 'uk_xxx',
  binaryPath: '/Applications/UmbraBrowser.app/Contents/MacOS/UmbraBrowser',
})
const { profile_id } = await umbra.createProfile({ name: '抖音-01' })
const { wsEndpoint } = await umbra.openBrowser(profile_id)

// Playwright 连接
const browser = await chromium.connectOverCDP(wsEndpoint)
const context = browser.contexts()[0]
const page = context.pages()[0]

await page.goto('https://www.douyin.com')
// ...

await browser.close()
await umbra.closeBrowser(profile_id)
await umbra.shutdown()

多浏览器并发

const umbra = await createUmbra({
  licenseKey: 'uk_xxx',
  binaryPath: '/Applications/UmbraBrowser.app/Contents/MacOS/UmbraBrowser',
})

// 批量创建 Profile
const profiles = []
for (let i = 0; i < 10; i++) {
  const p = await umbra.createProfile({ name: `账号-${i + 1}` })
  profiles.push(p.profile_id)
}

// 并发启动
const browsers = await Promise.all(
  profiles.map(id => umbra.openBrowser(id))
)

// 每个浏览器都有独立的指纹、Cookie、缓存
// browsers[0].wsEndpoint → 连接第 1 个
// browsers[1].wsEndpoint → 连接第 2 个
// ...

// 平铺排列窗口
await umbra.tileBrowsers()

// 全部关闭
for (const id of profiles) {
  await umbra.closeBrowser(id)
}

await umbra.shutdown()

Electron 应用集成示例

开发阶段

在你的 Electron 主进程中:

// main.ts (Electron main process)
import path from 'path'
import { createUmbra } from '@umbra/core'

let umbra: Awaited<ReturnType<typeof createUmbra>>

app.whenReady().then(async () => {
  // 根据环境选择二进制路径
  const binaryPath = app.isPackaged
    ? path.join(process.resourcesPath, 'umbra-engine', 'UmbraBrowser.app', 'Contents', 'MacOS', 'UmbraBrowser')
    : '/Applications/UmbraBrowser.app/Contents/MacOS/UmbraBrowser'

  // 应用启动时初始化 SDK
  umbra = await createUmbra({
    licenseKey: 'uk_xxx',
    binaryPath,
  })

  // 创建你的应用窗口
  const win = new BrowserWindow({ ... })

  // 监听渲染进程请求
  ipcMain.handle('browser:open', async (_, profileId) => {
    const info = await umbra.openBrowser(profileId)
    return info  // 返回 wsEndpoint 等信息
  })

  ipcMain.handle('browser:close', async (_, profileId) => {
    await umbra.closeBrowser(profileId)
  })

  ipcMain.handle('profile:create', async (_, params) => {
    return umbra.createProfile(params)
  })

  ipcMain.handle('profile:list', async (_, params) => {
    return umbra.listProfiles(params)
  })
})

app.on('will-quit', async () => {
  await umbra.shutdown()
})

打包发布

将 UmbraBrowser 二进制打包进你的应用,让最终用户无需单独安装。

1. 准备二进制文件

将我们提供的 UmbraBrowser 安装包解压到你项目的 umbra-engine/ 目录:

你的项目/
  ├── src/
  ├── umbra-engine/                      ← 把 UmbraBrowser 放这里
  │   ├── UmbraBrowser.app/              (macOS)
  │   └── UmbraBrowser.exe + 依赖文件    (Windows)
  ├── package.json
  └── electron-builder.yml

2. 配置 electron-builder

electron-builder.yml 中添加:

extraResources:
  - from: ./umbra-engine
    to: umbra-engine

这会把 umbra-engine/ 目录打包到应用的 Resources/ 下。

3. 代码中使用相对路径

import path from 'path'

// 打包后自动找到内嵌的 UmbraBrowser
const binaryPath = path.join(
  process.resourcesPath,
  'umbra-engine',
  process.platform === 'darwin'
    ? 'UmbraBrowser.app/Contents/MacOS/UmbraBrowser'
    : 'UmbraBrowser.exe'
)

const umbra = await createUmbra({
  licenseKey: 'uk_xxx',
  binaryPath,
})

4. 最终用户体验

用户安装你的应用后,UmbraBrowser 已经内嵌在里面,用户完全不知道它的存在。调用 SDK 打开浏览器时,窗口直接弹出。

重要:正确的初始化方式

createUmbra() 必须在整个应用生命周期中只调用一次。 它会在后台 spawn 一个 UmbraBrowser 进程。多次调用会导致多个进程竞争同一端口,出现数据不一致(如 create 成功但 list 返回空)。

正确做法:单例初始化

// lib/umbra.ts — 全局单例
import { createUmbra } from '@umbra/core'

let umbraInstance: Awaited<ReturnType<typeof createUmbra>> | null = null

export async function getUmbra() {
  if (!umbraInstance) {
    umbraInstance = await createUmbra({
      licenseKey: 'uk_xxx',
      binaryPath: '/Applications/UmbraBrowser.app/Contents/MacOS/UmbraBrowser',
    })
  }
  return umbraInstance
}

错误做法(会导致 bug)

// ❌ 每次操作都 createUmbra — 会 spawn 多个进程
async function createProxy() {
  const umbra = await createUmbra({ licenseKey: 'uk_xxx', binaryPath: '...' })
  return umbra.createProxy({ ... })
}

async function listProxies() {
  const umbra = await createUmbra({ licenseKey: 'uk_xxx', binaryPath: '...' })  // 又起一个进程!
  return umbra.listProxies()
}

Next.js / Server Action 中的正确用法

Next.js 开发模式会频繁热重载模块,需要用 globalThis 防止实例被重置:

// lib/umbra.ts
import { createUmbra } from '@umbra/core'

const globalForUmbra = globalThis as unknown as {
  umbra: Awaited<ReturnType<typeof createUmbra>> | undefined
}

export async function getUmbra() {
  if (!globalForUmbra.umbra) {
    globalForUmbra.umbra = await createUmbra({
      licenseKey: process.env.UMBRA_LICENSE_KEY!,
      binaryPath: process.env.UMBRA_BINARY_PATH!,
    })
  }
  return globalForUmbra.umbra
}
// app/actions/proxies.ts
'use server'
import { getUmbra } from '@/lib/umbra'

export async function createProxy(data: any) {
  const umbra = await getUmbra()
  return umbra.createProxy(data)
}

export async function listProxies() {
  const umbra = await getUmbra()
  return umbra.listProxies()
}

只连接不启动:createClient

如果 UmbraBrowser 已在运行(桌面端或其他程序启动),可以用 createClient 直接连接,不 spawn 新进程:

import { createClient } from '@umbra/core'

const umbra = createClient({ port: 17200, licenseKey: 'uk_xxx' })

// 直接使用,无需 shutdown
const proxy = await umbra.createProxy({ type: 'http', host: '1.2.3.4', port: 8080 })
const list = await umbra.listProxies()

createClient 是纯函数,无副作用,可以安全地多次调用。


错误处理

SDK 的所有方法在失败时会抛出 Error,常见错误码:

| 错误消息 | 含义 | 处理建议 | |---------|------|---------| | LICENSE_INVALID | License key 无效或已吊销 | 检查 key 是否正确 | | LICENSE_PROFILE_LIMIT | 超过 Profile 数量限制 | 升级套餐或删除旧 Profile | | 已达到最大同时打开数 | 超过并发浏览器限制 | 关闭部分浏览器 | | API call limit exceeded | 月度 API 调用超限 | 联系我们提升额度 | | 引擎未找到 | 浏览器引擎未下载 | 检查 postinstall 是否执行成功 |

try {
  await umbra.openBrowser(profileId)
} catch (err) {
  if (err.message === 'LICENSE_INVALID') {
    console.error('授权无效,请联系 UmbraBrowser 团队')
  } else if (err.message.includes('最大同时打开数')) {
    console.error('并发数超限,请关闭部分浏览器')
  } else {
    console.error('浏览器启动失败:', err.message)
  }
}

排查:create 成功但 list 返回空

如果 createProxy 返回了完整对象(带 id),但 listProxies 返回 { total: 0, items: [] }

第 1 步:确认只有一个 UmbraBrowser 进程

# macOS / Linux
ps aux | grep UmbraBrowser | grep -v grep

# Windows (PowerShell)
Get-Process | Where-Object { $_.ProcessName -like '*UmbraBrowser*' }

多个 --silent 进程 = createUmbra() 被多次调用。Kill 掉全部,改用单例模式。

第 2 步:确认端口归属

lsof -i :17200        # macOS / Linux
netstat -ano | findstr 17200   # Windows

如果占用端口的不是你 SDK 启动的进程(比如是桌面端),要么关掉桌面端,要么 SDK 换端口。

第 3 步:用 curl 直接测 API

curl -X POST http://127.0.0.1:17200/api/v1/proxy/create \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer uk_你的key" \
  -d '{"name":"test","type":"http","host":"1.2.3.4","port":8080}'

curl http://127.0.0.1:17200/api/v1/proxy/list \
  -H "Authorization: Bearer uk_你的key"

curl 正常 = API 没问题,排查你的调用方式。curl 也不正常 = 联系我们。


注意事项

  1. createUmbra() 只调用一次:它会 spawn 后台进程。多次调用导致端口冲突和数据不一致。用单例模式管理实例。
  2. 浏览器窗口独立弹出:反指纹浏览器使用独立的 Chromium 进程,窗口无法嵌入 Electron 的 BrowserView 中。这是行业通用限制(AdsPower、Multilogin 等均如此)。
  3. binaryPath 必须正确:SDK 需要知道 UmbraBrowser 二进制的路径。开发阶段指向本地安装路径,打包后指向 process.resourcesPath 下的内嵌路径。
  4. 一个 license key 的数据完全隔离:不同 key 之间的 Profile、Cookie、代理互不影响。
  5. 进程管理:SDK 会在后台运行 UmbraBrowser 进程。调用 shutdown() 会关闭所有浏览器并终止后台进程。应用退出前务必调用。
  6. 端口冲突:如果 17200 端口被占用,通过 port 参数指定其他端口。
  7. 首次启动:第一次使用时需要联网验证 license,之后支持 24 小时离线宽限。
  8. 打包体积:UmbraBrowser 二进制约 200-300MB,打包进你的应用后会增加相应体积。

交付清单

我们提供给你的:

| 交付物 | 说明 | |-------|------| | UmbraBrowser 安装包 | macOS (.dmg) 和 Windows (.exe) 安装包 | | SDK 包 (@umbra/core) | 本目录,本地 npm install ./umbra-sdk 安装 | | License Key | uk_xxx 格式,我们为你生成并提供 |

系统要求

  • Node.js >= 18
  • macOS 12+ (arm64/x64) 或 Windows 10+ (x64)
  • 磁盘空间:约 300MB(引擎二进制 + 运行数据)

技术支持

遇到问题请联系 UmbraBrowser 团队,附上:

  • 错误信息
  • license key 前 8 位(uk_xxxx****
  • ps aux | grep UmbraBrowser 的输出(排查多进程问题)