@askjo/camofox-browser 1.0.12
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/Dockerfile.camoufox +59 -0
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/lib/macros.js +30 -0
- package/openclaw.plugin.json +39 -0
- package/package.json +68 -0
- package/plugin.ts +647 -0
- package/run-camoufox.sh +37 -0
- package/server-camoufox.js +1288 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
FROM node:20-slim
|
|
2
|
+
|
|
3
|
+
# Pinned Camoufox version for reproducible builds
|
|
4
|
+
# Update these when upgrading Camoufox
|
|
5
|
+
ARG CAMOUFOX_VERSION=135.0.1
|
|
6
|
+
ARG CAMOUFOX_RELEASE=beta.24
|
|
7
|
+
ARG CAMOUFOX_URL=https://github.com/daijro/camoufox/releases/download/v${CAMOUFOX_VERSION}-${CAMOUFOX_RELEASE}/camoufox-${CAMOUFOX_VERSION}-${CAMOUFOX_RELEASE}-lin.x86_64.zip
|
|
8
|
+
|
|
9
|
+
# Install dependencies for Camoufox (Firefox-based)
|
|
10
|
+
RUN apt-get update && apt-get install -y \
|
|
11
|
+
# Firefox dependencies
|
|
12
|
+
libgtk-3-0 \
|
|
13
|
+
libdbus-glib-1-2 \
|
|
14
|
+
libxt6 \
|
|
15
|
+
libasound2 \
|
|
16
|
+
libx11-xcb1 \
|
|
17
|
+
libxcomposite1 \
|
|
18
|
+
libxcursor1 \
|
|
19
|
+
libxdamage1 \
|
|
20
|
+
libxfixes3 \
|
|
21
|
+
libxi6 \
|
|
22
|
+
libxrandr2 \
|
|
23
|
+
libxrender1 \
|
|
24
|
+
libxss1 \
|
|
25
|
+
libxtst6 \
|
|
26
|
+
# Fonts
|
|
27
|
+
fonts-liberation \
|
|
28
|
+
fonts-noto-color-emoji \
|
|
29
|
+
fontconfig \
|
|
30
|
+
# Utils
|
|
31
|
+
ca-certificates \
|
|
32
|
+
curl \
|
|
33
|
+
unzip \
|
|
34
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
35
|
+
|
|
36
|
+
# Pre-bake Camoufox browser binary into image
|
|
37
|
+
# This avoids downloading at runtime and pins the version
|
|
38
|
+
# Note: unzip returns exit code 1 for warnings (Unicode filenames), so we use || true and verify
|
|
39
|
+
RUN mkdir -p /root/.cache/camoufox \
|
|
40
|
+
&& curl -L -o /tmp/camoufox.zip "${CAMOUFOX_URL}" \
|
|
41
|
+
&& (unzip -q /tmp/camoufox.zip -d /root/.cache/camoufox || true) \
|
|
42
|
+
&& rm /tmp/camoufox.zip \
|
|
43
|
+
&& chmod -R 755 /root/.cache/camoufox \
|
|
44
|
+
&& echo "{\"version\":\"${CAMOUFOX_VERSION}\",\"release\":\"${CAMOUFOX_RELEASE}\"}" > /root/.cache/camoufox/version.json \
|
|
45
|
+
&& test -f /root/.cache/camoufox/camoufox-bin && echo "Camoufox installed successfully"
|
|
46
|
+
|
|
47
|
+
WORKDIR /app
|
|
48
|
+
|
|
49
|
+
COPY package.json ./
|
|
50
|
+
RUN npm install --production
|
|
51
|
+
|
|
52
|
+
COPY server-camoufox.js ./
|
|
53
|
+
|
|
54
|
+
ENV NODE_ENV=production
|
|
55
|
+
ENV PORT=3000
|
|
56
|
+
|
|
57
|
+
EXPOSE 3000
|
|
58
|
+
|
|
59
|
+
CMD ["node", "server-camoufox.js"]
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jo, Inc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="camofox.png" alt="camofox-browser" width="200" />
|
|
3
|
+
<h1>camofox-browser</h1>
|
|
4
|
+
<p><strong>Headless browser server for AI agents with C++ anti-detection</strong></p>
|
|
5
|
+
<p>
|
|
6
|
+
<a href="https://github.com/jo-inc/camofox-browser/actions"><img src="https://img.shields.io/badge/build-passing-brightgreen" alt="Build" /></a>
|
|
7
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue" alt="License" /></a>
|
|
8
|
+
<a href="https://camoufox.com"><img src="https://img.shields.io/badge/engine-Camoufox-red" alt="Camoufox" /></a>
|
|
9
|
+
<a href="https://hub.docker.com"><img src="https://img.shields.io/badge/docker-ready-blue" alt="Docker" /></a>
|
|
10
|
+
</p>
|
|
11
|
+
<p>
|
|
12
|
+
Powered by <a href="https://camoufox.com">Camoufox</a> — a Firefox fork with fingerprint spoofing at the C++ level.<br/>
|
|
13
|
+
The same engine behind <a href="https://askjo.ai">askjo.ai</a>'s web scraping.
|
|
14
|
+
</p>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Why
|
|
20
|
+
|
|
21
|
+
AI agents need to browse the real web. Playwright gets blocked. Headless Chrome gets fingerprinted. Stealth plugins become the fingerprint.
|
|
22
|
+
|
|
23
|
+
Camoufox patches Firefox at the **C++ implementation level** — `navigator.hardwareConcurrency`, WebGL renderers, AudioContext, screen geometry, WebRTC — all spoofed before JavaScript ever sees them. No shims, no wrappers, no tells.
|
|
24
|
+
|
|
25
|
+
This project wraps that engine in a REST API built for agents: accessibility snapshots instead of bloated HTML, stable element refs for clicking, and search macros for common sites.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- **C++ Anti-Detection** — bypasses Google, Cloudflare, and most bot detection
|
|
30
|
+
- **Element Refs** — stable `e1`, `e2`, `e3` identifiers for reliable interaction
|
|
31
|
+
- **Token-Efficient** — accessibility snapshots are 90% smaller than raw HTML
|
|
32
|
+
- **Session Isolation** — separate cookies/storage per user
|
|
33
|
+
- **Search Macros** — `@google_search`, `@youtube_search`, `@amazon_search`, and 10 more
|
|
34
|
+
- **Docker Ready** — production Dockerfile with pre-baked Camoufox binary
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
### OpenClaw Plugin
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
openclaw plugins install @askjo/camofox-browser
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
# ~/.openclaw/openclaw.json
|
|
46
|
+
plugins:
|
|
47
|
+
entries:
|
|
48
|
+
camofox-browser:
|
|
49
|
+
enabled: true
|
|
50
|
+
config:
|
|
51
|
+
url: "http://localhost:9377"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Tools:** `camofox_create_tab` · `camofox_snapshot` · `camofox_click` · `camofox_type` · `camofox_navigate` · `camofox_scroll` · `camofox_screenshot` · `camofox_close_tab` · `camofox_list_tabs`
|
|
55
|
+
|
|
56
|
+
### Standalone
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/jo-inc/camofox-browser
|
|
60
|
+
cd camofox-browser
|
|
61
|
+
npm install
|
|
62
|
+
npm start # downloads Camoufox on first run (~300MB)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Docker
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
docker build -f Dockerfile.camoufox -t camofox-browser .
|
|
69
|
+
docker run -p 3000:3000 camofox-browser
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Create a tab
|
|
76
|
+
curl -X POST http://localhost:3000/tabs \
|
|
77
|
+
-d '{"userId": "agent1", "sessionKey": "task1", "url": "https://example.com"}'
|
|
78
|
+
|
|
79
|
+
# Get page snapshot with element refs
|
|
80
|
+
curl "http://localhost:3000/tabs/TAB_ID/snapshot?userId=agent1"
|
|
81
|
+
# → { "snapshot": "[button e1] Submit [link e2] Learn more", ... }
|
|
82
|
+
|
|
83
|
+
# Click by ref
|
|
84
|
+
curl -X POST http://localhost:3000/tabs/TAB_ID/click \
|
|
85
|
+
-d '{"userId": "agent1", "ref": "e1"}'
|
|
86
|
+
|
|
87
|
+
# Search with macros
|
|
88
|
+
curl -X POST http://localhost:3000/tabs/TAB_ID/navigate \
|
|
89
|
+
-d '{"userId": "agent1", "macro": "@google_search", "query": "best coffee beans"}'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## API
|
|
93
|
+
|
|
94
|
+
| Method | Endpoint | Description |
|
|
95
|
+
|--------|----------|-------------|
|
|
96
|
+
| `POST` | `/tabs` | Create tab |
|
|
97
|
+
| `GET` | `/tabs/:id/snapshot` | Accessibility snapshot with refs |
|
|
98
|
+
| `POST` | `/tabs/:id/click` | Click element by ref |
|
|
99
|
+
| `POST` | `/tabs/:id/type` | Type into element |
|
|
100
|
+
| `POST` | `/tabs/:id/navigate` | Navigate to URL or macro |
|
|
101
|
+
| `POST` | `/tabs/:id/scroll` | Scroll page |
|
|
102
|
+
| `GET` | `/tabs/:id/screenshot` | Screenshot |
|
|
103
|
+
| `GET` | `/tabs/:id/links` | All links on page |
|
|
104
|
+
| `POST` | `/tabs/:id/back` | Go back |
|
|
105
|
+
| `POST` | `/tabs/:id/forward` | Go forward |
|
|
106
|
+
| `POST` | `/tabs/:id/refresh` | Refresh |
|
|
107
|
+
| `DELETE` | `/tabs/:id` | Close tab |
|
|
108
|
+
| `GET` | `/tabs?userId=X` | List tabs |
|
|
109
|
+
| `DELETE` | `/sessions/:userId` | Close all user tabs |
|
|
110
|
+
| `GET` | `/health` | Health check |
|
|
111
|
+
|
|
112
|
+
## Search Macros
|
|
113
|
+
|
|
114
|
+
`@google_search` · `@youtube_search` · `@amazon_search` · `@reddit_search` · `@wikipedia_search` · `@twitter_search` · `@yelp_search` · `@spotify_search` · `@netflix_search` · `@linkedin_search` · `@instagram_search` · `@tiktok_search` · `@twitch_search`
|
|
115
|
+
|
|
116
|
+
## Architecture
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Browser Instance
|
|
120
|
+
└── User Session (BrowserContext) — isolated cookies/storage
|
|
121
|
+
├── Tab Group (sessionKey: "conv1")
|
|
122
|
+
│ ├── Tab (google.com)
|
|
123
|
+
│ └── Tab (github.com)
|
|
124
|
+
└── Tab Group (sessionKey: "conv2")
|
|
125
|
+
└── Tab (amazon.com)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Sessions auto-expire after 30 minutes of inactivity.
|
|
129
|
+
|
|
130
|
+
## Testing
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm test # e2e tests
|
|
134
|
+
npm run test:live # live Google tests
|
|
135
|
+
npm run test:debug # with server output
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Credits
|
|
139
|
+
|
|
140
|
+
- [Camoufox](https://camoufox.com) — Firefox-based browser with C++ anti-detection
|
|
141
|
+
- [Amp](https://ampcode.com) — AI coding agent
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
package/lib/macros.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const MACROS = {
|
|
2
|
+
'@google_search': (query) => `https://www.google.com/search?q=${encodeURIComponent(query || '')}`,
|
|
3
|
+
'@youtube_search': (query) => `https://www.youtube.com/results?search_query=${encodeURIComponent(query || '')}`,
|
|
4
|
+
'@amazon_search': (query) => `https://www.amazon.com/s?k=${encodeURIComponent(query || '')}`,
|
|
5
|
+
'@reddit_search': (query) => `https://www.reddit.com/search/?q=${encodeURIComponent(query || '')}`,
|
|
6
|
+
'@wikipedia_search': (query) => `https://en.wikipedia.org/wiki/Special:Search?search=${encodeURIComponent(query || '')}`,
|
|
7
|
+
'@twitter_search': (query) => `https://twitter.com/search?q=${encodeURIComponent(query || '')}`,
|
|
8
|
+
'@yelp_search': (query) => `https://www.yelp.com/search?find_desc=${encodeURIComponent(query || '')}`,
|
|
9
|
+
'@spotify_search': (query) => `https://open.spotify.com/search/${encodeURIComponent(query || '')}`,
|
|
10
|
+
'@netflix_search': (query) => `https://www.netflix.com/search?q=${encodeURIComponent(query || '')}`,
|
|
11
|
+
'@linkedin_search': (query) => `https://www.linkedin.com/search/results/all/?keywords=${encodeURIComponent(query || '')}`,
|
|
12
|
+
'@instagram_search': (query) => `https://www.instagram.com/explore/tags/${encodeURIComponent(query || '')}`,
|
|
13
|
+
'@tiktok_search': (query) => `https://www.tiktok.com/search?q=${encodeURIComponent(query || '')}`,
|
|
14
|
+
'@twitch_search': (query) => `https://www.twitch.tv/search?term=${encodeURIComponent(query || '')}`
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function expandMacro(macro, query) {
|
|
18
|
+
const macroFn = MACROS[macro];
|
|
19
|
+
return macroFn ? macroFn(query) : null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getSupportedMacros() {
|
|
23
|
+
return Object.keys(MACROS);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
expandMacro,
|
|
28
|
+
getSupportedMacros,
|
|
29
|
+
MACROS
|
|
30
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "camofox-browser",
|
|
3
|
+
"name": "Camoufox Browser",
|
|
4
|
+
"description": "Anti-detection browser automation for AI agents using Camoufox (Firefox-based)",
|
|
5
|
+
"version": "1.0.11",
|
|
6
|
+
"configSchema": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"url": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Camoufox browser server URL"
|
|
12
|
+
},
|
|
13
|
+
"port": {
|
|
14
|
+
"type": "number",
|
|
15
|
+
"description": "Server port (used if url not set)",
|
|
16
|
+
"default": 9377
|
|
17
|
+
},
|
|
18
|
+
"autoStart": {
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"description": "Auto-start the camofox-browser server with the Gateway",
|
|
21
|
+
"default": true
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"additionalProperties": false
|
|
25
|
+
},
|
|
26
|
+
"uiHints": {
|
|
27
|
+
"url": {
|
|
28
|
+
"label": "Server URL",
|
|
29
|
+
"placeholder": "http://localhost:9377"
|
|
30
|
+
},
|
|
31
|
+
"port": {
|
|
32
|
+
"label": "Server Port",
|
|
33
|
+
"placeholder": "9377"
|
|
34
|
+
},
|
|
35
|
+
"autoStart": {
|
|
36
|
+
"label": "Auto-start server with Gateway"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@askjo/camofox-browser",
|
|
3
|
+
"version": "1.0.12",
|
|
4
|
+
"description": "Headless browser automation server and OpenClaw plugin for AI agents - anti-detection, element refs, and session isolation",
|
|
5
|
+
"main": "server-camoufox.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Jo Inc <oss@askjo.ai>",
|
|
8
|
+
"homepage": "https://github.com/jo-inc/camofox-browser#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/jo-inc/camofox-browser.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/jo-inc/camofox-browser/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"browser",
|
|
18
|
+
"automation",
|
|
19
|
+
"headless",
|
|
20
|
+
"scraping",
|
|
21
|
+
"camofox",
|
|
22
|
+
"camoufox",
|
|
23
|
+
"anti-detection",
|
|
24
|
+
"ai-agent",
|
|
25
|
+
"openclaw",
|
|
26
|
+
"clawdbot",
|
|
27
|
+
"moltbot",
|
|
28
|
+
"playwright",
|
|
29
|
+
"firefox"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"server-camoufox.js",
|
|
36
|
+
"lib/",
|
|
37
|
+
"plugin.ts",
|
|
38
|
+
"openclaw.plugin.json",
|
|
39
|
+
"run-camoufox.sh",
|
|
40
|
+
"Dockerfile.camoufox",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"openclaw": {
|
|
45
|
+
"extensions": ["plugin.ts"]
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"start": "node server-camoufox.js",
|
|
49
|
+
"start:chrome": "node server.js",
|
|
50
|
+
"test": "jest --runInBand --forceExit",
|
|
51
|
+
"test:e2e": "jest --runInBand --forceExit tests/e2e",
|
|
52
|
+
"test:live": "RUN_LIVE_TESTS=1 jest --runInBand --forceExit tests/live",
|
|
53
|
+
"test:debug": "DEBUG_SERVER=1 jest --runInBand --forceExit",
|
|
54
|
+
"postinstall": "npx camoufox-js fetch || true"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"camoufox-js": "^0.8.5",
|
|
58
|
+
"dotenv": "^17.2.3",
|
|
59
|
+
"express": "^4.18.2",
|
|
60
|
+
"playwright": "^1.50.0",
|
|
61
|
+
"playwright-core": "^1.58.0",
|
|
62
|
+
"playwright-extra": "^4.3.6",
|
|
63
|
+
"puppeteer-extra-plugin-stealth": "^2.11.2"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"jest": "^29.7.0"
|
|
67
|
+
}
|
|
68
|
+
}
|