beast-agent 1.9.0 → 2.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.
Files changed (52) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +130 -130
  3. package/bin/beast-agent.js +137 -137
  4. package/package.json +1 -1
  5. package/scripts/fix-electron.js +138 -138
  6. package/scripts/release.js +137 -131
  7. package/scripts/swap-electron.js +40 -40
  8. package/src/agent/agentdefs.js +122 -122
  9. package/src/agent/bots.js +580 -580
  10. package/src/agent/bus.js +389 -389
  11. package/src/agent/computeruse.js +200 -200
  12. package/src/agent/config.js +284 -284
  13. package/src/agent/discord.js +332 -332
  14. package/src/agent/engine.js +75 -10
  15. package/src/agent/kb.js +123 -123
  16. package/src/agent/llm.js +430 -430
  17. package/src/agent/logger.js +90 -90
  18. package/src/agent/mcp.js +427 -427
  19. package/src/agent/mem0.js +605 -605
  20. package/src/agent/memory.js +427 -427
  21. package/src/agent/mqueue.js +124 -124
  22. package/src/agent/pdf.js +20 -20
  23. package/src/agent/research.js +133 -133
  24. package/src/agent/scripts/news.py +113 -113
  25. package/src/agent/scripts/stealthsearch.py +30 -30
  26. package/src/agent/scripts/websearch.py +225 -225
  27. package/src/agent/searxng.js +325 -325
  28. package/src/agent/seeds/brainstorming/SKILL.md +90 -90
  29. package/src/agent/seeds/dispatching-parallel-agents/SKILL.md +120 -120
  30. package/src/agent/seeds/executing-plans/SKILL.md +60 -60
  31. package/src/agent/seeds/subagent-driven-development/SKILL.md +167 -167
  32. package/src/agent/seeds/systematic-debugging/SKILL.md +131 -131
  33. package/src/agent/seeds/test-driven-development/SKILL.md +152 -152
  34. package/src/agent/seeds/verification-before-completion/SKILL.md +63 -63
  35. package/src/agent/seeds/writing-plans/SKILL.md +162 -162
  36. package/src/agent/seeds/writing-skills/SKILL.md +229 -229
  37. package/src/agent/skills.js +652 -652
  38. package/src/agent/store.js +378 -378
  39. package/src/agent/telegram.js +155 -155
  40. package/src/agent/tokens.js +39 -39
  41. package/src/agent/usage.js +125 -125
  42. package/src/agent/watchers.js +312 -312
  43. package/src/agent/watext.js +80 -80
  44. package/src/agent/whatsapp.js +555 -555
  45. package/src/cron.js +255 -255
  46. package/src/main.js +348 -6
  47. package/src/preload.js +9 -0
  48. package/src/renderer/browserPreload.js +73 -73
  49. package/src/renderer/i18n.js +4 -0
  50. package/src/renderer/index.html +448 -409
  51. package/src/renderer/renderer.js +824 -41
  52. package/src/renderer/style.css +264 -5
