beast-agent 0.24.5 → 0.25.1

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/src/main.js CHANGED
@@ -620,29 +620,29 @@ function emitWaEventSafe(ev) {
620
620
  function waSlashHelp() {
621
621
  return [
622
622
  '*Beast komutları*',
623
- '• /help – bu liste',
624
- '• /new – yeni oturum aç (kod verilir)',
625
- '• /open <kod> – o koddaki oturuma geç',
626
- '• /sessions – bu sohbetin oturumları',
627
- '• /stop – koşan işleri durdur (ajanlar+turlar; cron/izleyici/olay sürer)',
628
- '• /start – durdurulan servisleri devam ettir',
629
- '• /restart – uygulamayı yeniden başlat',
630
- '• /change – modelleri listele (/change 5 ile 5.modele geç)',
631
- '• /notes – bu oturumun notlarını göster',
632
- '• /notify on|off – hata mail bildirimini aç/kapa',
633
- '• /think <0-5> – düşünme seviyesi (0 kapalı · 1 low · 2 medium · 3 high · 4 xhigh · 5 max)',
634
- '• /clear – bu oturumun geçmişini temizle',
635
- '• /screenshot – masaüstü ekran görüntüsünü gönder',
636
- '• /rule <metin> – kalıcı kural ekle (/rules: liste)',
637
- '• /allow <isim> <numara> – WhatsApp allow listesine kişi ekle (örn: /allow batu 905414178456)',
638
- '• /block – allow listesini numaralarıyla listele (/block 3: 3. kişiyi çıkar; 1 = sahip, silinemez)',
639
- '• /approve – bekleyen riskli işlemi onayla (/approve always: bir daha sorulmasın · /deny: reddet)',
640
- '• /update – yeni sürüm kontrolü (/update now: indirileni hemen kur)',
641
- '• /model – aktif modeli göster (/model <isim> ile değiştir)',
642
- '• /skills – kurulu skill\u2019ler',
643
- '• /usage – bugünkü kullanım',
644
- '• /backup – tüm veriyi ŞİFRELİ yedekle (Beast Kodu imzalı, Masaüstü\\Beast-Backups)',
645
- '• /status – bağlantı ve servis durumu',
623
+ '• */help* – bu liste',
624
+ '• */new* – yeni oturum aç (kod verilir)',
625
+ '• */open* <kod> – o koddaki oturuma geç',
626
+ '• */sessions* – bu sohbetin oturumları',
627
+ '• */stop* – koşan işleri durdur (ajanlar+turlar; cron/izleyici/olay sürer)',
628
+ '• */start* – durdurulan servisleri devam ettir',
629
+ '• */restart* – uygulamayı yeniden başlat',
630
+ '• */change* – modelleri listele (*/change 5* ile 5.modele geç)',
631
+ '• */notes* – bu oturumun notlarını göster',
632
+ '• */notify* on|off – hata mail bildirimini aç/kapa',
633
+ '• */think* <0-5> – düşünme seviyesi (0 kapalı · 1 low · 2 medium · 3 high · 4 xhigh · 5 max)',
634
+ '• */clear* – bu oturumun geçmişini temizle',
635
+ '• */screenshot* – masaüstü ekran görüntüsünü gönder',
636
+ '• */rule* <metin> – kalıcı kural ekle (*/rules*: liste)',
637
+ '• */allow* <isim> <numara> – WhatsApp allow listesine kişi ekle (örn: /allow batu 905414178456)',
638
+ '• */block* – allow listesini numaralarıyla listele (*/block 3*: 3. kişiyi çıkar; 1 = sahip, silinemez)',
639
+ '• */approve* – bekleyen riskli işlemi onayla (*/approve always*: bir daha sorulmasın · */deny*: reddet)',
640
+ '• */update* – yeni sürüm kontrolü (*/update now*: indirileni hemen kur)',
641
+ '• */model* – aktif modeli göster (*/model* <isim> ile değiştir)',
642
+ '• */skills* – kurulu skill\u2019ler',
643
+ '• */usage* – bugünkü kullanım',
644
+ '• */backup* – tüm veriyi ŞİFRELİ yedekle (Beast Kodu imzalı, Masaüstü\\Beast-Backups)',
645
+ '• */status* – bağlantı ve servis durumu',
646
646
  '',
647
647
  'Gruplarda beni @mention ile çağır; ardışık mesajlarını tek cevapta birleştiririm.',
648
648
  ].join('\n');
@@ -2117,6 +2117,19 @@ function reloadBackend() {
2117
2117
  }
2118
2118
  } catch {}
2119
2119
  }
