spectrawl 0.3.18 → 0.3.22
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 +14 -10
- package/package.json +2 -2
- package/src/server.js +59 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The unified web layer for AI agents. Search, browse, authenticate, and act on platforms — one package, self-hosted.
|
|
4
4
|
|
|
5
|
-
**5,000 free searches/month** via Gemini Grounded Search. Full page scraping, stealth browsing,
|
|
5
|
+
**5,000 free searches/month** via Gemini Grounded Search. Full page scraping, stealth browsing, 19 platform adapters.
|
|
6
6
|
|
|
7
7
|
## What It Does
|
|
8
8
|
|
|
@@ -55,7 +55,7 @@ Different tools for different needs.
|
|
|
55
55
|
| Returns | Snippets + AI answer | Full page content + snippets |
|
|
56
56
|
| Self-hosted | No | Yes |
|
|
57
57
|
| Stealth browsing | No | Yes (Camoufox + Playwright) |
|
|
58
|
-
| Platform posting | No |
|
|
58
|
+
| Platform posting | No | 19 adapters |
|
|
59
59
|
| Auth management | No | Cookie store + auto-refresh |
|
|
60
60
|
| Cached repeats | No | <1ms |
|
|
61
61
|
|
|
@@ -63,15 +63,16 @@ Different tools for different needs.
|
|
|
63
63
|
|
|
64
64
|
## Search
|
|
65
65
|
|
|
66
|
-
Default cascade: **Gemini Grounded →
|
|
66
|
+
Default cascade: **Gemini Grounded → Tavily → Brave**
|
|
67
67
|
|
|
68
68
|
Gemini Grounded Search gives you Google-quality results through the Gemini API. Free tier: 5,000 grounded queries/month.
|
|
69
69
|
|
|
70
70
|
| Engine | Free Tier | Key Required | Default |
|
|
71
71
|
|--------|-----------|-------------|---------|
|
|
72
72
|
| **Gemini Grounded** | 5,000/month | `GEMINI_API_KEY` | ✅ Primary |
|
|
73
|
-
|
|
|
74
|
-
|
|
|
73
|
+
| Tavily | 1,000/month | `TAVILY_API_KEY` | ✅ 1st fallback |
|
|
74
|
+
| Brave | 2,000/month | `BRAVE_API_KEY` | ✅ 2nd fallback |
|
|
75
|
+
| DuckDuckGo | Unlimited | None | Available |
|
|
75
76
|
| Bing | Unlimited | None | Available |
|
|
76
77
|
| Serper | 2,500 trial | `SERPER_API_KEY` | Available |
|
|
77
78
|
| Google CSE | 100/day | `GOOGLE_CSE_KEY` | Available |
|
|
@@ -123,9 +124,9 @@ const accounts = await web.auth.getStatus()
|
|
|
123
124
|
|
|
124
125
|
Cookie refresh cron fires `cookie_expiring` and `cookie_expired` events before accounts go stale.
|
|
125
126
|
|
|
126
|
-
## Act —
|
|
127
|
+
## Act — 19 Platform Adapters
|
|
127
128
|
|
|
128
|
-
Post to
|
|
129
|
+
Post to 19 platforms with one API:
|
|
129
130
|
|
|
130
131
|
```js
|
|
131
132
|
await web.act('github', 'create-issue', { repo: 'user/repo', title: 'Bug report', body: '...' })
|
|
@@ -134,7 +135,7 @@ await web.act('devto', 'post', { title: '...', body: '...', tags: ['ai'] })
|
|
|
134
135
|
await web.act('huggingface', 'create-repo', { name: 'my-model', type: 'model' })
|
|
135
136
|
```
|
|
136
137
|
|
|
137
|
-
**Live tested:** GitHub ✅, Reddit ✅, Dev.to ✅, HuggingFace ✅, X (reads) ✅
|
|
138
|
+
**Live tested:** GitHub ✅, Reddit ✅, Dev.to ✅, HuggingFace ✅, X (reads) ✅, Hashnode ✅, Discord ✅, Product Hunt ✅
|
|
138
139
|
|
|
139
140
|
| Platform | Auth Method | Actions |
|
|
140
141
|
|----------|-------------|---------|
|
|
@@ -153,7 +154,10 @@ await web.act('huggingface', 'create-repo', { name: 'my-model', type: 'model' })
|
|
|
153
154
|
| Quora | Browser automation | answer |
|
|
154
155
|
| HuggingFace | Hub API | repo, model card, upload |
|
|
155
156
|
| BetaList | REST API | submit |
|
|
156
|
-
|
|
|
157
|
+
| AlternativeTo | Cookie session | submit, claim |
|
|
158
|
+
| DevHunt | Supabase auth | submit, upvote |
|
|
159
|
+
| SaaSHub | Generic adapter | submit |
|
|
160
|
+
| **Generic Directory** | Configurable | submit |
|
|
157
161
|
|
|
158
162
|
Built-in rate limiting, content dedup (MD5, 24h window), and dead letter queue for retries.
|
|
159
163
|
|
|
@@ -216,7 +220,7 @@ npx spectrawl install-stealth # download Camoufox browser
|
|
|
216
220
|
```json
|
|
217
221
|
{
|
|
218
222
|
"search": {
|
|
219
|
-
"cascade": ["gemini-grounded", "
|
|
223
|
+
"cascade": ["gemini-grounded", "tavily", "brave"],
|
|
220
224
|
"scrapeTop": 5
|
|
221
225
|
},
|
|
222
226
|
"cache": {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spectrawl",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "The unified web layer for AI agents. Search (
|
|
3
|
+
"version": "0.3.22",
|
|
4
|
+
"description": "The unified web layer for AI agents. Search (8 engines), stealth browse, auth, act on 24 platforms. Self-hosted.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"bin": {
|
package/src/server.js
CHANGED
|
@@ -61,6 +61,65 @@ const server = http.createServer(async (req, res) => {
|
|
|
61
61
|
return json(res, result)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// Threads OAuth callback
|
|
65
|
+
if (req.method === 'GET' && path === '/auth/callback/threads') {
|
|
66
|
+
const code = url.searchParams.get('code')
|
|
67
|
+
const errParam = url.searchParams.get('error')
|
|
68
|
+
if (errParam) {
|
|
69
|
+
res.writeHead(200, { 'Content-Type': 'text/html' })
|
|
70
|
+
return res.end(`<h2>❌ Auth error: ${errParam}</h2>`)
|
|
71
|
+
}
|
|
72
|
+
if (!code) {
|
|
73
|
+
res.writeHead(400, { 'Content-Type': 'text/html' })
|
|
74
|
+
return res.end('<h2>❌ No code received</h2>')
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
// Exchange code for token
|
|
78
|
+
const fetch = require('node:https')
|
|
79
|
+
const params = new URLSearchParams({
|
|
80
|
+
client_id: '1574846783732558',
|
|
81
|
+
client_secret: 'f8589ca3523b0ea5bab3fac2c2ae4c15',
|
|
82
|
+
code,
|
|
83
|
+
grant_type: 'authorization_code',
|
|
84
|
+
redirect_uri: 'https://gateway.xanos.org/auth/callback/threads'
|
|
85
|
+
})
|
|
86
|
+
const tokenRes = await new Promise((resolve, reject) => {
|
|
87
|
+
const postData = params.toString()
|
|
88
|
+
const options = {
|
|
89
|
+
hostname: 'graph.threads.net',
|
|
90
|
+
path: '/oauth/access_token',
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: {
|
|
93
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
94
|
+
'Content-Length': Buffer.byteLength(postData)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const req2 = fetch.request(options, (r) => {
|
|
98
|
+
let data = ''
|
|
99
|
+
r.on('data', chunk => data += chunk)
|
|
100
|
+
r.on('end', () => resolve(JSON.parse(data)))
|
|
101
|
+
})
|
|
102
|
+
req2.on('error', reject)
|
|
103
|
+
req2.write(postData)
|
|
104
|
+
req2.end()
|
|
105
|
+
})
|
|
106
|
+
// Save to credentials
|
|
107
|
+
const fs = require('fs')
|
|
108
|
+
const credsPath = '/root/.openclaw/workspace-dijiclaw/.openclaw/credentials/threads-api.json'
|
|
109
|
+
const creds = JSON.parse(fs.readFileSync(credsPath, 'utf8'))
|
|
110
|
+
creds.user_token = tokenRes.access_token
|
|
111
|
+
creds.user_id = tokenRes.user_id
|
|
112
|
+
creds.token_type = tokenRes.token_type
|
|
113
|
+
creds.note = 'User token saved via OAuth callback'
|
|
114
|
+
fs.writeFileSync(credsPath, JSON.stringify(creds, null, 2))
|
|
115
|
+
res.writeHead(200, { 'Content-Type': 'text/html' })
|
|
116
|
+
return res.end('<h2>✅ Threads connected! You can close this tab.</h2>')
|
|
117
|
+
} catch (e) {
|
|
118
|
+
res.writeHead(500, { 'Content-Type': 'text/html' })
|
|
119
|
+
return res.end(`<h2>❌ Token exchange failed: ${e.message}</h2>`)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
64
123
|
return error(res, 404, 'Not found')
|
|
65
124
|
} catch (err) {
|
|
66
125
|
console.error('Server error:', err)
|