@@ -1,325 +1,325 @@
1
- 'use strict';
2
-
3
- /* SearXNG — yerel metasearch entegrasyonu (github.com/searxng/searxng)
4
- SearXNG resmi olarak Linux/Docker hedefler; Windows'ta `import pwd`
5
- yüzünden patlar. Bu modül Beast'ın GÖMÜLÜ Python'ına gerçek searxng'yi
6
- (GitHub master) kurar + pwd shim'i yazar + 127.0.0.1:8888'de arka planda
7
- çalıştırır. Arama zinciri 'searxng' motorunu yalnız AYAKTAYSA kullanır.
8
-
9
- CLI: beast searxng → gerekirse kur + başlat
10
- beast searxng status → durum
11
- beast searxng stop → kulla */
12
-
13
- const fs = require('fs');
14
- const path = require('path');
15
- const os = require('os');
16
- const { spawn, execFile, execSync } = require('child_process');
17
-
18
- const REQS_URL = 'https://raw.githubusercontent.com/searxng/searxng/master/requirements.txt';
19
- const PKG_URL = 'https://github.com/searxng/searxng/archive/refs/heads/master.zip';
20
- const GETPIP_URL = 'https://bootstrap.pypa.io/get-pip.py';
21
- const DEFAULT_URL = 'http://127.0.0.1:8888';
22
-
23
- /* ---------- yollar ---------- */
24
-
25
- function beastAppDir() {
26
- if (process.env.BEAST_DATA) return process.env.BEAST_DATA;
27
- return process.env.APPDATA
28
- ? path.join(process.env.APPDATA, 'beast')
29
- : path.join(os.homedir(), 'AppData', 'Roaming', 'beast');
30
- }
31
-
32
- function dir() {
33
- const d = path.join(beastAppDir(), 'searxng');
34
- try { fs.mkdirSync(d, { recursive: true }); } catch {}
35
- return d;
36
- }
37
-
38
- function settingsPath() { return path.join(dir(), 'settings.yml'); }
39
- function runPyPath() { return path.join(dir(), 'run.py'); }
40
- function pidPath() { return path.join(dir(), 'server.pid'); }
41
- function pyExe() { return path.join(beastAppDir(), 'py', 'python.exe'); }
42
-
43
- /* ---------- durum yoklama (cache'li) ---------- */
44
-
45
- let _probe = { at: 0, up: false, url: '' };
46
- const PROBE_UP_TTL = 5 * 60 * 1000;
47
- const PROBE_DOWN_TTL = 2 * 60 * 1000;
48
-
49
- function resetProbeCache() { _probe = { at: 0, up: false, url: '' }; }
50
-
51
- async function isUp(baseUrl = DEFAULT_URL, { force = false } = {}) {
52
- const base = String(baseUrl || DEFAULT_URL).replace(/\/+$/, '');
53
- const ttl = _probe.up ? PROBE_UP_TTL : PROBE_DOWN_TTL;
54
- if (!force && _probe.url === base && Date.now() - _probe.at < ttl) return _probe.up;
55
- let up = false;
56
- try {
57
- const r = await fetch(`${base}/search?q=beast&format=json`, { signal: AbortSignal.timeout(1800) });
58
- if (r.ok) {
59
- const j = await r.json();
60
- up = j && Array.isArray(j.results);
61
- }
62
- } catch {}
63
- _probe = { at: Date.now(), up, url: base };
64
- return up;
65
- }
66
-
67
- /* ---------- arama (zincir motoru) ---------- */
68
-
69
- async function search(query, { maxResults = 8, signal, baseUrl } = {}) {
70
- const base = String(baseUrl || process.env.BEAST_SEARXNG_URL || DEFAULT_URL).replace(/\/+$/, '');
71
- if (!(await isUp(base))) return null; // ayakta değil → zincir sıradaki motora geçer
72
- try {
73
- const sig =
74
- signal && typeof AbortSignal !== 'undefined' && AbortSignal.any
75
- ? AbortSignal.any([signal, AbortSignal.timeout(15000)])
76
- : AbortSignal.timeout(15000);
77
- const r = await fetch(
78
- `${base}/search?q=${encodeURIComponent(String(query || ''))}&format=json&safesearch=0`,
79
- { signal: sig }
80
- );
81
- if (!r.ok) return null;
82
- const j = await r.json();
83
- const results = (j.results || [])
84
- .map((x) => ({
85
- title: String(x.title || ''),
86
- url: String(x.url || ''),
87
- snippet: String(x.content || '').replace(/\s+/g, ' ').slice(0, 400),
88
- engine: 'searxng',
89
- }))
90
- .filter((x) => x.url && x.title);
91
- if (!results.length) return null;
92
- return {
93
- ok: true,
94
- engine: 'searxng',
95
- query,
96
- results: results.slice(0, Math.max(1, Math.min(12, Number(maxResults) || 8))),
97
- };
98
- } catch {
99
- return null;
100
- }
101
- }
102
-
103
- /* ---------- kurulum ---------- */
104
-
105
- function sh(exe, args, timeoutMs = 600000) {
106
- return new Promise((resolve, reject) => {
107
- execFile(exe, args, { timeout: timeoutMs, windowsHide: true, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => {
108
- if (err) {
109
- const tail = String((stderr || stdout || err.message) || '').slice(-400);
110
- return reject(new Error(tail || String(err)));
111
- }
112
- resolve(String(stdout || ''));
113
- });
114
- });
115
- }
116
-
117
- async function downloadTo(url, dest) {
118
- const r = await fetch(url, { signal: AbortSignal.timeout(120000) });
119
- if (!r.ok) throw new Error(`indirme başarısız: HTTP ${r.status}`);
120
- fs.writeFileSync(dest, Buffer.from(await r.arrayBuffer()));
121
- }
122
-
123
- function sitePackagesDir(py) {
124
- return sh(py, ['-c', 'import sysconfig;print(sysconfig.get_paths()["purelib"])']).then((s) => s.trim());
125
- }
126
-
127
- /* gömülü python'un ._pth dosyasında `import site` açık değilse pip/paketler görünmez */
128
- function fixEmbeddedPth(py) {
129
- try {
130
- const dirPy = path.dirname(py);
131
- for (const f of fs.readdirSync(dirPy)) {
132
- if (/^python\d+\._pth$/.test(f)) {
133
- const p = path.join(dirPy, f);
134
- const cur = fs.readFileSync(p, 'utf8');
135
- if (!/^import site$/m.test(cur)) {
136
- fs.writeFileSync(p, cur.replace(/^#\s*import site/m, 'import site'), 'utf8');
137
- }
138
- }
139
- }
140
- } catch {}
141
- }
142
-
143
- async function ensurePip(py, onLog) {
144
- try {
145
- await sh(py, ['-m', 'pip', '--version'], 60000);
146
- return;
147
- } catch {}
148
- onLog('pip kuruluyor…');
149
- const gp = path.join(os.tmpdir(), 'beast-get-pip.py');
150
- await downloadTo(GETPIP_URL, gp);
151
- await sh(py, [gp, '--no-warn-script-location'], 300000);
152
- fixEmbeddedPth(py);
153
- }
154
-
155
- /** Tam kurulum — `beast searxng` komutunun yaptığı iş. */
156
- async function install({ onLog } = {}) {
157
- const log = (m) => { try { (onLog || ((s) => console.log(' • ' + s)))(m); } catch {} };
158
- const tools = require('./tools');
159
- const probe = await tools.ensurePython();
160
- const py = probe.exe;
161
- log('python: ' + py);
162
-
163
- await ensurePip(py, log);
164
- log('setuptools/wheel/tzdata kuruluyor…');
165
- await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', 'setuptools', 'wheel', 'tzdata'], 600000);
166
-
167
- log('searxng bağımlılıkları indiriliyor (~40 MB)…');
168
- const reqs = path.join(os.tmpdir(), 'beast-searxng-requirements.txt');
169
- await downloadTo(REQS_URL, reqs);
170
- await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', '-r', reqs], 900000);
171
-
172
- log('searxng motoru kuruluyor…');
173
- await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', '--no-build-isolation', PKG_URL], 900000);
174
-
175
- /* pwd shim — searxng/valkeydb.py unix socket sahipliği için pwd import eder */
176
- const sp = (await sitePackagesDir(py)) || path.join(path.dirname(py), 'Lib', 'site-packages');
177
- const shim = [
178
- '"""Windows shim: SearXNG valkeydb.py yalnizca unix-socket sahipligi icin pwd',
179
- 'kullanir; Windows\'ta valkey devre disi oldugundan donuk yanit yeterlidir."""',
180
- 'import os',
181
- '',
182
- 'class _Pw:',
183
- ' pw_name = "beast"',
184
- ' pw_uid = 0',
185
- ' pw_gid = 0',
186
- ' pw_dir = os.path.expanduser("~")',
187
- ' pw_shell = ""',
188
- '',
189
- 'def getpwuid(uid):',
190
- ' return _Pw()',
191
- '',
192
- 'def getpwnam(name):',
193
- ' return _Pw()',
194
- '',
195
- 'def getgrgid(gid):',
196
- ' class _Gr:',
197
- ' gr_name = "beast"',
198
- ' gr_gid = 0',
199
- ' return _Gr()',
200
- '',
201
- 'def getgrnam(name):',
202
- ' return getgrgid(0)',
203
- ].join('\n');
204
- fs.writeFileSync(path.join(sp, 'pwd.py'), shim, 'utf8');
205
-
206
- /* ayarlar + çalıştırıcı */
207
- const secret = 'beast-' + Math.random().toString(36).slice(2, 14);
208
- fs.writeFileSync(
209
- settingsPath(),
210
- [
211
- 'use_default_settings: true',
212
- 'server:',
213
- ` secret_key: "${secret}"`,
214
- ' port: 8888',
215
- ' bind_address: "127.0.0.1"',
216
- ' limiter: false',
217
- ' image_proxy: false',
218
- 'search:',
219
- ' formats:',
220
- ' - html',
221
- ' - json',
222
- '',
223
- ].join('\n'),
224
- 'utf8'
225
- );
226
- fs.writeFileSync(runPyPath(), 'from searx.webapp import run; run()', 'utf8');
227
- resetProbeCache();
228
- log('kurulum tamam');
229
- return { ok: true };
230
- }
231
-
232
- /* ---------- başlat / durdur / durum ---------- */
233
-
234
- function readPid() {
235
- try {
236
- const s = fs.readFileSync(pidPath(), 'utf8').trim();
237
- return /^\d+$/.test(s) ? Number(s) : null;
238
- } catch {
239
- return null;
240
- }
241
- }
242
-
243
- async function start({ installIfMissing = true, onLog } = {}) {
244
- if (await isUp(DEFAULT_URL, { force: true })) return { ok: true, already: true, url: DEFAULT_URL };
245
- if (!fs.existsSync(settingsPath()) || !fs.existsSync(runPyPath())) {
246
- if (!installIfMissing) return { ok: false, error: 'kurulu değil — önce: beast searxng' };
247
- await install({ onLog });
248
- }
249
- const tools = require('./tools');
250
- const probe = await tools.ensurePython();
251
- const child = spawn(probe.exe, [runPyPath()], {
252
- detached: true,
253
- stdio: 'ignore',
254
- windowsHide: true,
255
- env: { ...process.env, SEARXNG_SETTINGS_PATH: settingsPath(), PYTHONIOENCODING: 'utf-8' },
256
- });
257
- try { fs.writeFileSync(pidPath(), String(child.pid)); } catch {}
258
- child.unref();
259
- /* ayağa kalkmasını bekle (ilk açılış motor listesini derler — birkaç sn) */
260
- for (let i = 0; i < 20; i++) {
261
- await new Promise((r) => setTimeout(r, 1000));
262
- if (await isUp(DEFAULT_URL, { force: true })) return { ok: true, url: DEFAULT_URL, pid: child.pid };
263
- }
264
- return { ok: false, error: 'searxng başlatılamadı (20 sn içinde yanıt yok)' };
265
- }
266
-
267
- function stop() {
268
- const pid = readPid();
269
- if (!pid) return { ok: true, note: 'çalışmıyor' };
270
- try {
271
- execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore' });
272
- } catch {}
273
- try { fs.unlinkSync(pidPath()); } catch {}
274
- resetProbeCache();
275
- return { ok: true, stopped: pid };
276
- }
277
-
278
- async function status() {
279
- return {
280
- up: await isUp(DEFAULT_URL, { force: true }),
281
- url: DEFAULT_URL,
282
- pid: readPid(),
283
- installed: fs.existsSync(settingsPath()) && fs.existsSync(runPyPath()),
284
- };
285
- }
286
-
287
- /* ---------- CLI (bin/beast-agent.js → beast searxng) ---------- */
288
-
289
- async function cli(argv) {
290
- const sub = String(argv[0] || 'start').toLowerCase();
291
- console.log('\u27F3 SearXNG (yerel arama motoru)');
292
- if (sub === 'stop') {
293
- const r = stop();
294
- console.log(r.ok ? '\u2713 durduruldu' : '\u2717 durdurulamadı');
295
- process.exit(r.ok ? 0 : 1);
296
- }
297
- if (sub === 'status') {
298
- const s = await status();
299
- console.log(` kurulu : ${s.installed ? 'evet' : 'hayır'}`);
300
- console.log(` çalışıyor: ${s.up ? 'evet (' + s.url + ')' : 'hayır'}`);
301
- process.exit(0);
302
- }
303
- /* start */
304
- const r = await start({ installIfMissing: true, onLog: (m) => console.log(' \u2022 ' + m) });
305
- if (r.ok) {
306
- console.log(r.already ? '\u2713 SearXNG zaten çalışıyor — ' + r.url : '\u2713 SearXNG başlatıldı — ' + r.url);
307
- console.log('\u2139 arama zincirinde kullanılmaya hazır (Ayarlar → Web Arama sırası)');
308
- process.exit(0);
309
- }
310
- console.error('\u2717 ' + (r.error || 'başlatılamadı'));
311
- process.exit(1);
312
- }
313
-
314
- module.exports = {
315
- search,
316
- isUp,
317
- resetProbeCache,
318
- install,
319
- start,
320
- stop,
321
- status,
322
- cli,
323
- settingsPath,
324
- DEFAULT_URL,
325
- };
1
+ 'use strict';
2
+
3
+ /* SearXNG — yerel metasearch entegrasyonu (github.com/searxng/searxng)
4
+ SearXNG resmi olarak Linux/Docker hedefler; Windows'ta `import pwd`
5
+ yüzünden patlar. Bu modül Beast'ın GÖMÜLÜ Python'ına gerçek searxng'yi
6
+ (GitHub master) kurar + pwd shim'i yazar + 127.0.0.1:8888'de arka planda
7
+ çalıştırır. Arama zinciri 'searxng' motorunu yalnız AYAKTAYSA kullanır.
8
+
9
+ CLI: beast searxng → gerekirse kur + başlat
10
+ beast searxng status → durum
11
+ beast searxng stop → kulla */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const os = require('os');
16
+ const { spawn, execFile, execSync } = require('child_process');
17
+
18
+ const REQS_URL = 'https://raw.githubusercontent.com/searxng/searxng/master/requirements.txt';
19
+ const PKG_URL = 'https://github.com/searxng/searxng/archive/refs/heads/master.zip';
20
+ const GETPIP_URL = 'https://bootstrap.pypa.io/get-pip.py';
21
+ const DEFAULT_URL = 'http://127.0.0.1:8888';
22
+
23
+ /* ---------- yollar ---------- */
24
+
25
+ function beastAppDir() {
26
+ if (process.env.BEAST_DATA) return process.env.BEAST_DATA;
27
+ return process.env.APPDATA
28
+ ? path.join(process.env.APPDATA, 'beast')
29
+ : path.join(os.homedir(), 'AppData', 'Roaming', 'beast');
30
+ }
31
+
32
+ function dir() {
33
+ const d = path.join(beastAppDir(), 'searxng');
34
+ try { fs.mkdirSync(d, { recursive: true }); } catch {}
35
+ return d;
36
+ }
37
+
38
+ function settingsPath() { return path.join(dir(), 'settings.yml'); }
39
+ function runPyPath() { return path.join(dir(), 'run.py'); }
40
+ function pidPath() { return path.join(dir(), 'server.pid'); }
41
+ function pyExe() { return path.join(beastAppDir(), 'py', 'python.exe'); }
42
+
43
+ /* ---------- durum yoklama (cache'li) ---------- */
44
+
45
+ let _probe = { at: 0, up: false, url: '' };
46
+ const PROBE_UP_TTL = 5 * 60 * 1000;
47
+ const PROBE_DOWN_TTL = 2 * 60 * 1000;
48
+
49
+ function resetProbeCache() { _probe = { at: 0, up: false, url: '' }; }
50
+
51
+ async function isUp(baseUrl = DEFAULT_URL, { force = false } = {}) {
52
+ const base = String(baseUrl || DEFAULT_URL).replace(/\/+$/, '');
53
+ const ttl = _probe.up ? PROBE_UP_TTL : PROBE_DOWN_TTL;
54
+ if (!force && _probe.url === base && Date.now() - _probe.at < ttl) return _probe.up;
55
+ let up = false;
56
+ try {
57
+ const r = await fetch(`${base}/search?q=beast&format=json`, { signal: AbortSignal.timeout(1800) });
58
+ if (r.ok) {
59
+ const j = await r.json();
60
+ up = j && Array.isArray(j.results);
61
+ }
62
+ } catch {}
63
+ _probe = { at: Date.now(), up, url: base };
64
+ return up;
65
+ }
66
+
67
+ /* ---------- arama (zincir motoru) ---------- */
68
+
69
+ async function search(query, { maxResults = 8, signal, baseUrl } = {}) {
70
+ const base = String(baseUrl || process.env.BEAST_SEARXNG_URL || DEFAULT_URL).replace(/\/+$/, '');
71
+ if (!(await isUp(base))) return null; // ayakta değil → zincir sıradaki motora geçer
72
+ try {
73
+ const sig =
74
+ signal && typeof AbortSignal !== 'undefined' && AbortSignal.any
75
+ ? AbortSignal.any([signal, AbortSignal.timeout(15000)])
76
+ : AbortSignal.timeout(15000);
77
+ const r = await fetch(
78
+ `${base}/search?q=${encodeURIComponent(String(query || ''))}&format=json&safesearch=0`,
79
+ { signal: sig }
80
+ );
81
+ if (!r.ok) return null;
82
+ const j = await r.json();
83
+ const results = (j.results || [])
84
+ .map((x) => ({
85
+ title: String(x.title || ''),
86
+ url: String(x.url || ''),
87
+ snippet: String(x.content || '').replace(/\s+/g, ' ').slice(0, 400),
88
+ engine: 'searxng',
89
+ }))
90
+ .filter((x) => x.url && x.title);
91
+ if (!results.length) return null;
92
+ return {
93
+ ok: true,
94
+ engine: 'searxng',
95
+ query,
96
+ results: results.slice(0, Math.max(1, Math.min(12, Number(maxResults) || 8))),
97
+ };
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+
103
+ /* ---------- kurulum ---------- */
104
+
105
+ function sh(exe, args, timeoutMs = 600000) {
106
+ return new Promise((resolve, reject) => {
107
+ execFile(exe, args, { timeout: timeoutMs, windowsHide: true, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => {
108
+ if (err) {
109
+ const tail = String((stderr || stdout || err.message) || '').slice(-400);
110
+ return reject(new Error(tail || String(err)));
111
+ }
112
+ resolve(String(stdout || ''));
113
+ });
114
+ });
115
+ }
116
+
117
+ async function downloadTo(url, dest) {
118
+ const r = await fetch(url, { signal: AbortSignal.timeout(120000) });
119
+ if (!r.ok) throw new Error(`indirme başarısız: HTTP ${r.status}`);
120
+ fs.writeFileSync(dest, Buffer.from(await r.arrayBuffer()));
121
+ }
122
+
123
+ function sitePackagesDir(py) {
124
+ return sh(py, ['-c', 'import sysconfig;print(sysconfig.get_paths()["purelib"])']).then((s) => s.trim());
125
+ }
126
+
127
+ /* gömülü python'un ._pth dosyasında `import site` açık değilse pip/paketler görünmez */
128
+ function fixEmbeddedPth(py) {
129
+ try {
130
+ const dirPy = path.dirname(py);
131
+ for (const f of fs.readdirSync(dirPy)) {
132
+ if (/^python\d+\._pth$/.test(f)) {
133
+ const p = path.join(dirPy, f);
134
+ const cur = fs.readFileSync(p, 'utf8');
135
+ if (!/^import site$/m.test(cur)) {
136
+ fs.writeFileSync(p, cur.replace(/^#\s*import site/m, 'import site'), 'utf8');
137
+ }
138
+ }
139
+ }
140
+ } catch {}
141
+ }
142
+
143
+ async function ensurePip(py, onLog) {
144
+ try {
145
+ await sh(py, ['-m', 'pip', '--version'], 60000);
146
+ return;
147
+ } catch {}
148
+ onLog('pip kuruluyor…');
149
+ const gp = path.join(os.tmpdir(), 'beast-get-pip.py');
150
+ await downloadTo(GETPIP_URL, gp);
151
+ await sh(py, [gp, '--no-warn-script-location'], 300000);
152
+ fixEmbeddedPth(py);
153
+ }
154
+
155
+ /** Tam kurulum — `beast searxng` komutunun yaptığı iş. */
156
+ async function install({ onLog } = {}) {
157
+ const log = (m) => { try { (onLog || ((s) => console.log(' • ' + s)))(m); } catch {} };
158
+ const tools = require('./tools');
159
+ const probe = await tools.ensurePython();
160
+ const py = probe.exe;
161
+ log('python: ' + py);
162
+
163
+ await ensurePip(py, log);
164
+ log('setuptools/wheel/tzdata kuruluyor…');
165
+ await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', 'setuptools', 'wheel', 'tzdata'], 600000);
166
+
167
+ log('searxng bağımlılıkları indiriliyor (~40 MB)…');
168
+ const reqs = path.join(os.tmpdir(), 'beast-searxng-requirements.txt');
169
+ await downloadTo(REQS_URL, reqs);
170
+ await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', '-r', reqs], 900000);
171
+
172
+ log('searxng motoru kuruluyor…');
173
+ await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', '--no-build-isolation', PKG_URL], 900000);
174
+
175
+ /* pwd shim — searxng/valkeydb.py unix socket sahipliği için pwd import eder */
176
+ const sp = (await sitePackagesDir(py)) || path.join(path.dirname(py), 'Lib', 'site-packages');
177
+ const shim = [
178
+ '"""Windows shim: SearXNG valkeydb.py yalnizca unix-socket sahipligi icin pwd',
179
+ 'kullanir; Windows\'ta valkey devre disi oldugundan donuk yanit yeterlidir."""',
180
+ 'import os',
181
+ '',
182
+ 'class _Pw:',
183
+ ' pw_name = "beast"',
184
+ ' pw_uid = 0',
185
+ ' pw_gid = 0',
186
+ ' pw_dir = os.path.expanduser("~")',
187
+ ' pw_shell = ""',
188
+ '',
189
+ 'def getpwuid(uid):',
190
+ ' return _Pw()',
191
+ '',
192
+ 'def getpwnam(name):',
193
+ ' return _Pw()',
194
+ '',
195
+ 'def getgrgid(gid):',
196
+ ' class _Gr:',
197
+ ' gr_name = "beast"',
198
+ ' gr_gid = 0',
199
+ ' return _Gr()',
200
+ '',
201
+ 'def getgrnam(name):',
202
+ ' return getgrgid(0)',
203
+ ].join('\n');
204
+ fs.writeFileSync(path.join(sp, 'pwd.py'), shim, 'utf8');
205
+
206
+ /* ayarlar + çalıştırıcı */
207
+ const secret = 'beast-' + Math.random().toString(36).slice(2, 14);
208
+ fs.writeFileSync(
209
+ settingsPath(),
210
+ [
211
+ 'use_default_settings: true',
212
+ 'server:',
213
+ ` secret_key: "${secret}"`,
214
+ ' port: 8888',
215
+ ' bind_address: "127.0.0.1"',
216
+ ' limiter: false',
217
+ ' image_proxy: false',
218
+ 'search:',
219
+ ' formats:',
220
+ ' - html',
221
+ ' - json',
222
+ '',
223
+ ].join('\n'),
224
+ 'utf8'
225
+ );
226
+ fs.writeFileSync(runPyPath(), 'from searx.webapp import run; run()', 'utf8');
227
+ resetProbeCache();
228
+ log('kurulum tamam');
229
+ return { ok: true };
230
+ }
231
+
232
+ /* ---------- başlat / durdur / durum ---------- */
233
+
234
+ function readPid() {
235
+ try {
236
+ const s = fs.readFileSync(pidPath(), 'utf8').trim();
237
+ return /^\d+$/.test(s) ? Number(s) : null;
238
+ } catch {
239
+ return null;
240
+ }
241
+ }
242
+
243
+ async function start({ installIfMissing = true, onLog } = {}) {
244
+ if (await isUp(DEFAULT_URL, { force: true })) return { ok: true, already: true, url: DEFAULT_URL };
245
+ if (!fs.existsSync(settingsPath()) || !fs.existsSync(runPyPath())) {
246
+ if (!installIfMissing) return { ok: false, error: 'kurulu değil — önce: beast searxng' };
247
+ await install({ onLog });
248
+ }
249
+ const tools = require('./tools');
250
+ const probe = await tools.ensurePython();
251
+ const child = spawn(probe.exe, [runPyPath()], {
252
+ detached: true,
253
+ stdio: 'ignore',
254
+ windowsHide: true,
255
+ env: { ...process.env, SEARXNG_SETTINGS_PATH: settingsPath(), PYTHONIOENCODING: 'utf-8' },
256
+ });
257
+ try { fs.writeFileSync(pidPath(), String(child.pid)); } catch {}
258
+ child.unref();
259
+ /* ayağa kalkmasını bekle (ilk açılış motor listesini derler — birkaç sn) */
260
+ for (let i = 0; i < 20; i++) {
261
+ await new Promise((r) => setTimeout(r, 1000));
262
+ if (await isUp(DEFAULT_URL, { force: true })) return { ok: true, url: DEFAULT_URL, pid: child.pid };
263
+ }
264
+ return { ok: false, error: 'searxng başlatılamadı (20 sn içinde yanıt yok)' };
265
+ }
266
+
267
+ function stop() {
268
+ const pid = readPid();
269
+ if (!pid) return { ok: true, note: 'çalışmıyor' };
270
+ try {
271
+ execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore' });
272
+ } catch {}
273
+ try { fs.unlinkSync(pidPath()); } catch {}
274
+ resetProbeCache();
275
+ return { ok: true, stopped: pid };
276
+ }
277
+
278
+ async function status() {
279
+ return {
280
+ up: await isUp(DEFAULT_URL, { force: true }),
281
+ url: DEFAULT_URL,
282
+ pid: readPid(),
283
+ installed: fs.existsSync(settingsPath()) && fs.existsSync(runPyPath()),
284
+ };
285
+ }
286
+
287
+ /* ---------- CLI (bin/beast-agent.js → beast searxng) ---------- */
288
+
289
+ async function cli(argv) {
290
+ const sub = String(argv[0] || 'start').toLowerCase();
291
+ console.log('\u27F3 SearXNG (yerel arama motoru)');
292
+ if (sub === 'stop') {
293
+ const r = stop();
294
+ console.log(r.ok ? '\u2713 durduruldu' : '\u2717 durdurulamadı');
295
+ process.exit(r.ok ? 0 : 1);
296
+ }
297
+ if (sub === 'status') {
298
+ const s = await status();
299
+ console.log(` kurulu : ${s.installed ? 'evet' : 'hayır'}`);
300
+ console.log(` çalışıyor: ${s.up ? 'evet (' + s.url + ')' : 'hayır'}`);
301
+ process.exit(0);
302
+ }
303
+ /* start */
304
+ const r = await start({ installIfMissing: true, onLog: (m) => console.log(' \u2022 ' + m) });
305
+ if (r.ok) {
306
+ console.log(r.already ? '\u2713 SearXNG zaten çalışıyor — ' + r.url : '\u2713 SearXNG başlatıldı — ' + r.url);
307
+ console.log('\u2139 arama zincirinde kullanılmaya hazır (Ayarlar → Web Arama sırası)');
308
+ process.exit(0);
309
+ }
310
+ console.error('\u2717 ' + (r.error || 'başlatılamadı'));
311
+ process.exit(1);
312
+ }
313
+
314
+ module.exports = {
315
+ search,
316
+ isUp,
317
+ resetProbeCache,
318
+ install,
319
+ start,
320
+ stop,
321
+ status,
322
+ cli,
323
+ settingsPath,
324
+ DEFAULT_URL,
325
+ };