@maximilianslawik/tbrowser
v0.1.1
Published
Full Node.js and npm runtime for the tbrowser local browser control plane
Maintainers
Readme
tbrowser
Full Node.js and npm runtime for tbrowser.
This repository now includes both:
- a typed Node client and CLI
- a full Node control plane that launches isolated Chromium sessions in Docker
The npm package can run the server itself with tbrowser serve. It does not depend on the Rust implementation at runtime.
What it includes
- session lifecycle API
- navigation, eval, DOM snapshots, screenshots
- desktop mouse and keyboard control through
docker exec+xdotool - approvals and local policy enforcement
- uploads, download detection, artifacts
- tabs, network captures, traces
- audit history and WebSocket session events
- named profile import and export
- bundled browser image assets for local Docker builds
Install
npm install @maximilianslawik/tbrowserRequirements
- Node.js 18 or newer
- Docker daemon reachable from the local host
Build the browser image
The server expects a browser image tagged as tbrowser/browser-base:local unless you override TBROWSER_BROWSER_IMAGE.
From this repository:
npm run build:imageRun the server
tbrowser serveOr with explicit settings:
tbrowser serve \
--bind-addr 127.0.0.1:3000 \
--data-dir ./storage \
--browser-image tbrowser/browser-base:local \
--public-host 127.0.0.1Default API base:
http://127.0.0.1:3000Environment variables
TBROWSER_BIND_ADDRDefault:127.0.0.1:3000TBROWSER_DATA_DIRDefault:./storageTBROWSER_DATABASE_URLDefault:sqlite:${TBROWSER_DATA_DIR}/state/tbrowser.dbTBROWSER_BROWSER_IMAGEDefault:tbrowser/browser-base:localTBROWSER_PUBLIC_HOSTDefault:127.0.0.1TBROWSER_CLEANUP_INTERVAL_SECONDSDefault:30TBROWSER_IDLE_TIMEOUT_SECONDSDefault:300TBROWSER_APPROVAL_TTL_SECONDSDefault:600TBROWSER_POLICY_PATHOptional JSON file for blocked hosts, approval hosts, approval-gated actions, and sensitive eval keywords
Policy file shape
{
"blocked_hosts": ["*.internal.example"],
"approval_hosts": ["accounts.google.com", "*.bank.example"],
"approval_actions": ["desktop", "upload", "download", "tab_close"],
"sensitive_eval_keywords": ["document.cookie", "navigator.clipboard"]
}Client usage
import { TbrowserClient } from "@maximilianslawik/tbrowser";
const client = new TbrowserClient({
baseUrl: "http://127.0.0.1:3000"
});
const session = await client.createSession({
label: "demo",
initial_url: "https://example.com",
launch: {
headless: true
}
});
const snapshot = await client.snapshot(session.id);
console.log(snapshot.title);Embedded server usage from code
import { createTbrowserServer } from "@maximilianslawik/tbrowser";
const server = await createTbrowserServer({
bindAddr: "127.0.0.1:3000",
dataDir: "./storage"
});
await server.listen();CLI examples
tbrowser health
tbrowser policy
tbrowser sessions create --json '{"label":"demo","launch":{"headless":true}}'
tbrowser sessions navigate SESSION_ID --url https://example.com
tbrowser sessions screenshot SESSION_ID --output ./page.png
tbrowser uploads from-path SESSION_ID --selector '#upload' --path ./document.pdf
tbrowser events stream SESSION_IDAPI coverage
The Node runtime exposes the same control-plane surface as the Rust repo:
POST /v1/sessionsGET /v1/sessionsGET /v1/sessions/{session_id}DELETE /v1/sessions/{session_id}GET /v1/sessions/{session_id}/livePOST /v1/sessions/{session_id}/navigatePOST /v1/sessions/{session_id}/evalPOST /v1/sessions/{session_id}/snapshotPOST /v1/sessions/{session_id}/screenshotGET /v1/sessions/{session_id}/eventsGET /v1/sessions/{session_id}/events/wsGET /v1/policyGET /v1/sessions/{session_id}/approvalsPOST /v1/sessions/{session_id}/approvalsPOST /v1/approvals/{approval_id}/decisionGET /v1/sessions/{session_id}/artifactsGET /v1/sessions/{session_id}/artifacts/{artifact_id}GET /v1/sessions/{session_id}/tabsPOST /v1/sessions/{session_id}/tabsPOST /v1/sessions/{session_id}/tabs/{tab_id}/activatePOST /v1/sessions/{session_id}/tabs/{tab_id}/closePOST /v1/sessions/{session_id}/uploadsPOST /v1/sessions/{session_id}/downloads/waitPOST /v1/sessions/{session_id}/captures/networkPOST /v1/sessions/{session_id}/captures/tracePOST /v1/sessions/{session_id}/desktop/mouse/movePOST /v1/sessions/{session_id}/desktop/mouse/clickPOST /v1/sessions/{session_id}/desktop/mouse/dragPOST /v1/sessions/{session_id}/desktop/keyboard/typePOST /v1/sessions/{session_id}/desktop/keyboard/keyPOST /v1/sessions/{session_id}/desktop/drag-elementGET /v1/profilesGET /v1/profiles/{profile_id}POST /v1/profiles/importPOST /v1/profiles/{profile_id}/export
Development
npm install
npm test
npm run build:imagePublish
The package is structured for npm publish:
npm publishIf you want a scoped package name, change the name field in package.json before publishing.
