junso-browser 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # junso-browser
2
+
3
+ A standalone **CloakBrowser host**. It runs one stealth-Chromium instance **per profile**, each with its **own deterministic device fingerprint + proxy + cookies**, and exposes each over a **token-authed CDP gateway**. Drive it with anything that speaks CDP — `agent-browser`, Playwright, Puppeteer — via `--cdp`.
4
+
5
+ Built for [Jun](../jun-codex): Jun keeps its whole tool surface and just connects to a junso-browser profile's CDP URL, gaining real per-profile device identities (the gap a shared local browser can't close).
6
+
7
+ ## Why
8
+
9
+ A fresh profile gives new cookies + storage; a proxy gives a new IP — but on one machine every profile shares the same **device fingerprint** (canvas/WebGL/timezone/fonts/TLS), so a site can still link them. CloakBrowser derives a full, consistent fingerprint from a **seed** (same seed = same device; different seed = different device). junso-browser runs one seeded instance per profile and hands out its CDP endpoint.
10
+
11
+ ## How it works
12
+
13
+ ```
14
+ client (agent-browser --cdp wss://host/cdp/<profile>?token=…)
15
+ │ WS relay + /json discovery (token-authed gateway)
16
+
17
+ junso-browser ──spawn──> cloak chrome --fingerprint=<seed> --fingerprint-platform=…
18
+ --proxy-server=… --user-data-dir=… --remote-debugging-port=0
19
+ ```
20
+
21
+ - **No Playwright at runtime** — the cloak *binary* applies the fingerprint from `--fingerprint*` flags (`--headless=new`); junso-browser is a thin process supervisor that spawns it and proxies CDP.
22
+ - The cloak CDP binds `127.0.0.1` with no auth; junso-browser is the only thing that touches it and fronts it with a token gateway.
23
+
24
+ ## Install & run
25
+
26
+ ```bash
27
+ bun i -g junso-browser # installs the host + MCP bins
28
+ JUNSO_BROWSER_TOKEN=secret JUNSO_BROWSER_HOST=0.0.0.0 junso-browser # start the host (:8790)
29
+ ```
30
+
31
+ The CloakBrowser binary (~200 MB) **auto-downloads** on first run (opt out: `JUNSO_BROWSER_NO_CLOAK_DOWNLOAD=true`). Runs on **Bun** (`Bun.serve`/`Bun.spawn`).
32
+
33
+ The package ships two bins: **`junso-browser`** (the host) and **`junso-browser-mcp`** (the optional MCP — see below). From source: `bun install` then `bun run start` / `bun run mcp`. Build: `bun run build` (bundle `dist/`) or `bun run build:binary` (single compiled binary).
34
+
35
+ ### Config (env)
36
+
37
+ | Var | Default | |
38
+ |---|---|---|
39
+ | `JUNSO_BROWSER_PORT` | `8790` | listen port |
40
+ | `JUNSO_BROWSER_HOST` | `127.0.0.1` | listen addr (token **required** if non-loopback) |
41
+ | `JUNSO_BROWSER_TOKEN` | — | shared secret for API + CDP |
42
+ | `JUNSO_BROWSER_DATA_DIR` | `~/.junso-browser` | profiles.json + user-data dirs |
43
+ | `JUNSO_BROWSER_MAX_INSTANCES` | `3` | concurrent cloak instances (LRU-reaped) |
44
+ | `JUNSO_BROWSER_IDLE_MS` | `1800000` | reap an instance after this idle |
45
+ | `JUNSO_BROWSER_PUBLIC_URL` | — | base for handed-out CDP URLs (e.g. `wss://browser.example.com`) |
46
+ | `JUNSO_BROWSER_CLOAK_PATH` | auto | override the binary path |
47
+
48
+ ## API
49
+
50
+ All `/api/*` require the token (`Authorization: Bearer …`, `x-junso-token`, or `?token=`).
51
+
52
+ | Method | Path | |
53
+ |---|---|---|
54
+ | GET | `/health` | status + cloak presence + running count |
55
+ | GET | `/api/profiles` | list (proxy masked) + running flag |
56
+ | POST | `/api/profiles` | `{ name, fingerprint?: { seed?, platform?, timezone?, locale? }, proxy? }` |
57
+ | GET/PATCH/DELETE | `/api/profiles/:id` | read / update (incl. `rotateSeed`) / delete |
58
+ | POST | `/api/profiles/:id/launch` | start instance → `{ cdpUrl }` |
59
+ | POST | `/api/profiles/:id/stop` | stop instance |
60
+ | GET | `/api/profiles/:id/cdp` | `{ cdpUrl }` (auto-launches on connect) |
61
+ | WS/GET | `/cdp/:id` | CDP gateway (ws relay + `/cdp/:id/json*` discovery) |
62
+
63
+ ## Interactive viewport
64
+
65
+ Open `http://<host>:8790/view/<profile>?token=<token>` in any browser to **watch and take control** of a profile's browser — live screencast + your mouse/keyboard forwarded over CDP. Use it for human-in-the-loop steps (log in by hand, solve a CAPTCHA, check something) on the *same* fingerprinted profile the agent drives via MCP. CDP supports multiple clients, so the viewport and the MCP coexist. (Frames stream on repaint — a static page shows one frame until you interact.)
66
+
67
+ ## Optional browser MCP
68
+
69
+ junso-browser also ships an **MCP server** (`src/mcp.ts`, bin `junso-browser-mcp`) — the way to give any MCP client (Jun, Codex, Claude Desktop) browser tools without a local browser. It resolves a profile's CDP URL from the API and drives it via `agent-browser --cdp`, so it works whether junso-browser is local or remote.
70
+
71
+ Add it to a client's MCP config (once `junso-browser` is installed globally):
72
+
73
+ ```json
74
+ {
75
+ "command": "junso-browser-mcp",
76
+ "env": { "JUNSO_BROWSER_URL": "https://browser.example.com:8790", "JUNSO_BROWSER_TOKEN": "secret", "JUNSO_BROWSER_PROFILE": "default" }
77
+ }
78
+ ```
79
+
80
+ (or `"command": "bunx", "args": ["-y", "junso-browser-mcp"]` without a global install).
81
+
82
+ Tools: `browser_status`, `browser_navigate`, `browser_snapshot`, `browser_click`, `browser_fill`, `browser_type`, `browser_press`, `browser_select`, `browser_scroll`, `browser_wait`, `browser_read`, `browser_eval`, `browser_screenshot`, `browser_exec`, `browser_list_profiles`, `browser_switch_profile`. The active profile (env `JUNSO_BROWSER_PROFILE`, default `default`) is auto-created on the host if missing; `browser_switch_profile` changes identity at runtime.
83
+
84
+ ## Status
85
+
86
+ Phase 1 MVP — verified e2e (profile → fingerprinted cloak → external CDP client sees the configured fingerprint, deterministic per seed). Next: Jun integration (opt-in `JUN_BROWSER_CDP` backend), single-binary deploy, optional fingerprint personas.
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * `junso-browser-mcp` — the optional browser MCP server (stdio). Add it to any
4
+ * MCP client: { "command": "junso-browser-mcp", "env": { "JUNSO_BROWSER_URL": … } }
5
+ * (or `bunx -y junso-browser-mcp`). Runs under Bun.
6
+ */
7
+ if (typeof Bun === "undefined") {
8
+ console.error("junso-browser-mcp runs on Bun, not Node. Install Bun (https://bun.sh).");
9
+ process.exit(1);
10
+ }
11
+
12
+ import path from "node:path";
13
+ await import(path.join(path.resolve(import.meta.dir, ".."), "dist", "mcp.js"));
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * `junso-browser` CLI — entry point of the published package. The host uses Bun
4
+ * APIs (Bun.serve/Bun.spawn), so it must run under Bun; the guard below catches
5
+ * the Node case with a pointer instead of a cryptic crash.
6
+ */
7
+ if (typeof Bun === "undefined") {
8
+ console.error("junso-browser runs on Bun, not Node. Install Bun (https://bun.sh):\n bun install -g junso-browser");
9
+ process.exit(1);
10
+ }
11
+
12
+ import path from "node:path";
13
+
14
+ const root = path.resolve(import.meta.dir, "..");
15
+ const cmd = process.argv[2] ?? "start";
16
+
17
+ if (cmd === "version" || cmd === "--version" || cmd === "-v") {
18
+ console.log(require(path.join(root, "package.json")).version);
19
+ } else if (cmd === "mcp") {
20
+ await import(path.join(root, "dist", "mcp.js")); // also available as `junso-browser-mcp`
21
+ } else if (cmd === "start" || cmd === undefined) {
22
+ await import(path.join(root, "dist", "server.js"));
23
+ } else {
24
+ console.error(`Unknown command: ${cmd}\nUsage: junso-browser [start|mcp|version]`);
25
+ process.exit(1);
26
+ }