beast-agent 0.24.4 → 0.25.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "0.24.4",
4
+ "version": "0.25.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -333,41 +333,6 @@ class Engine {
333
333
  else this.sessionTools.delete(id);
334
334
  }
335
335
 
336
- /* ANA SOHBETE DAVETLİ BOTLAR: oturuma guest bot ekle/çıkar (persist) */
337
- setSessionGuests(sessionId, guests) {
338
- const s = this._load(String(sessionId || ''));
339
- if (!s) return false;
340
- const items = (Array.isArray(guests) ? guests : [])
341
- .slice(0, 4)
342
- .map((g) => ({ id: String((g && g.id) || ''), code: String((g && g.code) || ''), name: String((g && g.name) || '').slice(0, 40) }))
343
- .filter((g) => g.id && g.code);
344
- s.guests = items;
345
- try {
346
- const file = this._file(s.id);
347
- const lines = fs.readFileSync(file, 'utf8').split('\n').filter((l) => l.trim());
348
- const kept = lines.filter((l) => {
349
- try { return JSON.parse(l).t !== 'guests'; } catch { return true; }
350
- });
351
- const out = kept.join('\n') + (items.length ? '\n' + JSON.stringify({ t: 'guests', items }) : '') + '\n';
352
- const tmp = file + '.tmp';
353
- fs.writeFileSync(tmp, out);
354
- fs.renameSync(tmp, file);
355
- } catch {}
356
- return true;
357
- }
358
-
359
- /* Davetli botlar system prompt bloğu — ajan bot_dm ile onlara danışır */
360
- _guestsBlock(session) {
361
- const g = Array.isArray(session && session.guests) ? session.guests : [];
362
- if (!g.length) return '';
363
- const lines = g.map((x) => `- ${x.name} (kod: ${x.code})`).join('\n');
364
- return (
365
- `# DAVETLİ BOTLAR\n` +
366
- `Bu sohbete şu botlar davet edildi. Kullanıcı onlara hitap ederse veya uzmanlıkları gereken bir konu olursa ` +
367
- `bot_dm aracıyla (to=5 haneli kod) onlara yaz; aldığın cevabı [BotAdı] etiketiyle kullanıcıya aktar:\n${lines}`
368
- );
369
- }
370
-
371
336
  /* Oturumun bağlı olduğu MÜŞTERİ botu (admin/seasız → null → global hafıza) */
