@umbra-sdk/core
v1.1.5
Published
UmbraBrowser SDK - Anti-detect browser automation
Maintainers
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.yml2. 配置 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 也不正常 = 联系我们。
注意事项
createUmbra()只调用一次:它会 spawn 后台进程。多次调用导致端口冲突和数据不一致。用单例模式管理实例。- 浏览器窗口独立弹出:反指纹浏览器使用独立的 Chromium 进程,窗口无法嵌入 Electron 的 BrowserView 中。这是行业通用限制(AdsPower、Multilogin 等均如此)。
- binaryPath 必须正确:SDK 需要知道 UmbraBrowser 二进制的路径。开发阶段指向本地安装路径,打包后指向
process.resourcesPath下的内嵌路径。 - 一个 license key 的数据完全隔离:不同 key 之间的 Profile、Cookie、代理互不影响。
- 进程管理:SDK 会在后台运行 UmbraBrowser 进程。调用
shutdown()会关闭所有浏览器并终止后台进程。应用退出前务必调用。 - 端口冲突:如果 17200 端口被占用,通过
port参数指定其他端口。 - 首次启动:第一次使用时需要联网验证 license,之后支持 24 小时离线宽限。
- 打包体积: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的输出(排查多进程问题)
