@askjo/camoufox-browser 1.0.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/.env.bak +4 -0
- package/.github/workflows/deploy.yml +21 -0
- package/AGENTS.md +153 -0
- package/Dockerfile.camoufox +59 -0
- package/LICENSE +21 -0
- package/README.md +234 -0
- package/SKILL.md +165 -0
- package/experimental/chromium/Dockerfile +35 -0
- package/experimental/chromium/README.md +47 -0
- package/experimental/chromium/run.sh +24 -0
- package/experimental/chromium/server.js +812 -0
- package/fly.toml +29 -0
- package/jest.config.js +41 -0
- package/lib/macros.js +30 -0
- package/openclaw.plugin.json +31 -0
- package/package.json +30 -0
- package/plugin.ts +312 -0
- package/run-camoufox.sh +37 -0
- package/server-camoufox.js +946 -0
- package/tests/e2e/concurrency.test.js +103 -0
- package/tests/e2e/formSubmission.test.js +129 -0
- package/tests/e2e/macroNavigation.test.js +92 -0
- package/tests/e2e/navigation.test.js +128 -0
- package/tests/e2e/scroll.test.js +81 -0
- package/tests/e2e/snapshotLinks.test.js +141 -0
- package/tests/e2e/tabLifecycle.test.js +149 -0
- package/tests/e2e/typingEnter.test.js +147 -0
- package/tests/helpers/client.js +222 -0
- package/tests/helpers/startJoBrowser.js +95 -0
- package/tests/helpers/testSite.js +238 -0
- package/tests/live/googleSearch.test.js +93 -0
- package/tests/live/macroExpansion.test.js +132 -0
- package/tests/unit/macros.test.js +123 -0
package/.env.bak
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Deploy to Fly.io
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
deploy:
|
|
11
|
+
name: Deploy to Fly.io
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
concurrency: deploy-group
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: superfly/flyctl-actions/setup-flyctl@master
|
|
18
|
+
|
|
19
|
+
- run: flyctl deploy --remote-only
|
|
20
|
+
env:
|
|
21
|
+
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# camoufox-browser Agent Guide
|
|
2
|
+
|
|
3
|
+
Headless browser automation server for AI agents. Run locally or deploy to any cloud provider.
|
|
4
|
+
|
|
5
|
+
## Quick Start for Agents
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install and start
|
|
9
|
+
npm install && npm start
|
|
10
|
+
# Server runs on http://localhost:3000
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Core Workflow
|
|
14
|
+
|
|
15
|
+
1. **Create a tab** → Get `tabId`
|
|
16
|
+
2. **Navigate** → Go to URL or use search macro
|
|
17
|
+
3. **Get snapshot** → Receive page content with element refs (`e1`, `e2`, etc.)
|
|
18
|
+
4. **Interact** → Click/type using refs
|
|
19
|
+
5. **Repeat** steps 3-4 as needed
|
|
20
|
+
|
|
21
|
+
## API Reference
|
|
22
|
+
|
|
23
|
+
### Create Tab
|
|
24
|
+
```bash
|
|
25
|
+
POST /tabs
|
|
26
|
+
{"userId": "agent1", "listItemId": "task1", "url": "https://example.com"}
|
|
27
|
+
```
|
|
28
|
+
Returns: `{"tabId": "abc123", "url": "...", "title": "..."}`
|
|
29
|
+
|
|
30
|
+
### Navigate
|
|
31
|
+
```bash
|
|
32
|
+
POST /tabs/:tabId/navigate
|
|
33
|
+
{"userId": "agent1", "url": "https://google.com"}
|
|
34
|
+
# Or use macro:
|
|
35
|
+
{"userId": "agent1", "macro": "@google_search", "query": "weather today"}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Get Snapshot
|
|
39
|
+
```bash
|
|
40
|
+
GET /tabs/:tabId/snapshot?userId=agent1
|
|
41
|
+
```
|
|
42
|
+
Returns accessibility tree with refs:
|
|
43
|
+
```
|
|
44
|
+
[heading] Example Domain
|
|
45
|
+
[paragraph] This domain is for use in examples.
|
|
46
|
+
[link e1] More information...
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Click Element
|
|
50
|
+
```bash
|
|
51
|
+
POST /tabs/:tabId/click
|
|
52
|
+
{"userId": "agent1", "ref": "e1"}
|
|
53
|
+
# Or CSS selector:
|
|
54
|
+
{"userId": "agent1", "selector": "button.submit"}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Type Text
|
|
58
|
+
```bash
|
|
59
|
+
POST /tabs/:tabId/type
|
|
60
|
+
{"userId": "agent1", "ref": "e2", "text": "hello world"}
|
|
61
|
+
# Add enter: {"userId": "agent1", "ref": "e2", "text": "search query", "pressEnter": true}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Scroll
|
|
65
|
+
```bash
|
|
66
|
+
POST /tabs/:tabId/scroll
|
|
67
|
+
{"userId": "agent1", "direction": "down", "amount": 500}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Navigation
|
|
71
|
+
```bash
|
|
72
|
+
POST /tabs/:tabId/back {"userId": "agent1"}
|
|
73
|
+
POST /tabs/:tabId/forward {"userId": "agent1"}
|
|
74
|
+
POST /tabs/:tabId/refresh {"userId": "agent1"}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Get Links
|
|
78
|
+
```bash
|
|
79
|
+
GET /tabs/:tabId/links?userId=agent1&limit=50
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Close Tab
|
|
83
|
+
```bash
|
|
84
|
+
DELETE /tabs/:tabId?userId=agent1
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Search Macros
|
|
88
|
+
|
|
89
|
+
Use these instead of constructing URLs:
|
|
90
|
+
|
|
91
|
+
| Macro | Site |
|
|
92
|
+
|-------|------|
|
|
93
|
+
| `@google_search` | Google |
|
|
94
|
+
| `@youtube_search` | YouTube |
|
|
95
|
+
| `@amazon_search` | Amazon |
|
|
96
|
+
| `@reddit_search` | Reddit |
|
|
97
|
+
| `@wikipedia_search` | Wikipedia |
|
|
98
|
+
| `@twitter_search` | Twitter/X |
|
|
99
|
+
| `@yelp_search` | Yelp |
|
|
100
|
+
| `@linkedin_search` | LinkedIn |
|
|
101
|
+
|
|
102
|
+
## Element Refs
|
|
103
|
+
|
|
104
|
+
Refs like `e1`, `e2` are stable identifiers for page elements:
|
|
105
|
+
|
|
106
|
+
1. Call `/snapshot` to get current refs
|
|
107
|
+
2. Use ref in `/click` or `/type`
|
|
108
|
+
3. Refs reset on navigation - get new snapshot after
|
|
109
|
+
|
|
110
|
+
## Session Management
|
|
111
|
+
|
|
112
|
+
- `userId` isolates cookies/storage between users
|
|
113
|
+
- `listItemId` groups tabs by conversation/task
|
|
114
|
+
- Sessions timeout after 30 minutes of inactivity
|
|
115
|
+
- Delete all user data: `DELETE /sessions/:userId`
|
|
116
|
+
|
|
117
|
+
## Running Engines
|
|
118
|
+
|
|
119
|
+
### Camoufox (Default - Recommended)
|
|
120
|
+
```bash
|
|
121
|
+
npm start
|
|
122
|
+
# Or: ./run-camoufox.sh
|
|
123
|
+
```
|
|
124
|
+
Firefox-based with anti-detection. Bypasses Google captcha.
|
|
125
|
+
|
|
126
|
+
### Chrome (Legacy)
|
|
127
|
+
```bash
|
|
128
|
+
npm run start:chrome
|
|
129
|
+
# Or: ./run.sh
|
|
130
|
+
```
|
|
131
|
+
Playwright + stealth plugin. May be blocked by Google.
|
|
132
|
+
|
|
133
|
+
## Testing
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npm test # E2E tests
|
|
137
|
+
npm run test:live # Live Google tests
|
|
138
|
+
npm run test:debug # With server output
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Docker
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
docker build -f Dockerfile.camoufox -t camoufox-browser .
|
|
145
|
+
docker run -p 3000:3000 camoufox-browser
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Key Files
|
|
149
|
+
|
|
150
|
+
- `server-camoufox.js` - Camoufox engine (default)
|
|
151
|
+
- `server.js` - Chrome engine (legacy)
|
|
152
|
+
- `Dockerfile.camoufox` - Production container
|
|
153
|
+
- `fly.toml` - Fly.io deployment config
|
|
@@ -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,234 @@
|
|
|
1
|
+
# camoufox-browser
|
|
2
|
+
|
|
3
|
+
A headless browser automation server designed for AI agents. Powered by [Camoufox](https://camoufox.com) - a Firefox-based browser with C++ anti-detection that bypasses bot detection including Google captcha.
|
|
4
|
+
|
|
5
|
+
This is the same browser engine that powers [askjo.ai](https://askjo.ai)'s web scraping capabilities.
|
|
6
|
+
|
|
7
|
+
**Perfect for:** [OpenClaw](https://openclaw.ai), Claude Code, LangChain agents, AutoGPT, and any AI system that needs to browse the web.
|
|
8
|
+
|
|
9
|
+
## Motivation
|
|
10
|
+
|
|
11
|
+
AI agents need to browse the web, but current solutions have significant limitations:
|
|
12
|
+
|
|
13
|
+
- **Playwright/Puppeteer + stealth plugins** get detected and blocked by Google, Cloudflare, and most anti-bot systems. JavaScript-level evasion isn't enough.
|
|
14
|
+
- **Headless Chrome** is trivially fingerprinted. Sites check for hundreds of browser characteristics that reveal automation.
|
|
15
|
+
- **API-based scrapers** (Browserless, ScrapingBee) work but cost money per request and add latency.
|
|
16
|
+
- **Raw HTTP requests** can't handle JavaScript-rendered content or complex interactions.
|
|
17
|
+
|
|
18
|
+
Camoufox solves this with **C++ level anti-detection** baked into the Firefox source code itself - not a plugin that can be detected, but fundamental changes to how the browser reports its fingerprint. This is the same approach used by commercial anti-detect browsers.
|
|
19
|
+
|
|
20
|
+
This project wraps Camoufox in a simple REST API optimized for AI agents: accessibility snapshots instead of bloated HTML, stable element refs for reliable clicking, and search macros for common sites.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- **Anti-Detection** - Camoufox engine (Firefox-based) with C++ fingerprint spoofing, bypasses Google captcha
|
|
25
|
+
- **Element Refs** - Stable `e1`, `e2`, `e3` references for clicking/typing
|
|
26
|
+
- **Session Isolation** - Separate cookies/storage per user, tabs grouped by conversation
|
|
27
|
+
- **Search Macros** - 13 built-in URL macros (`@google_search`, `@youtube_search`, etc.)
|
|
28
|
+
- **Token-Efficient** - Accessibility snapshots instead of full HTML (90% smaller)
|
|
29
|
+
- **Docker Ready** - Production Dockerfile with pre-baked Camoufox binary
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### OpenClaw Plugin
|
|
34
|
+
|
|
35
|
+
Install as an OpenClaw plugin for agent tool access:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
openclaw plugins install @jo-inc/camoufox-browser
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then start the server and configure:
|
|
42
|
+
|
|
43
|
+
```yaml
|
|
44
|
+
# ~/.openclaw/openclaw.json
|
|
45
|
+
plugins:
|
|
46
|
+
entries:
|
|
47
|
+
camoufox-browser:
|
|
48
|
+
enabled: true
|
|
49
|
+
config:
|
|
50
|
+
url: "http://localhost:9377"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The plugin provides these agent tools:
|
|
54
|
+
- `camoufox_create_tab` - Create a browser tab
|
|
55
|
+
- `camoufox_snapshot` - Get page snapshot with element refs
|
|
56
|
+
- `camoufox_click` - Click elements by ref
|
|
57
|
+
- `camoufox_type` - Type into elements
|
|
58
|
+
- `camoufox_navigate` - Navigate to URLs or use search macros
|
|
59
|
+
- `camoufox_scroll` - Scroll the page
|
|
60
|
+
- `camoufox_screenshot` - Take screenshots
|
|
61
|
+
- `camoufox_close_tab` - Close tabs
|
|
62
|
+
- `camoufox_list_tabs` - List open tabs
|
|
63
|
+
|
|
64
|
+
### Standalone Server
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Clone and install
|
|
68
|
+
git clone https://github.com/jo-inc/camoufox-browser
|
|
69
|
+
cd camoufox-browser
|
|
70
|
+
npm install
|
|
71
|
+
|
|
72
|
+
# Start server (downloads Camoufox browser on first run)
|
|
73
|
+
npm start # Default port 3000
|
|
74
|
+
./run-camoufox.sh -p 3001 # Custom port
|
|
75
|
+
|
|
76
|
+
# Server runs on http://localhost:3000
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API Overview
|
|
80
|
+
|
|
81
|
+
### Create a Tab
|
|
82
|
+
```bash
|
|
83
|
+
curl -X POST http://localhost:3000/tabs \
|
|
84
|
+
-H "Content-Type: application/json" \
|
|
85
|
+
-d '{"userId": "user1", "listItemId": "conv1", "url": "https://example.com"}'
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Get Page Snapshot (with element refs)
|
|
89
|
+
```bash
|
|
90
|
+
curl "http://localhost:3000/tabs/TAB_ID/snapshot?userId=user1"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Response includes refs for interaction:
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"snapshot": "[button e1] Submit [link e2] Learn more",
|
|
97
|
+
"refs": {
|
|
98
|
+
"e1": { "role": "button", "name": "Submit" },
|
|
99
|
+
"e2": { "role": "link", "name": "Learn more" }
|
|
100
|
+
},
|
|
101
|
+
"url": "https://example.com"
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Click an Element
|
|
106
|
+
```bash
|
|
107
|
+
curl -X POST http://localhost:3000/tabs/TAB_ID/click \
|
|
108
|
+
-H "Content-Type: application/json" \
|
|
109
|
+
-d '{"userId": "user1", "ref": "e1"}'
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Search with Macros
|
|
113
|
+
```bash
|
|
114
|
+
curl -X POST http://localhost:3000/tabs/TAB_ID/navigate \
|
|
115
|
+
-H "Content-Type: application/json" \
|
|
116
|
+
-d '{"userId": "user1", "macro": "@google_search", "query": "best coffee beans"}'
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## All Endpoints
|
|
120
|
+
|
|
121
|
+
| Method | Path | Description |
|
|
122
|
+
|--------|------|-------------|
|
|
123
|
+
| GET | `/health` | Health check |
|
|
124
|
+
| GET | `/tabs?userId=X` | List all tab groups for user |
|
|
125
|
+
| POST | `/tabs` | Create new tab |
|
|
126
|
+
| DELETE | `/tabs/:tabId` | Close tab |
|
|
127
|
+
| POST | `/tabs/:tabId/navigate` | Navigate to URL or macro |
|
|
128
|
+
| GET | `/tabs/:tabId/snapshot` | Get accessibility snapshot with refs |
|
|
129
|
+
| POST | `/tabs/:tabId/click` | Click element by ref or selector |
|
|
130
|
+
| POST | `/tabs/:tabId/type` | Type into element |
|
|
131
|
+
| POST | `/tabs/:tabId/scroll` | Scroll page |
|
|
132
|
+
| POST | `/tabs/:tabId/back` | Go back |
|
|
133
|
+
| POST | `/tabs/:tabId/forward` | Go forward |
|
|
134
|
+
| POST | `/tabs/:tabId/refresh` | Refresh page |
|
|
135
|
+
| GET | `/tabs/:tabId/links` | Get all links |
|
|
136
|
+
| GET | `/tabs/:tabId/screenshot` | Get screenshot |
|
|
137
|
+
| DELETE | `/sessions/:userId` | Close all tabs for user |
|
|
138
|
+
|
|
139
|
+
## Search Macros
|
|
140
|
+
|
|
141
|
+
Navigate supports these macros for common sites:
|
|
142
|
+
|
|
143
|
+
- `@google_search` - Google
|
|
144
|
+
- `@youtube_search` - YouTube
|
|
145
|
+
- `@amazon_search` - Amazon
|
|
146
|
+
- `@reddit_search` - Reddit
|
|
147
|
+
- `@wikipedia_search` - Wikipedia
|
|
148
|
+
- `@twitter_search` - Twitter/X
|
|
149
|
+
- `@yelp_search` - Yelp
|
|
150
|
+
- `@spotify_search` - Spotify
|
|
151
|
+
- `@netflix_search` - Netflix
|
|
152
|
+
- `@linkedin_search` - LinkedIn
|
|
153
|
+
- `@instagram_search` - Instagram
|
|
154
|
+
- `@tiktok_search` - TikTok
|
|
155
|
+
- `@twitch_search` - Twitch
|
|
156
|
+
|
|
157
|
+
## Architecture
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
Browser Instance
|
|
161
|
+
└── User Session (BrowserContext) - isolated cookies/storage
|
|
162
|
+
├── Tab Group (listItemId: "conv1") - conversation A
|
|
163
|
+
│ ├── Tab (google.com)
|
|
164
|
+
│ └── Tab (github.com)
|
|
165
|
+
└── Tab Group (listItemId: "conv2") - conversation B
|
|
166
|
+
└── Tab (amazon.com)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- One browser instance shared across users
|
|
170
|
+
- Separate BrowserContext per user (isolated cookies/storage)
|
|
171
|
+
- Tabs grouped by `listItemId` (conversation/task)
|
|
172
|
+
- 30-minute session timeout with automatic cleanup
|
|
173
|
+
|
|
174
|
+
## Running Locally
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Start with default port (3000)
|
|
178
|
+
./run-camoufox.sh
|
|
179
|
+
# Or: npm start
|
|
180
|
+
|
|
181
|
+
# Start with custom port
|
|
182
|
+
./run-camoufox.sh -p 3001
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
First run downloads the Camoufox browser (~300MB).
|
|
186
|
+
|
|
187
|
+
### Experimental
|
|
188
|
+
|
|
189
|
+
See [`experimental/`](experimental/) for alternative browser engines.
|
|
190
|
+
|
|
191
|
+
## Docker Deployment
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Build with Camoufox (recommended)
|
|
195
|
+
docker build -f Dockerfile.camoufox -t camoufox-browser .
|
|
196
|
+
|
|
197
|
+
# Run
|
|
198
|
+
docker run -p 3000:3000 camoufox-browser
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Fly.io Deployment
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
fly launch --no-deploy
|
|
205
|
+
fly deploy
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Testing
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
npm test # Run all e2e tests
|
|
212
|
+
npm run test:live # Run live Google tests (may hit captcha)
|
|
213
|
+
npm run test:debug # Show server output for debugging
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Environment Variables
|
|
217
|
+
|
|
218
|
+
| Variable | Description | Default |
|
|
219
|
+
|----------|-------------|---------|
|
|
220
|
+
| `PORT` | Server port | 3000 |
|
|
221
|
+
| `NODE_ENV` | Environment | development |
|
|
222
|
+
|
|
223
|
+
## Contributing
|
|
224
|
+
|
|
225
|
+
Contributions are welcome! Please open an issue or submit a PR.
|
|
226
|
+
|
|
227
|
+
## Credits
|
|
228
|
+
|
|
229
|
+
- [Camoufox](https://camoufox.com) - Firefox-based browser with C++ anti-detection
|
|
230
|
+
- [Amp](https://ampcode.com) - AI coding agent
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
MIT
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: camoufox-browser
|
|
3
|
+
description: Control a headless Camoufox browser with anti-detection. Browse websites, search Google/YouTube/Amazon, click elements, fill forms, and extract content.
|
|
4
|
+
globs:
|
|
5
|
+
requirements:
|
|
6
|
+
bins:
|
|
7
|
+
- node
|
|
8
|
+
- npm
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# camoufox-browser Skill
|
|
12
|
+
|
|
13
|
+
You can control a headless Camoufox browser running at `http://localhost:9377`. Camoufox is a Firefox-based browser with C++ anti-detection that bypasses bot detection including Google captcha.
|
|
14
|
+
|
|
15
|
+
This is the same browser engine that powers [askjo.ai](https://askjo.ai)'s web scraping.
|
|
16
|
+
|
|
17
|
+
## Install as OpenClaw Plugin
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Clone and install
|
|
21
|
+
git clone https://github.com/jo-inc/camoufox-browser ~/.openclaw/extensions/camoufox-browser
|
|
22
|
+
cd ~/.openclaw/extensions/camoufox-browser
|
|
23
|
+
npm install
|
|
24
|
+
|
|
25
|
+
# Start the server (port 9377)
|
|
26
|
+
./run-camoufox.sh -p 9377 &
|
|
27
|
+
|
|
28
|
+
# Enable the plugin
|
|
29
|
+
openclaw plugins enable camoufox-browser
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then add the tools to your agent allowlist:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
openclaw configure --section agents
|
|
36
|
+
# Add camoufox-browser to tools.allow
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start (Standalone)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cd {baseDir}
|
|
43
|
+
npm install
|
|
44
|
+
npm start
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The server runs on port 3000 by default. Use `./run-camoufox.sh -p 9377` for custom port.
|
|
48
|
+
|
|
49
|
+
Check health: `curl http://localhost:9377/health`
|
|
50
|
+
|
|
51
|
+
## How to Use
|
|
52
|
+
|
|
53
|
+
### 1. Create a Tab
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
curl -X POST http://localhost:9377/tabs \
|
|
57
|
+
-H "Content-Type: application/json" \
|
|
58
|
+
-d '{"userId": "openclaw", "listItemId": "task1", "url": "https://example.com"}'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Save the `tabId` from the response for subsequent requests.
|
|
62
|
+
|
|
63
|
+
### 2. Get Page Content
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
curl "http://localhost:9377/tabs/TAB_ID/snapshot?userId=openclaw"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This returns an accessibility snapshot with element refs like `e1`, `e2`, `e3`. These refs are stable identifiers you use to click or type into elements.
|
|
70
|
+
|
|
71
|
+
### 3. Click Elements
|
|
72
|
+
|
|
73
|
+
Use the ref from the snapshot:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
curl -X POST http://localhost:9377/tabs/TAB_ID/click \
|
|
77
|
+
-H "Content-Type: application/json" \
|
|
78
|
+
-d '{"userId": "openclaw", "ref": "e1"}'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 4. Type Text
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
curl -X POST http://localhost:9377/tabs/TAB_ID/type \
|
|
85
|
+
-H "Content-Type: application/json" \
|
|
86
|
+
-d '{"userId": "openclaw", "ref": "e2", "text": "hello world", "pressEnter": true}'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 5. Search with Macros
|
|
90
|
+
|
|
91
|
+
Instead of navigating to search URLs, use macros:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
curl -X POST http://localhost:9377/tabs/TAB_ID/navigate \
|
|
95
|
+
-H "Content-Type: application/json" \
|
|
96
|
+
-d '{"userId": "openclaw", "macro": "@google_search", "query": "weather in New York"}'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Available macros:
|
|
100
|
+
- `@google_search` - Search Google
|
|
101
|
+
- `@youtube_search` - Search YouTube
|
|
102
|
+
- `@amazon_search` - Search Amazon
|
|
103
|
+
- `@reddit_search` - Search Reddit
|
|
104
|
+
- `@wikipedia_search` - Search Wikipedia
|
|
105
|
+
- `@twitter_search` - Search Twitter/X
|
|
106
|
+
|
|
107
|
+
### 6. Scroll the Page
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
curl -X POST http://localhost:9377/tabs/TAB_ID/scroll \
|
|
111
|
+
-H "Content-Type: application/json" \
|
|
112
|
+
-d '{"userId": "openclaw", "direction": "down", "amount": 500}'
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 7. Navigate Back/Forward
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
curl -X POST http://localhost:9377/tabs/TAB_ID/back \
|
|
119
|
+
-H "Content-Type: application/json" \
|
|
120
|
+
-d '{"userId": "openclaw"}'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 8. Get All Links
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
curl "http://localhost:9377/tabs/TAB_ID/links?userId=openclaw&limit=50"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 9. Close Tab When Done
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
curl -X DELETE "http://localhost:9377/tabs/TAB_ID?userId=openclaw"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Workflow Pattern
|
|
136
|
+
|
|
137
|
+
1. Create tab with initial URL
|
|
138
|
+
2. Get snapshot to see page content and element refs
|
|
139
|
+
3. Click/type using refs from snapshot
|
|
140
|
+
4. Get new snapshot after each navigation (refs reset)
|
|
141
|
+
5. Repeat until task complete
|
|
142
|
+
6. Close tab
|
|
143
|
+
|
|
144
|
+
## Tips
|
|
145
|
+
|
|
146
|
+
- Always get a fresh snapshot after clicking links or submitting forms
|
|
147
|
+
- Use `pressEnter: true` when typing in search boxes
|
|
148
|
+
- Element refs (`e1`, `e2`) reset after navigation - always get new snapshot
|
|
149
|
+
- Use macros for searching instead of constructing URLs manually
|
|
150
|
+
- The browser has anti-detection so it works with Google, Amazon, etc.
|
|
151
|
+
|
|
152
|
+
## All Endpoints
|
|
153
|
+
|
|
154
|
+
| Method | Path | Description |
|
|
155
|
+
|--------|------|-------------|
|
|
156
|
+
| POST | `/tabs` | Create tab |
|
|
157
|
+
| GET | `/tabs/:id/snapshot` | Get page content with refs |
|
|
158
|
+
| POST | `/tabs/:id/navigate` | Go to URL or macro |
|
|
159
|
+
| POST | `/tabs/:id/click` | Click element |
|
|
160
|
+
| POST | `/tabs/:id/type` | Type text |
|
|
161
|
+
| POST | `/tabs/:id/scroll` | Scroll page |
|
|
162
|
+
| POST | `/tabs/:id/back` | Go back |
|
|
163
|
+
| POST | `/tabs/:id/forward` | Go forward |
|
|
164
|
+
| GET | `/tabs/:id/links` | Get all links |
|
|
165
|
+
| DELETE | `/tabs/:id` | Close tab |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
FROM debian:bookworm-slim
|
|
2
|
+
|
|
3
|
+
# Install Chromium and dependencies
|
|
4
|
+
RUN apt-get update && apt-get install -y \
|
|
5
|
+
chromium \
|
|
6
|
+
fonts-liberation \
|
|
7
|
+
fonts-noto-color-emoji \
|
|
8
|
+
libnss3 \
|
|
9
|
+
libatk-bridge2.0-0 \
|
|
10
|
+
libdrm2 \
|
|
11
|
+
libxkbcommon0 \
|
|
12
|
+
libgbm1 \
|
|
13
|
+
libasound2 \
|
|
14
|
+
libxshmfence1 \
|
|
15
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
16
|
+
|
|
17
|
+
# Install Node.js
|
|
18
|
+
RUN apt-get update && apt-get install -y \
|
|
19
|
+
nodejs \
|
|
20
|
+
npm \
|
|
21
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
22
|
+
|
|
23
|
+
WORKDIR /app
|
|
24
|
+
|
|
25
|
+
COPY package.json ./
|
|
26
|
+
RUN npm install --production
|
|
27
|
+
|
|
28
|
+
COPY server.js ./
|
|
29
|
+
|
|
30
|
+
ENV NODE_ENV=production
|
|
31
|
+
ENV PORT=3000
|
|
32
|
+
|
|
33
|
+
EXPOSE 3000
|
|
34
|
+
|
|
35
|
+
CMD ["node", "server.js"]
|