372
337
  _sessionBotCtx(session) {
373
338
  if (!session || !session.botId || typeof this.resolveBot !== 'function') return null;
@@ -947,9 +912,6 @@ class Engine {
947
912
  session.isBotDm = true;
948
913
  session.dmA = String(rec.a || '');
949
914
  session.dmB = String(rec.b || '');
950
- } else if (rec.t === 'guests') {
951
- /* ana sohbete davetli botlar */
952
- session.guests = Array.isArray(rec.items) ? rec.items : [];
953
915
  } else if (rec.t === 'msg') {
954
916
  delete rec.t;
955
917
  session.messages.push(rec);
@@ -1086,7 +1048,6 @@ class Engine {
1086
1048
  count: s.messages.length,
1087
1049
  isBg,
1088
1050
  isBotDm: !!s.isBotDm,
1089
- guests: Array.isArray(s.guests) ? s.guests : [],
1090
1051
  bgStatus: isBg ? (job ? job.status : null) : null,
1091
1052
  };
1092
1053
  }
@@ -1330,11 +1291,11 @@ class Engine {
1330
1291
  : 'Bağımsız alt-işleri delegate_task ile devret; kendi başına halledebileceğin işleri devretme.',
1331
1292
  'Güncel/dış bilgi gerekiyorsa web_search kullan (zincir DAHİLİ tarayıcıyla başlar — gerçek Chromium ile Google); hızlı ham metin okuması için http_fetch kullan.',
1332
1293
  'ARAMA-DİSİPLİN: bir bilgiyi 2-3 denemede bulamazsan TAKILMA — farklı bir açıya/sorguya geç, yine yoksa bulabildiğin kısmi bilgiyle cevap ver ve neyi bulamadığını açıkça söyle. Kapalı/gizli içerik (private profil, login arkası veri) peşinde KOŞMA — bulunamayacağı belliyse hemen vazgeç.',
1333
- 'TARAYICI VARSAYILANI (ZORUNLU): kullanıcı "şu siteyi aç", "bunu ara / google\u2019da ara", "şu sayfaya git" gibi bir web isteği verdiğinde HEP DAHİLİ TARAYICIYI KULLAN (browser_open ile aç → browser_snapshot al → ref numaralarıyla browser_click/browser_type/browser_select ile hareket et, browser_read ile metin oku; sadece görsel yerleşim/grafik gerekiyorsa browser_screenshot çek). Panel ekranın sağında açılır ve kullanıcı da sayfayı anında görür; JS/login/SPA/dinamik içerik için idealdir. Bu kural TÜM oturumları kapsar — masaüstü sohbeti ve çok kullanıcılı botlar (WhatsApp vb.) dahil.' +
1294
+ 'TARAYICI VARSAYILANI (ZORUNLU): kullanıcı "şu siteyi aç", "bunu ara / google\u2019da ara", "şu sayfaya git" gibi bir web isteği verdiğinde HEP DAHİLİ TARAYICIYI KULLAN (browser_open ile aç → açılış yanıtında hazır snapshot gelir → ref numaralarıyla browser_click/browser_type/browser_select ile hareket et; HER eylem yanıtında taze snapshot döner — ayrıca browser_snapshot çağırma, yanıttaki refleri kullan; browser_read ile metin oku; sadece görsel yerleşim/grafik gerekiyorsa browser_screenshot çek). Tarih/saat alanlarını (type=date/time) browser_type ile DÜZ METİN yaz ("15.03.2026", "2026-03-15") — takvimden tıklamaya çalışma, alan programatik ayarlanır. Panel ekranın sağında açılır ve kullanıcı da sayfayı anında görür; JS/login/SPA/dinamik içerik için idealdir. Bu kural TÜM oturumları kapsar — masaüstü sohbeti ve çok kullanıcılı botlar (WhatsApp vb.) dahil.' +
1334
1295
  ' DIŞ TARAYICI İSTİSNASI: kullanıcı AÇIKÇA "chrome\u2019da aç", "firefox\u2019ta aç", "başka tarayıcıda aç", "normal tarayıcıda aç", "kendi tarayıcımda aç" derse O ZAMAN run_command ile `start "" <url>` çalıştır — sistem varsayılan (dış) tarayıcısında açılır. Kullanıcı dış tarayıcı istemedikçe ASLA dış tarayıcı açma.' +
1335
1296
  ' Görselleri GÖREMEMİYORSAN (metin-model) görüntüdeki metni okumak için ocr_read kullan: source:"browser" ile tarayıcı sayfasını, "screen" ile masaüstünü OCR\u2019la okursun (captcha/canvas/görsel metin dahil).' +
1336
1297
  (this.ceoMode ? ' (CEO: bu araçları KENDİN ÇAĞIRMA — içinde tarayıcı geçen işi run_background ile paralel ajana devret.)' : ''),
1337
- 'Tarayıcı eylemlerinin yanıtındaki recent günlüğü ve navigated bilgisini takip et; sayfa değişmişse yeni snapshot al.',
1298
+ 'Tarayıcı eylemlerinin yanıtındaki recent günlüğü ve navigated bilgisini takip et; eylem yanıtları zaten taze snapshot içerir — refler tutarsız görünürse yeni snapshot al.',
1338
1299
  'CONUŞMA ODAĞI SENDE KALSIN: kullanıcı seninle konuşurken iş çıkmışsa — uzun da olsa UFACIK da (tek komutluk dizin listesi, tek dosya okuma, tek arama…) — run_background ile PARALEL ajana devret; ana sohbet hiçbir işi beklemez; bittiğinde özet otomatik düşer.',
1339
1300
  'Python işleri için python_run kullan: küçük betikler inline code ile; tekrarlayan işler %APPDATA%\\beast\\scripts klasöründeki dosyalarla (ör. news.py = RSS haber toplayıcı: args ["--limit","8","--json"]). Python kurulu olmasa bile ilk çağrıda taşınabilir gömülü runtime otomatik iner.',
1340
1301
  'PYTHON DURUMU: makinede sistem Python\'u görünmese bile ŞAŞIRMA ve "python yok" DEME — python_run aracı kendi taşınabilir runtime\'ını (%APPDATA%\\beast\\py\\python.exe) otomatik indirir/kullanır ve bu klasör run_command PATH\'inde önceliklidir; yani run_command içinde de `python` çalışır. Ham Google/Bing scrape yerine önce web_search aracını kullan (zaten Python çoklu-motor + Exa destekli), script gerektiğinde python_run yaz.',
@@ -2220,8 +2181,6 @@ class Engine {
2220
2181
  /* bot kimliği: bota bağlı oturumlarda kişilik + izolasyon kuralları */
2221
2182
  const botBlock = this._botSystemBlock(session);
2222
2183
  if (botBlock) system += '\n\n' + botBlock;
2223
- const guestBlock = this._guestsBlock(session);
2224
- if (guestBlock) system += '\n\n' + guestBlock;
2225
2184
  if (session.notes) {
2226
2185
  system +=
2227
2186
  `\n\n# OTURUM NOTLARI (oturum ${session.code || '?'} — bu oturumun önceki konuşma özeti; birebir geçmişi buradan hatırla)\n` +
@@ -3112,23 +3071,38 @@ class Engine {
3112
3071
  return JSON.stringify(r);
3113
3072
  }
3114
3073
  if (name === 'web_search') {
3115
- /* SIRA: 0) TinyFish (anahtar girildiyse ZİRVEDE)
3116
- 1) dahili tarayıcı (gerçek Chromium ile DİREK GOOGLEbot koruması yok)
3117
- 2) python çoklu-motor (ddgs / DDG+Bing+Mojeek)
3118
- 3) Exa (anahtar girildiyse, son çare) */ const q = String((args && args.query) || '');
3074
+ /* SIRA: 1) dahili tarayıcı (DİREK GOOGLE — ücretsiz + AI cevabı)
3075
+ 2) TinyFish (tarayıcı sorun çıkarsa HEMEN devreye gireranahtar girildiyse)
3076
+ 3) python çoklu-motor (ddgs / DDG+Bing+Mojeek)
3077
+ 4) Exa (anahtar girildiyse, son çare)
3078
+ Tarayıcıda CAPTCHA/"olağandışı trafik"/boş sonuç algılanırsa 10 dk boyunca
3079
+ tarayıcı ATLANIR — her arama TinyFish'ten başlar, süre dolunca tarayıcı geri döner. */
3080
+ const q = String((args && args.query) || '');
3081
+ const n = Number((args && args.max_results) || 8);
3119
3082
  let r = null;
3120
- try { r = await tools.tinyfishSearch(q, Number((args && args.max_results) || 8), signal); } catch {}
3121
- if (!r || !r.ok || !(r.results || []).length) {
3122
- if (this.browser && typeof this.browser.search === 'function') {
3123
- try { r = await this.browser.search(q, signal); } catch {}
3083
+ let banned = false;
3084
+ const browserUsable = this.browser && typeof this.browser.search === 'function';
3085
+ if (Date.now() < (this._webSearchBrowserBanUntil || 0)) banned = true;
3086
+ if (browserUsable && !banned) {
3087
+ try { r = await this.browser.search(q, signal); } catch {}
3088
+ if (!r || !r.ok || !(r.results || []).length) {
3089
+ /* tarayıcı sorunlu — 10 dk atla, alternatif zincir hemen devrede */
3090
+ this._webSearchBrowserBanUntil = Date.now() + 10 * 60 * 1000;
3091
+ r = null;
3092
+ banned = true;
3124
3093
  }
3125
3094
  }
3095
+ if (!r || !r.ok || !(r.results || []).length) {
3096
+ try { r = await tools.tinyfishSearch(q, n, signal); } catch {}
3097
+ }
3126
3098
  if (!r || !r.ok || !(r.results || []).length) {
3127
3099
  try {
3128
3100
  r = JSON.parse(await tools.exec('web_search', args, { cwd: this._sessionWorkspace(sessionId), signal }));
3129
3101
  } catch {}
3130
3102
  }
3131
- return JSON.stringify(r || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' });
3103
+ const out = r || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' };
3104
+ if (out && out.ok && banned) out.note = 'tarayıcı araması 10 dk askıda (CAPTCHA/trafik) — TinyFish/alternatif zincir kullanıldı';
3105
+ return JSON.stringify(out);
3132
3106
  }
3133
3107
  if (name === 'ocr_read') {
3134
3108
  return JSON.stringify(await this._ocrRead(args || {}, signal));
@@ -3676,7 +3650,7 @@ const TOOLS = [
3676
3650
  function: {
3677
3651
  name: 'browser_open',
3678
3652
  description:
3679
- 'Open a URL in the built-in visible browser panel (JS-rendered pages work). This is the DEFAULT for every "open this site / search this / go to page" request — the user watches the page live in the side panel; ideal for login, SPA and dynamic content. Always follow with browser_snapshot, then act via refs (browser_click/browser_type/browser_select) and read via browser_read. Open the OS default (external) browser ONLY when the user explicitly asks for chrome/firefox/another/normal/my-own browser — in that case use run_command with `start "" <url>` instead of this tool.',
3653
+ 'Open a URL in the built-in visible browser panel (JS-rendered pages work). This is the DEFAULT for every "open this site / search this / go to page" request — the user watches the page live in the side panel; ideal for login, SPA and dynamic content. The response ALREADY includes a fresh snapshot with numbered refs act directly with browser_click/browser_type/browser_select; no separate browser_snapshot needed. Open the OS default (external) browser ONLY when the user explicitly asks for chrome/firefox/another/normal/my-own browser — in that case use run_command with `start "" <url>` instead of this tool.',
3680
3654
  parameters: {
3681
3655
  type: 'object',
3682
3656
  properties: {
@@ -3709,7 +3683,7 @@ const TOOLS = [
3709
3683
  function: {
3710
3684
  name: 'browser_snapshot',
3711
3685
  description:
3712
- 'List all interactive elements of the built-in browser page as numbered refs: [3] <button> "Gönder". ALWAYS start with this; then act using ref numbers. Much more reliable than CSS selectors.',
3686
+ 'List all interactive elements of the built-in browser page as numbered refs: [3] <button> "Gönder". NOTE: every browser action response already includes a fresh snapshot — call this separately only when refs seem stale or you need a re-scan (e.g. after an action whose response had no snapshot). Open popups/calendars/datepickers are listed FIRST. Much more reliable than CSS selectors.',
3713
3687
  parameters: { type: 'object', properties: {}, required: [] },
3714
3688
  },
3715
3689
  },
@@ -3733,7 +3707,7 @@ const TOOLS = [
3733
3707
  function: {
3734
3708
  name: 'browser_click',
3735
3709
  description:
3736
- 'Click an element in the built-in browser. Prefer ref from browser_snapshot (e.g. {"ref":3}); CSS selector or text=X also accepted.',
3710
+ 'Click an element in the built-in browser. Prefer ref from the latest snapshot (e.g. {"ref":3}); CSS selector or text=X also accepted. The response includes a FRESH snapshot with new refs — continue with those directly instead of calling browser_snapshot again.',
3737
3711
  parameters: {
3738
3712
  type: 'object',
3739
3713
  properties: {
@@ -3748,7 +3722,7 @@ const TOOLS = [
3748
3722
  function: {
3749
3723
  name: 'browser_type',
3750
3724
  description:
3751
- 'Type text into an input/textarea/contenteditable in the built-in browser (prefer ref). Set submit=true to press Enter afterwards.',
3725
+ 'Type text into an input/textarea/contenteditable in the built-in browser (prefer ref). Set submit=true to press Enter afterwards. Date/time fields (input type=date/time/month/datetime-local) are set PROGRAMMATICALLY — just send the date as text in any common format ("2026-03-15", "15.03.2026", "15 Mart 2026"); do NOT click the calendar popup. The response includes a FRESH snapshot with new refs.',
3752
3726
  parameters: {
3753
3727
  type: 'object',
3754
3728
  properties: {
@@ -3766,7 +3740,7 @@ const TOOLS = [
3766
3740
  function: {
3767
3741
  name: 'browser_press',
3768
3742
  description:
3769
- 'Press a key (Enter, Tab, Escape, ArrowDown…) on the focused element in the built-in browser; optionally focus a ref first.',
3743
+ 'Press a key (Enter, Tab, Escape, ArrowDown…) on the focused element in the built-in browser; optionally focus a ref first. The response includes a FRESH snapshot with new refs.',
3770
3744
  parameters: {
3771
3745
  type: 'object',
3772
3746
  properties: {
@@ -3781,7 +3755,8 @@ const TOOLS = [
3781
3755
  type: 'function',
3782
3756
  function: {
3783
3757
  name: 'browser_scroll',
3784
- description: 'Scroll the built-in browser page. direction: up | down.',
3758
+ description:
3759
+ 'Scroll the built-in browser page. direction: up | down. The response includes a FRESH snapshot with newly visible refs.',
3785
3760
  parameters: {
3786
3761
  type: 'object',
3787
3762
  properties: {
@@ -3797,7 +3772,7 @@ const TOOLS = [
3797
3772
  function: {
3798
3773
  name: 'browser_select',
3799
3774
  description:
3800
- 'Pick an <option> of a dropdown (<select>) in the built-in browser by value/text (prefer ref).',
3775
+ 'Pick an <option> of a dropdown (<select>) in the built-in browser by value/text (prefer ref). The response includes a FRESH snapshot with new refs. For CUSTOM (JS) dropdowns that are not <select>, click the trigger, then click the [role=option] ref from the snapshot (popups are listed first).',
3801
3776
  parameters: {
3802
3777
  type: 'object',
3803
3778
  properties: {
@@ -620,7 +620,7 @@ const definitions = [
620
620
  function: {
621
621
  name: 'web_search',
622
622
  description:
623
- 'FAST web search with an automatic chain: built-in browser first (real Chromium searching GOOGLE directly with AI Mode — no bot protection; the response may include an `ai` field holding Google\'s own AI answer, ideal for "who/what is X" questions), then Python multi-engine (ddgs / DuckDuckGo+Bing+Mojeek parallel), then Exa API as last resort if a key is configured (Ayarlar → Web Arama). Returns {ai?, results[{title,url,snippet}]}. Use when fresh or external info is needed; skip for things you already know.',
623
+ 'FAST web search with an automatic chain: built-in browser first (real Chromium searching GOOGLE directly with AI Mode — no bot protection; the response may include an `ai` field holding Google\'s own AI answer, ideal for "who/what is X" questions), then TinyFish API (if a key is configured, used IMMEDIATELY whenever the browser hits CAPTCHA/unusual traffic — the browser is then skipped for 10 minutes), then Python multi-engine (ddgs / DuckDuckGo+Bing+Mojeek parallel), then Exa API as last resort if a key is configured (Ayarlar → Web Arama). Returns {ai?, results[{title,url,snippet}]}. Use when fresh or external info is needed; skip for things you already know.',
624
624
  parameters: {
625
625
  type: 'object',
626
626
  properties: {
package/src/main.js CHANGED
@@ -2285,11 +2285,24 @@ function ensureDesktopShortcut() {
2285
2285
  try {
2286
2286
  const desktop = app.getPath('desktop');
2287
2287
  const lnk = path.join(desktop, 'Beast Agent.lnk');
2288
- if (fs.existsSync(lnk)) return;
2288
+ /* çalışma klasörü kullanıcı home'u olsun: paket klasöründe başlarsa güncelleme
2289
+ cmd'si orada açılır ve npm install -g klasör kilidi (EBUSY) yemek zorunda kalır */
2290
+ const goodCwd = app.getPath('home');
2291
+ if (fs.existsSync(lnk)) {
2292
+ /* eski kısayollar paket klasörünü çalışma klasörü olarak taşıyordu — onar */
2293
+ try {
2294
+ const cur = shell.readShortcutLink(lnk);
2295
+ if (cur && cur.cwd && /node_modules[\\/]beast-agent/i.test(cur.cwd)) {
2296
+ shell.writeShortcutLink(lnk, 'update', { ...cur, cwd: goodCwd });
2297
+ log.info('main', 'Masaüstü kısayolu çalışma klasörü home\u2019a taşındı');
2298
+ }
2299
+ } catch {}
2300
+ return;
2301
+ }
2289
2302
  const ok = shell.writeShortcutLink(lnk, 'create', {
2290
2303
  target: process.execPath,
2291
2304
  args: app.getAppPath(),
2292
- cwd: app.getAppPath(),
2305
+ cwd: goodCwd,
2293
2306
  description: 'Beast Agent — hızlı, hafif ve becerikli',
2294
2307
  icon: path.join(__dirname, '..', 'assets', 'app.ico'),
2295
2308
  iconIndex: 0,
@@ -2542,7 +2555,31 @@ function setBrowserOpen(v, forceVisible) {
2542
2555
  browserEmit({ open: true, width: browser.width, url });
2543
2556
  }
2544
2557
 
2558
+ /* --- Paralel ajan trafik düzeni: ajanlar aynı saniyede sorgu atınca Google
2559
+ "olağandışı trafik" uyarısı veriyor. Gezinme/arama isteklerini tek kuyrukta
2560
+ sıraya alıp aralarına min 1.2 sn + rastgele 0-600 ms jitter koyuyoruz.
2561
+ Kuyruğu tutan işin İÇİNDEN yapılan gezinmeler kapıyı atlar (kilitlenme olmaz). --- */
2562
+ const BROWSER_TRAFFIC_GAP = 1200;
2563
+ let __trafficLast = 0;
2564
+ let __trafficTail = Promise.resolve();
2565
+ function browserTrafficWait() {
2566
+ const wait = Math.max(0, __trafficLast + BROWSER_TRAFFIC_GAP + Math.floor(Math.random() * 600) - Date.now());
2567
+ __trafficLast = Date.now() + wait;
2568
+ return wait > 0 ? new Promise((r) => setTimeout(r, wait)) : Promise.resolve();
2569
+ }
2570
+ function browserGate(job) {
2571
+ const p = __trafficTail.then(job, job);
2572
+ __trafficTail = p.catch(() => {});
2573
+ return p;
2574
+ }
2545
2575
  async function browserNavigate(raw, signal) {
2576
+ return browserGate(async () => {
2577
+ await browserTrafficWait();
2578
+ return browserNavigateNow(raw, signal);
2579
+ });
2580
+ }
2581
+
2582
+ async function browserNavigateNow(raw, signal) {
2546
2583
  let url = String(raw || '').trim();
2547
2584
  if (!url) return { ok: false, error: 'boş adres' };
2548
2585
  if (!/^https?:\/\//i.test(url)) {
@@ -2572,7 +2609,21 @@ async function browserNavigate(raw, signal) {
2572
2609
  try { title = wc.getTitle(); finalUrl = wc.getURL() || url; } catch {}
2573
2610
  browserEmit({ open: true, width: browser.width, url: finalUrl });
2574
2611
  flushBrowserStorage(); // oturum çerezleri diske — ani kapanışta kaybolmasın
2575
- return { ok: true, url: finalUrl, title };
2612
+ /* açılışta snapshot da göm model ayrı snapshot çağırmadan ref'lere başlar */
2613
+ let snap = null;
2614
+ try {
2615
+ const sraw = await wc.executeJavaScript(BROWSER_SNAPSHOT_JS, true);
2616
+ const sobj = JSON.parse(sraw);
2617
+ if (sobj && typeof sobj.count === 'number') snap = sobj;
2618
+ } catch {}
2619
+ return {
2620
+ ok: true,
2621
+ url: finalUrl,
2622
+ title,
2623
+ ...(snap
2624
+ ? { snapshot: snap.snapshot, refCount: snap.count, note: 'sayfa acildi — güncel snapshot hazır (' + snap.count + ' ref); ref numarasıyla browser_click/browser_type ile devam et' }
2625
+ : { note: 'sayfa acildi — etkileşimli elemanlar için browser_snapshot al' }),
2626
+ };
2576
2627
  }
2577
2628
 
2578
2629
  async function browserRead(signal) {
@@ -2637,6 +2688,14 @@ async function ocrRead({ image, lang = 'tur+eng' } = {}) {
2637
2688
  yapmak (kullanıcının console'da yaptığı gibi) — sayfa bile değişmeden
2638
2689
  DOMParser ile sonuç çekilir. Fetch olmazsa direk gezinme fallback'i var. */
2639
2690
  async function browserSearch(query, signal) {
2691
+ /* paralel ajan sorguları trafik kapısından sırayla geçer */
2692
+ return browserGate(async () => {
2693
+ await browserTrafficWait();
2694
+ return browserSearchNow(query, signal);
2695
+ });
2696
+ }
2697
+
2698
+ async function browserSearchNow(query, signal) {
2640
2699
  try {
2641
2700
  if (!win || win.isDestroyed()) return null;
2642
2701
  if (signal && signal.aborted) return null;
@@ -2650,7 +2709,7 @@ async function browserSearch(query, signal) {
2650
2709
  let cur = '';
2651
2710
  try { cur = wc.getURL() || ''; } catch {}
2652
2711
  if (!cur.startsWith('https://www.google.com')) {
2653
- await browserNavigate('https://www.google.com/', signal);
2712
+ await browserNavigateNow('https://www.google.com/', signal);
2654
2713
  }
2655
2714
  if (!browser.view || !browser.open) return null;
2656
2715
 
@@ -2720,7 +2779,7 @@ async function browserSearch(query, signal) {
2720
2779
 
2721
2780
  /* fallback: fetch yerine direk gezinme + DOM'dan çek */
2722
2781
  if (!results.length) {
2723
- await browserNavigate(
2782
+ await browserNavigateNow(
2724
2783
  'https://www.google.com/search?q=' + encodeURIComponent(q) + '&num=10&hl=tr&pws=0&aep=1',
2725
2784
  signal
2726
2785
  );
@@ -2776,7 +2835,14 @@ async function browserSearch(query, signal) {
2776
2835
  results = parsed.rows || [];
2777
2836
  }
2778
2837
 
2779
- if (!results.length && !parsed.ai) return null;
2838
+ if (!results.length && !parsed.ai) {
2839
+ /* CAPTCHA / olağandışı trafik sinyali — zincir TinyFish'e kaymalı */
2840
+ let blocked = false;
2841
+ try { blocked = /google\.com\/sorry/i.test(wc.getURL() || ''); } catch {}
2842
+ return blocked
2843
+ ? { ok: false, blocked: true, engine: 'browser-google', query: q, error: 'unusual traffic (CAPTCHA)' }
2844
+ : null;
2845
+ }
2780
2846
  flushBrowserStorage();
2781
2847
  const out = { ok: true, engine: 'browser-google', query: q, results };
2782
2848
  if (parsed.ai) out.ai = parsed.ai;
@@ -2840,26 +2906,37 @@ const BROWSER_JS_HELPERS = `
2840
2906
 
2841
2907
  const BROWSER_SNAPSHOT_JS = `(function(){
2842
2908
  ${BROWSER_JS_HELPERS}
2843
- const sel='a[href],button,input:not([type="hidden"]),textarea,select,[role="button"],[role="link"],[role="tab"],[contenteditable="true"],summary';
2844
- const els=[...document.querySelectorAll(sel)];
2909
+ const sel='a[href],button,input:not([type="hidden"]),textarea,select,[role="button"],[role="link"],[role="tab"],[role="option"],[role="menuitem"],[role="gridcell"],[role="checkbox"],[role="switch"],[role="combobox"],[role="textbox"],[role="searchbox"],[contenteditable="true"],summary';
2910
+ /* açık popup/takvim/dialog varsa içindekiler ÖNCE listelenir (tarih seçici, özel dropdown vb.) */
2911
+ const popSel='[role="dialog"],dialog,[role="listbox"],[role="menu"],.flatpickr-calendar,.ui-datepicker,[class*="datepicker" i],[class*="calendar" i],[class*="dropdown" i],[class*="popup" i]';
2912
+ const pops=[...document.querySelectorAll(popSel)].filter(__vis);
2913
+ const inPop=new Set();
2914
+ for(const p of pops){[...p.querySelectorAll(sel)].forEach(e=>inPop.add(e));}
2845
2915
  window.__beMap={};
2916
+ const seen=new Set();
2846
2917
  const lines=[];
2847
2918
  let i=0;
2848
- for(const e of els){
2849
- if(!__vis(e)) continue;
2850
- if(e.disabled||e.getAttribute('aria-disabled')==='true') continue;
2919
+ function add(e){
2920
+ if(!e||seen.has(e)) return;
2921
+ seen.add(e);
2922
+ if(!__vis(e)||e.disabled||e.getAttribute('aria-disabled')==='true') return;
2851
2923
  i++;
2852
2924
  window.__beMap[i]=e;
2853
2925
  const tag=e.tagName.toLowerCase();
2854
- const type=e.getAttribute('type');
2926
+ const type=(e.getAttribute&&e.getAttribute('type'))||'';
2855
2927
  let s='['+i+'] <'+tag+(type?' type='+type:'')+'>';
2856
2928
  const l=__label(e);
2857
2929
  if(l) s+=' "'+l+'"';
2858
- if(tag==='input'&&(type==='text'||type==='email'||type==='password'||type==='search'||type==='tel'||type==='url'||type==='number')&&e.value) s+=' deger="'+String(e.value).slice(0,30)+'"';
2930
+ if(tag==='input'&&/^(text|email|password|search|tel|url|number|date|time|month|datetime-local)$/.test(type)&&e.value) s+=' deger="'+String(e.value).slice(0,30)+'"';
2859
2931
  if(tag==='select'){s+=' secenekler=['+[...e.options].slice(0,6).map(o=>o.text.trim()).filter(Boolean).join('|').slice(0,60)+']';}
2860
2932
  lines.push(s);
2861
- if(i>=100) break;
2862
2933
  }
2934
+ if(inPop.size){
2935
+ lines.push('--- ACIK POPUP/TAKVIM ICINDEKILER (once bunlari kullan) ---');
2936
+ for(const e of inPop){ add(e); if(i>=60) break; }
2937
+ lines.push('--- SAYFA ---');
2938
+ }
2939
+ for(const e of document.querySelectorAll(sel)){ if(i>=100) break; add(e); }
2863
2940
  return JSON.stringify({count:i,title:document.title,url:location.href,snapshot:lines.join('\\n')});
2864
2941
  })()`;
2865
2942
 
@@ -2892,10 +2969,52 @@ function browserActionJs(kind, args) {
2892
2969
  if (kind === 'type') {
2893
2970
  const text = JSON.stringify(String(args.text ?? ''));
2894
2971
  const submit = args.submit ? 'true' : 'false';
2895
- return `(function(){${BROWSER_JS_HELPERS}${resolveTarget};return new Promise((res)=>{try{
2972
+ return `(function(){${BROWSER_JS_HELPERS}${resolveTarget}
2973
+ /*__DT_HELPERS_START__*/
2974
+ function __norm(s){return String(s).toLowerCase().split('').map(function(c){return {'ş':'s','ğ':'g','ü':'u','ı':'i','ö':'o','ç':'c','İ':'i'}[c]||c;}).join('');}
2975
+ function __parseDate(raw){
2976
+ raw=String(raw||'').trim(); if(!raw) return null;
2977
+ const MON={ocak:1,subat:2,mart:3,nisan:4,mayis:5,haziran:6,temmuz:7,agustos:8,eylul:9,ekim:10,kasim:11,aralik:12,jan:1,feb:2,mar:3,apr:4,may:5,jun:6,jul:7,aug:8,sep:9,oct:10,nov:11,dec:12,january:1,february:2,march:3,april:4,june:6,july:7,august:8,september:9,october:10,november:11,december:12};
2978
+ let m=raw.match(/^(\\d{4})[\\-.\\/,](\\d{1,2})[\\-.\\/,](\\d{1,2})$/);
2979
+ if(m) return [+m[1],+m[2],+m[3]];
2980
+ m=raw.match(/^(\\d{1,2})[\\-.\\/ ](\\d{1,2})[\\-.\\/ ](\\d{2,4})$/);
2981
+ if(m){let d=+m[1],mo=+m[2],y=+m[3];if(y<100)y+=2000;if(mo>12&&d<=12){const t=d;d=mo;mo=t;}return [y,mo,d];}
2982
+ m=raw.match(/^(\\d{1,2})[\\-.\\/ ]+([a-zçğıöşü]+)[\\-.\\/ ]+(\\d{2,4})$/i);
2983
+ if(m){const mo=MON[__norm(m[2])];if(mo){let y=+m[3];if(y<100)y+=2000;return [y,mo,+m[1]];}}
2984
+ return null;
2985
+ }
2986
+ function __parseTime(raw){
2987
+ const m=String(raw||'').trim().match(/^(\\d{1,2})[:.h](\\d{2})/);
2988
+ if(!m) return null;
2989
+ return [Math.min(23,+m[1]),Math.min(59,+m[2])];
2990
+ }
2991
+ /*__DT_HELPERS_END__*/
2992
+ return new Promise((res)=>{try{
2896
2993
  const t=__target();
2897
2994
  if(!t) return res(JSON.stringify({typed:false,reason:'eleman bulunamadi (ref eski olabilir - browser_snapshot al)'}));
2898
2995
  const how=t.how,el=t.el;
2996
+ /* tarih/saat alanları: programatik değer + input/change event — native takvim popup'ı hiç açılmaz */
2997
+ const it=(el.tagName==='INPUT'?(el.type||'').toLowerCase():'');
2998
+ if(it==='date'||it==='month'||it==='time'||it==='datetime-local'){
2999
+ const RAW=${text};
3000
+ let v=null;
3001
+ if(it==='time'){
3002
+ const p=__parseTime(RAW); if(p) v=p.map(function(n){return String(n).padStart(2,'0');}).join(':');
3003
+ } else {
3004
+ const p=__parseDate(RAW);
3005
+ if(p&&p.every(Number.isFinite)&&p[1]>=1&&p[1]<=12&&p[2]>=1&&p[2]<=31){
3006
+ const pad=function(n){return String(n).padStart(2,'0');};
3007
+ const ymd=p[0]+'-'+pad(p[1])+'-'+pad(p[2]);
3008
+ v = it==='month' ? (p[0]+'-'+pad(p[1])) : it==='datetime-local' ? (ymd+'T'+((__parseTime(RAW)||[12,0])).map(pad).join(':')) : ymd;
3009
+ }
3010
+ }
3011
+ if(!v) return res(JSON.stringify({typed:false,inputType:it,reason:'tarih/saat alanı — metin anlaşılamadı. "2026-03-15", "15.03.2026" veya "15 Mart 2026" gibi gönder'}));
3012
+ const setter=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set;
3013
+ setter.call(el,v);
3014
+ el.dispatchEvent(new Event('input',{bubbles:true}));
3015
+ el.dispatchEvent(new Event('change',{bubbles:true}));
3016
+ return res(JSON.stringify({typed:true,target:how+' <input type='+it+'>',value:v,note:'tarih/saat programatik ayarlandi — takvim tiklamak gerekmez'}));
3017
+ }
2899
3018
  el.scrollIntoView({block:'center'});el.focus();
2900
3019
  setTimeout(()=>{try{
2901
3020
  if(el.isContentEditable){el.textContent=${text};}
@@ -3022,6 +3141,14 @@ async function browserAct(kind, args, signal) {
3022
3141
  : (obj.reason || 'tamam')
3023
3142
  );
3024
3143
 
3144
+ /* HIZ: eylem cevabına taze snapshot göm — model ayrı browser_snapshot çağırmaz (tur sayısı yarıya iner) */
3145
+ let freshSnap = null;
3146
+ try {
3147
+ const sraw = await wc.executeJavaScript(BROWSER_SNAPSHOT_JS, true);
3148
+ const sobj = JSON.parse(sraw);
3149
+ if (sobj && typeof sobj.count === 'number') freshSnap = sobj;
3150
+ } catch {}
3151
+
3025
3152
  return {
3026
3153
  ok: true,
3027
3154
  action: kind,
@@ -3029,7 +3156,12 @@ async function browserAct(kind, args, signal) {
3029
3156
  url: wc.getURL(),
3030
3157
  title: wc.getTitle(),
3031
3158
  navigated,
3032
- ...(navigated ? { note: 'sayfa degisti gerekirse yeni browser_snapshot al' } : {}),
3159
+ ...(freshSnap ? { snapshot: freshSnap.snapshot, refCount: freshSnap.count } : {}),
3160
+ ...(navigated
3161
+ ? { note: 'sayfa degisti — yanıtta güncel snapshot var; refler eskiyse yeni browser_snapshot al' }
3162
+ : freshSnap
3163
+ ? { note: 'yanıtta güncel snapshot (' + freshSnap.count + ' ref) — sonraki hamlede bunları kullan, ayrıca snapshot alma' }
3164
+ : {}),
3033
3165
  recent: recentLog(),
3034
3166
  };
3035
3167
  } catch (e) {
@@ -4105,14 +4237,14 @@ const NPM_ONLY_TEXT =
4105
4237
  /* buton akışı: görünür cmd + "beast update" + uygulama kapanışı */
4106
4238
  function npmUpdateViaCmd() {
4107
4239
  try {
4108
- const shim = path.join(process.env.APPDATA || '', 'npm', 'beast-agent.cmd');
4109
- /* TIRNAKSIZ komut: cmd /k tırnaklı yol alırsa komutu 'yazılmış ama çalıştırılmamış'
4110
- gösterir. Önce PATH'ten dene; %APPDATA% değişkeni cmd içinde genişler. */
4111
- const shimOk = fs.existsSync(shim);
4112
- const inner = shimOk ? '%APPDATA%\\npm\\beast-agent.cmd update' : 'beast update';
4240
+ /* cmd KULLANICININ kendi klasöründe açılır (cd /d %USERPROFILE%): Electron kısayoldan
4241
+ paket klasörü (node_modules\beast-agent) içinde başlatıldıysa cmd orada açılır ve
4242
+ npm install -g değiştirmeye çalıştığı paketi KİLİTLER (EBUSY) 5 denemede de
4243
+ patlar. Home'da açılınca kurulum sorunsuz akar; "beast" PATH'te %APPDATA%\npm'de.
4244
+ NOT: spawn argümanlarında ÇİFT TIRNAK kullanma node \" olarak escape'ler ve cmd bozar. */
4113
4245
  spawn(
4114
4246
  'cmd.exe',
4115
- ['/c', 'start', 'Beast Guncelleme', 'cmd', '/k', inner],
4247
+ ['/c', 'start', 'Beast Guncelleme', 'cmd', '/k', 'cd /d %USERPROFILE% && beast update'],
4116
4248
  { detached: true, stdio: 'ignore', windowsHide: false }
4117
4249
  ).unref();
4118
4250
  } catch {}
@@ -4390,22 +4522,9 @@ ipcMain.handle('terminal:toggle', () => {
4390
4522
  return { ok: true, cwd: (engine && engine.workspace) || settings.workspace || app.getPath('home') };
4391
4523
  });
4392
4524
 
4393
- /* Git Bash kuruluysa yolunu bulur; yoksa null döner */
4394
- function findGitBash() {
4395
- const cands = [];
4396
- if (process.env['ProgramFiles']) cands.push(path.join(process.env['ProgramFiles'], 'Git', 'bin', 'bash.exe'));
4397
- if (process.env['ProgramFiles(x86)']) cands.push(path.join(process.env['ProgramFiles(x86)'], 'Git', 'bin', 'bash.exe'));
4398
- if (process.env['LocalAppData']) cands.push(path.join(process.env['LocalAppData'], 'Programs', 'Git', 'bin', 'bash.exe'));
4399
- cands.push('C:\\Program Files\\Git\\bin\\bash.exe');
4400
- for (const c of cands) {
4401
- try { if (fs.existsSync(c)) return c; } catch {}
4402
- }
4403
- return null;
4404
- }
4405
-
4406
4525
  ipcMain.handle('terminal:run', (_e, payload) => {
4407
4526
  const cmd = String((payload && payload.cmd) || '').trim();
4408
- const shell = String((payload && payload.shell) || 'powershell');
4527
+ const shell = String((payload && payload.shell) || 'cmd');
4409
4528
  if (!cmd) return { ok: false, error: 'boş komut' };
4410
4529
  if (termChild) return { ok: false, error: 'önceki komut sürüyor — ■ ile durdurabilirsin' };
4411
4530
  const cwd = (engine && engine.workspace) || settings.workspace || app.getPath('home');
@@ -4414,12 +4533,8 @@ ipcMain.handle('terminal:run', (_e, payload) => {
4414
4533
  if (shell === 'cmd') {
4415
4534
  file = 'cmd.exe';
4416
4535
  args = ['/d', '/s', '/c', cmd];
4417
- } else if (shell === 'bash') {
4418
- const bash = findGitBash();
4419
- if (!bash) return { ok: false, error: 'Git Bash bulunamadı — Git for Windows kurulu mu? (https://git-scm.com)' };
4420
- file = bash;
4421
- args = ['-c', cmd];
4422
4536
  } else {
4537
+ /* geriye dönük uyumluluk — UI artık yalnız CMD gönderir */
4423
4538
  file = 'powershell.exe';
4424
4539
  args = ['-NoProfile', '-NoLogo', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', cmd];
4425
4540
  }
@@ -5412,10 +5527,61 @@ ipcMain.handle('bots:add', (_e, input) => {
5412
5527
  ipcMain.handle('bots:dm:list', () => engine.listBotDmSessions());
5413
5528
  ipcMain.handle('bots:dm:read', (_e, id) => engine.readBotDm(id));
5414
5529
 
5415
- /* Ana sohbete davetli botlar */
5416
- ipcMain.handle('sessions:guests:set', (_e, { sessionId, guests }) => {
5417
- try { engine.setSessionGuests(sessionId, guests); } catch {}
5418
- return { ok: true };
5530
+ /* GITHUB MODALI Trending / Tüm Repolar: GitHub Search API.
5531
+ opts.mode: 'trend' (varsayılan) | 'all' (tüm repolar yıldız tabanı yok)
5532
+ opts.range: all | 6m | 1m | 2w | 1w (tüm zamanlar / 6 ay / 1 ay / 2 hafta / 1 hafta)
5533
+ opts.order: desc (azalan) | asc (artan) — yıldız sırası
5534
+ opts.q: arama terimi (trend modunda tarih filtresi UYGULANMAZ — her zaman tüm zamanlar)
5535
+ Kimliksiz 60 istek/saat limiti var — 403'te UI bilgi gösterir. */
5536
+ ipcMain.handle('github:trending', async (_e, opts) => {
5537
+ const o = opts && typeof opts === 'object' ? opts : {};
5538
+ const mode = o.mode === 'all' ? 'all' : 'trend';
5539
+ const range = ['all', '6m', '1m', '2w', '1w'].includes(o.range) ? o.range : '2w';
5540
+ const order = o.order === 'asc' ? 'asc' : 'desc';
5541
+ const q = String(o.q || '').trim().slice(0, 120);
5542
+ const days = { all: 0, '6m': 180, '1m': 30, '2w': 14, '1w': 7 }[range];
5543
+ const since = days ? new Date(Date.now() - days * 86400000).toISOString().slice(0, 10) : '';
5544
+ /* aralık başına yıldız tabanı: listeler anlamlı kalsın diye */
5545
+ const floor = { all: 5000, '6m': 200, '1m': 50, '2w': 20, '1w': 10 }[range];
5546
+ let query;
5547
+ let useSort = true; /* 'all' + best eşleşmede sort param gönderilmez */
5548
+ if (mode === 'all') {
5549
+ if (!q) return { ok: true, items: [] };
5550
+ query = q; /* yıldız tabanı YOK — 0 yıldızlı dahil tüm repolar bulunur */
5551
+ if (o.allSort === 'best') useSort = false;
5552
+ } else if (q) {
5553
+ /* arama modu: tarih filtresi YOK — her zaman tüm zamanlar içinde arar,
5554
+ yıldız sırasına göre döner (küçük taban: çöp listelenmesin diye) */
5555
+ query = q + ' stars:>=10';
5556
+ } else if (range === 'all') {
5557
+ query = 'stars:>=' + floor;
5558
+ } else {
5559
+ query = 'created:>=' + since + ' stars:>=' + floor;
5560
+ }
5561
+ let url = 'https://api.github.com/search/repositories?q=' + encodeURIComponent(query).replace(/%20/g, '+') + '&per_page=20';
5562
+ if (useSort) url += '&sort=stars&order=' + order;
5563
+ try {
5564
+ const res = await fetch(url, {
5565
+ headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'beast-agent' },
5566
+ signal: AbortSignal.timeout(15000),
5567
+ });
5568
+ if (res.status === 403 || res.status === 429) {
5569
+ return { ok: false, error: 'GitHub API limiti doldu (403) — bir saat sonra tekrar dene' };
5570
+ }
5571
+ if (!res.ok) return { ok: false, error: 'GitHub API ' + res.status };
5572
+ const data = await res.json();
5573
+ const items = (data.items || []).map((r) => ({
5574
+ full_name: r.full_name,
5575
+ html_url: r.html_url,
5576
+ description: String(r.description || '').slice(0, 240),
5577
+ language: r.language || '',
5578
+ stars: r.stargazers_count || 0,
5579
+ pushed_at: r.pushed_at || '',
5580
+ }));
5581
+ return { ok: true, items };
5582
+ } catch (e) {
5583
+ return { ok: false, error: String((e && e.message) || e) };
5584
+ }
5419
5585
  });
5420
5586
 
5421
5587
  ipcMain.handle('bots:update', (_e, { id, patch }) => {
package/src/preload.js CHANGED
@@ -143,7 +143,7 @@ contextBridge.exposeInMainWorld('beast', {
143
143
  botsAdd: (input) => ipcRenderer.invoke('bots:add', input),
144
144
  botsDmList: () => ipcRenderer.invoke('bots:dm:list'),
145
145
  botsDmRead: (id) => ipcRenderer.invoke('bots:dm:read', id),
146
- sessionSetGuests: (sessionId, guests) => ipcRenderer.invoke('sessions:guests:set', { sessionId, guests }),
146
+ githubTrending: (opts) => ipcRenderer.invoke('github:trending', opts),
147
147
  botsUpdate: (id, patch) => ipcRenderer.invoke('bots:update', { id, patch }),
148
148
  botsRemove: (id) => ipcRenderer.invoke('bots:remove', id),
149
149
  botsStats: () => ipcRenderer.invoke('bots:stats'),