@getfoundry/unbrowse-openclaw 0.3.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 +243 -0
- package/clawdbot.plugin.json +58 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +523 -0
- package/hooks/auto-discover/HOOK.md +45 -0
- package/native/unbrowse-native.darwin-arm64.node +0 -0
- package/native/unbrowse-native.darwin-x64.node +0 -0
- package/native/unbrowse-native.linux-x64-gnu.node +0 -0
- package/native/unbrowse-native.win32-x64-msvc.node +0 -0
- package/openclaw.plugin.json +119 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Unbrowse
|
|
2
|
+
|
|
3
|
+
**100x faster web access for OpenClaw agents.**
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
AI agents like OpenClaw need to interact with websites. Today, they have two options:
|
|
8
|
+
|
|
9
|
+
1. **Official APIs** — Fast and reliable, but only ~1% of websites have them
|
|
10
|
+
2. **Browser automation** — Universal but painfully slow (5-60 seconds per action)
|
|
11
|
+
|
|
12
|
+
Browser automation means launching headless Chrome, waiting for pages to load, finding elements with fragile CSS selectors, clicking buttons, waiting for navigation, parsing DOM... all to get data that's already available as clean JSON in the network layer.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Browser Automation Reality
|
|
16
|
+
──────────────────────────
|
|
17
|
+
Launch browser 3-5s
|
|
18
|
+
Navigate to page 2-10s
|
|
19
|
+
Wait for load 1-5s
|
|
20
|
+
Find element 0.5-2s
|
|
21
|
+
Click/interact 0.5-1s
|
|
22
|
+
Wait for response 2-10s
|
|
23
|
+
Parse DOM 1-3s
|
|
24
|
+
──────────────────────────
|
|
25
|
+
Total: 10-40 seconds per action
|
|
26
|
+
Success rate: 70-85%
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## The Solution
|
|
30
|
+
|
|
31
|
+
Every website has internal APIs — the XHR/fetch calls their frontend makes to load data. These endpoints are undocumented but return clean JSON, handle auth properly, and are 50-100x faster than browser automation.
|
|
32
|
+
|
|
33
|
+
**Unbrowse captures these internal APIs and turns them into skills your agent can call directly.**
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Unbrowse
|
|
37
|
+
────────
|
|
38
|
+
Direct API call → JSON response
|
|
39
|
+
────────
|
|
40
|
+
Total: 200-500ms
|
|
41
|
+
Success rate: 95%+
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## How It Works
|
|
45
|
+
|
|
46
|
+
### 1. You browse normally
|
|
47
|
+
|
|
48
|
+
Login to Twitter, scroll your feed, like a post. Unbrowse captures every API call the frontend makes:
|
|
49
|
+
|
|
50
|
+
- `GET /2/timeline/home.json` — fetches your feed
|
|
51
|
+
- `POST /2/timeline/like.json` — likes a tweet
|
|
52
|
+
- Auth headers, cookies, rate limits — all recorded
|
|
53
|
+
|
|
54
|
+
### 2. Unbrowse generates a skill
|
|
55
|
+
|
|
56
|
+
From captured traffic, Unbrowse creates a callable skill:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// Generated automatically
|
|
60
|
+
twitter.getTimeline() // 200ms, returns JSON
|
|
61
|
+
twitter.likeTweet(id) // 150ms, returns status
|
|
62
|
+
twitter.postTweet(text) // 180ms, returns tweet
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 3. Your agent uses it forever
|
|
66
|
+
|
|
67
|
+
No browser. No waiting. No fragile selectors. Just direct API calls at network speed.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
71
|
+
│ BEFORE UNBROWSE │
|
|
72
|
+
│ │
|
|
73
|
+
│ Agent → Launch browser → Load page → Find element → │
|
|
74
|
+
│ Click → Wait → Parse DOM → Extract data │
|
|
75
|
+
│ │
|
|
76
|
+
│ Time: 30 seconds Success: 75% │
|
|
77
|
+
├─────────────────────────────────────────────────────────────┤
|
|
78
|
+
│ WITH UNBROWSE │
|
|
79
|
+
│ │
|
|
80
|
+
│ Agent → API call → JSON response │
|
|
81
|
+
│ │
|
|
82
|
+
│ Time: 300ms Success: 95%+ │
|
|
83
|
+
└─────────────────────────────────────────────────────────────┘
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## What is OpenClaw?
|
|
87
|
+
|
|
88
|
+
[OpenClaw](https://openclaw.ai) is an open-source AI assistant that runs locally on your devices. It connects to your messaging apps (WhatsApp, Telegram, Slack, Discord, iMessage) and acts as a proactive digital assistant — managing emails, updating calendars, running commands, and taking autonomous actions across your online life.
|
|
89
|
+
|
|
90
|
+
Unlike cloud-based assistants, OpenClaw:
|
|
91
|
+
- Runs on your hardware (your data stays local)
|
|
92
|
+
- Has persistent memory across sessions
|
|
93
|
+
- Can execute shell commands and scripts
|
|
94
|
+
- Extends via community-built skills and plugins
|
|
95
|
+
|
|
96
|
+
**Unbrowse is an OpenClaw extension that gives your agent fast, reliable web access without browser automation overhead.**
|
|
97
|
+
|
|
98
|
+
## Installation
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
openclaw plugins install @getfoundry/unbrowse-openclaw
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Also works on Clawdbot and Moltbot:
|
|
105
|
+
```bash
|
|
106
|
+
clawdbot plugins install @getfoundry/unbrowse-openclaw
|
|
107
|
+
moltbot plugins install @getfoundry/unbrowse-openclaw
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Quick Start
|
|
111
|
+
|
|
112
|
+
### Capture APIs
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Tell your agent to browse a site
|
|
116
|
+
"Browse twitter.com and capture the API"
|
|
117
|
+
|
|
118
|
+
# Or use the tool directly
|
|
119
|
+
unbrowse_capture url="twitter.com"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Generate a skill
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
unbrowse_generate_skill domain="twitter.com"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Use it
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Your agent can now call Twitter's internal API directly
|
|
132
|
+
unbrowse_replay skill="twitter" action="get_timeline"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Tools
|
|
136
|
+
|
|
137
|
+
### Capture & Generate
|
|
138
|
+
|
|
139
|
+
| Tool | Description |
|
|
140
|
+
|------|-------------|
|
|
141
|
+
| `unbrowse_browse` | Open URL with traffic capture |
|
|
142
|
+
| `unbrowse_capture` | Capture API traffic from domain |
|
|
143
|
+
| `unbrowse_generate_skill` | Generate skill from captured endpoints |
|
|
144
|
+
| `unbrowse_replay` | Execute API calls using skills |
|
|
145
|
+
|
|
146
|
+
### Auth & Sessions
|
|
147
|
+
|
|
148
|
+
| Tool | Description |
|
|
149
|
+
|------|-------------|
|
|
150
|
+
| `unbrowse_login` | Login and save session |
|
|
151
|
+
| `unbrowse_session` | Manage saved sessions |
|
|
152
|
+
| `unbrowse_cookies` | Export cookies for a domain |
|
|
153
|
+
|
|
154
|
+
### Marketplace
|
|
155
|
+
|
|
156
|
+
| Tool | Description |
|
|
157
|
+
|------|-------------|
|
|
158
|
+
| `unbrowse_search` | Find skills others have created |
|
|
159
|
+
| `unbrowse_install` | Install a skill (own it forever) |
|
|
160
|
+
| `unbrowse_publish` | Share your skills |
|
|
161
|
+
|
|
162
|
+
## Skill Marketplace
|
|
163
|
+
|
|
164
|
+
Don't want to capture APIs yourself? Browse skills others have already created.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Search for Twitter skills
|
|
168
|
+
unbrowse_search query="twitter"
|
|
169
|
+
|
|
170
|
+
# Install one (you own it forever)
|
|
171
|
+
unbrowse_install skill="twitter-timeline"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Publish Your Own
|
|
175
|
+
|
|
176
|
+
Share captured APIs with other agents:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Free
|
|
180
|
+
unbrowse_publish name="twitter-timeline"
|
|
181
|
+
|
|
182
|
+
# Paid ($2.50 USDC)
|
|
183
|
+
unbrowse_publish name="twitter-timeline" price="2.50"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Earnings:** 70% to creator, 30% platform. Instant payout via x402 protocol on Solana.
|
|
187
|
+
|
|
188
|
+
**Ownership model:** Buyers own skills forever. One purchase, unlimited use. No per-execution fees.
|
|
189
|
+
|
|
190
|
+
## Configuration
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"plugins": {
|
|
195
|
+
"entries": {
|
|
196
|
+
"unbrowse-openclaw": {
|
|
197
|
+
"enabled": true,
|
|
198
|
+
"config": {
|
|
199
|
+
"skillsOutputDir": "~/.openclaw/skills",
|
|
200
|
+
"autoDiscover": true,
|
|
201
|
+
"creatorWallet": "YOUR_SOLANA_ADDRESS"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
| Option | Default | Description |
|
|
210
|
+
|--------|---------|-------------|
|
|
211
|
+
| `skillsOutputDir` | `~/.openclaw/skills` | Where skills are saved |
|
|
212
|
+
| `autoDiscover` | `true` | Auto-generate skills while browsing |
|
|
213
|
+
| `creatorWallet` | - | Solana address for marketplace earnings |
|
|
214
|
+
| `credentialSource` | `none` | Password lookup: none/keychain/1password |
|
|
215
|
+
|
|
216
|
+
## Why Not Just Use Browser Automation?
|
|
217
|
+
|
|
218
|
+
| | Browser Automation | Unbrowse |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| **Speed** | 10-40 seconds | 200-500ms |
|
|
221
|
+
| **Reliability** | 70-85% (DOM changes break selectors) | 95%+ (APIs rarely change) |
|
|
222
|
+
| **Resource usage** | High (headless browser) | Minimal (HTTP calls) |
|
|
223
|
+
| **Auth handling** | Complex (cookies, sessions, CAPTCHAs) | Built-in (captured with traffic) |
|
|
224
|
+
| **Data format** | Parse from DOM | Clean JSON |
|
|
225
|
+
|
|
226
|
+
Browser automation has its place — when you need to interact with sites that truly require JavaScript rendering. But most "automation" is just fetching data that's already available via internal APIs.
|
|
227
|
+
|
|
228
|
+
## x402 Payments
|
|
229
|
+
|
|
230
|
+
Skill purchases use the [x402 protocol](https://x402.org) for machine-to-machine payments:
|
|
231
|
+
|
|
232
|
+
1. Agent requests skill download
|
|
233
|
+
2. Server returns HTTP 402 with payment requirements
|
|
234
|
+
3. Agent signs USDC transaction on Solana
|
|
235
|
+
4. Server verifies, returns skill
|
|
236
|
+
|
|
237
|
+
No intermediaries. Direct creator payment. Instant settlement.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
**Skip the browser. Call the API.**
|
|
242
|
+
|
|
243
|
+
*Built for [OpenClaw](https://openclaw.ai). Powered by [x402](https://x402.org).*
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "unbrowse-openclaw",
|
|
3
|
+
"uiHints": {
|
|
4
|
+
"skillsOutputDir": {
|
|
5
|
+
"label": "Skills Output Directory",
|
|
6
|
+
"help": "Where generated skills are saved",
|
|
7
|
+
"placeholder": "~/.openclaw/skills"
|
|
8
|
+
},
|
|
9
|
+
"browserPort": {
|
|
10
|
+
"label": "Browser Control Port",
|
|
11
|
+
"help": "Port for browser CDP control API (default: 18791)"
|
|
12
|
+
},
|
|
13
|
+
"browserUseApiKey": {
|
|
14
|
+
"label": "Browser Use API Key",
|
|
15
|
+
"help": "API key for stealth cloud browsers (browser-use.com). Enables unbrowse_stealth tool.",
|
|
16
|
+
"sensitive": true,
|
|
17
|
+
"placeholder": "bu_..."
|
|
18
|
+
},
|
|
19
|
+
"autoDiscover": {
|
|
20
|
+
"label": "Auto-Discover Skills",
|
|
21
|
+
"help": "Automatically generate skills when the agent browses APIs (default: true)"
|
|
22
|
+
},
|
|
23
|
+
"skillIndexUrl": {
|
|
24
|
+
"label": "Skill Index URL",
|
|
25
|
+
"help": "Cloud skill marketplace API URL",
|
|
26
|
+
"placeholder": "https://skills.unbrowse.ai"
|
|
27
|
+
},
|
|
28
|
+
"creatorWallet": {
|
|
29
|
+
"label": "Creator Wallet (Solana)",
|
|
30
|
+
"help": "Your Solana wallet address for receiving USDC when others download your skills",
|
|
31
|
+
"placeholder": "ABC123..."
|
|
32
|
+
},
|
|
33
|
+
"skillIndexSolanaPrivateKey": {
|
|
34
|
+
"label": "Payment Private Key (Solana)",
|
|
35
|
+
"help": "Base58-encoded Solana private key for paying x402 USDC when downloading skills from the index",
|
|
36
|
+
"sensitive": true,
|
|
37
|
+
"placeholder": "base58..."
|
|
38
|
+
},
|
|
39
|
+
"credentialSource": {
|
|
40
|
+
"label": "Credential Source (Login Passwords)",
|
|
41
|
+
"help": "Opt-in: where to look up login passwords for auto-login. 'keychain' = macOS Keychain, '1password' = 1Password CLI, 'vault' = local encrypted vault, 'none' = disabled (default)"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"configSchema": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"properties": {
|
|
48
|
+
"skillsOutputDir": { "type": "string" },
|
|
49
|
+
"browserPort": { "type": "number", "default": 18791 },
|
|
50
|
+
"browserUseApiKey": { "type": "string" },
|
|
51
|
+
"autoDiscover": { "type": "boolean", "default": true },
|
|
52
|
+
"skillIndexUrl": { "type": "string", "default": "https://skills.unbrowse.ai" },
|
|
53
|
+
"creatorWallet": { "type": "string" },
|
|
54
|
+
"skillIndexSolanaPrivateKey": { "type": "string" },
|
|
55
|
+
"credentialSource": { "type": "string", "enum": ["none", "auto", "keychain", "1password", "vault"], "default": "none" }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unbrowse — Reverse-engineer internal APIs from any website.
|
|
3
|
+
*
|
|
4
|
+
* Pure native Rust implementation. All functionality provided by unbrowse-native.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./native/index.js";
|
|
7
|
+
export default function unbrowsePlugin(api: any): {
|
|
8
|
+
name: string;
|
|
9
|
+
version: any;
|
|
10
|
+
native: any;
|
|
11
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unbrowse — Reverse-engineer internal APIs from any website.
|
|
3
|
+
*
|
|
4
|
+
* Pure native Rust implementation. All functionality provided by unbrowse-native.
|
|
5
|
+
*/
|
|
6
|
+
// @ts-nocheck - Native module types handled separately
|
|
7
|
+
import * as native from "./native/index.js";
|
|
8
|
+
// Re-export native module
|
|
9
|
+
export * from "./native/index.js";
|
|
10
|
+
export default function unbrowsePlugin(api) {
|
|
11
|
+
// =========================================================================
|
|
12
|
+
// Tool: unbrowse_capture
|
|
13
|
+
// =========================================================================
|
|
14
|
+
api.registerTool({
|
|
15
|
+
name: "unbrowse_capture",
|
|
16
|
+
description: `Capture internal API traffic from browser and generate a skill.
|
|
17
|
+
|
|
18
|
+
Visit URLs in the browser, capture all API calls, extract auth tokens, and generate a reusable skill package.
|
|
19
|
+
|
|
20
|
+
Returns: Skill with endpoints, auth method, and generated TypeScript client.`,
|
|
21
|
+
parameters: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
urls: {
|
|
25
|
+
type: "array",
|
|
26
|
+
items: { type: "string" },
|
|
27
|
+
description: "URLs to visit and capture API traffic from",
|
|
28
|
+
},
|
|
29
|
+
output_dir: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Optional output directory for skill files",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
required: ["urls"],
|
|
35
|
+
},
|
|
36
|
+
async execute(args) {
|
|
37
|
+
const apiData = await native.captureFromUrls(args.urls, undefined);
|
|
38
|
+
const result = native.generateSkill(apiData, args.output_dir, undefined);
|
|
39
|
+
return {
|
|
40
|
+
success: true,
|
|
41
|
+
service: result.service,
|
|
42
|
+
skill_dir: result.skillDir,
|
|
43
|
+
endpoints_count: result.endpointsCount,
|
|
44
|
+
auth_method: result.authMethod,
|
|
45
|
+
message: `Captured ${result.endpointsCount} endpoints from ${result.service}. Skill saved to ${result.skillDir}`,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
// =========================================================================
|
|
50
|
+
// Tool: unbrowse_replay
|
|
51
|
+
// =========================================================================
|
|
52
|
+
api.registerTool({
|
|
53
|
+
name: "unbrowse_replay",
|
|
54
|
+
description: `Call an internal API endpoint using captured auth.
|
|
55
|
+
|
|
56
|
+
Execute HTTP requests against internal APIs with proper authentication headers and cookies.
|
|
57
|
+
|
|
58
|
+
Returns: Response status, body, and timing.`,
|
|
59
|
+
parameters: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
service: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "Service name (skill name) to use for auth",
|
|
65
|
+
},
|
|
66
|
+
method: {
|
|
67
|
+
type: "string",
|
|
68
|
+
enum: ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
69
|
+
description: "HTTP method",
|
|
70
|
+
},
|
|
71
|
+
path: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "API path (e.g., /api/users)",
|
|
74
|
+
},
|
|
75
|
+
body: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "Request body (JSON string)",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ["service", "method", "path"],
|
|
81
|
+
},
|
|
82
|
+
async execute(args) {
|
|
83
|
+
const skillInfo = native.getSkillInfo(args.service);
|
|
84
|
+
if (!skillInfo) {
|
|
85
|
+
return { success: false, error: `Skill not found: ${args.service}` };
|
|
86
|
+
}
|
|
87
|
+
const vaultEntry = await native.vaultGet(args.service);
|
|
88
|
+
const authHeaders = vaultEntry?.headers || {};
|
|
89
|
+
const cookies = vaultEntry?.cookies || {};
|
|
90
|
+
const baseUrl = vaultEntry?.baseUrl || `https://${args.service}`;
|
|
91
|
+
const result = await native.testEndpoint(baseUrl, args.method, args.path, authHeaders, cookies, 30000);
|
|
92
|
+
return {
|
|
93
|
+
success: result.status >= 200 && result.status < 400,
|
|
94
|
+
status: result.status,
|
|
95
|
+
latency_ms: result.latencyMs,
|
|
96
|
+
response_shape: result.responseShape,
|
|
97
|
+
response_size: result.responseSize,
|
|
98
|
+
error: result.error,
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
// =========================================================================
|
|
103
|
+
// Tool: unbrowse_login
|
|
104
|
+
// =========================================================================
|
|
105
|
+
api.registerTool({
|
|
106
|
+
name: "unbrowse_login",
|
|
107
|
+
description: `Login to a website and capture session auth.
|
|
108
|
+
|
|
109
|
+
Navigates to login page, fills credentials, and captures resulting session cookies/tokens.
|
|
110
|
+
|
|
111
|
+
Returns: Captured auth headers and cookies.`,
|
|
112
|
+
parameters: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {
|
|
115
|
+
url: {
|
|
116
|
+
type: "string",
|
|
117
|
+
description: "Login page URL",
|
|
118
|
+
},
|
|
119
|
+
username: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "Username or email (optional - will lookup from keychain)",
|
|
122
|
+
},
|
|
123
|
+
password: {
|
|
124
|
+
type: "string",
|
|
125
|
+
description: "Password (optional - will lookup from keychain)",
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
required: ["url"],
|
|
129
|
+
},
|
|
130
|
+
async execute(args) {
|
|
131
|
+
const domain = new URL(args.url).hostname;
|
|
132
|
+
let username = args.username;
|
|
133
|
+
let password = args.password;
|
|
134
|
+
if (!username || !password) {
|
|
135
|
+
const creds = native.lookupCredentials(domain);
|
|
136
|
+
if (creds) {
|
|
137
|
+
username = username || creds.username;
|
|
138
|
+
password = password || creds.password;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!username || !password) {
|
|
142
|
+
return { success: false, error: "Credentials not provided and not found in keychain" };
|
|
143
|
+
}
|
|
144
|
+
await native.browserStart(undefined);
|
|
145
|
+
await native.browserNavigate(args.url, undefined);
|
|
146
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
147
|
+
const snapshot = await native.browserSnapshot(undefined);
|
|
148
|
+
for (const el of snapshot.elements) {
|
|
149
|
+
const elType = el.elementType?.toLowerCase() || "";
|
|
150
|
+
const elName = el.name?.toLowerCase() || "";
|
|
151
|
+
if (elType === "email" || elType === "text" || elName.includes("user") || elName.includes("email")) {
|
|
152
|
+
await native.browserAct("type", el.index, username, undefined);
|
|
153
|
+
}
|
|
154
|
+
if (elType === "password" || elName.includes("pass")) {
|
|
155
|
+
await native.browserAct("type", el.index, password, undefined);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
for (const el of snapshot.elements) {
|
|
159
|
+
const elType = el.elementType?.toLowerCase() || "";
|
|
160
|
+
const elText = el.text?.toLowerCase() || "";
|
|
161
|
+
if (elType === "submit" || elText.includes("sign in") || elText.includes("log in")) {
|
|
162
|
+
await native.browserAct("click", el.index, undefined, undefined);
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
167
|
+
const authJson = await native.extractBrowserAuth(domain, undefined);
|
|
168
|
+
await native.vaultStore(authJson.service, authJson.baseUrl, authJson.authMethod, authJson.headers || {}, authJson.cookies || {});
|
|
169
|
+
return {
|
|
170
|
+
success: true,
|
|
171
|
+
service: authJson.service,
|
|
172
|
+
auth_method: authJson.authMethod,
|
|
173
|
+
headers_count: Object.keys(authJson.headers || {}).length,
|
|
174
|
+
cookies_count: Object.keys(authJson.cookies || {}).length,
|
|
175
|
+
message: `Logged in and captured auth for ${authJson.service}`,
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
// =========================================================================
|
|
180
|
+
// Tool: unbrowse_learn
|
|
181
|
+
// =========================================================================
|
|
182
|
+
api.registerTool({
|
|
183
|
+
name: "unbrowse_learn",
|
|
184
|
+
description: `Parse a HAR file and generate an API skill.
|
|
185
|
+
|
|
186
|
+
Takes a HAR file (from browser DevTools export) and generates a complete skill package.
|
|
187
|
+
|
|
188
|
+
Returns: Generated skill with endpoints and auth.`,
|
|
189
|
+
parameters: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
har_path: {
|
|
193
|
+
type: "string",
|
|
194
|
+
description: "Path to HAR file",
|
|
195
|
+
},
|
|
196
|
+
seed_url: {
|
|
197
|
+
type: "string",
|
|
198
|
+
description: "Seed URL to determine service name",
|
|
199
|
+
},
|
|
200
|
+
output_dir: {
|
|
201
|
+
type: "string",
|
|
202
|
+
description: "Optional output directory",
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
required: ["har_path"],
|
|
206
|
+
},
|
|
207
|
+
async execute(args) {
|
|
208
|
+
const fs = await import("node:fs");
|
|
209
|
+
if (!fs.existsSync(args.har_path)) {
|
|
210
|
+
return { success: false, error: `HAR file not found: ${args.har_path}` };
|
|
211
|
+
}
|
|
212
|
+
const harJson = fs.readFileSync(args.har_path, "utf-8");
|
|
213
|
+
const apiData = native.parseHar(harJson, args.seed_url);
|
|
214
|
+
const result = native.generateSkill(apiData, args.output_dir, undefined);
|
|
215
|
+
await native.vaultStore(apiData.service, apiData.baseUrl, apiData.authMethod, apiData.authHeaders, apiData.cookies);
|
|
216
|
+
return {
|
|
217
|
+
success: true,
|
|
218
|
+
service: result.service,
|
|
219
|
+
skill_dir: result.skillDir,
|
|
220
|
+
endpoints_count: result.endpointsCount,
|
|
221
|
+
auth_method: result.authMethod,
|
|
222
|
+
};
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
// =========================================================================
|
|
226
|
+
// Tool: unbrowse_skills
|
|
227
|
+
// =========================================================================
|
|
228
|
+
api.registerTool({
|
|
229
|
+
name: "unbrowse_skills",
|
|
230
|
+
description: `List all captured API skills.
|
|
231
|
+
|
|
232
|
+
Shows locally learned skills with their endpoints and auth methods.`,
|
|
233
|
+
parameters: {
|
|
234
|
+
type: "object",
|
|
235
|
+
properties: {},
|
|
236
|
+
},
|
|
237
|
+
async execute() {
|
|
238
|
+
const skills = native.listSkills();
|
|
239
|
+
const details = skills.map((service) => {
|
|
240
|
+
const info = native.getSkillInfo(service);
|
|
241
|
+
return {
|
|
242
|
+
service,
|
|
243
|
+
name: info?.name,
|
|
244
|
+
endpoints: info?.endpointsCount || 0,
|
|
245
|
+
version: info?.version,
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
return { success: true, count: skills.length, skills: details };
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
// =========================================================================
|
|
252
|
+
// Tool: unbrowse_auth
|
|
253
|
+
// =========================================================================
|
|
254
|
+
api.registerTool({
|
|
255
|
+
name: "unbrowse_auth",
|
|
256
|
+
description: `Extract auth from current browser session.
|
|
257
|
+
|
|
258
|
+
Captures cookies, localStorage, and request headers from the browser.`,
|
|
259
|
+
parameters: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
domain: {
|
|
263
|
+
type: "string",
|
|
264
|
+
description: "Domain to extract auth for",
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
required: ["domain"],
|
|
268
|
+
},
|
|
269
|
+
async execute(args) {
|
|
270
|
+
const authJson = await native.extractBrowserAuth(args.domain, undefined);
|
|
271
|
+
await native.vaultStore(authJson.service, authJson.baseUrl, authJson.authMethod, authJson.headers || {}, authJson.cookies || {});
|
|
272
|
+
return {
|
|
273
|
+
success: true,
|
|
274
|
+
service: authJson.service,
|
|
275
|
+
auth_method: authJson.authMethod,
|
|
276
|
+
base_url: authJson.baseUrl,
|
|
277
|
+
headers: Object.keys(authJson.headers || {}),
|
|
278
|
+
cookies: Object.keys(authJson.cookies || {}),
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
// =========================================================================
|
|
283
|
+
// Tool: unbrowse_publish
|
|
284
|
+
// =========================================================================
|
|
285
|
+
api.registerTool({
|
|
286
|
+
name: "unbrowse_publish",
|
|
287
|
+
description: `Publish a skill to the marketplace.
|
|
288
|
+
|
|
289
|
+
Shares your API skill for others to use. Credentials are stripped before publishing.`,
|
|
290
|
+
parameters: {
|
|
291
|
+
type: "object",
|
|
292
|
+
properties: {
|
|
293
|
+
service: {
|
|
294
|
+
type: "string",
|
|
295
|
+
description: "Service name to publish",
|
|
296
|
+
},
|
|
297
|
+
description: {
|
|
298
|
+
type: "string",
|
|
299
|
+
description: "Description of the skill",
|
|
300
|
+
},
|
|
301
|
+
tags: {
|
|
302
|
+
type: "array",
|
|
303
|
+
items: { type: "string" },
|
|
304
|
+
description: "Tags for discoverability",
|
|
305
|
+
},
|
|
306
|
+
price_usdc: {
|
|
307
|
+
type: "number",
|
|
308
|
+
description: "Price in USDC (0 for free)",
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
required: ["service"],
|
|
312
|
+
},
|
|
313
|
+
async execute(args) {
|
|
314
|
+
const fs = await import("node:fs");
|
|
315
|
+
const path = await import("node:path");
|
|
316
|
+
const os = await import("node:os");
|
|
317
|
+
const skillDir = path.join(os.homedir(), ".openclaw", "skills", args.service);
|
|
318
|
+
const skillMdPath = path.join(skillDir, "SKILL.md");
|
|
319
|
+
const apiTsPath = path.join(skillDir, "scripts", "api.ts");
|
|
320
|
+
const authJsonPath = path.join(skillDir, "auth.json");
|
|
321
|
+
if (!fs.existsSync(skillMdPath)) {
|
|
322
|
+
return { success: false, error: `Skill not found: ${args.service}` };
|
|
323
|
+
}
|
|
324
|
+
const skillMd = fs.readFileSync(skillMdPath, "utf-8");
|
|
325
|
+
const apiTs = fs.existsSync(apiTsPath) ? fs.readFileSync(apiTsPath, "utf-8") : undefined;
|
|
326
|
+
const authJson = fs.existsSync(authJsonPath) ? fs.readFileSync(authJsonPath, "utf-8") : "{}";
|
|
327
|
+
const payload = native.prepareForPublish(skillMd, apiTs, authJson);
|
|
328
|
+
payload.description = args.description;
|
|
329
|
+
payload.tags = args.tags;
|
|
330
|
+
payload.priceUsdc = args.price_usdc;
|
|
331
|
+
const wallet = native.walletGetOrCreate();
|
|
332
|
+
const message = JSON.stringify({ service: args.service, timestamp: Date.now() });
|
|
333
|
+
const signature = native.walletSign(message);
|
|
334
|
+
const result = await native.marketplacePublish(payload, wallet.pubkey, signature, undefined);
|
|
335
|
+
return {
|
|
336
|
+
success: true,
|
|
337
|
+
id: result.id,
|
|
338
|
+
name: result.name,
|
|
339
|
+
service: result.service,
|
|
340
|
+
};
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
// =========================================================================
|
|
344
|
+
// Tool: unbrowse_search
|
|
345
|
+
// =========================================================================
|
|
346
|
+
api.registerTool({
|
|
347
|
+
name: "unbrowse_search",
|
|
348
|
+
description: `Search the skill marketplace.
|
|
349
|
+
|
|
350
|
+
Find API skills others have created and shared.`,
|
|
351
|
+
parameters: {
|
|
352
|
+
type: "object",
|
|
353
|
+
properties: {
|
|
354
|
+
query: {
|
|
355
|
+
type: "string",
|
|
356
|
+
description: "Search query",
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
required: ["query"],
|
|
360
|
+
},
|
|
361
|
+
async execute(args) {
|
|
362
|
+
const results = await native.marketplaceSearch(args.query, undefined);
|
|
363
|
+
return {
|
|
364
|
+
success: true,
|
|
365
|
+
count: results.length,
|
|
366
|
+
skills: results.map((s) => ({
|
|
367
|
+
id: s.id,
|
|
368
|
+
name: s.name,
|
|
369
|
+
service: s.service,
|
|
370
|
+
description: s.description,
|
|
371
|
+
author: s.author,
|
|
372
|
+
endpoints: s.endpointsCount,
|
|
373
|
+
installs: s.installs,
|
|
374
|
+
price_usdc: s.priceUsdc,
|
|
375
|
+
badge: s.badge,
|
|
376
|
+
})),
|
|
377
|
+
};
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
// =========================================================================
|
|
381
|
+
// Tool: unbrowse_download
|
|
382
|
+
// =========================================================================
|
|
383
|
+
api.registerTool({
|
|
384
|
+
name: "unbrowse_download",
|
|
385
|
+
description: `Download a skill from the marketplace.
|
|
386
|
+
|
|
387
|
+
Install a skill locally. May require x402 payment for paid skills.`,
|
|
388
|
+
parameters: {
|
|
389
|
+
type: "object",
|
|
390
|
+
properties: {
|
|
391
|
+
skill_id: {
|
|
392
|
+
type: "string",
|
|
393
|
+
description: "Skill ID to download",
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
required: ["skill_id"],
|
|
397
|
+
},
|
|
398
|
+
async execute(args) {
|
|
399
|
+
const fs = await import("node:fs");
|
|
400
|
+
const path = await import("node:path");
|
|
401
|
+
const os = await import("node:os");
|
|
402
|
+
const skillInfo = await native.marketplaceGetSkill(args.skill_id, undefined);
|
|
403
|
+
if (!skillInfo) {
|
|
404
|
+
return { success: false, error: `Skill not found: ${args.skill_id}` };
|
|
405
|
+
}
|
|
406
|
+
let paymentSig;
|
|
407
|
+
if (skillInfo.priceUsdc && skillInfo.priceUsdc > 0 && skillInfo.authorWallet) {
|
|
408
|
+
paymentSig = native.walletSignPayment(args.skill_id, skillInfo.priceUsdc, skillInfo.authorWallet);
|
|
409
|
+
}
|
|
410
|
+
const pkg = await native.marketplaceDownload(args.skill_id, paymentSig, undefined);
|
|
411
|
+
const skillDir = path.join(os.homedir(), ".openclaw", "skills", pkg.id);
|
|
412
|
+
fs.mkdirSync(path.join(skillDir, "scripts"), { recursive: true });
|
|
413
|
+
fs.mkdirSync(path.join(skillDir, "references"), { recursive: true });
|
|
414
|
+
fs.writeFileSync(path.join(skillDir, "SKILL.md"), pkg.skillMd);
|
|
415
|
+
if (pkg.apiTs) {
|
|
416
|
+
fs.writeFileSync(path.join(skillDir, "scripts", "api.ts"), pkg.apiTs);
|
|
417
|
+
}
|
|
418
|
+
if (pkg.referenceMd) {
|
|
419
|
+
fs.writeFileSync(path.join(skillDir, "references", "REFERENCE.md"), pkg.referenceMd);
|
|
420
|
+
}
|
|
421
|
+
await native.marketplaceTrackInstall(args.skill_id, undefined);
|
|
422
|
+
return {
|
|
423
|
+
success: true,
|
|
424
|
+
id: pkg.id,
|
|
425
|
+
skill_dir: skillDir,
|
|
426
|
+
endpoints: pkg.endpoints.length,
|
|
427
|
+
auth_method: pkg.authMethod,
|
|
428
|
+
};
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
// =========================================================================
|
|
432
|
+
// Tool: unbrowse_wallet
|
|
433
|
+
// =========================================================================
|
|
434
|
+
api.registerTool({
|
|
435
|
+
name: "unbrowse_wallet",
|
|
436
|
+
description: `Manage your marketplace wallet.
|
|
437
|
+
|
|
438
|
+
Create or view your Ed25519 wallet for x402 payments.`,
|
|
439
|
+
parameters: {
|
|
440
|
+
type: "object",
|
|
441
|
+
properties: {
|
|
442
|
+
action: {
|
|
443
|
+
type: "string",
|
|
444
|
+
enum: ["get", "create"],
|
|
445
|
+
description: "Action to perform",
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
required: ["action"],
|
|
449
|
+
},
|
|
450
|
+
async execute(args) {
|
|
451
|
+
if (args.action === "create") {
|
|
452
|
+
const existing = native.walletGet();
|
|
453
|
+
if (existing) {
|
|
454
|
+
return { success: true, pubkey: existing.pubkey, created_at: existing.createdAt, message: "Wallet already exists" };
|
|
455
|
+
}
|
|
456
|
+
const wallet = native.walletCreate();
|
|
457
|
+
return { success: true, pubkey: wallet.pubkey, created_at: wallet.createdAt, message: "Created new wallet" };
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
const wallet = native.walletGet();
|
|
461
|
+
if (!wallet) {
|
|
462
|
+
return { success: false, error: "No wallet found. Use action: create" };
|
|
463
|
+
}
|
|
464
|
+
return { success: true, pubkey: wallet.pubkey, created_at: wallet.createdAt };
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
// =========================================================================
|
|
469
|
+
// Tool: unbrowse_record
|
|
470
|
+
// =========================================================================
|
|
471
|
+
api.registerTool({
|
|
472
|
+
name: "unbrowse_record",
|
|
473
|
+
description: `Record a workflow session.
|
|
474
|
+
|
|
475
|
+
Start/stop recording browser interactions to learn multi-step workflows.`,
|
|
476
|
+
parameters: {
|
|
477
|
+
type: "object",
|
|
478
|
+
properties: {
|
|
479
|
+
action: {
|
|
480
|
+
type: "string",
|
|
481
|
+
enum: ["start", "stop", "status"],
|
|
482
|
+
description: "Recording action",
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
required: ["action"],
|
|
486
|
+
},
|
|
487
|
+
async execute(args) {
|
|
488
|
+
if (args.action === "start") {
|
|
489
|
+
const id = native.recordingStart();
|
|
490
|
+
return { success: true, session_id: id, message: "Recording started" };
|
|
491
|
+
}
|
|
492
|
+
else if (args.action === "stop") {
|
|
493
|
+
const session = native.recordingStop();
|
|
494
|
+
if (!session) {
|
|
495
|
+
return { success: false, error: "No active recording" };
|
|
496
|
+
}
|
|
497
|
+
const workflow = native.workflowLearn(session);
|
|
498
|
+
return {
|
|
499
|
+
success: true,
|
|
500
|
+
session_id: session.id,
|
|
501
|
+
steps: session.steps.length,
|
|
502
|
+
domains: session.domains,
|
|
503
|
+
workflow_id: workflow.id,
|
|
504
|
+
workflow_name: workflow.name,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
const isActive = native.recordingIsActive();
|
|
509
|
+
const current = native.recordingCurrent();
|
|
510
|
+
return {
|
|
511
|
+
success: true,
|
|
512
|
+
is_active: isActive,
|
|
513
|
+
session: current ? { id: current.id, steps: current.steps.length, domains: current.domains } : null,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
return {
|
|
519
|
+
name: "unbrowse",
|
|
520
|
+
version: native.getVersion(),
|
|
521
|
+
native: native.isNative(),
|
|
522
|
+
};
|
|
523
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unbrowse-auto-discover
|
|
3
|
+
description: Auto-generates skills when the agent browses APIs
|
|
4
|
+
metadata:
|
|
5
|
+
openclaw:
|
|
6
|
+
emoji: "🔍"
|
|
7
|
+
events: ["tool_result_persist"]
|
|
8
|
+
export: "default"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Auto-Discover Hook
|
|
12
|
+
|
|
13
|
+
Watches browser tool usage and automatically generates skills when API traffic is detected.
|
|
14
|
+
|
|
15
|
+
## How It Works
|
|
16
|
+
|
|
17
|
+
1. Listens for `tool_result_persist` events from the `browser` and `browse` tools
|
|
18
|
+
2. Polls captured network requests from the browser control API
|
|
19
|
+
3. When 5+ API calls to a new domain are detected, auto-generates a skill
|
|
20
|
+
4. Skills are saved to `~/.openclaw/skills/<service>/`
|
|
21
|
+
|
|
22
|
+
## Configuration
|
|
23
|
+
|
|
24
|
+
Enable/disable in `~/.openclaw/openclaw.json`:
|
|
25
|
+
|
|
26
|
+
```json5
|
|
27
|
+
{
|
|
28
|
+
plugins: {
|
|
29
|
+
entries: {
|
|
30
|
+
unbrowse: {
|
|
31
|
+
config: {
|
|
32
|
+
autoDiscover: true // default: true
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Generated Output
|
|
41
|
+
|
|
42
|
+
For each discovered API:
|
|
43
|
+
- `SKILL.md` — OpenClaw-compatible skill definition
|
|
44
|
+
- `auth.json` — Extracted authentication (headers, cookies)
|
|
45
|
+
- `scripts/api.ts` — TypeScript API client
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "unbrowse-openclaw",
|
|
3
|
+
"name": "Unbrowse",
|
|
4
|
+
"description": "API reverse engineering for OpenClaw. Capture any API and turn it into monetizable skills for AI agents.",
|
|
5
|
+
"version": "0.2.1",
|
|
6
|
+
"repository": "github:lekt9/unbrowse-openclaw",
|
|
7
|
+
"configSchema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"additionalProperties": false,
|
|
10
|
+
"properties": {
|
|
11
|
+
"skillsOutputDir": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "Directory where generated skills are saved"
|
|
14
|
+
},
|
|
15
|
+
"browserPort": {
|
|
16
|
+
"type": "number",
|
|
17
|
+
"default": 18791,
|
|
18
|
+
"description": "Port for browser CDP control API"
|
|
19
|
+
},
|
|
20
|
+
"autoDiscover": {
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"default": true,
|
|
23
|
+
"description": "Automatically generate skills when the agent browses APIs"
|
|
24
|
+
},
|
|
25
|
+
"skillIndexUrl": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"default": "https://index.unbrowse.ai",
|
|
28
|
+
"description": "Cloud skill marketplace API URL"
|
|
29
|
+
},
|
|
30
|
+
"marketplace": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"properties": {
|
|
33
|
+
"creatorWallet": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Solana wallet address for receiving USDC payments"
|
|
36
|
+
},
|
|
37
|
+
"solanaPrivateKey": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "Base58-encoded Solana private key for x402 payments"
|
|
40
|
+
},
|
|
41
|
+
"defaultPrice": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"default": "0",
|
|
44
|
+
"description": "Default price for published skills (0 = free, or $0.10-$100 USDC)"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"browser": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"properties": {
|
|
51
|
+
"useApiKey": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Browser Use API key for stealth cloud browsers"
|
|
54
|
+
},
|
|
55
|
+
"proxyCountry": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"default": "us",
|
|
58
|
+
"description": "Default proxy country for stealth browser (us, gb, de, etc.)"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"credentialSource": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"enum": ["none", "auto", "keychain", "1password", "vault"],
|
|
65
|
+
"default": "none",
|
|
66
|
+
"description": "Where to look up login passwords for auto-login"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"uiHints": {
|
|
71
|
+
"skillsOutputDir": {
|
|
72
|
+
"label": "Skills Output Directory",
|
|
73
|
+
"help": "Where generated skills are saved locally",
|
|
74
|
+
"placeholder": "~/.openclaw/skills"
|
|
75
|
+
},
|
|
76
|
+
"browserPort": {
|
|
77
|
+
"label": "Browser Control Port",
|
|
78
|
+
"help": "Port for browser CDP control API (default: 18791)"
|
|
79
|
+
},
|
|
80
|
+
"autoDiscover": {
|
|
81
|
+
"label": "Auto-Discover Skills",
|
|
82
|
+
"help": "Automatically generate skills when browsing APIs"
|
|
83
|
+
},
|
|
84
|
+
"skillIndexUrl": {
|
|
85
|
+
"label": "Skill Marketplace URL",
|
|
86
|
+
"help": "Cloud skill marketplace API endpoint"
|
|
87
|
+
},
|
|
88
|
+
"marketplace.creatorWallet": {
|
|
89
|
+
"label": "Creator Wallet (Solana)",
|
|
90
|
+
"help": "Your Solana wallet address for receiving USDC when others download your skills",
|
|
91
|
+
"placeholder": "ABC123..."
|
|
92
|
+
},
|
|
93
|
+
"marketplace.solanaPrivateKey": {
|
|
94
|
+
"label": "Payment Private Key",
|
|
95
|
+
"help": "Base58-encoded Solana private key for paying x402 USDC when downloading paid skills",
|
|
96
|
+
"sensitive": true
|
|
97
|
+
},
|
|
98
|
+
"marketplace.defaultPrice": {
|
|
99
|
+
"label": "Default Skill Price",
|
|
100
|
+
"help": "Default price when publishing skills (0 = free)",
|
|
101
|
+
"placeholder": "0"
|
|
102
|
+
},
|
|
103
|
+
"browser.useApiKey": {
|
|
104
|
+
"label": "Browser Use API Key",
|
|
105
|
+
"help": "API key for stealth cloud browsers (browser-use.com)",
|
|
106
|
+
"sensitive": true,
|
|
107
|
+
"placeholder": "bu_..."
|
|
108
|
+
},
|
|
109
|
+
"browser.proxyCountry": {
|
|
110
|
+
"label": "Proxy Country",
|
|
111
|
+
"help": "Default proxy location for stealth browser",
|
|
112
|
+
"placeholder": "us"
|
|
113
|
+
},
|
|
114
|
+
"credentialSource": {
|
|
115
|
+
"label": "Credential Source",
|
|
116
|
+
"help": "Where to look up login passwords. 'keychain' = macOS Keychain, '1password' = 1Password CLI"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@getfoundry/unbrowse-openclaw",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"description": "API reverse engineering for OpenClaw. Capture any API and turn it into monetizable skills for AI agents.",
|
|
8
|
+
"license": "UNLICENSED",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/lekt9/unbrowse-openclaw"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"openclaw",
|
|
15
|
+
"clawdbot",
|
|
16
|
+
"ai-agent",
|
|
17
|
+
"api-reverse-engineering",
|
|
18
|
+
"browser-automation",
|
|
19
|
+
"skill-marketplace",
|
|
20
|
+
"x402",
|
|
21
|
+
"solana"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
"dist/index.js",
|
|
25
|
+
"dist/index.d.ts",
|
|
26
|
+
"native/index.js",
|
|
27
|
+
"native/index.d.ts",
|
|
28
|
+
"native/unbrowse-native.darwin-arm64.node",
|
|
29
|
+
"native/unbrowse-native.darwin-x64.node",
|
|
30
|
+
"native/unbrowse-native.linux-x64-gnu.node",
|
|
31
|
+
"native/unbrowse-native.win32-x64-msvc.node",
|
|
32
|
+
"hooks/",
|
|
33
|
+
"openclaw.plugin.json",
|
|
34
|
+
"clawdbot.plugin.json"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"openclaw": {
|
|
41
|
+
"extensions": ["./dist/index.js"],
|
|
42
|
+
"hooks": ["./hooks/auto-discover"]
|
|
43
|
+
},
|
|
44
|
+
"clawdbot": {
|
|
45
|
+
"extensions": ["./dist/index.js"],
|
|
46
|
+
"hooks": ["./hooks/auto-discover"]
|
|
47
|
+
},
|
|
48
|
+
"moltbot": {
|
|
49
|
+
"extensions": ["./dist/index.js"],
|
|
50
|
+
"hooks": ["./hooks/auto-discover"]
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"openclaw": "*",
|
|
55
|
+
"clawdbot": "*",
|
|
56
|
+
"moltbot": "*"
|
|
57
|
+
},
|
|
58
|
+
"peerDependenciesMeta": {
|
|
59
|
+
"openclaw": { "optional": true },
|
|
60
|
+
"clawdbot": { "optional": true },
|
|
61
|
+
"moltbot": { "optional": true }
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/node": "^22.0.0",
|
|
65
|
+
"typescript": "^5.7.0"
|
|
66
|
+
}
|
|
67
|
+
}
|