beast-agent 0.27.0 → 0.29.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/package.json +1 -1
- package/src/agent/bots.js +8 -0
- package/src/agent/engine.js +96 -55
- package/src/agent/llm.js +153 -71
- package/src/agent/obscura.js +276 -0
- package/src/agent/skills.js +14 -10
- package/src/agent/tools.js +105 -51
- package/src/main.js +117 -30
- package/src/preload.js +6 -3
- package/src/renderer/i18n.js +72 -26
- package/src/renderer/index.html +1 -0
- package/src/renderer/renderer.js +177 -41
- package/src/renderer/style.css +38 -1
- package/tests/bg-jobs.test.js +71 -3
- package/tests/bots-name.test.js +42 -0
- package/tests/llm.test.js +70 -1
- package/tests/obscura.test.js +124 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* ---------- Obscura entegrasyonu ----------
|
|
4
|
+
Obscura (github.com/h4ckf0r0day/obscura): Rust tabanlı stealth headless
|
|
5
|
+
tarayıcı — anti-detect + V8 JS + gerçek rendering, Chromium'suz.
|
|
6
|
+
Beast kullanımı:
|
|
7
|
+
1) Kurulum: releases'taki Windows stealth zip'i %APPDATA%\beast\obscura'ya
|
|
8
|
+
açılır (obscura.exe + obscura-worker.exe). İlk açılışta OTOMATİK kurulur.
|
|
9
|
+
2) Arama: `obscura fetch https://html.duckduckgo.com/html/?q=...` çıktısı
|
|
10
|
+
DDG sonuç ayrıştırıcısıyla results'a döner — web_search zincirinin
|
|
11
|
+
sıralı bir motoru (Ayarlar → Web Arama'dan sıra değiştirilebilir). */
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const https = require('https');
|
|
17
|
+
const { execFile, spawn } = require('child_process');
|
|
18
|
+
|
|
19
|
+
const RELEASE_BASE = 'https://github.com/h4ckf0r0day/obscura/releases/latest/download/';
|
|
20
|
+
const WINDOWS_ZIP = 'obscura-x86_64-windows-stealth.zip';
|
|
21
|
+
const MAX_ZIP_BYTES = 250 * 1024 * 1024;
|
|
22
|
+
const FETCH_TIMEOUT_MS = 25000;
|
|
23
|
+
/* content-length gelmezse yüzde tahmini için yaklaşık boyut (stealth zip ≈74 MB) */
|
|
24
|
+
const EST_ZIP_BYTES = 74 * 1024 * 1024;
|
|
25
|
+
|
|
26
|
+
function obscuraDir() {
|
|
27
|
+
const base = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
28
|
+
return path.join(base, 'beast', 'obscura');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function obscuraExe() {
|
|
32
|
+
return path.join(obscuraDir(), 'obscura.exe');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function obscuraInstalled() {
|
|
36
|
+
try { return fs.existsSync(obscuraExe()); } catch { return false; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* kurulan varyant işareti: stealth zip'te --stealth bayrağı geçerli */
|
|
40
|
+
function markVariant(v) {
|
|
41
|
+
try { fs.writeFileSync(path.join(obscuraDir(), 'variant.txt'), String(v || 'stealth') + '\n', 'utf8'); } catch {}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readVariant() {
|
|
45
|
+
try { return String(fs.readFileSync(path.join(obscuraDir(), 'variant.txt'), 'utf8')).trim() || 'stealth'; } catch { return 'stealth'; }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* bina sağ mı: --help 10 sn içinde cevap veriyor mu (oturum başına tek deneme) */
|
|
49
|
+
let _okProbe = null;
|
|
50
|
+
function obscuraOk() {
|
|
51
|
+
if (!obscuraInstalled()) return Promise.resolve(false);
|
|
52
|
+
if (_okProbe) return _okProbe;
|
|
53
|
+
_okProbe = new Promise((resolve) => {
|
|
54
|
+
try {
|
|
55
|
+
execFile(obscuraExe(), ['--help'], { timeout: 10000, windowsHide: true }, (err) => resolve(!err));
|
|
56
|
+
} catch {
|
|
57
|
+
resolve(false);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return _okProbe;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function httpsDownload(url, redirectsLeft = 5, signal, onProgress) {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
const req = https.get(url, { headers: { 'User-Agent': 'BeastAgent/1.0 (+obscura bootstrap)' } }, (res) => {
|
|
66
|
+
if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location && redirectsLeft > 0) {
|
|
67
|
+
res.resume();
|
|
68
|
+
return resolve(httpsDownload(new URL(res.headers.location, url).toString(), redirectsLeft - 1, signal, onProgress));
|
|
69
|
+
}
|
|
70
|
+
if (res.statusCode !== 200) {
|
|
71
|
+
res.resume();
|
|
72
|
+
return reject(new Error(`indirme başarısız: HTTP ${res.statusCode}`));
|
|
73
|
+
}
|
|
74
|
+
const total = Number(res.headers['content-length']) || EST_ZIP_BYTES;
|
|
75
|
+
const chunks = [];
|
|
76
|
+
let size = 0;
|
|
77
|
+
let lastPct = -1;
|
|
78
|
+
const tick = () => {
|
|
79
|
+
/* yüzdeyi INDIRME fazının %1-88 aralığına map et; %1'lik adımda bildir */
|
|
80
|
+
const pct = 1 + Math.min(88, Math.floor((size / total) * 88));
|
|
81
|
+
if (typeof onProgress === 'function' && pct !== lastPct) {
|
|
82
|
+
lastPct = pct;
|
|
83
|
+
try { onProgress({ pct, phase: 'indiriliyor', loaded: size, total }); } catch {}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
res.on('data', (c) => {
|
|
87
|
+
size += c.length;
|
|
88
|
+
if (size > MAX_ZIP_BYTES) {
|
|
89
|
+
req.destroy(new Error('dosya çok büyük'));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
chunks.push(c);
|
|
93
|
+
tick();
|
|
94
|
+
});
|
|
95
|
+
res.on('end', () => {
|
|
96
|
+
tick();
|
|
97
|
+
resolve(Buffer.concat(chunks));
|
|
98
|
+
});
|
|
99
|
+
res.on('error', reject);
|
|
100
|
+
});
|
|
101
|
+
req.on('error', reject);
|
|
102
|
+
if (signal) {
|
|
103
|
+
if (signal.aborted) return reject(new Error('iptal'));
|
|
104
|
+
signal.addEventListener('abort', () => req.destroy(new Error('iptal')), { once: true });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function psExpand(zipPath, destDir) {
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
const cmd = `Expand-Archive -LiteralPath "${zipPath}" -DestinationPath "${destDir}" -Force`;
|
|
112
|
+
const child = spawn(
|
|
113
|
+
'powershell.exe',
|
|
114
|
+
['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', cmd],
|
|
115
|
+
{ windowsHide: true }
|
|
116
|
+
);
|
|
117
|
+
let err = '';
|
|
118
|
+
const finish = (ok) => {
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
resolve(ok);
|
|
121
|
+
};
|
|
122
|
+
const timer = setTimeout(() => {
|
|
123
|
+
try { spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true }); } catch {}
|
|
124
|
+
finish(false);
|
|
125
|
+
}, 180000);
|
|
126
|
+
child.stderr.setEncoding('utf8');
|
|
127
|
+
child.stderr.on('data', (d) => { err += d; });
|
|
128
|
+
child.on('error', () => finish(false));
|
|
129
|
+
child.on('close', (code) => finish(code === 0 && !/Expand-Archive :/i.test(err)));
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Obscura'yı kur / güncelle (yeniden indirip üzerine yazar).
|
|
134
|
+
onProgress({pct, phase}) — 1-88 indirme, 89-96 açma, 97-99 doğrulama. */
|
|
135
|
+
async function installObscura(signal, onProgress) {
|
|
136
|
+
const tick = (pct, phase) => {
|
|
137
|
+
if (typeof onProgress === 'function') {
|
|
138
|
+
try { onProgress({ pct, phase: phase || 'indiriliyor' }); } catch {}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
try {
|
|
142
|
+
tick(0, 'hazırlanıyor');
|
|
143
|
+
const dest = obscuraDir();
|
|
144
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
145
|
+
const zipPath = path.join(os.tmpdir(), 'beast-obscura.zip');
|
|
146
|
+
const buf = await httpsDownload(RELEASE_BASE + WINDOWS_ZIP, 5, signal, onProgress);
|
|
147
|
+
tick(89, 'kuruluyor');
|
|
148
|
+
fs.writeFileSync(zipPath, buf);
|
|
149
|
+
tick(90, 'kuruluyor');
|
|
150
|
+
const ok = await psExpand(zipPath, dest);
|
|
151
|
+
try { fs.unlinkSync(zipPath); } catch {}
|
|
152
|
+
tick(97, 'doğrulanıyor');
|
|
153
|
+
if (!ok || !obscuraInstalled()) {
|
|
154
|
+
return { ok: false, error: 'obscura kurulumu başarısız (zip açılamadı)' };
|
|
155
|
+
}
|
|
156
|
+
markVariant('stealth');
|
|
157
|
+
_okProbe = null; /* yeniden doğrula */
|
|
158
|
+
const good = await obscuraOk();
|
|
159
|
+
tick(good ? 100 : 98, good ? 'tamamlandı' : 'doğrulanıyor');
|
|
160
|
+
return { ok: good, dir: dest, error: good ? null : 'obscura.exe doğrulanamadı' };
|
|
161
|
+
} catch (e) {
|
|
162
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* obscura fetch — sayfa HTML'i (abort ile süreç kesilir) */
|
|
167
|
+
function obscuraFetchHtml(url, signal, timeoutMs = FETCH_TIMEOUT_MS) {
|
|
168
|
+
return new Promise((resolve, reject) => {
|
|
169
|
+
const args = ['fetch', url, '--dump', 'html', '--quiet', '--timeout', '20'];
|
|
170
|
+
if (readVariant() === 'stealth') args.push('--stealth');
|
|
171
|
+
let settled = false;
|
|
172
|
+
const finish = (err, html) => {
|
|
173
|
+
if (settled) return;
|
|
174
|
+
settled = true;
|
|
175
|
+
clearTimeout(timer);
|
|
176
|
+
if (signal) signal.removeEventListener('abort', onAbort);
|
|
177
|
+
err ? reject(err) : resolve(String(html || ''));
|
|
178
|
+
};
|
|
179
|
+
let child = null;
|
|
180
|
+
const onAbort = () => {
|
|
181
|
+
try {
|
|
182
|
+
if (child && child.pid) spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true });
|
|
183
|
+
} catch {}
|
|
184
|
+
finish(new Error('obscura fetch iptal edildi'));
|
|
185
|
+
};
|
|
186
|
+
const timer = setTimeout(() => {
|
|
187
|
+
try {
|
|
188
|
+
if (child && child.pid) spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true });
|
|
189
|
+
} catch {}
|
|
190
|
+
finish(new Error('obscura fetch zaman aşımı'));
|
|
191
|
+
}, timeoutMs);
|
|
192
|
+
if (signal) {
|
|
193
|
+
if (signal.aborted) return finish(new Error('iptal'));
|
|
194
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
child = execFile(
|
|
198
|
+
obscuraExe(),
|
|
199
|
+
args,
|
|
200
|
+
{ timeout: timeoutMs, windowsHide: true, maxBuffer: 8 * 1024 * 1024 },
|
|
201
|
+
(err, stdout) => finish(err, stdout)
|
|
202
|
+
);
|
|
203
|
+
} catch (e) {
|
|
204
|
+
finish(e);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* lite.duckduckgo sonuç ayrıştırıcı (tablo düzeni, result-link çıpaları) */
|
|
210
|
+
function parseDdgLite(html, limit = 8) {
|
|
211
|
+
const out = [];
|
|
212
|
+
const re = /<a\s+[^>]*class="[^"]*result-link[^"]*"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/g;
|
|
213
|
+
let m;
|
|
214
|
+
while ((m = re.exec(String(html || ''))) && out.length < limit) {
|
|
215
|
+
let href = m[1] || '';
|
|
216
|
+
try {
|
|
217
|
+
const u = new URL(href, 'https://duckduckgo.com');
|
|
218
|
+
const uddg = u.searchParams.get('uddg');
|
|
219
|
+
href = uddg ? decodeURIComponent(uddg) : u.toString();
|
|
220
|
+
} catch {}
|
|
221
|
+
const title = String(m[2] || '').replace(/<[^>]+>/g, '').trim();
|
|
222
|
+
if (!title || !/^https?:\/\//i.test(href)) continue;
|
|
223
|
+
if (out.some((r) => r.url === href)) continue;
|
|
224
|
+
out.push({ title, url: href, snippet: '' });
|
|
225
|
+
}
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* Obscura arama motoru: DDG html (önce) → DDG lite (yedek), stealth ile.
|
|
230
|
+
Döndüremiyorsa null — zincir sıradaki motora geçer. */
|
|
231
|
+
async function obscuraSearch(query, { maxResults = 8, signal } = {}) {
|
|
232
|
+
const q = String(query || '').trim().slice(0, 400);
|
|
233
|
+
if (!q || !obscuraInstalled()) return null;
|
|
234
|
+
const cap = Math.max(1, Math.min(12, Number(maxResults) || 8));
|
|
235
|
+
const targets = [
|
|
236
|
+
'https://html.duckduckgo.com/html/?q=' + encodeURIComponent(q),
|
|
237
|
+
'https://lite.duckduckgo.com/lite/?q=' + encodeURIComponent(q),
|
|
238
|
+
];
|
|
239
|
+
for (const url of targets) {
|
|
240
|
+
let html = '';
|
|
241
|
+
try {
|
|
242
|
+
html = await obscuraFetchHtml(url, signal);
|
|
243
|
+
} catch {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (!html) continue;
|
|
247
|
+
let results = [];
|
|
248
|
+
const { parseDdgResults } = require('./tools'); /* döngüsel require — çağrı anında */
|
|
249
|
+
if (/result__a/.test(html)) results = parseDdgResults(html, cap);
|
|
250
|
+
if (!results.length) results = parseDdgLite(html, cap);
|
|
251
|
+
if (results.length) {
|
|
252
|
+
return {
|
|
253
|
+
ok: true,
|
|
254
|
+
engine: 'obscura',
|
|
255
|
+
query: q,
|
|
256
|
+
count: results.length,
|
|
257
|
+
results: results.map((r) => ({ ...r, engine: 'obscura' })),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module.exports = {
|
|
265
|
+
ENGINE: 'obscura',
|
|
266
|
+
RELEASE_BASE,
|
|
267
|
+
WINDOWS_ZIP,
|
|
268
|
+
obscuraDir,
|
|
269
|
+
obscuraExe,
|
|
270
|
+
obscuraInstalled,
|
|
271
|
+
obscuraOk,
|
|
272
|
+
installObscura,
|
|
273
|
+
obscuraFetchHtml,
|
|
274
|
+
obscuraSearch,
|
|
275
|
+
parseDdgLite,
|
|
276
|
+
};
|
package/src/agent/skills.js
CHANGED
|
@@ -164,19 +164,27 @@ Send-MailMessage -SmtpServer smtp.gmail.com -Port 587 -UseSsl -From 'adres@gmail
|
|
|
164
164
|
force: true,
|
|
165
165
|
body: `---
|
|
166
166
|
name: python-web-search
|
|
167
|
-
description: VARSAYILAN web arama zinciri — 1) dahili tarayıcı DİREK GOOGLE (gerçek Chromium, bot koruması yok; Google AI cevabi 'ai' alanında hazır gelir) 2) python çoklu-motor (ddgs / DDG+Bing+Mojeek, anahtarsız)
|
|
168
|
-
version: 1.
|
|
167
|
+
description: VARSAYILAN web arama zinciri — 1) dahili tarayıcı DİREK GOOGLE (gerçek Chromium, bot koruması yok; Google AI cevabi 'ai' alanında hazır gelir) 2) Obscura (stealth headless → DuckDuckGo; otomatik kurulur, varsayılan AKTİF) 3) TinyFish (anahtar varsa) 4) python çoklu-motor (ddgs / DDG+Bing+Mojeek, anahtarsız). Sıra Ayarlar → Web Arama'dan değiştirilir. Takılmama kuralları içerir.
|
|
168
|
+
version: 1.4.0
|
|
169
169
|
---
|
|
170
170
|
|
|
171
171
|
# Web Arama Zinciri (VARSAYILAN)
|
|
172
172
|
|
|
173
|
-
web_search aracı çağrıldığında zincir OTOMATİK çalışır — elle motor seçmen gerekmez:
|
|
173
|
+
web_search aracı çağrıldığında zincir OTOMATİK çalışır — elle motor seçmen gerekmez. Sıra Ayarlar → Web Arama sekmesinden değiştirilebilir:
|
|
174
174
|
|
|
175
175
|
## 1. Dahili tarayıcı (ilk tercih — Google AI)
|
|
176
176
|
|
|
177
|
-
Gerçek Chromium olduğu için Google'a bot koruması uygulamaz. Arama aep=1 ile açılır: Google'ın KENDİ AI cevabı yanıtın 'ai' alanında HAZIR gelir — "kimdir/nedir" sorularında önce onu kullan, kaynak linkleri de 'results' alanında gelir. Sağ panelde açılır, kullanıcı da görebilir.
|
|
177
|
+
Gerçek Chromium olduğu için Google'a bot koruması uygulamaz. Arama aep=1 ile açılır: Google'ın KENDİ AI cevabı yanıtın 'ai' alanında HAZIR gelir — "kimdir/nedir" sorularında önce onu kullan, kaynak linkleri de 'results' alanında gelir. Sağ panelde açılır, kullanıcı da görebilir. CAPTCHA/trafik uyarısı gelirse tarayıcı 10 dk atlanır, sıradaki motor devreye girer.
|
|
178
178
|
|
|
179
|
-
## 2.
|
|
179
|
+
## 2. Obscura (stealth headless — DuckDuckGo)
|
|
180
|
+
|
|
181
|
+
Rust tabanlı gizli tarayıcı: anti-detect parmak izi + V8 JS. Kurulu değilse uygulama açılışında OTOMATİK indirilir (%APPDATA%\\beast\\obscura). Varsayılan AKTİF — tarayıcı engellenirse bot korumasını aşarak DuckDuckGo'dan sonuç getirir.
|
|
182
|
+
|
|
183
|
+
## 3. TinyFish API (anahtar varsa)
|
|
184
|
+
|
|
185
|
+
Ayarlar → Web Arama'dan anahtar girildiyse kendi sırasında devreye girer; anahtar yoksa otomatik atlanır.
|
|
186
|
+
|
|
187
|
+
## 4. Python çoklu-motor
|
|
180
188
|
|
|
181
189
|
ddgs kütüphanesi (github.com/deedy5/ddgs — anti-bot korumalı metasearch) veya DuckDuckGo + Bing + Mojeek PARALEL — anahtar gerekmez. Gömülü Python runtime otomatik yönetilir.
|
|
182
190
|
|
|
@@ -187,10 +195,6 @@ web_search {"query": "sorgu", "max_results": 6}
|
|
|
187
195
|
- Çoklu sorgu gerekiyorsa: aynı turda birden ÇOK web_search çağrısını PARALEL ver — toplam süre tek arama kadar olur.
|
|
188
196
|
- Sonuç listesinden 1-2 URL'nin içeriğini okumak için http_fetch kullan.
|
|
189
197
|
|
|
190
|
-
## 3. Exa (son çare)
|
|
191
|
-
|
|
192
|
-
Yukarıdakiler boş dönerse ve Ayarlar → Web Arama'ya Exa anahtarı girildiyse devreye girer.
|
|
193
|
-
|
|
194
198
|
## Toplu / derin işler (python_run)
|
|
195
199
|
|
|
196
200
|
- Haber başlıkları: python_run script=news.py args ["--limit","8","--json"]
|
|
@@ -208,7 +212,7 @@ for q in ["sorgu 1", "sorgu 2", "sorgu 3"]:
|
|
|
208
212
|
|
|
209
213
|
- Bir bilgi 2-3 farklı sorguyla bulunamıyorsa PEŞİNİ BIRAK: bulduğun kısmi bilgiyle devam et, neyi bulamadığını raporda açıkça yaz.
|
|
210
214
|
- Kapalı/gizli içerik (Instagram gizli hesap, private profil, login arkası veri) ARANMAZ — bulunamayacağı belliyse hemen alternatif kaynağa/geçişe geç.
|
|
211
|
-
- DDG boş/engelli dönerse "DuckDuckGo kapandı" DEME: bot koruması devrededir (~1 saatte kendiliğinden açılır) ve zincir otomatik olarak Bing/Mojeek/tarayıcıya geçer — bu normal çalışmadır.
|
|
215
|
+
- DDG boş/engelli dönerse "DuckDuckGo kapandı" DEME: bot koruması devrededir (~1 saatte kendiliğinden açılır) ve zincir otomatik olarak Obscura/Bing/Mojeek/tarayıcıya geçer — bu normal çalışmadır.
|
|
212
216
|
- Araştırma hedefi: ~3 dakika, 3-5 kaynak. Derinleşme = zaman kaybı.
|
|
213
217
|
- Motor hata verirse bir kez farklı sorguyla dene, sonra elindekiyle raporu kapat.`,
|
|
214
218
|
},
|
package/src/agent/tools.js
CHANGED
|
@@ -7,6 +7,7 @@ const https = require('https');
|
|
|
7
7
|
const { execFile } = require('child_process');
|
|
8
8
|
const { spawn } = require('child_process');
|
|
9
9
|
const research = require('./research');
|
|
10
|
+
const obscura = require('./obscura');
|
|
10
11
|
|
|
11
12
|
const MAX_CMD_OUTPUT = 16000;
|
|
12
13
|
const MAX_FILE_CHARS = 200000;
|
|
@@ -627,7 +628,7 @@ const definitions = [
|
|
|
627
628
|
function: {
|
|
628
629
|
name: 'web_search',
|
|
629
630
|
description:
|
|
630
|
-
'FAST web search with an automatic chain: built-in browser
|
|
631
|
+
'FAST web search with an automatic chain (order is configurable in Ayarlar → Web Arama): built-in browser (real Chromium searching GOOGLE directly with AI Mode — no bot protection; the response may include an `ai` field holding Google\'s own AI answer), Obscura stealth headless browser (anti-detect; searches DuckDuckGo, installed automatically and ACTIVE by default), TinyFish API (only if a key is configured), then Python multi-engine (ddgs / DuckDuckGo+Bing+Mojeek). If the browser hits CAPTCHA/unusual traffic it is skipped for 10 minutes and the next engine takes over. Returns {ai?, results[{title,url,snippet}]}. Use when fresh or external info is needed; skip for things you already know.',
|
|
631
632
|
parameters: {
|
|
632
633
|
type: 'object',
|
|
633
634
|
properties: {
|
|
@@ -758,13 +759,9 @@ async function exec(name, args, ctx) {
|
|
|
758
759
|
case 'web_search': {
|
|
759
760
|
const q = String(args.query || '');
|
|
760
761
|
const n = Number(args.max_results) || 8;
|
|
761
|
-
/*
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
if (!r || !r.ok || !(r.results || []).length) {
|
|
765
|
-
const exa = await exaSearch(q, n, ctx.signal);
|
|
766
|
-
if (exa) return JSON.stringify(exa);
|
|
767
|
-
}
|
|
762
|
+
/* sıralı zincir (obscura/tinyfish/python — dahili tarayıcı engine
|
|
763
|
+
tarafındaki hook'tan gelir); Exa KALDIRILDI */
|
|
764
|
+
const r = await searchChainWeb(q, n, { signal: ctx.signal });
|
|
768
765
|
return JSON.stringify(r);
|
|
769
766
|
}
|
|
770
767
|
case 'deep_search': {
|
|
@@ -772,7 +769,7 @@ async function exec(name, args, ctx) {
|
|
|
772
769
|
Gizli tarayıcı okuması engine.research hook'undan gelir (main process). */
|
|
773
770
|
const r = await research.deepSearch(
|
|
774
771
|
args,
|
|
775
|
-
{ search: (q) =>
|
|
772
|
+
{ search: (q) => searchChainWeb(q, 10, { signal: ctx.signal }) },
|
|
776
773
|
ctx.signal
|
|
777
774
|
);
|
|
778
775
|
return JSON.stringify(r);
|
|
@@ -836,53 +833,101 @@ async function exec(name, args, ctx) {
|
|
|
836
833
|
}
|
|
837
834
|
}
|
|
838
835
|
|
|
839
|
-
/* ----------
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
836
|
+
/* ---------- Sıralı arama zinciri (Ayarlar → Web Arama'dan değiştirilir) ----------
|
|
837
|
+
Motorlar: browser (dahili Chromium → Google) · obscura (stealth headless →
|
|
838
|
+
DuckDuckGo) · tinyfish (API, anahtar gerekir) · python (ddgs/DDG+Bing+Mojeek).
|
|
839
|
+
Sıra + aç/kapa ayarı settings.json'da (searchChain) saklanır; Obscura
|
|
840
|
+
varsayılan AKTİF. Tarayıcı CAPTCHA/trafik verirse 10 dk atlanır. */
|
|
841
|
+
const SEARCH_ENGINE_IDS = ['browser', 'obscura', 'tinyfish', 'python'];
|
|
842
|
+
const DEFAULT_SEARCH_CHAIN = SEARCH_ENGINE_IDS.map((id) => ({ id, on: true }));
|
|
843
|
+
|
|
844
|
+
let _searchChain = DEFAULT_SEARCH_CHAIN.map((x) => ({ ...x }));
|
|
845
|
+
let _obscuraEnabled = true; /* Obscura varsayılan AKTİF */
|
|
846
|
+
let _browserBanUntil = 0;
|
|
847
|
+
|
|
848
|
+
function normalizeSearchChain(list) {
|
|
849
|
+
const arr = Array.isArray(list) ? list : [];
|
|
850
|
+
const rows = [];
|
|
851
|
+
const seen = new Set();
|
|
852
|
+
for (const item of arr) {
|
|
853
|
+
const id = String((item && item.id) || item || '').trim();
|
|
854
|
+
if (!SEARCH_ENGINE_IDS.includes(id) || seen.has(id)) continue;
|
|
855
|
+
seen.add(id);
|
|
856
|
+
rows.push({ id, on: !(item && item.on === false) });
|
|
857
|
+
}
|
|
858
|
+
for (const id of SEARCH_ENGINE_IDS) {
|
|
859
|
+
if (!seen.has(id)) rows.push({ id, on: true }); /* listede eksik motor varsayılan AÇIK */
|
|
860
|
+
}
|
|
861
|
+
/* obscura varsayılan AKTİF; zincir boş kalmasın: hepsi kapalıysa browser'ı aç */
|
|
862
|
+
if (!rows.some((r) => r.on)) {
|
|
863
|
+
const b = rows.find((r) => r.id === 'browser');
|
|
864
|
+
if (b) b.on = true;
|
|
865
|
+
}
|
|
866
|
+
return rows;
|
|
867
|
+
}
|
|
843
868
|
|
|
844
|
-
function
|
|
845
|
-
|
|
869
|
+
function setSearchChain(list) {
|
|
870
|
+
_searchChain = normalizeSearchChain(list);
|
|
871
|
+
return _searchChain;
|
|
846
872
|
}
|
|
847
873
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
874
|
+
function setSearchObscuraEnabled(v) {
|
|
875
|
+
_obscuraEnabled = v !== false;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
function getSearchChain() {
|
|
879
|
+
return _searchChain.map((x) => ({ ...x }));
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function browserBanned() {
|
|
883
|
+
return Date.now() < _browserBanUntil;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function banBrowser(minutes = 10) {
|
|
887
|
+
_browserBanUntil = Date.now() + minutes * 60 * 1000;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/* Zinciri sırayla koştur: her motor boş/hata dönerse sıradakine geç.
|
|
891
|
+
browser motoru yalnız engine'den gelir (hook); araç-bağımsız çağrılarda atlanır. */
|
|
892
|
+
async function searchChainWeb(query, maxResults, { signal, browser } = {}) {
|
|
893
|
+
const q = String(query || '').trim().slice(0, 400);
|
|
894
|
+
if (!q) return { ok: false, error: 'boş sorgu' };
|
|
895
|
+
const cap = Math.max(1, Math.min(12, Number(maxResults) || 8));
|
|
896
|
+
let out = null;
|
|
897
|
+
const banned = browserBanned();
|
|
898
|
+
for (const row of _searchChain) {
|
|
899
|
+
if (out && out.ok && (out.results || []).length) break;
|
|
900
|
+
if (!row.on) continue;
|
|
901
|
+
try {
|
|
902
|
+
if (row.id === 'browser') {
|
|
903
|
+
if (typeof browser !== 'function' || banned) continue;
|
|
904
|
+
out = await browser();
|
|
905
|
+
if (!out || !out.ok || !(out.results || []).length) {
|
|
906
|
+
banBrowser(); /* tarayıcı sorunlu — 10 dk atla, alternatifler devrede */
|
|
907
|
+
out = null;
|
|
908
|
+
}
|
|
909
|
+
} else if (row.id === 'obscura') {
|
|
910
|
+
if (!_obscuraEnabled) continue;
|
|
911
|
+
out = await obscura.obscuraSearch(q, { maxResults: cap, signal });
|
|
912
|
+
} else if (row.id === 'tinyfish') {
|
|
913
|
+
out = await tinyfishSearch(q, cap, signal);
|
|
914
|
+
} else if (row.id === 'python') {
|
|
915
|
+
out = await webSearchFast(q, { maxResults: cap, signal });
|
|
916
|
+
}
|
|
917
|
+
} catch {
|
|
918
|
+
out = null;
|
|
919
|
+
}
|
|
920
|
+
if (out && out.ok && !(out.results || []).length) out = null; /* boş → sıradaki motor */
|
|
921
|
+
}
|
|
922
|
+
if (out && out.ok && banned && (out.results || []).length) {
|
|
923
|
+
out.note = 'dahili tarayıcı 10 dk askıda (CAPTCHA/trafik) — alternatif motor kullanıldı';
|
|
879
924
|
}
|
|
925
|
+
return out || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' };
|
|
880
926
|
}
|
|
881
927
|
|
|
882
|
-
/* ---------- TinyFish (
|
|
928
|
+
/* ---------- TinyFish (anahtar girilirse zincirdeki kendi sırasında) ----------
|
|
883
929
|
GET https://api.search.tinyfish.ai?query=... · Header: X-API-Key
|
|
884
|
-
|
|
885
|
-
hata/sonuç yoksa eski zincir (tarayıcı → python çoklu-motor → Exa) devam eder. */
|
|
930
|
+
Anahtar yoksa bu motor otomatik atlanır; sıradaki motor devreye girer. */
|
|
886
931
|
let _tinyfishKey = null;
|
|
887
932
|
|
|
888
933
|
function setTinyfishKey(key) {
|
|
@@ -936,10 +981,19 @@ module.exports = {
|
|
|
936
981
|
webSearchFast,
|
|
937
982
|
seedScript,
|
|
938
983
|
bundledScriptPath,
|
|
939
|
-
|
|
940
|
-
|
|
984
|
+
/* arama zinciri (sıra + aç/kapa Ayarlar → Web Arama'dan) */
|
|
985
|
+
SEARCH_ENGINE_IDS,
|
|
986
|
+
DEFAULT_SEARCH_CHAIN,
|
|
987
|
+
setSearchChain,
|
|
988
|
+
setSearchObscuraEnabled,
|
|
989
|
+
getSearchChain,
|
|
990
|
+
searchChainWeb,
|
|
991
|
+
browserBanned,
|
|
992
|
+
banBrowser,
|
|
941
993
|
setTinyfishKey,
|
|
942
994
|
tinyfishSearch,
|
|
995
|
+
/* obscura geçişi */
|
|
996
|
+
obscura,
|
|
943
997
|
/* python altyapısı */
|
|
944
998
|
ensurePython,
|
|
945
999
|
findSystemPython,
|