beast-agent 0.24.5 → 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 +1 -1
- package/src/agent/engine.js +34 -59
- package/src/agent/tools.js +1 -1
- package/src/main.js +190 -37
- package/src/preload.js +1 -1
- package/src/renderer/i18n.js +2 -4
- package/src/renderer/index.html +45 -9
- package/src/renderer/renderer.js +345 -143
- package/src/renderer/style.css +195 -30
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -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ç →
|
|
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;
|
|
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:
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3074
|
+
/* SIRA: 1) dahili tarayıcı (DİREK GOOGLE — ücretsiz + AI cevabı)
|
|
3075
|
+
2) TinyFish (tarayıcı sorun çıkarsa HEMEN devreye girer — anahtar 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
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
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
|
-
|
|
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.
|
|
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".
|
|
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
|
|
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:
|
|
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: {
|
package/src/agent/tools.js
CHANGED
|
@@ -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
|
@@ -2555,7 +2555,31 @@ function setBrowserOpen(v, forceVisible) {
|
|
|
2555
2555
|
browserEmit({ open: true, width: browser.width, url });
|
|
2556
2556
|
}
|
|
2557
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
|
+
}
|
|
2558
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) {
|
|
2559
2583
|
let url = String(raw || '').trim();
|
|
2560
2584
|
if (!url) return { ok: false, error: 'boş adres' };
|
|
2561
2585
|
if (!/^https?:\/\//i.test(url)) {
|
|
@@ -2585,7 +2609,21 @@ async function browserNavigate(raw, signal) {
|
|
|
2585
2609
|
try { title = wc.getTitle(); finalUrl = wc.getURL() || url; } catch {}
|
|
2586
2610
|
browserEmit({ open: true, width: browser.width, url: finalUrl });
|
|
2587
2611
|
flushBrowserStorage(); // oturum çerezleri diske — ani kapanışta kaybolmasın
|
|
2588
|
-
|
|
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
|
+
};
|
|
2589
2627
|
}
|
|
2590
2628
|
|
|
2591
2629
|
async function browserRead(signal) {
|
|
@@ -2650,6 +2688,14 @@ async function ocrRead({ image, lang = 'tur+eng' } = {}) {
|
|
|
2650
2688
|
yapmak (kullanıcının console'da yaptığı gibi) — sayfa bile değişmeden
|
|
2651
2689
|
DOMParser ile sonuç çekilir. Fetch olmazsa direk gezinme fallback'i var. */
|
|
2652
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) {
|
|
2653
2699
|
try {
|
|
2654
2700
|
if (!win || win.isDestroyed()) return null;
|
|
2655
2701
|
if (signal && signal.aborted) return null;
|
|
@@ -2663,7 +2709,7 @@ async function browserSearch(query, signal) {
|
|
|
2663
2709
|
let cur = '';
|
|
2664
2710
|
try { cur = wc.getURL() || ''; } catch {}
|
|
2665
2711
|
if (!cur.startsWith('https://www.google.com')) {
|
|
2666
|
-
await
|
|
2712
|
+
await browserNavigateNow('https://www.google.com/', signal);
|
|
2667
2713
|
}
|
|
2668
2714
|
if (!browser.view || !browser.open) return null;
|
|
2669
2715
|
|
|
@@ -2733,7 +2779,7 @@ async function browserSearch(query, signal) {
|
|
|
2733
2779
|
|
|
2734
2780
|
/* fallback: fetch yerine direk gezinme + DOM'dan çek */
|
|
2735
2781
|
if (!results.length) {
|
|
2736
|
-
await
|
|
2782
|
+
await browserNavigateNow(
|
|
2737
2783
|
'https://www.google.com/search?q=' + encodeURIComponent(q) + '&num=10&hl=tr&pws=0&aep=1',
|
|
2738
2784
|
signal
|
|
2739
2785
|
);
|
|
@@ -2789,7 +2835,14 @@ async function browserSearch(query, signal) {
|
|
|
2789
2835
|
results = parsed.rows || [];
|
|
2790
2836
|
}
|
|
2791
2837
|
|
|
2792
|
-
if (!results.length && !parsed.ai)
|
|
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
|
+
}
|
|
2793
2846
|
flushBrowserStorage();
|
|
2794
2847
|
const out = { ok: true, engine: 'browser-google', query: q, results };
|
|
2795
2848
|
if (parsed.ai) out.ai = parsed.ai;
|
|
@@ -2853,26 +2906,37 @@ const BROWSER_JS_HELPERS = `
|
|
|
2853
2906
|
|
|
2854
2907
|
const BROWSER_SNAPSHOT_JS = `(function(){
|
|
2855
2908
|
${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
|
-
|
|
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));}
|
|
2858
2915
|
window.__beMap={};
|
|
2916
|
+
const seen=new Set();
|
|
2859
2917
|
const lines=[];
|
|
2860
2918
|
let i=0;
|
|
2861
|
-
|
|
2862
|
-
if(!
|
|
2863
|
-
|
|
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;
|
|
2864
2923
|
i++;
|
|
2865
2924
|
window.__beMap[i]=e;
|
|
2866
2925
|
const tag=e.tagName.toLowerCase();
|
|
2867
|
-
const type=e.getAttribute('type');
|
|
2926
|
+
const type=(e.getAttribute&&e.getAttribute('type'))||'';
|
|
2868
2927
|
let s='['+i+'] <'+tag+(type?' type='+type:'')+'>';
|
|
2869
2928
|
const l=__label(e);
|
|
2870
2929
|
if(l) s+=' "'+l+'"';
|
|
2871
|
-
if(tag==='input'
|
|
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)+'"';
|
|
2872
2931
|
if(tag==='select'){s+=' secenekler=['+[...e.options].slice(0,6).map(o=>o.text.trim()).filter(Boolean).join('|').slice(0,60)+']';}
|
|
2873
2932
|
lines.push(s);
|
|
2874
|
-
if(i>=100) break;
|
|
2875
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); }
|
|
2876
2940
|
return JSON.stringify({count:i,title:document.title,url:location.href,snapshot:lines.join('\\n')});
|
|
2877
2941
|
})()`;
|
|
2878
2942
|
|
|
@@ -2905,10 +2969,52 @@ function browserActionJs(kind, args) {
|
|
|
2905
2969
|
if (kind === 'type') {
|
|
2906
2970
|
const text = JSON.stringify(String(args.text ?? ''));
|
|
2907
2971
|
const submit = args.submit ? 'true' : 'false';
|
|
2908
|
-
return `(function(){${BROWSER_JS_HELPERS}${resolveTarget}
|
|
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{
|
|
2909
2993
|
const t=__target();
|
|
2910
2994
|
if(!t) return res(JSON.stringify({typed:false,reason:'eleman bulunamadi (ref eski olabilir - browser_snapshot al)'}));
|
|
2911
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
|
+
}
|
|
2912
3018
|
el.scrollIntoView({block:'center'});el.focus();
|
|
2913
3019
|
setTimeout(()=>{try{
|
|
2914
3020
|
if(el.isContentEditable){el.textContent=${text};}
|
|
@@ -3035,6 +3141,14 @@ async function browserAct(kind, args, signal) {
|
|
|
3035
3141
|
: (obj.reason || 'tamam')
|
|
3036
3142
|
);
|
|
3037
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
|
+
|
|
3038
3152
|
return {
|
|
3039
3153
|
ok: true,
|
|
3040
3154
|
action: kind,
|
|
@@ -3042,7 +3156,12 @@ async function browserAct(kind, args, signal) {
|
|
|
3042
3156
|
url: wc.getURL(),
|
|
3043
3157
|
title: wc.getTitle(),
|
|
3044
3158
|
navigated,
|
|
3045
|
-
...(
|
|
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
|
+
: {}),
|
|
3046
3165
|
recent: recentLog(),
|
|
3047
3166
|
};
|
|
3048
3167
|
} catch (e) {
|
|
@@ -4403,22 +4522,9 @@ ipcMain.handle('terminal:toggle', () => {
|
|
|
4403
4522
|
return { ok: true, cwd: (engine && engine.workspace) || settings.workspace || app.getPath('home') };
|
|
4404
4523
|
});
|
|
4405
4524
|
|
|
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
4525
|
ipcMain.handle('terminal:run', (_e, payload) => {
|
|
4420
4526
|
const cmd = String((payload && payload.cmd) || '').trim();
|
|
4421
|
-
const shell = String((payload && payload.shell) || '
|
|
4527
|
+
const shell = String((payload && payload.shell) || 'cmd');
|
|
4422
4528
|
if (!cmd) return { ok: false, error: 'boş komut' };
|
|
4423
4529
|
if (termChild) return { ok: false, error: 'önceki komut sürüyor — ■ ile durdurabilirsin' };
|
|
4424
4530
|
const cwd = (engine && engine.workspace) || settings.workspace || app.getPath('home');
|
|
@@ -4427,12 +4533,8 @@ ipcMain.handle('terminal:run', (_e, payload) => {
|
|
|
4427
4533
|
if (shell === 'cmd') {
|
|
4428
4534
|
file = 'cmd.exe';
|
|
4429
4535
|
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
4536
|
} else {
|
|
4537
|
+
/* geriye dönük uyumluluk — UI artık yalnız CMD gönderir */
|
|
4436
4538
|
file = 'powershell.exe';
|
|
4437
4539
|
args = ['-NoProfile', '-NoLogo', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', cmd];
|
|
4438
4540
|
}
|
|
@@ -5425,10 +5527,61 @@ ipcMain.handle('bots:add', (_e, input) => {
|
|
|
5425
5527
|
ipcMain.handle('bots:dm:list', () => engine.listBotDmSessions());
|
|
5426
5528
|
ipcMain.handle('bots:dm:read', (_e, id) => engine.readBotDm(id));
|
|
5427
5529
|
|
|
5428
|
-
/*
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
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
|
+
}
|
|
5432
5585
|
});
|
|
5433
5586
|
|
|
5434
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
|
-
|
|
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'),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -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',
|