@askjo/camofox-browser 1.8.7 → 1.8.9
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 +36 -36
- package/lib/resources.js +0 -4
- package/openclaw.plugin.json +26 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
|
|
18
18
|
> <a href="https://askjo.ai?ref=camofox"><img src="jo-logo.png" alt="Jo" width="80" height="80" align="left" /></a>
|
|
19
19
|
>
|
|
20
|
-
> Built by the team behind <a href="https://askjo.ai?ref=camofox"><strong>jo
|
|
20
|
+
> Built by the team behind <a href="https://askjo.ai?ref=camofox"><strong>jo -- a personal AI agent</strong></a> that runs half on your Mac, half on a dedicated cloud machine just for you -- with zero maintenance needed. Available on macOS, Telegram, WhatsApp, and email. <a href="https://askjo.ai?ref=camofox">Try the beta free -></a>
|
|
21
21
|
|
|
22
22
|
<br/>
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
git clone https://github.com/jo-inc/camofox-browser && cd camofox-browser
|
|
26
26
|
npm install && npm start
|
|
27
|
-
#
|
|
27
|
+
# -> http://localhost:9377
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
---
|
|
@@ -42,7 +42,7 @@ This project wraps that engine in a REST API built for agents: accessibility sna
|
|
|
42
42
|
- **C++ Anti-Detection** - bypasses Google, Cloudflare, and most bot detection
|
|
43
43
|
- **Element Refs** - stable `e1`, `e2`, `e3` identifiers for reliable interaction
|
|
44
44
|
- **Token-Efficient** - accessibility snapshots are ~90% smaller than raw HTML
|
|
45
|
-
- **Runs on Anything** - lazy browser launch + idle shutdown keeps memory at ~40MB when idle. Designed to share a box with the rest of your stack
|
|
45
|
+
- **Runs on Anything** - lazy browser launch + idle shutdown keeps memory at ~40MB when idle. Designed to share a box with the rest of your stack -- Raspberry Pi, $5 VPS, shared infra.
|
|
46
46
|
- **Session Isolation** - separate cookies/storage per user
|
|
47
47
|
- **Cookie Import** - inject Netscape-format cookie files for authenticated browsing
|
|
48
48
|
- **Proxy + GeoIP** - route traffic through residential proxies with automatic locale/timezone
|
|
@@ -111,7 +111,7 @@ make up ARCH=x86_64
|
|
|
111
111
|
make up VERSION=135.0.1 RELEASE=beta.24
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
> **⚠️ Do not run `docker build` directly.** The Dockerfile uses bind mounts to pull pre-downloaded binaries from `dist/`. Always use `make up` (or `make fetch` then `make build`)
|
|
114
|
+
> **⚠️ Do not run `docker build` directly.** The Dockerfile uses bind mounts to pull pre-downloaded binaries from `dist/`. Always use `make up` (or `make fetch` then `make build`) -- it downloads the binaries first.
|
|
115
115
|
|
|
116
116
|
### Fly.io
|
|
117
117
|
|
|
@@ -154,7 +154,7 @@ export CAMOFOX_API_KEY="your-generated-key"
|
|
|
154
154
|
openclaw start
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
The same key is used by both the plugin (to authenticate requests) and the server (to verify them). Both run from the same environment
|
|
157
|
+
The same key is used by both the plugin (to authenticate requests) and the server (to verify them). Both run from the same environment -- set it once.
|
|
158
158
|
|
|
159
159
|
> **Why an env var?** The key is a secret. Plugin config in `openclaw.json` is stored in plaintext, so secrets don't belong there. Set `CAMOFOX_API_KEY` in your shell profile, systemd unit, Docker env, or Fly.io secrets.
|
|
160
160
|
|
|
@@ -177,7 +177,7 @@ The default directory is `~/.camofox/cookies/`. Override with `CAMOFOX_COOKIES_D
|
|
|
177
177
|
|
|
178
178
|
> Import my LinkedIn cookies from linkedin.txt
|
|
179
179
|
|
|
180
|
-
The agent calls `camofox_import_cookies`
|
|
180
|
+
The agent calls `camofox_import_cookies` -> reads the file -> POSTs to the server with the Bearer token -> cookies are injected into the browser session. Subsequent `camofox_create_tab` calls to linkedin.com will be authenticated.
|
|
181
181
|
|
|
182
182
|
#### How it works
|
|
183
183
|
|
|
@@ -198,13 +198,13 @@ camofox server (validates, sanitizes, injects)
|
|
|
198
198
|
Camoufox browser session (authenticated browsing)
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
-
- `cookiesPath` is resolved relative to the cookies directory
|
|
201
|
+
- `cookiesPath` is resolved relative to the cookies directory -- path traversal outside it is blocked
|
|
202
202
|
- Max 500 cookies per request, 5MB file size limit
|
|
203
203
|
- Cookie objects are sanitized to an allowlist of Playwright fields
|
|
204
204
|
|
|
205
205
|
### Session Persistence
|
|
206
206
|
|
|
207
|
-
By default, camofox persists each user's cookies and localStorage to `~/.camofox/profiles/`. Sessions survive browser restarts
|
|
207
|
+
By default, camofox persists each user's cookies and localStorage to `~/.camofox/profiles/`. Sessions survive browser restarts -- log in once (via cookies or VNC), and subsequent sessions restore the authenticated state automatically.
|
|
208
208
|
|
|
209
209
|
```
|
|
210
210
|
~/.camofox/
|
|
@@ -334,26 +334,26 @@ When a proxy is configured:
|
|
|
334
334
|
|
|
335
335
|
### Crash Reporter
|
|
336
336
|
|
|
337
|
-
Browser automation fails in ways that are hard to predict
|
|
337
|
+
Browser automation fails in ways that are hard to predict -- Cloudflare challenges, site redesigns breaking selectors, redirect loops, dialog storms, renderer crashes. The scope is wide and the failure modes are diverse. Without telemetry, the only signal is "it didn't work."
|
|
338
338
|
|
|
339
339
|
The crash reporter gives us structured data on *which sites fail*, *how they fail*, and *how often*, so we can prioritize fixes for the patterns that actually affect users. It files GitHub Issues automatically when:
|
|
340
340
|
|
|
341
341
|
- **Uncaught exceptions** crash the process
|
|
342
342
|
- **Event loop stalls** exceed 5 seconds (watchdog detection)
|
|
343
|
-
- **Frustration patterns**
|
|
343
|
+
- **Frustration patterns** -- 3+ consecutive failures (timeout, dead context, navigation abort) on the same tab
|
|
344
344
|
|
|
345
|
-
Each report includes the failure type, stack trace, tab health counters (HTTP status histogram, console errors, request failures, redirect depth), and the target URL
|
|
345
|
+
Each report includes the failure type, stack trace, tab health counters (HTTP status histogram, console errors, request failures, redirect depth), and the target URL -- all anonymized.
|
|
346
346
|
|
|
347
347
|
#### How it works
|
|
348
348
|
|
|
349
|
-
Reports are sent to a lightweight Cloudflare Worker relay at [`https://camofox-crash-relay.askjo.workers.dev`](https://camofox-crash-relay.askjo.workers.dev/health). The relay holds the GitHub App credentials as environment secrets
|
|
349
|
+
Reports are sent to a lightweight Cloudflare Worker relay at [`https://camofox-crash-relay.askjo.workers.dev`](https://camofox-crash-relay.askjo.workers.dev/health). The relay holds the GitHub App credentials as environment secrets -- **no secrets are shipped in this package**.
|
|
350
350
|
|
|
351
351
|
```
|
|
352
352
|
lib/reporter.js (client, no secrets)
|
|
353
|
-
│ anonymize
|
|
353
|
+
│ anonymize -> POST https://camofox-crash-relay.askjo.workers.dev/report
|
|
354
354
|
▼
|
|
355
355
|
Cloudflare Worker (holds GitHub App key)
|
|
356
|
-
│ validate
|
|
356
|
+
│ validate -> rate-limit -> dedup -> create GitHub Issue
|
|
357
357
|
▼
|
|
358
358
|
GitHub Issue created
|
|
359
359
|
```
|
|
@@ -362,12 +362,12 @@ The relay source code is in this repo at [`workers/crash-reporter/index.ts`](wor
|
|
|
362
362
|
|
|
363
363
|
#### Verification
|
|
364
364
|
|
|
365
|
-
You don't have to trust us
|
|
365
|
+
You don't have to trust us -- verify what the live relay is running:
|
|
366
366
|
|
|
367
367
|
```bash
|
|
368
368
|
# 1. Ask the relay what code it's running
|
|
369
369
|
curl https://camofox-crash-relay.askjo.workers.dev/source
|
|
370
|
-
#
|
|
370
|
+
# -> { "commit": "abc1234", "sha256": "e3b0c44...", "source": "https://github.com/..." }
|
|
371
371
|
|
|
372
372
|
# 2. Compare the sha256 against the source in this repo
|
|
373
373
|
sha256sum workers/crash-reporter/index.ts
|
|
@@ -377,20 +377,20 @@ sha256sum workers/crash-reporter/index.ts
|
|
|
377
377
|
git log --oneline workers/crash-reporter/index.ts | head -1
|
|
378
378
|
```
|
|
379
379
|
|
|
380
|
-
If the hashes don't match, the relay is running different code than what's in the repo. The deploy workflow ([`.github/workflows/crash-relay-deploy.yml`](.github/workflows/crash-relay-deploy.yml)) injects the commit and source hash at deploy time
|
|
380
|
+
If the hashes don't match, the relay is running different code than what's in the repo. The deploy workflow ([`.github/workflows/crash-relay-deploy.yml`](.github/workflows/crash-relay-deploy.yml)) injects the commit and source hash at deploy time -- every deploy is auditable in [GitHub Actions](https://github.com/jo-inc/camofox-browser/actions/workflows/crash-relay-deploy.yml).
|
|
381
381
|
|
|
382
382
|
Or skip verification entirely: `CAMOFOX_CRASH_REPORT_ENABLED=false` disables all reporting, or point to [your own relay](#self-hosted-relay) with `CAMOFOX_CRASH_REPORT_URL`.
|
|
383
383
|
|
|
384
384
|
#### Privacy
|
|
385
385
|
|
|
386
|
-
All reported data goes through paranoid anonymization ([`lib/reporter.js` L28
|
|
386
|
+
All reported data goes through paranoid anonymization ([`lib/reporter.js` L28-290](lib/reporter.js#L28-L290)) before leaving the process:
|
|
387
387
|
|
|
388
|
-
- **URLs**
|
|
389
|
-
- **File paths**
|
|
390
|
-
- **Tokens, secrets, API keys**
|
|
391
|
-
- **IPs, emails, env vars**
|
|
392
|
-
- **Docker/Fly machine IDs**
|
|
393
|
-
- **Tab health**
|
|
388
|
+
- **URLs** -- well-known public domains (Google, Amazon, Reddit, Cloudflare, etc.) are shown verbatim so we can identify which sites cause problems. Private/unknown domains are replaced with a stable HMAC hash (`site-a1b2c3d4`) -- same hash across reports for correlation, but not reversible to the original domain. Path segments become `•/•/•` (depth only). Query params become `?[3]` (count only). No keys, values, or path content is ever included.
|
|
389
|
+
- **File paths** -> stripped to filename only (`<path>/server.js`)
|
|
390
|
+
- **Tokens, secrets, API keys** -> `<token>`
|
|
391
|
+
- **IPs, emails, env vars** -> redacted
|
|
392
|
+
- **Docker/Fly machine IDs** -> `<id>`
|
|
393
|
+
- **Tab health** -- pure counters (crash count, error count, status code histogram). No page content, no URLs, no user data.
|
|
394
394
|
|
|
395
395
|
Duplicate issues are detected by stack signature and get a `+1` comment instead of a new issue.
|
|
396
396
|
|
|
@@ -409,14 +409,14 @@ export CAMOFOX_CRASH_REPORT_RATE_LIMIT=5
|
|
|
409
409
|
|
|
410
410
|
To file crash reports in your own GitHub repo instead of `jo-inc/camofox-browser`:
|
|
411
411
|
|
|
412
|
-
1. **Create a GitHub App**
|
|
413
|
-
- Permissions: **Repository
|
|
414
|
-
- Uncheck **Webhook
|
|
415
|
-
- Click **Generate a key**
|
|
416
|
-
- Install the app on your target repo (Install App
|
|
412
|
+
1. **Create a GitHub App** -- [Settings -> Developer settings -> GitHub Apps -> New](https://github.com/settings/apps/new)
|
|
413
|
+
- Permissions: **Repository -> Issues -> Read & Write**
|
|
414
|
+
- Uncheck **Webhook -> Active** (not needed)
|
|
415
|
+
- Click **Generate a key** -- downloads a `.pem` file
|
|
416
|
+
- Install the app on your target repo (Install App -> select repo)
|
|
417
417
|
- Note your **App ID** (number on the app's General page) and **Installation ID** (from the URL after installing: `github.com/settings/installations/{id}`)
|
|
418
418
|
|
|
419
|
-
2. **Deploy the relay**
|
|
419
|
+
2. **Deploy the relay** -- clone this repo and deploy the worker:
|
|
420
420
|
```bash
|
|
421
421
|
cd workers/crash-reporter
|
|
422
422
|
# Edit wrangler.toml: set account_id to your Cloudflare account ID
|
|
@@ -444,7 +444,7 @@ To file crash reports in your own GitHub repo instead of `jo-inc/camofox-browser
|
|
|
444
444
|
5. **Verify:**
|
|
445
445
|
```bash
|
|
446
446
|
curl https://your-worker.your-subdomain.workers.dev/health
|
|
447
|
-
#
|
|
447
|
+
# -> {"status":"ok"}
|
|
448
448
|
```
|
|
449
449
|
|
|
450
450
|
### Structured Logging
|
|
@@ -468,7 +468,7 @@ curl -X POST http://localhost:9377/tabs \
|
|
|
468
468
|
|
|
469
469
|
# Get accessibility snapshot with element refs
|
|
470
470
|
curl "http://localhost:9377/tabs/TAB_ID/snapshot?userId=agent1"
|
|
471
|
-
#
|
|
471
|
+
# -> { "snapshot": "[button e1] Submit [link e2] Learn more", ... }
|
|
472
472
|
|
|
473
473
|
# Click by ref
|
|
474
474
|
curl -X POST http://localhost:9377/tabs/TAB_ID/click \
|
|
@@ -528,10 +528,10 @@ curl -X POST http://localhost:9377/tabs/TAB_ID/navigate \
|
|
|
528
528
|
curl -X POST http://localhost:9377/youtube/transcript \
|
|
529
529
|
-H 'Content-Type: application/json' \
|
|
530
530
|
-d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "languages": ["en"]}'
|
|
531
|
-
#
|
|
531
|
+
# -> { "status": "ok", "transcript": "[00:18] ♪ We're no strangers to love ♪\n...", "video_title": "...", "total_words": 548 }
|
|
532
532
|
```
|
|
533
533
|
|
|
534
|
-
Uses [yt-dlp](https://github.com/yt-dlp/yt-dlp) when available (fast, no browser needed). Falls back to a browser-based intercept method if yt-dlp is not installed
|
|
534
|
+
Uses [yt-dlp](https://github.com/yt-dlp/yt-dlp) when available (fast, no browser needed). Falls back to a browser-based intercept method if yt-dlp is not installed -- this is slower and less reliable due to YouTube ad pre-rolls.
|
|
535
535
|
|
|
536
536
|
### Server
|
|
537
537
|
|
|
@@ -554,7 +554,7 @@ Uses [yt-dlp](https://github.com/yt-dlp/yt-dlp) when available (fast, no browser
|
|
|
554
554
|
|
|
555
555
|
Reddit macros return JSON directly (no HTML parsing needed):
|
|
556
556
|
- `@reddit_search` - search all of Reddit, returns JSON with 25 results
|
|
557
|
-
- `@reddit_subreddit` - browse a subreddit (e.g., query `"programming"`
|
|
557
|
+
- `@reddit_subreddit` - browse a subreddit (e.g., query `"programming"` -> `/r/programming.json`)
|
|
558
558
|
|
|
559
559
|
## Environment Variables
|
|
560
560
|
|
|
@@ -610,7 +610,7 @@ Browser Instance (Camoufox)
|
|
|
610
610
|
|
|
611
611
|
Sessions auto-expire after 30 minutes of inactivity. The browser itself shuts down after 5 minutes with no active sessions, and relaunches on the next request.
|
|
612
612
|
|
|
613
|
-
When a session's tab limit is reached, the oldest/least-used tab is automatically recycled instead of returning an error
|
|
613
|
+
When a session's tab limit is reached, the oldest/least-used tab is automatically recycled instead of returning an error -- so long-running agent sessions don't hit dead ends.
|
|
614
614
|
|
|
615
615
|
## Testing
|
|
616
616
|
|
package/lib/resources.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// in the same file (avoids OpenClaw scanner "potential-exfiltration" pattern).
|
|
4
4
|
|
|
5
5
|
import fs from 'fs';
|
|
6
|
-
import { execSync } from 'child_process';
|
|
7
6
|
|
|
8
7
|
// ============================================================================
|
|
9
8
|
// Process resource snapshot (memory, handles, FDs, browser RSS)
|
|
@@ -45,9 +44,6 @@ export function collectResourceSnapshot(opts = {}) {
|
|
|
45
44
|
const status = fs.readFileSync(`/proc/${opts.browserPid}/status`, 'utf8');
|
|
46
45
|
const match = status.match(/VmRSS:\s+(\d+)\s+kB/);
|
|
47
46
|
if (match) snap.browserRssMb = Math.round(parseInt(match[1], 10) / 1024);
|
|
48
|
-
} else if (process.platform === 'darwin') {
|
|
49
|
-
const out = execSync(`ps -o rss= -p ${opts.browserPid}`, { timeout: 1000 }).toString().trim();
|
|
50
|
-
if (out) snap.browserRssMb = Math.round(parseInt(out, 10) / 1024);
|
|
51
47
|
}
|
|
52
48
|
} catch { /* process gone or permission denied */ }
|
|
53
49
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,31 @@
|
|
|
2
2
|
"id": "camofox-browser",
|
|
3
3
|
"name": "Camofox Browser",
|
|
4
4
|
"description": "Anti-detection browser automation for AI agents using Camoufox (Firefox-based)",
|
|
5
|
-
"version": "1.8.
|
|
5
|
+
"version": "1.8.9",
|
|
6
|
+
"envVars": {
|
|
7
|
+
"CAMOFOX_API_KEY": {
|
|
8
|
+
"description": "Secret key for the cookie-import endpoint. Cookie import is disabled when unset. Only set this if you need to import browser cookies and the server is local or access-controlled.",
|
|
9
|
+
"required": false,
|
|
10
|
+
"sensitive": true
|
|
11
|
+
},
|
|
12
|
+
"CAMOFOX_ACCESS_KEY": {
|
|
13
|
+
"description": "Global bearer token for all routes (except /health). When set, every request must include Authorization: Bearer <key>. Recommended when exposing the server beyond localhost.",
|
|
14
|
+
"required": false,
|
|
15
|
+
"sensitive": true
|
|
16
|
+
},
|
|
17
|
+
"CAMOFOX_CRASH_REPORT_ENABLED": {
|
|
18
|
+
"description": "Enable or disable anonymized crash/hang reporting. Set to 'false' to disable all outbound crash reports.",
|
|
19
|
+
"required": false,
|
|
20
|
+
"sensitive": false,
|
|
21
|
+
"default": "true"
|
|
22
|
+
},
|
|
23
|
+
"CAMOFOX_CRASH_REPORT_URL": {
|
|
24
|
+
"description": "Crash report relay endpoint. Override to point to a self-hosted relay instead of the default Cloudflare Worker.",
|
|
25
|
+
"required": false,
|
|
26
|
+
"sensitive": false,
|
|
27
|
+
"default": "https://camofox-crash-relay.askjo.workers.dev/report"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
6
30
|
"configSchema": {
|
|
7
31
|
"type": "object",
|
|
8
32
|
"properties": {
|
|
@@ -68,7 +92,7 @@
|
|
|
68
92
|
},
|
|
69
93
|
"telemetry": {
|
|
70
94
|
"crashReporter": {
|
|
71
|
-
"description": "Anonymized crash/hang reports sent to a Cloudflare Worker relay. All credentials are environment secrets on the relay
|
|
95
|
+
"description": "Anonymized crash/hang reports sent to a Cloudflare Worker relay. All credentials are environment secrets on the relay -- nothing sensitive ships in this package.",
|
|
72
96
|
"enabled": true,
|
|
73
97
|
"optOut": "CAMOFOX_CRASH_REPORT_ENABLED=false",
|
|
74
98
|
"relay": "https://camofox-crash-relay.askjo.workers.dev/report",
|
package/package.json
CHANGED