2120
+ /* WA ara yorumları: ajanın araç çağrıları ARASINDAKİ açıklama metinleri
2121
+ ("işe başlıyorum…" vb.) da WhatsApp'a gitsin. Son cevap 'done'da
2122
+ gönderildiği için burada yalnız tool_calls TAŞIYAN ara mesajlar alınır. */
2123
+ if (ev.type === 'message' && ev.message && ev.message.role === 'assistant' && wa && wa.connected) {
2124
+ const mtxt = typeof ev.message.content === 'string' ? ev.message.content.trim() : '';
2125
+ const isInterim = Array.isArray(ev.message.tool_calls) && ev.message.tool_calls.length > 0;
2126
+ if (mtxt && isInterim) {
2127
+ try {
2128
+ const hit = [...waChats.entries()].find(([, s]) => s === ev.sessionId);
2129
+ if (hit) sendWaSafe(hit[0], mtxt).catch(() => {});
2130
+ } catch {}
2131
+ }
2132
+ }
2120
2133
  /* #14 paralel ajan: arka plan oturumu bitince ana sohbete özet bas.
2121
2134
  aborted (öz-kurtarma/wrap-up kick) turları BİTİŞ değildir — rapor basma */
2122
2135
  if (ev.type === 'done' && ev.sessionId && !ev.aborted) {
@@ -2555,7 +2568,31 @@ function setBrowserOpen(v, forceVisible) {
2555
2568
  browserEmit({ open: true, width: browser.width, url });
2556
2569
  }
2557
2570
 
2571
+ /* --- Paralel ajan trafik düzeni: ajanlar aynı saniyede sorgu atınca Google
2572
+ "olağandışı trafik" uyarısı veriyor. Gezinme/arama isteklerini tek kuyrukta
2573
+ sıraya alıp aralarına min 1.2 sn + rastgele 0-600 ms jitter koyuyoruz.
2574
+ Kuyruğu tutan işin İÇİNDEN yapılan gezinmeler kapıyı atlar (kilitlenme olmaz). --- */
2575
+ const BROWSER_TRAFFIC_GAP = 1200;
2576
+ let __trafficLast = 0;
2577
+ let __trafficTail = Promise.resolve();
2578
+ function browserTrafficWait() {
2579
+ const wait = Math.max(0, __trafficLast + BROWSER_TRAFFIC_GAP + Math.floor(Math.random() * 600) - Date.now());
2580
+ __trafficLast = Date.now() + wait;
2581
+ return wait > 0 ? new Promise((r) => setTimeout(r, wait)) : Promise.resolve();
2582
+ }
2583
+ function browserGate(job) {
2584
+ const p = __trafficTail.then(job, job);
2585
+ __trafficTail = p.catch(() => {});
2586
+ return p;
2587
+ }
2558
2588
  async function browserNavigate(raw, signal) {
2589
+ return browserGate(async () => {
2590
+ await browserTrafficWait();
2591
+ return browserNavigateNow(raw, signal);
2592
+ });
2593
+ }
2594
+
2595
+ async function browserNavigateNow(raw, signal) {
2559
2596
  let url = String(raw || '').trim();
2560
2597
  if (!url) return { ok: false, error: 'boş adres' };
2561
2598
  if (!/^https?:\/\//i.test(url)) {
@@ -2585,7 +2622,21 @@ async function browserNavigate(raw, signal) {
2585
2622
  try { title = wc.getTitle(); finalUrl = wc.getURL() || url; } catch {}
2586
2623
  browserEmit({ open: true, width: browser.width, url: finalUrl });
2587
2624
  flushBrowserStorage(); // oturum çerezleri diske — ani kapanışta kaybolmasın
2588
- return { ok: true, url: finalUrl, title };
2625
+ /* açılışta snapshot da göm — model ayrı snapshot çağırmadan ref'lere başlar */
2626
+ let snap = null;
2627
+ try {
2628
+ const sraw = await wc.executeJavaScript(BROWSER_SNAPSHOT_JS, true);
2629
+ const sobj = JSON.parse(sraw);
2630
+ if (sobj && typeof sobj.count === 'number') snap = sobj;
2631
+ } catch {}
2632
+ return {
2633
+ ok: true,
2634
+ url: finalUrl,
2635
+ title,
2636
+ ...(snap
2637
+ ? { 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' }
2638
+ : { note: 'sayfa acildi — etkileşimli elemanlar için browser_snapshot al' }),
2639
+ };
2589
2640
  }
2590
2641
 
2591
2642
  async function browserRead(signal) {
@@ -2650,6 +2701,14 @@ async function ocrRead({ image, lang = 'tur+eng' } = {}) {
2650
2701
  yapmak (kullanıcının console'da yaptığı gibi) — sayfa bile değişmeden
2651
2702
  DOMParser ile sonuç çekilir. Fetch olmazsa direk gezinme fallback'i var. */
2652
2703
  async function browserSearch(query, signal) {
2704
+ /* paralel ajan sorguları trafik kapısından sırayla geçer */
2705
+ return browserGate(async () => {
2706
+ await browserTrafficWait();
2707
+ return browserSearchNow(query, signal);
2708
+ });
2709
+ }
2710
+
2711
+ async function browserSearchNow(query, signal) {
2653
2712
  try {
2654
2713
  if (!win || win.isDestroyed()) return null;
2655
2714
  if (signal && signal.aborted) return null;
@@ -2663,7 +2722,7 @@ async function browserSearch(query, signal) {
2663
2722
  let cur = '';
2664
2723
  try { cur = wc.getURL() || ''; } catch {}
2665
2724
  if (!cur.startsWith('https://www.google.com')) {
2666
- await browserNavigate('https://www.google.com/', signal);
2725
+ await browserNavigateNow('https://www.google.com/', signal);
2667
2726
  }
2668
2727
  if (!browser.view || !browser.open) return null;
2669
2728
 
@@ -2733,7 +2792,7 @@ async function browserSearch(query, signal) {
2733
2792
 
2734
2793
  /* fallback: fetch yerine direk gezinme + DOM'dan çek */
2735
2794
  if (!results.length) {
2736
- await browserNavigate(
2795
+ await browserNavigateNow(
2737
2796
  'https://www.google.com/search?q=' + encodeURIComponent(q) + '&num=10&hl=tr&pws=0&aep=1',
2738
2797
  signal
2739
2798
  );
@@ -2789,7 +2848,14 @@ async function browserSearch(query, signal) {
2789
2848
  results = parsed.rows || [];
2790
2849
  }
2791
2850
 
2792
- if (!results.length && !parsed.ai) return null;
2851
+ if (!results.length && !parsed.ai) {
2852
+ /* CAPTCHA / olağandışı trafik sinyali — zincir TinyFish'e kaymalı */
2853
+ let blocked = false;
2854
+ try { blocked = /google\.com\/sorry/i.test(wc.getURL() || ''); } catch {}
2855
+ return blocked
2856
+ ? { ok: false, blocked: true, engine: 'browser-google', query: q, error: 'unusual traffic (CAPTCHA)' }
2857
+ : null;
2858
+ }
2793
2859
  flushBrowserStorage();
2794
2860
  const out = { ok: true, engine: 'browser-google', query: q, results };
2795
2861
  if (parsed.ai) out.ai = parsed.ai;
@@ -2853,26 +2919,37 @@ const BROWSER_JS_HELPERS = `
2853
2919
 
2854
2920
  const BROWSER_SNAPSHOT_JS = `(function(){
2855
2921
  ${BROWSER_JS_HELPERS}
2856
- const sel='a[href],button,input:not([type="hidden"]),textarea,select,[role="button"],[role="link"],[role="tab"],[contenteditable="true"],summary';
2857
- const els=[...document.querySelectorAll(sel)];
2922
+ 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';
2923
+ /* açık popup/takvim/dialog varsa içindekiler ÖNCE listelenir (tarih seçici, özel dropdown vb.) */
2924
+ 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]';
2925
+ const pops=[...document.querySelectorAll(popSel)].filter(__vis);
2926
+ const inPop=new Set();
2927
+ for(const p of pops){[...p.querySelectorAll(sel)].forEach(e=>inPop.add(e));}
2858
2928
  window.__beMap={};
2929
+ const seen=new Set();
2859
2930
  const lines=[];
2860
2931
  let i=0;
2861
- for(const e of els){
2862
- if(!__vis(e)) continue;
2863
- if(e.disabled||e.getAttribute('aria-disabled')==='true') continue;
2932
+ function add(e){
2933
+ if(!e||seen.has(e)) return;
2934
+ seen.add(e);
2935
+ if(!__vis(e)||e.disabled||e.getAttribute('aria-disabled')==='true') return;
2864
2936
  i++;
2865
2937
  window.__beMap[i]=e;
2866
2938
  const tag=e.tagName.toLowerCase();
2867
- const type=e.getAttribute('type');
2939
+ const type=(e.getAttribute&&e.getAttribute('type'))||'';
2868
2940
  let s='['+i+'] <'+tag+(type?' type='+type:'')+'>';
2869
2941
  const l=__label(e);
2870
2942
  if(l) s+=' "'+l+'"';
2871
- 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)+'"';
2943
+ 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)+'"';
2872
2944
  if(tag==='select'){s+=' secenekler=['+[...e.options].slice(0,6).map(o=>o.text.trim()).filter(Boolean).join('|').slice(0,60)+']';}
2873
2945
  lines.push(s);
2874
- if(i>=100) break;
2875
2946
  }
2947
+ if(inPop.size){
2948
+ lines.push('--- ACIK POPUP/TAKVIM ICINDEKILER (once bunlari kullan) ---');
2949
+ for(const e of inPop){ add(e); if(i>=60) break; }
2950
+ lines.push('--- SAYFA ---');
2951
+ }
2952
+ for(const e of document.querySelectorAll(sel)){ if(i>=100) break; add(e); }
2876
2953
  return JSON.stringify({count:i,title:document.title,url:location.href,snapshot:lines.join('\\n')});
2877
2954
  })()`;
2878
2955
 
@@ -2905,10 +2982,52 @@ function browserActionJs(kind, args) {
2905
2982
  if (kind === 'type') {
2906
2983
  const text = JSON.stringify(String(args.text ?? ''));
2907
2984
  const submit = args.submit ? 'true' : 'false';
2908
- return `(function(){${BROWSER_JS_HELPERS}${resolveTarget};return new Promise((res)=>{try{
2985
+ return `(function(){${BROWSER_JS_HELPERS}${resolveTarget}
2986
+ /*__DT_HELPERS_START__*/
2987
+ function __norm(s){return String(s).toLowerCase().split('').map(function(c){return {'ş':'s','ğ':'g','ü':'u','ı':'i','ö':'o','ç':'c','İ':'i'}[c]||c;}).join('');}
2988
+ function __parseDate(raw){
2989
+ raw=String(raw||'').trim(); if(!raw) return null;
2990
+ 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};
2991
+ let m=raw.match(/^(\\d{4})[\\-.\\/,](\\d{1,2})[\\-.\\/,](\\d{1,2})$/);
2992
+ if(m) return [+m[1],+m[2],+m[3]];
2993
+ m=raw.match(/^(\\d{1,2})[\\-.\\/ ](\\d{1,2})[\\-.\\/ ](\\d{2,4})$/);
2994
+ 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];}
2995
+ m=raw.match(/^(\\d{1,2})[\\-.\\/ ]+([a-zçğıöşü]+)[\\-.\\/ ]+(\\d{2,4})$/i);
2996
+ if(m){const mo=MON[__norm(m[2])];if(mo){let y=+m[3];if(y<100)y+=2000;return [y,mo,+m[1]];}}
2997
+ return null;
2998
+ }
2999
+ function __parseTime(raw){
3000
+ const m=String(raw||'').trim().match(/^(\\d{1,2})[:.h](\\d{2})/);
3001
+ if(!m) return null;
3002
+ return [Math.min(23,+m[1]),Math.min(59,+m[2])];
3003
+ }
3004
+ /*__DT_HELPERS_END__*/
3005
+ return new Promise((res)=>{try{
2909
3006
  const t=__target();
2910
3007
  if(!t) return res(JSON.stringify({typed:false,reason:'eleman bulunamadi (ref eski olabilir - browser_snapshot al)'}));
2911
3008
  const how=t.how,el=t.el;
3009
+ /* tarih/saat alanları: programatik değer + input/change event — native takvim popup'ı hiç açılmaz */
3010
+ const it=(el.tagName==='INPUT'?(el.type||'').toLowerCase():'');
3011
+ if(it==='date'||it==='month'||it==='time'||it==='datetime-local'){
3012
+ const RAW=${text};
3013
+ let v=null;
3014
+ if(it==='time'){
3015
+ const p=__parseTime(RAW); if(p) v=p.map(function(n){return String(n).padStart(2,'0');}).join(':');
3016
+ } else {
3017
+ const p=__parseDate(RAW);
3018
+ if(p&&p.every(Number.isFinite)&&p[1]>=1&&p[1]<=12&&p[2]>=1&&p[2]<=31){
3019
+ const pad=function(n){return String(n).padStart(2,'0');};
3020
+ const ymd=p[0]+'-'+pad(p[1])+'-'+pad(p[2]);
3021
+ v = it==='month' ? (p[0]+'-'+pad(p[1])) : it==='datetime-local' ? (ymd+'T'+((__parseTime(RAW)||[12,0])).map(pad).join(':')) : ymd;
3022
+ }
3023
+ }
3024
+ 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'}));
3025
+ const setter=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set;
3026
+ setter.call(el,v);
3027
+ el.dispatchEvent(new Event('input',{bubbles:true}));
3028
+ el.dispatchEvent(new Event('change',{bubbles:true}));
3029
+ return res(JSON.stringify({typed:true,target:how+' <input type='+it+'>',value:v,note:'tarih/saat programatik ayarlandi — takvim tiklamak gerekmez'}));
3030
+ }
2912
3031
  el.scrollIntoView({block:'center'});el.focus();
2913
3032
  setTimeout(()=>{try{
2914
3033
  if(el.isContentEditable){el.textContent=${text};}
@@ -3035,6 +3154,14 @@ async function browserAct(kind, args, signal) {
3035
3154
  : (obj.reason || 'tamam')
3036
3155
  );
3037
3156
 
3157
+ /* HIZ: eylem cevabına taze snapshot göm — model ayrı browser_snapshot çağırmaz (tur sayısı yarıya iner) */
3158
+ let freshSnap = null;
3159
+ try {
3160
+ const sraw = await wc.executeJavaScript(BROWSER_SNAPSHOT_JS, true);
3161
+ const sobj = JSON.parse(sraw);
3162
+ if (sobj && typeof sobj.count === 'number') freshSnap = sobj;
3163
+ } catch {}
3164
+
3038
3165
  return {
3039
3166
  ok: true,
3040
3167
  action: kind,
@@ -3042,7 +3169,12 @@ async function browserAct(kind, args, signal) {
3042
3169
  url: wc.getURL(),
3043
3170
  title: wc.getTitle(),
3044
3171
  navigated,
3045
- ...(navigated ? { note: 'sayfa degisti — gerekirse yeni browser_snapshot al' } : {}),
3172
+ ...(freshSnap ? { snapshot: freshSnap.snapshot, refCount: freshSnap.count } : {}),
3173
+ ...(navigated
3174
+ ? { note: 'sayfa degisti — yanıtta güncel snapshot var; refler eskiyse yeni browser_snapshot al' }
3175
+ : freshSnap
3176
+ ? { note: 'yanıtta güncel snapshot (' + freshSnap.count + ' ref) — sonraki hamlede bunları kullan, ayrıca snapshot alma' }
3177
+ : {}),
3046
3178
  recent: recentLog(),
3047
3179
  };
3048
3180
  } catch (e) {
@@ -4403,22 +4535,9 @@ ipcMain.handle('terminal:toggle', () => {
4403
4535
  return { ok: true, cwd: (engine && engine.workspace) || settings.workspace || app.getPath('home') };
4404
4536
  });
4405
4537
 
4406
- /* Git Bash kuruluysa yolunu bulur; yoksa null döner */
4407
- function findGitBash() {
4408
- const cands = [];
4409
- if (process.env['ProgramFiles']) cands.push(path.join(process.env['ProgramFiles'], 'Git', 'bin', 'bash.exe'));
4410
- if (process.env['ProgramFiles(x86)']) cands.push(path.join(process.env['ProgramFiles(x86)'], 'Git', 'bin', 'bash.exe'));
4411
- if (process.env['LocalAppData']) cands.push(path.join(process.env['LocalAppData'], 'Programs', 'Git', 'bin', 'bash.exe'));
4412
- cands.push('C:\\Program Files\\Git\\bin\\bash.exe');
4413
- for (const c of cands) {
4414
- try { if (fs.existsSync(c)) return c; } catch {}
4415
- }
4416
- return null;
4417
- }
4418
-
4419
4538
  ipcMain.handle('terminal:run', (_e, payload) => {
4420
4539
  const cmd = String((payload && payload.cmd) || '').trim();
4421
- const shell = String((payload && payload.shell) || 'powershell');
4540
+ const shell = String((payload && payload.shell) || 'cmd');
4422
4541
  if (!cmd) return { ok: false, error: 'boş komut' };
4423
4542
  if (termChild) return { ok: false, error: 'önceki komut sürüyor — ■ ile durdurabilirsin' };
4424
4543
  const cwd = (engine && engine.workspace) || settings.workspace || app.getPath('home');
@@ -4427,12 +4546,8 @@ ipcMain.handle('terminal:run', (_e, payload) => {
4427
4546
  if (shell === 'cmd') {
4428
4547
  file = 'cmd.exe';
4429
4548
  args = ['/d', '/s', '/c', cmd];
4430
- } else if (shell === 'bash') {
4431
- const bash = findGitBash();
4432
- if (!bash) return { ok: false, error: 'Git Bash bulunamadı — Git for Windows kurulu mu? (https://git-scm.com)' };
4433
- file = bash;
4434
- args = ['-c', cmd];
4435
4549
  } else {
4550
+ /* geriye dönük uyumluluk — UI artık yalnız CMD gönderir */
4436
4551
  file = 'powershell.exe';
4437
4552
  args = ['-NoProfile', '-NoLogo', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', cmd];
4438
4553
  }
@@ -5425,10 +5540,61 @@ ipcMain.handle('bots:add', (_e, input) => {
5425
5540
  ipcMain.handle('bots:dm:list', () => engine.listBotDmSessions());
5426
5541
  ipcMain.handle('bots:dm:read', (_e, id) => engine.readBotDm(id));
5427
5542
 
5428
- /* Ana sohbete davetli botlar */
5429
- ipcMain.handle('sessions:guests:set', (_e, { sessionId, guests }) => {
5430
- try { engine.setSessionGuests(sessionId, guests); } catch {}
5431
- return { ok: true };
5543
+ /* GITHUB MODALI → Trending / Tüm Repolar: GitHub Search API.
5544
+ opts.mode: 'trend' (varsayılan) | 'all' (tüm repolar — yıldız tabanı yok)
5545
+ opts.range: all | 6m | 1m | 2w | 1w (tüm zamanlar / 6 ay / 1 ay / 2 hafta / 1 hafta)
5546
+ opts.order: desc (azalan) | asc (artan) — yıldız sırası
5547
+ opts.q: arama terimi (trend modunda tarih filtresi UYGULANMAZ — her zaman tüm zamanlar)
5548
+ Kimliksiz 60 istek/saat limiti var — 403'te UI bilgi gösterir. */
5549
+ ipcMain.handle('github:trending', async (_e, opts) => {
5550
+ const o = opts && typeof opts === 'object' ? opts : {};
5551
+ const mode = o.mode === 'all' ? 'all' : 'trend';
5552
+ const range = ['all', '6m', '1m', '2w', '1w'].includes(o.range) ? o.range : '2w';
5553
+ const order = o.order === 'asc' ? 'asc' : 'desc';
5554
+ const q = String(o.q || '').trim().slice(0, 120);
5555
+ const days = { all: 0, '6m': 180, '1m': 30, '2w': 14, '1w': 7 }[range];
5556
+ const since = days ? new Date(Date.now() - days * 86400000).toISOString().slice(0, 10) : '';
5557
+ /* aralık başına yıldız tabanı: listeler anlamlı kalsın diye */
5558
+ const floor = { all: 5000, '6m': 200, '1m': 50, '2w': 20, '1w': 10 }[range];
5559
+ let query;
5560
+ let useSort = true; /* 'all' + best eşleşmede sort param gönderilmez */
5561
+ if (mode === 'all') {
5562
+ if (!q) return { ok: true, items: [] };
5563
+ query = q; /* yıldız tabanı YOK — 0 yıldızlı dahil tüm repolar bulunur */
5564
+ if (o.allSort === 'best') useSort = false;
5565
+ } else if (q) {
5566
+ /* arama modu: tarih filtresi YOK — her zaman tüm zamanlar içinde arar,
5567
+ yıldız sırasına göre döner (küçük taban: çöp listelenmesin diye) */
5568
+ query = q + ' stars:>=10';
5569
+ } else if (range === 'all') {
5570
+ query = 'stars:>=' + floor;
5571
+ } else {
5572
+ query = 'created:>=' + since + ' stars:>=' + floor;
5573
+ }
5574
+ let url = 'https://api.github.com/search/repositories?q=' + encodeURIComponent(query).replace(/%20/g, '+') + '&per_page=20';
5575
+ if (useSort) url += '&sort=stars&order=' + order;
5576
+ try {
5577
+ const res = await fetch(url, {
5578
+ headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'beast-agent' },
5579
+ signal: AbortSignal.timeout(15000),
5580
+ });
5581
+ if (res.status === 403 || res.status === 429) {
5582
+ return { ok: false, error: 'GitHub API limiti doldu (403) — bir saat sonra tekrar dene' };
5583
+ }
5584
+ if (!res.ok) return { ok: false, error: 'GitHub API ' + res.status };
5585
+ const data = await res.json();
5586
+ const items = (data.items || []).map((r) => ({
5587
+ full_name: r.full_name,
5588
+ html_url: r.html_url,
5589
+ description: String(r.description || '').slice(0, 240),
5590
+ language: r.language || '',
5591
+ stars: r.stargazers_count || 0,
5592
+ pushed_at: r.pushed_at || '',
5593
+ }));
5594
+ return { ok: true, items };
5595
+ } catch (e) {
5596
+ return { ok: false, error: String((e && e.message) || e) };
5597
+ }
5432
5598
  });
5433
5599
 
5434
5600
  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'),
@@ -445,8 +445,6 @@
445
445
  bot_bind_title: 'Bu numarayı hangi bot karşılasın?',
446
446
  bot_sel_empty: '— bot seçme —',
447
447
  bot_code_title: 'Botun benzersiz 5 haneli kodu — botlar arası DM adresi',
448
- bot_vis: 'Ana sohbete davet edilebilir (sohbet görünürlüğü)',
449
- tipGuestBot: 'Konuşmaya bot davet et — davetli botlara ajan bot_dm ile danışır',
450
448
  bot_switch_hint: 'Tıkla: bu botta çalış · ⚙: yönet',
451
449
  bot_switched: 'bottasın — sohbetler bu botun kimliğiyle gider',
452
450
  bot_manage: 'Botu yönet',
@@ -472,6 +470,7 @@
472
470
  q_flushed: '${n} kuyruktaki mesaj gönderiliyor',
473
471
  bot_num_ro_hint: 'Numara ekleme/kişi adı Ayarlar → Entegrasyonlar (WhatsApp izin listesi) üzerinden yapılır; burada yalnızca bağlı numaralar görünür.',
474
472
  tipStore: 'Skills Store',
473
+ tipGithub: 'GitHub Trending — depoları favorile, chate at, ajana araştır',
475
474
  tipIde: 'IDE Modu — dosyalar + chat + preview',
476
475
  ide_files: 'DOSYALAR',
477
476
  ide_pick: 'Klasör seç',
@@ -1022,8 +1021,6 @@
1022
1021
  bot_bind_title: 'Which bot should answer this number?',
1023
1022
  bot_sel_empty: '— no bot —',
1024
1023
  bot_code_title: "This bot's unique 5-digit code — the inter-bot DM address",
1025
- bot_vis: 'Invitable to the main chat (chat visibility)',
1026
- tipGuestBot: 'Invite a bot to this chat — the agent consults invited bots via bot_dm',
1027
1024
  bot_switch_hint: 'Click: work as this bot · ⚙: manage',
1028
1025
  bot_switched: 'active — chats run with this bot\u2019s identity',
1029
1026
  bot_manage: 'Manage bot',
@@ -1049,6 +1046,7 @@
1049
1046
  q_flushed: '${n} queued message(s) being sent',
1050
1047
  bot_num_ro_hint: 'Add numbers / person names via Settings → Integrations (WhatsApp allow list); only linked numbers are shown here.',
1051
1048
  tipStore: 'Skills Store',
1049
+ tipGithub: 'GitHub Trending — favorite repos, send to chat, ask the agent',
1052
1050
  tipIde: 'IDE Mode — files + chat + preview',
1053
1051
  ide_files: 'FILES',
1054
1052
  ide_pick: 'Choose folder',
@@ -39,6 +39,7 @@
39
39
  <button id="themeBtn" title="Koyu tema">&#x263E;</button>
40
40
  <button id="langBtn" title="Dil değiştir / Switch language" data-i18n-title="tipLang"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="9"/><path d="M3 12h18"/><path d="M12 3c2.6 2.6 2.6 15.4 0 18M12 3c-2.6 2.6-2.6 15.4 0 18"/></svg></button>
41
41
  <button id="storeBtn" title="Skills Store" data-i18n-title="tipStore"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16l-1.3 12.1a2 2 0 0 1-2 1.9H7.3a2 2 0 0 1-2-1.9L4 7z"/><path d="M8 10V6a4 4 0 0 1 8 0v4"/></svg></button>
42
+ <button id="gitBtn" title="GitHub Trending" data-i18n-title="tipGithub"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></button>
42
43
  <button id="ideBtn" title="IDE Modu" data-i18n-title="tipIde"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 6l-5 6 5 6"/><path d="M16 6l5 6-5 6"/><path d="M13.5 4l-3 16"/></svg></button>
43
44
  </div>
44
45
  </aside>
@@ -70,11 +71,9 @@
70
71
  <div id="cfgList" class="dd-list"></div>
71
72
  </div>
72
73
  </div>
73
- <button id="railBtn" class="dd-btn dd-icon-btn on" title="Paralel Ajan Konsolu" data-i18n-title="tipRail">&#x25A4;</button>
74
+ <button id="railBtn" class="dd-btn dd-icon-btn" title="Paralel Ajan Konsolu" data-i18n-title="tipRail">&#x25A4;</button>
74
75
  <button id="watchBtn" class="dd-btn dd-icon-btn" title="İzleyiciler (Watchers)" data-i18n-title="tipWatch">&#x25CE;</button>
75
76
  <button id="cronBtn" class="dd-btn dd-icon-btn" title="Cron Görevler" data-i18n-title="tipCron">&#x23F1;&#xFE0E;</button>
76
- <button id="termBtn" class="dd-btn dd-icon-btn" title="Terminal (PowerShell)" data-i18n-title="tipTerm">&gt;</button>
77
- <button id="termGBtn" class="dd-btn dd-icon-btn" title="Git Bash terminali" data-i18n-title="tipTermG">&#x25C8;</button>
78
77
  <button id="termCBtn" class="dd-btn dd-icon-btn" title="CMD terminali" data-i18n-title="tipTermC">&#x276F;&#xFE0E;</button>
79
78
  <button id="browserBtn" class="dd-btn dd-icon-btn" title="Dahili tarayıcı" data-i18n-title="tipBrowser">&#x29C9;</button>
80
79
  <button id="eyeBtn" class="dd-btn dd-icon-btn" title="Ajan tarayıcısı görünür/gizli — göz açıkken aramalar panelde izlenir, kapalıyken gizli çalışır" data-i18n-title="tipEye"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/><line class="eye-slash" x1="4" y1="4" x2="20" y2="20"/></svg></button>
@@ -91,15 +90,13 @@
91
90
 
92
91
  <div id="composerWrap">
93
92
  <div id="todoPanel" hidden></div>
94
- <div id="statusPill" hidden></div>
93
+ <div id="statusRow"><div id="statusPill" hidden></div><div id="toast" hidden></div></div>
95
94
  <div id="chips" hidden></div>
96
95
  <div id="slashMenu" hidden></div>
97
96
  <div id="composer">
98
97
  <div id="composerLeft" class="dd">
99
98
  <button id="attachBtn" title="Dosya / resim ekle" data-i18n-title="tipAttach">+</button>
100
99
  <button id="micBtn" title="Sesli komut — konuş, bırakınca yazıya çevirsin" data-i18n-title="tipMic"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><path d="M12 19v3"/></svg></button>
101
- <button id="guestBotBtn" title="Konuşmaya bot davet et — davetli botlara ajan bot_dm ile danışır" data-i18n-title="tipGuestBot" hidden><span style="filter:grayscale(1)">🤖</span></button>
102
- <div id="guestBotMenu" class="dd-menu" hidden></div>
103
100
  </div>
104
101
  <textarea id="input" rows="1" placeholder="Bir şey sor ya da görev ver… (Enter: gönder · Shift+Enter: satır · /: komutlar)" data-i18n-ph="composerPh"></textarea>
105
102
  <button id="stopBtn" title="Durdur" hidden data-i18n-title="tipStop">■</button>
@@ -272,6 +269,47 @@
272
269
  </div>
273
270
  </div>
274
271
 
272
+ <div id="ghOverlay" hidden>
273
+ <div id="ghDialog">
274
+ <div id="ghHead">
275
+ <span id="ghTitle">GITHUB TRENDING</span>
276
+ <span class="store-spacer"></span>
277
+ <button id="ghClose" class="mini-close" title="Kapat" data-i18n-title="btn_close">&#x00D7;</button>
278
+ </div>
279
+ <div id="ghBody">
280
+ <section id="ghTrendPane" class="gh-pane">
281
+ <div class="gh-pane-tabs">
282
+ <button class="gh-tab on" data-ghtab="trend">Trend Depolar</button>
283
+ <button class="gh-tab" data-ghtab="all">Tüm Repolar</button>
284
+ </div>
285
+ <div class="gh-pane-subline" id="ghTrendSub">— son 2 haftanın yıldız yükselenleri</div>
286
+ <div id="ghTrendTools" class="gh-filters">
287
+ <div class="gh-ranges" id="ghRanges">
288
+ <button data-range="all" title="Tüm zamanlar">Tümü</button>
289
+ <button data-range="6m" title="Son 6 ay">6 Ay</button>
290
+ <button data-range="1m" title="Son 1 ay">1 Ay</button>
291
+ <button data-range="2w" class="on" title="Son 2 hafta">2 Hafta</button>
292
+ <button data-range="1w" title="Son 1 hafta">1 Hafta</button>
293
+ </div>
294
+ <button id="ghSortBtn" title="Yıldız sırası: azalan / artan">↓ Yıldız</button>
295
+ <input id="ghSearch" placeholder="Repo ara… (Enter)" spellcheck="false" />
296
+ <button id="ghTrendRefresh" class="gh-refresh" title="Sıfırla ve yenile — modal ilk açıldığı gibi">&#x27F3;</button>
297
+ </div>
298
+ <div id="ghAllTools" class="gh-filters" hidden>
299
+ <input id="ghAllSearch" placeholder="Tüm GitHub'da ara… (Enter)" spellcheck="false" />
300
+ <button id="ghAllSortBtn" title="Sıralama: en iyi eşleşme / yıldız">⭐ En İyi</button>
301
+ <button id="ghAllRefresh" class="gh-refresh" title="Sıfırla ve yenile">&#x27F3;</button>
302
+ </div>
303
+ <div id="ghTrendList" class="gh-pane-list"></div>
304
+ </section>
305
+ <section id="ghFavPane" class="gh-pane">
306
+ <div class="gh-pane-title">Favoriler <span class="gh-pane-sub">— yıldızladıkların burada saklanır</span></div>
307
+ <div id="ghFavList" class="gh-pane-list"></div>
308
+ </section>
309
+ </div>
310
+ </div>
311
+ </div>
312
+
275
313
  <div id="watchOverlay" class="mini-overlay" hidden>
276
314
  <div class="mini-dialog">
277
315
  <div class="mini-head">
@@ -318,15 +356,13 @@
318
356
  <div id="termOut"></div>
319
357
  </div>
320
358
  <div id="termInputRow">
321
- <span id="termPrompt">PS&gt;</span>
359
+ <span id="termPrompt">CMD&gt;</span>
322
360
  <input id="termInput" spellcheck="false" autocomplete="off" placeholder="komut yaz, Enter'la çalıştır…" data-i18n-ph="term_ph" />
323
361
  <button id="termStop" title="Çalışan komutu durdur" hidden data-i18n-title="term_stop">&#x25A0;&#xFE0E;</button>
324
362
  </div>
325
363
  </div>
326
364
  <div id="termResize" hidden title="Sürükleyerek boyutlandır"></div>
327
365
 
328
- <div id="toast"></div>
329
-
330
366
  <div id="fileCtxMenu" hidden></div>
331
367
 
332
368
  <input type="file" id="fileInput" multiple hidden