beast-agent 0.25.5 → 0.25.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/bots.js +8 -0
- package/src/agent/discord.js +234 -0
- package/src/agent/engine.js +116 -70
- package/src/agent/llm.js +46 -1
- package/src/agent/skills.js +8 -1
- package/src/agent/tools.js +20 -14
- package/src/main.js +311 -34
- package/src/preload.js +8 -0
- package/src/renderer/i18n.js +18 -0
- package/src/renderer/renderer.js +299 -13
- package/tests/bg-jobs.test.js +5 -10
- package/tests/llm.test.js +81 -1
- package/tests/python.test.js +3 -2
package/src/agent/skills.js
CHANGED
|
@@ -232,14 +232,21 @@ Zorunlu kurallar:
|
|
|
232
232
|
},
|
|
233
233
|
{
|
|
234
234
|
folder: 'pdf',
|
|
235
|
+
force: true,
|
|
235
236
|
body: `---
|
|
236
237
|
name: pdf
|
|
237
238
|
description: PDF işleme rehberi — metin çıkarma (pdf-parse), Türkçe karakterli PDF oluşturma/değiştirme (pdf-lib/pdfkit), sayfa→görsel (pdf-to-img) ve taranmış PDF için OCR (tesseract.js). Kullanıcı bir .pdf dosyasından bahsederse ÖNCE bu skill'i oku.
|
|
238
|
-
version: 1.
|
|
239
|
+
version: 1.1.0
|
|
239
240
|
---
|
|
240
241
|
|
|
241
242
|
# PDF İşleme Rehberi
|
|
242
243
|
|
|
244
|
+
## EN KRİTİK KURALLAR (önce bunlar)
|
|
245
|
+
|
|
246
|
+
1. Türkçe karakterli PDF için pip\u2019ten paket KURMA/KULLANMA (fpdf, fpdf2, markdown-pdf, weasyprint, reportlab vb.) — bunlar ğ ş ı İ karakterlerini bozar. Doğru kit Node\u2019ta ZATEN kurulu: \`pdf-lib\` + \`@pdf-lib/fontkit\` ve \`pdfkit\`. Script\u2019i write_file ile .js yaz, run_command ile \`node script.js\` çalıştır (python_run İLE DEĞİL).
|
|
247
|
+
2. md→pdf çevirici YOKTUR ve kurulmaz — kullanıcıya rapor/özet/belge çıktısı vereceksen .md gönderme; aynı içeriği doğrudan PDF olarak ÜRET ve o dosyayı send_file ile gönder.
|
|
248
|
+
3. \`pdf-parse\` ile \`pdf-to-img\` aynı process\u2019e yüklenmez (aşağıdaki çakışma kuralı).
|
|
249
|
+
|
|
243
250
|
Kurulu paketler ve rolleri:
|
|
244
251
|
|
|
245
252
|
| İstek | Paket |
|
package/src/agent/tools.js
CHANGED
|
@@ -501,21 +501,27 @@ async function fetchWithTimeout(url, opts = {}, timeoutMs = 20000, signal) {
|
|
|
501
501
|
async function webSearch(query, { maxResults = 8, signal } = {}) {
|
|
502
502
|
const q = String(query || '').trim().slice(0, 400);
|
|
503
503
|
if (!q) return { ok: false, error: 'boş sorgu' };
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
504
|
+
let res;
|
|
505
|
+
try {
|
|
506
|
+
res = await fetchWithTimeout(
|
|
507
|
+
'https://html.duckduckgo.com/html/',
|
|
508
|
+
{
|
|
509
|
+
method: 'POST',
|
|
510
|
+
headers: {
|
|
511
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
512
|
+
'User-Agent': UA,
|
|
513
|
+
Accept: 'text/html',
|
|
514
|
+
},
|
|
515
|
+
body: new URLSearchParams({ q }).toString(),
|
|
512
516
|
},
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
517
|
+
20000,
|
|
518
|
+
signal
|
|
519
|
+
);
|
|
520
|
+
} catch (e) {
|
|
521
|
+
/* ağ hatası/rate-limit exception fırlatmasın — zincir devam edebilsin */
|
|
522
|
+
return { ok: false, error: 'DDG erişilemedi: ' + String((e && e.message) || e) };
|
|
523
|
+
}
|
|
524
|
+
if (!res.ok) return { ok: false, error: `DDG HTTP ${res.status}` };
|
|
519
525
|
const html = await res.text();
|
|
520
526
|
const results = parseDdgResults(html, Math.min(Math.max(Number(maxResults) || 8, 1), 12));
|
|
521
527
|
if (!results.length) {
|
package/src/main.js
CHANGED
|
@@ -16,6 +16,7 @@ const skillsMod = require('./agent/skills');
|
|
|
16
16
|
const storeMod = require('./agent/store');
|
|
17
17
|
const { WhatsAppBridge } = require('./agent/whatsapp');
|
|
18
18
|
const { TelegramBridge } = require('./agent/telegram');
|
|
19
|
+
const { DiscordBridge } = require('./agent/discord');
|
|
19
20
|
const cron = require('./cron');
|
|
20
21
|
const watchers = require('./agent/watchers');
|
|
21
22
|
const usageMod = require('./agent/usage');
|
|
@@ -149,8 +150,7 @@ function startNpmUpdateWatch() {
|
|
|
149
150
|
check();
|
|
150
151
|
setInterval(check, 6 * 60 * 60 * 1000);
|
|
151
152
|
}
|
|
152
|
-
const { htmlToText, setExaKey, setTinyfishKey
|
|
153
|
-
const research = require('./agent/research');
|
|
153
|
+
const { htmlToText, setExaKey, setTinyfishKey } = require('./agent/tools');
|
|
154
154
|
const { waToolLine } = require('./agent/watext');
|
|
155
155
|
|
|
156
156
|
/* #3 merkezî log sistemine process-seviye hataları da düşsün */
|
|
@@ -230,6 +230,11 @@ let tg = null;
|
|
|
230
230
|
let tgChats = new Map(); // telegram chatId -> aktif session id
|
|
231
231
|
let tgHistory = new Map(); // chatId -> [sid,...]
|
|
232
232
|
const TG_HISTORY_CAP = 20;
|
|
233
|
+
let dc = null;
|
|
234
|
+
let dcChats = new Map(); // discord channelId -> aktif session id
|
|
235
|
+
let dcHistory = new Map(); // channelId -> [sid,...]
|
|
236
|
+
const DC_HISTORY_CAP = 20;
|
|
237
|
+
const DC_CHATS_FILE = path.join(APP_DIR, 'dc-chats.json');
|
|
233
238
|
let tray = null;
|
|
234
239
|
app.isQuitting = false;
|
|
235
240
|
|
|
@@ -1184,7 +1189,7 @@ async function waFlush(jid) {
|
|
|
1184
1189
|
if (payload.participant && !merged.participant) merged.participant = payload.participant;
|
|
1185
1190
|
if (payload.participantPn && !merged.participantPn) merged.participantPn = payload.participantPn;
|
|
1186
1191
|
if (payload.participantUsername && !merged.participantUsername) merged.participantUsername = payload.participantUsername;
|
|
1187
|
-
if (payload.mentioned) merged.mentioned = true;
|
|
1192
|
+
if (payload.mentioned) merged.mentioned = true;
|
|
1188
1193
|
if (!senderNum && sn) senderNum = sn;
|
|
1189
1194
|
}
|
|
1190
1195
|
try {
|
|
@@ -1712,6 +1717,7 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
|
1712
1717
|
} else {
|
|
1713
1718
|
engine.setSessionTools(sid, null);
|
|
1714
1719
|
}
|
|
1720
|
+
engine.setSessionModel(sid, botCfg && !botCfg.admin ? (botCfg.model || null) : null);
|
|
1715
1721
|
waLog(`perm=${fmtPerm(perm)} bot=${botId} sid=${sid}`);
|
|
1716
1722
|
|
|
1717
1723
|
const participantName = payload.participant ? '+' + String(payload.participant).split('@')[0].split(':')[0] : '';
|
|
@@ -1980,6 +1986,7 @@ async function processTgMessage(chatId, payload, requeues = 0) {
|
|
|
1980
1986
|
} else {
|
|
1981
1987
|
engine.setSessionTools(sid, null);
|
|
1982
1988
|
}
|
|
1989
|
+
engine.setSessionModel(sid, botCfg && !botCfg.admin ? (botCfg.model || null) : null);
|
|
1983
1990
|
tgLog(`perm=${fmtPerm(perm)} bot=${botId} sid=${sid}`);
|
|
1984
1991
|
|
|
1985
1992
|
/* #v13.1 rol: SAHİP vs MİSAFİR — ajan kime konuştuğunu net bilsin */
|
|
@@ -2038,6 +2045,224 @@ async function restartTg() {
|
|
|
2038
2045
|
}
|
|
2039
2046
|
}
|
|
2040
2047
|
|
|
2048
|
+
/* ---------- DISCORD: allow list — WA/TG ile aynı mantık ----------
|
|
2049
|
+
Liste formatı: [{ id:'123456789' | '@kullanici_adi', name, perm, bot_id }, '*']
|
|
2050
|
+
Eşleşme: sayısal ID birebir, @username büyük/küçük harf duyarsız. */
|
|
2051
|
+
function dcLog(line) {
|
|
2052
|
+
try { log.info('discord', line); } catch {}
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
function dcFind(senderId, username) {
|
|
2056
|
+
const list = settings.dcAllow || [];
|
|
2057
|
+
if (!list.length) return null; // boş liste = kimseye cevap yok
|
|
2058
|
+
const id = String(senderId || '').trim();
|
|
2059
|
+
const uname = String(username || '').replace(/^@/, '').toLowerCase();
|
|
2060
|
+
for (const e of list) {
|
|
2061
|
+
if (e === '*') return { id: '*', name: '' };
|
|
2062
|
+
const eid = typeof e === 'string' ? e.trim() : String((e && e.id) || '').trim();
|
|
2063
|
+
if (!eid) continue;
|
|
2064
|
+
if (eid === '*') return { id: '*', name: '' };
|
|
2065
|
+
if (eid.startsWith('@')) {
|
|
2066
|
+
if (uname && eid.slice(1).toLowerCase() === uname) {
|
|
2067
|
+
return typeof e === 'string' ? { id: eid, name: '' } : e;
|
|
2068
|
+
}
|
|
2069
|
+
} else if (id && eid === id) {
|
|
2070
|
+
return typeof e === 'string' ? { id: eid, name: '' } : e;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
return null;
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
(function dcChatsLoad() {
|
|
2077
|
+
try {
|
|
2078
|
+
const raw = JSON.parse(fs.readFileSync(DC_CHATS_FILE, 'utf8'));
|
|
2079
|
+
if (raw && typeof raw.chats === 'object') {
|
|
2080
|
+
for (const [c, s] of Object.entries(raw.chats)) {
|
|
2081
|
+
if (typeof s === 'string') dcChats.set(c, s);
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
if (raw && typeof raw.history === 'object') {
|
|
2085
|
+
for (const [c, arr] of Object.entries(raw.history)) {
|
|
2086
|
+
if (Array.isArray(arr)) dcHistory.set(c, arr.filter((x) => typeof x === 'string').slice(-DC_HISTORY_CAP));
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
for (const [c, s] of dcChats.entries()) {
|
|
2090
|
+
const h = dcHistory.get(c) || [];
|
|
2091
|
+
if (!h.includes(s)) h.push(s);
|
|
2092
|
+
dcHistory.set(c, h.slice(-DC_HISTORY_CAP));
|
|
2093
|
+
}
|
|
2094
|
+
} catch {}
|
|
2095
|
+
})();
|
|
2096
|
+
|
|
2097
|
+
function saveDcChats() {
|
|
2098
|
+
try {
|
|
2099
|
+
fs.writeFileSync(
|
|
2100
|
+
DC_CHATS_FILE,
|
|
2101
|
+
JSON.stringify({
|
|
2102
|
+
chats: Object.fromEntries(dcChats),
|
|
2103
|
+
history: Object.fromEntries([...dcHistory.entries()].map(([c, a]) => [c, a.slice(-DC_HISTORY_CAP)])),
|
|
2104
|
+
})
|
|
2105
|
+
);
|
|
2106
|
+
} catch {}
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
function dcRememberSession(channelId, sid) {
|
|
2110
|
+
const h = dcHistory.get(channelId) || [];
|
|
2111
|
+
if (!h.includes(sid)) h.push(sid);
|
|
2112
|
+
dcHistory.set(channelId, h.slice(-DC_HISTORY_CAP));
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
const DC_DEBOUNCE_MS = 4500;
|
|
2116
|
+
const dcQueue = new Map(); // channelId -> { payloads[] }
|
|
2117
|
+
|
|
2118
|
+
function dcQueuePush(channelId, payload) {
|
|
2119
|
+
let q = dcQueue.get(channelId);
|
|
2120
|
+
if (!q) {
|
|
2121
|
+
q = { payloads: [] };
|
|
2122
|
+
dcQueue.set(channelId, q);
|
|
2123
|
+
}
|
|
2124
|
+
q.payloads.push(payload);
|
|
2125
|
+
clearTimeout(q.timer);
|
|
2126
|
+
dcLog(`queue: mesaj kuyruğa girdi channel=${channelId} toplam=${q.payloads.length} (4.5 sn birleştirme)`);
|
|
2127
|
+
q.timer = setTimeout(() => {
|
|
2128
|
+
dcFlush(channelId).catch((e) => dcLog(`flush KRASİ: ${String((e && e.stack) || e)}`));
|
|
2129
|
+
}, DC_DEBOUNCE_MS);
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
async function dcFlush(channelId) {
|
|
2133
|
+
const q = dcQueue.get(channelId);
|
|
2134
|
+
if (!q) return;
|
|
2135
|
+
dcQueue.delete(channelId);
|
|
2136
|
+
const merged = { text: '', senderId: '', username: '', senderName: '' };
|
|
2137
|
+
for (const p of q.payloads) {
|
|
2138
|
+
if (p.text) merged.text += (merged.text ? '\n' : '') + p.text;
|
|
2139
|
+
if (!merged.senderId && p.senderId) { merged.senderId = p.senderId; merged.username = p.username; merged.senderName = p.senderName; }
|
|
2140
|
+
}
|
|
2141
|
+
await processDcMessage(channelId, merged);
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
async function handleDcIncoming(channelId, payload) {
|
|
2145
|
+
try {
|
|
2146
|
+
const hit = dcFind(payload.senderId, payload.username);
|
|
2147
|
+
dcLog(
|
|
2148
|
+
`incoming channel=${channelId} sender=${payload.senderId || '?'} user=${payload.username || '-'} allowed=${!!hit}` +
|
|
2149
|
+
(hit && hit.name ? ' name=' + hit.name : '')
|
|
2150
|
+
);
|
|
2151
|
+
if (!hit) return; // allowlist dışı yoksay
|
|
2152
|
+
/* İsimsiz kayıt: güvenlik için cevap verme — kullanıcıyı ayarlara yönlendir */
|
|
2153
|
+
if (hit.id !== '*' && !hit.name) {
|
|
2154
|
+
dcLog(`skip: isimsiz kayıt (${hit.id}) — cevap verilmedi, Entegrasyonlar'da isim ekle`);
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
resumeServices(); // pause durumunda gelen mesaj servisleri canlandırır
|
|
2158
|
+
dcQueuePush(String(channelId), payload);
|
|
2159
|
+
} catch (e) {
|
|
2160
|
+
dcLog(`handleDcIncoming KRASİ: ${String((e && e.stack) || e)}`);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
async function processDcMessage(channelId, payload) {
|
|
2165
|
+
const hit = dcFind(payload.senderId, payload.username);
|
|
2166
|
+
if (!hit) {
|
|
2167
|
+
dcLog(`skip flush: izinli eşleşme yok (sender=${payload.senderId || '?'})`);
|
|
2168
|
+
return;
|
|
2169
|
+
}
|
|
2170
|
+
let sid = dcChats.get(channelId);
|
|
2171
|
+
if (sid && engine.isBusy(sid)) {
|
|
2172
|
+
/* oturum meşgul — WA/TG ile aynı: kaybetme, iş bitene dek yeniden dene */
|
|
2173
|
+
await new Promise((r) => setTimeout(r, DC_DEBOUNCE_MS));
|
|
2174
|
+
return processDcMessage(channelId, payload, 1);
|
|
2175
|
+
}
|
|
2176
|
+
if (!sid) {
|
|
2177
|
+
const v = engine.createSession();
|
|
2178
|
+
sid = v.id;
|
|
2179
|
+
dcChats.set(channelId, sid);
|
|
2180
|
+
dcRememberSession(channelId, sid);
|
|
2181
|
+
saveDcChats();
|
|
2182
|
+
} else {
|
|
2183
|
+
dcRememberSession(channelId, sid);
|
|
2184
|
+
}
|
|
2185
|
+
/* Kişi bazlı granül izin: all/web/read/chat */
|
|
2186
|
+
let perm = hit.perm || (hit.lockdown ? 'chat' : 'all');
|
|
2187
|
+
engine.setSessionPerm(sid, perm);
|
|
2188
|
+
|
|
2189
|
+
/* BOT SİSTEMİ: izinli kayıtta bot_id yoksa beast'e düşer (WA/TG ile aynı) */
|
|
2190
|
+
let botId = hit && hit.bot_id ? String(hit.bot_id) : 'beast';
|
|
2191
|
+
if (!bots.get(botId)) {
|
|
2192
|
+
if (botId !== 'beast') dcLog(`bot="${botId}" yok — kayıt botsuz, beast (admin) botuna yönlendirildi`);
|
|
2193
|
+
botId = 'beast';
|
|
2194
|
+
}
|
|
2195
|
+
engine.setSessionBot(sid, botId);
|
|
2196
|
+
const botCfg = bots.get(botId);
|
|
2197
|
+
if (botCfg && !botCfg.admin) {
|
|
2198
|
+
const eff = moreRestrictivePerm(perm, botCfg.perm || 'all');
|
|
2199
|
+
if (eff !== perm) {
|
|
2200
|
+
perm = eff;
|
|
2201
|
+
engine.setSessionPerm(sid, eff);
|
|
2202
|
+
}
|
|
2203
|
+
engine.setSessionTools(sid, botToolSet(botCfg));
|
|
2204
|
+
} else {
|
|
2205
|
+
engine.setSessionTools(sid, null);
|
|
2206
|
+
}
|
|
2207
|
+
engine.setSessionModel(sid, botCfg && !botCfg.admin ? (botCfg.model || null) : null);
|
|
2208
|
+
dcLog(`perm=${fmtPerm(perm)} bot=${botId} sid=${sid}`);
|
|
2209
|
+
|
|
2210
|
+
/* #v13.1 rol: SAHİP vs MİSAFİR — ajan kime konuştuğunu net bilsin */
|
|
2211
|
+
const isOwner = !!hit.owner;
|
|
2212
|
+
const roleTag = isOwner
|
|
2213
|
+
? 'SAHİBİN (talepleri önceliklidir)'
|
|
2214
|
+
: 'MİSAFİR (izinli ama sahibin sözü önceliklidir)';
|
|
2215
|
+
const label =
|
|
2216
|
+
(hit.name || payload.senderName || '?') +
|
|
2217
|
+
(payload.username ? ` (@${payload.username})` : '') +
|
|
2218
|
+
` — ${roleTag}`;
|
|
2219
|
+
let text = `[Discord — gönderen: ${label}]`;
|
|
2220
|
+
if (!isOwner) {
|
|
2221
|
+
text += `\n[NOT: Bu kişi SAHİP DEĞİL, misafirdir. Sahibin ayarlarını/verilerini değiştirme; kalıcı hafızaya misafire özel bilgi yazma.]`;
|
|
2222
|
+
}
|
|
2223
|
+
text += `\n${String(payload.text || '').slice(0, 6000)}`;
|
|
2224
|
+
engine.send(sid, { text: text.slice(0, 8000), attachments: [] });
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
async function sendDcSafe(channelId, text) {
|
|
2228
|
+
if (!dc) return false;
|
|
2229
|
+
try {
|
|
2230
|
+
return !!(await dc.send(channelId, text));
|
|
2231
|
+
} catch (e) {
|
|
2232
|
+
dcLog(`send hata channel=${channelId}: ${String((e && e.message) || e)}`);
|
|
2233
|
+
return false;
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
function ensureDc() {
|
|
2238
|
+
if (!dc) {
|
|
2239
|
+
dc = new DiscordBridge({
|
|
2240
|
+
token: settings.dcToken || '',
|
|
2241
|
+
emit: (ev) => {
|
|
2242
|
+
if (ev.type === 'status') dcLog(`status=${ev.status}${ev.user ? ' user=' + ev.user : ''}`);
|
|
2243
|
+
if (win && !win.isDestroyed()) win.webContents.send('dc:event', ev);
|
|
2244
|
+
},
|
|
2245
|
+
onIncoming: handleDcIncoming,
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
2248
|
+
return dc;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
/* token değişimi / yeniden başlatma: eski köprüyü kapat, yenisini aç */
|
|
2252
|
+
async function restartDc() {
|
|
2253
|
+
if (dc) {
|
|
2254
|
+
try { await dc.stop(); } catch {}
|
|
2255
|
+
dc = null;
|
|
2256
|
+
}
|
|
2257
|
+
if (!settings.dcToken) return;
|
|
2258
|
+
const b = ensureDc();
|
|
2259
|
+
try {
|
|
2260
|
+
await b.start();
|
|
2261
|
+
} catch (e) {
|
|
2262
|
+
dcLog(`start başarısız: ${String((e && e.message) || e)}`);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2041
2266
|
function reloadBackend() {
|
|
2042
2267
|
if (engine && typeof engine.dispose === 'function') {
|
|
2043
2268
|
try { engine.dispose(); } catch {}
|
|
@@ -2100,13 +2325,9 @@ function reloadBackend() {
|
|
|
2100
2325
|
act: (k, a, s) => browserAct(k, a, s),
|
|
2101
2326
|
},
|
|
2102
2327
|
research: {
|
|
2103
|
-
/* deep_search
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
research.deepSearch(args || {}, {
|
|
2107
|
-
search: (q) => webSearchFast(q, { maxResults: 10, signal }),
|
|
2108
|
-
readPage: (u) => researchRead(u, signal),
|
|
2109
|
-
}, signal),
|
|
2328
|
+
/* deep_search'ün "sayfayı GİZLİ tarayıcıda açıp oku" parçası (Electron-only).
|
|
2329
|
+
Arama zinciri engine._webSearchChain içinde web_search ile birebir aynı. */
|
|
2330
|
+
readPage: (u, s) => researchRead(u, s),
|
|
2110
2331
|
},
|
|
2111
2332
|
emit: (ev) => {
|
|
2112
2333
|
if (win && !win.isDestroyed()) win.webContents.send('agent:event', ev);
|
|
@@ -2211,6 +2432,27 @@ function reloadBackend() {
|
|
|
2211
2432
|
})();
|
|
2212
2433
|
}
|
|
2213
2434
|
}
|
|
2435
|
+
// Discord oturumlarının son cevabını geri gönder (TG ile aynı akış)
|
|
2436
|
+
if ((ev.type === 'done' || ev.type === 'error') && dc && dc.connected) {
|
|
2437
|
+
const hitD = [...dcChats.entries()].find(([, s]) => s === ev.sessionId);
|
|
2438
|
+
if (hitD) {
|
|
2439
|
+
const dchid = hitD[0];
|
|
2440
|
+
(async () => {
|
|
2441
|
+
try {
|
|
2442
|
+
if (ev.type === 'error') {
|
|
2443
|
+
await sendDcSafe(dchid, 'Bir aksilik oldu: ' + String(ev.error || '').slice(0, 200));
|
|
2444
|
+
return;
|
|
2445
|
+
}
|
|
2446
|
+
if (!ev.aborted) {
|
|
2447
|
+
const s = engine.openSession(ev.sessionId);
|
|
2448
|
+
const lastA = [...s.messages].reverse().find((m) => m.role === 'assistant' && m.content);
|
|
2449
|
+
const txt = typeof (lastA && lastA.content) === 'string' ? lastA.content : '';
|
|
2450
|
+
if (txt.trim()) await sendDcSafe(dchid, txt);
|
|
2451
|
+
}
|
|
2452
|
+
} catch {}
|
|
2453
|
+
})();
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2214
2456
|
},
|
|
2215
2457
|
});
|
|
2216
2458
|
return engine.publicState();
|
|
@@ -2401,6 +2643,11 @@ app.whenReady().then(() => {
|
|
|
2401
2643
|
ensureTg().start().catch((e) => tgLog('autostart failed: ' + String((e && e.message) || e)));
|
|
2402
2644
|
}
|
|
2403
2645
|
|
|
2646
|
+
// Discord köprüsünü otomatik başlat (token kayıtlıysa)
|
|
2647
|
+
if (settings.dcToken) {
|
|
2648
|
+
ensureDc().start().catch((e) => dcLog('autostart failed: ' + String((e && e.message) || e)));
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2404
2651
|
app.on('activate', () => {
|
|
2405
2652
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
|
2406
2653
|
});
|
|
@@ -2598,26 +2845,13 @@ function browserGate(job) {
|
|
|
2598
2845
|
__trafficTail = p.catch(() => {});
|
|
2599
2846
|
return p;
|
|
2600
2847
|
}
|
|
2601
|
-
/*
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
} catch {
|
|
2609
|
-
return false;
|
|
2610
|
-
}
|
|
2611
|
-
}
|
|
2612
|
-
|
|
2613
|
-
/* bg ajan için açma stratejisi: kapalıysa GİZLİ aç; zaten açıksa mevcut
|
|
2614
|
-
görünürlüğe dokunma (kullanıcı paneli izliyorsa aniden saklamayalım) */
|
|
2615
|
-
function setBrowserOpenForAgent(ctx) {
|
|
2616
|
-
if (isBgBrowserCtx(ctx)) {
|
|
2617
|
-
if (!browser.open) setBrowserOpen(true, false);
|
|
2618
|
-
return;
|
|
2619
|
-
}
|
|
2620
|
-
setBrowserOpen(true);
|
|
2848
|
+
/* AJAN TARAYICI STRATEJİSİ — TÜM oturumlar (ana sohbet + WhatsApp botları + paralel ajanlar):
|
|
2849
|
+
ajan tarayıcıyı KENDİ açıyorsa hep GİZLİ açılır — panel ekrana fırlamaz,
|
|
2850
|
+
Paralel Ajan Konsolu kapanmaz. Kullanıcı izlemek isterse tarayıcı düğmesine
|
|
2851
|
+
basar (browser:toggle gizli paneli görünür kılar). Zaten açıksa (kullanıcı
|
|
2852
|
+
paneli açık tutuyorsa) görünürlüğe dokunulmaz. */
|
|
2853
|
+
function setBrowserOpenForAgent() {
|
|
2854
|
+
if (!browser.open) setBrowserOpen(true, false);
|
|
2621
2855
|
}
|
|
2622
2856
|
|
|
2623
2857
|
async function browserNavigate(raw, signal, ctx) {
|
|
@@ -2636,7 +2870,7 @@ async function browserNavigateNow(raw, signal, ctx) {
|
|
|
2636
2870
|
? 'https://' + url
|
|
2637
2871
|
: 'https://duckduckgo.com/?q=' + encodeURIComponent(url);
|
|
2638
2872
|
}
|
|
2639
|
-
setBrowserOpenForAgent(
|
|
2873
|
+
setBrowserOpenForAgent();
|
|
2640
2874
|
const wc = browser.view.webContents;
|
|
2641
2875
|
await new Promise((resolve) => {
|
|
2642
2876
|
let settled = false;
|
|
@@ -2863,7 +3097,7 @@ async function browserSearchNow(query, signal, ctx) {
|
|
|
2863
3097
|
if (signal && signal.aborted) return null;
|
|
2864
3098
|
const q = String(query || '').trim();
|
|
2865
3099
|
if (!q) return null;
|
|
2866
|
-
setBrowserOpenForAgent(
|
|
3100
|
+
setBrowserOpenForAgent();
|
|
2867
3101
|
const wc = browser.view && browser.view.webContents;
|
|
2868
3102
|
if (!wc) return null;
|
|
2869
3103
|
|
|
@@ -3469,6 +3703,7 @@ ipcMain.handle('sessions:create', () => {
|
|
|
3469
3703
|
} else {
|
|
3470
3704
|
engine.setSessionTools(v.id, null);
|
|
3471
3705
|
}
|
|
3706
|
+
engine.setSessionModel(v.id, b && !b.admin ? (b.model || null) : null);
|
|
3472
3707
|
return v;
|
|
3473
3708
|
});
|
|
3474
3709
|
ipcMain.handle('sessions:open', (_e, id) => engine.openSession(id));
|
|
@@ -4430,7 +4665,7 @@ ipcMain.handle('update:install', () => {
|
|
|
4430
4665
|
if (!isNpmMode()) return { ok: false, error: NPM_ONLY_TEXT };
|
|
4431
4666
|
npmUpdateViaCmd();
|
|
4432
4667
|
return { ok: true, npm: true };
|
|
4433
|
-
});
|
|
4668
|
+
});
|
|
4434
4669
|
|
|
4435
4670
|
ipcMain.handle('update:setAuto', (_e, cfg) => {
|
|
4436
4671
|
if (cfg && typeof cfg.autoCheck === 'boolean') settings.autoCheckUpdate = cfg.autoCheck;
|
|
@@ -4665,7 +4900,12 @@ ipcMain.handle('cron:runNow', (_e, id) => {
|
|
|
4665
4900
|
|
|
4666
4901
|
/* ---------------- tarayıcı IPC ---------------- */
|
|
4667
4902
|
ipcMain.handle('browser:toggle', () => {
|
|
4668
|
-
/*
|
|
4903
|
+
/* gizli çalışan ajan paneli varsa düğme ONU GÖRÜNÜR yapar; değilse aç/kapa.
|
|
4904
|
+
(ajanlar tarayıcıyı hep gizli açar — kullanıcı izlemek isterse buradan gösterir) */
|
|
4905
|
+
if (browser.open && !browser.visible) {
|
|
4906
|
+
setBrowserOpen(true, true);
|
|
4907
|
+
return { open: browser.open, visible: browser.visible };
|
|
4908
|
+
}
|
|
4669
4909
|
setBrowserOpen(!browser.open, true);
|
|
4670
4910
|
return { open: browser.open, visible: browser.visible };
|
|
4671
4911
|
});
|
|
@@ -5555,6 +5795,42 @@ ipcMain.handle('tg:allow:set', (_e, list) => {
|
|
|
5555
5795
|
});
|
|
5556
5796
|
ipcMain.handle('tg:sessions', () => [...tgChats.values()]);
|
|
5557
5797
|
|
|
5798
|
+
/* ---------- Discord IPC ---------- */
|
|
5799
|
+
ipcMain.handle('dc:status:get', () => {
|
|
5800
|
+
if (!dc) return { configured: !!settings.dcToken, status: 'disconnected', user: null, connected: false };
|
|
5801
|
+
return { configured: true, ...dc.snapshot() };
|
|
5802
|
+
});
|
|
5803
|
+
|
|
5804
|
+
/* token kaydet + köprüyü (yeniden) başlat */
|
|
5805
|
+
ipcMain.handle('dc:set', async (_e, token) => {
|
|
5806
|
+
const t = String(token || '').trim();
|
|
5807
|
+
if (t) settings.dcToken = t;
|
|
5808
|
+
saveSettings();
|
|
5809
|
+
await restartDc();
|
|
5810
|
+
return { configured: !!settings.dcToken, ...(dc ? dc.snapshot() : { status: 'disconnected', user: null }) };
|
|
5811
|
+
});
|
|
5812
|
+
|
|
5813
|
+
ipcMain.handle('dc:start', async () => {
|
|
5814
|
+
if (!settings.dcToken) return { ok: false, error: 'token yok — önce bot tokenı gir' };
|
|
5815
|
+
await restartDc();
|
|
5816
|
+
return { ok: true, ...(dc ? dc.snapshot() : {}) };
|
|
5817
|
+
});
|
|
5818
|
+
|
|
5819
|
+
ipcMain.handle('dc:stop', async () => {
|
|
5820
|
+
if (dc) {
|
|
5821
|
+
try { await dc.stop(); } catch {}
|
|
5822
|
+
}
|
|
5823
|
+
return { ok: true };
|
|
5824
|
+
});
|
|
5825
|
+
|
|
5826
|
+
ipcMain.handle('dc:allow:get', () => settings.dcAllow || []);
|
|
5827
|
+
ipcMain.handle('dc:allow:set', (_e, list) => {
|
|
5828
|
+
settings.dcAllow = Array.isArray(list) ? list : [];
|
|
5829
|
+
saveSettings();
|
|
5830
|
+
return settings.dcAllow;
|
|
5831
|
+
});
|
|
5832
|
+
ipcMain.handle('dc:sessions', () => [...dcChats.values()]);
|
|
5833
|
+
|
|
5558
5834
|
/* ---------- e-posta IPC ---------- */
|
|
5559
5835
|
|
|
5560
5836
|
ipcMain.handle('email:get', () => {
|
|
@@ -5816,6 +6092,7 @@ ipcMain.handle('bots:update', (_e, { id, patch }) => {
|
|
|
5816
6092
|
if ((v.botId || 'beast') === String(id || '')) {
|
|
5817
6093
|
const cfg = bots.get(String(id));
|
|
5818
6094
|
engine.setSessionTools(v.id, cfg && !cfg.admin ? botToolSet(cfg) : null);
|
|
6095
|
+
engine.setSessionModel(v.id, cfg && !cfg.admin ? (cfg.model || null) : null);
|
|
5819
6096
|
}
|
|
5820
6097
|
}
|
|
5821
6098
|
} catch {}
|
package/src/preload.js
CHANGED
|
@@ -125,6 +125,14 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
125
125
|
tgSetAllow: (list) => ipcRenderer.invoke('tg:allow:set', list),
|
|
126
126
|
tgListSessions: () => ipcRenderer.invoke('tg:sessions'),
|
|
127
127
|
onTgEvent: (cb) => ipcRenderer.on('tg:event', (_e, ev) => cb(ev)),
|
|
128
|
+
dcGetStatus: () => ipcRenderer.invoke('dc:status:get'),
|
|
129
|
+
dcSetToken: (token) => ipcRenderer.invoke('dc:set', token),
|
|
130
|
+
dcStart: () => ipcRenderer.invoke('dc:start'),
|
|
131
|
+
dcStop: () => ipcRenderer.invoke('dc:stop'),
|
|
132
|
+
dcGetAllow: () => ipcRenderer.invoke('dc:allow:get'),
|
|
133
|
+
dcSetAllow: (list) => ipcRenderer.invoke('dc:allow:set', list),
|
|
134
|
+
dcListSessions: () => ipcRenderer.invoke('dc:sessions'),
|
|
135
|
+
onDcEvent: (cb) => ipcRenderer.on('dc:event', (_e, ev) => cb(ev)),
|
|
128
136
|
getUsage: () => ipcRenderer.invoke('usage:get'),
|
|
129
137
|
resetUsage: () => ipcRenderer.invoke('usage:reset'),
|
|
130
138
|
createBackup: () => ipcRenderer.invoke('backup:create'),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -209,6 +209,12 @@
|
|
|
209
209
|
it_tg_token_bad: 'Token doğrulanamadı — @BotFather\u2019dan aldığın tokenı kontrol et',
|
|
210
210
|
it_tg_id_ph: 'Kullanıcı ID veya @kullanıcı_adı',
|
|
211
211
|
it_tg_note: 'İzin listesi boşsa kimse cevap almaz. Bot oluşturma: @BotFather → /newbot. Kendi ID\u2019ni öğrenmek için @userinfobot\u2019a mesaj at.',
|
|
212
|
+
it_dc_sub: 'Bot tokenı ile çalışır — izin listendeki kişilere Discord\u2019dan cevap verir (sunucularda @mention bekler)',
|
|
213
|
+
it_dc_token_ph: 'Bot tokenı (Developer Portal → Bot → Reset Token)',
|
|
214
|
+
it_dc_connecting: 'Discord\u2019a bağlanıyor…',
|
|
215
|
+
it_dc_token_bad: 'Token doğrulanamadı — Developer Portal\u2019dan aldığın tokenı kontrol et',
|
|
216
|
+
it_dc_id_ph: 'Kullanıcı ID veya @kullanıcı_adı',
|
|
217
|
+
it_dc_note: 'İzin listesi boşsa kimse cevap almaz. Bot oluşturma: discord.com/developers → New Application → Bot → Token. "MESSAGE CONTENT INTENT"i AÇ (yoksa mesajlar boş gelir). Sunucu daveti: OAuth2 → bot yetkisiyle. Kendi ID\u2019ni öğrenmek için geliştirici modunu açıp kendine sağ tıkla → ID\u2019yi kopyala.',
|
|
212
218
|
ev_h2: 'Olay Merkezi',
|
|
213
219
|
ev_sub: 'Cron\u2019suz canlı olaylar — kaynakları aç, abonelikleri agent kendisi kurar (event_subscribe). Tetiklenen olay ilgili sohbete düşer, cevap WhatsApp\u2019a gider.',
|
|
214
220
|
ev_on: 'Olay merkezi açık',
|
|
@@ -409,6 +415,9 @@
|
|
|
409
415
|
bot_perm: 'Yetki seviyesi',
|
|
410
416
|
bot_perm_note: 'all tek başına tüm araçları açar (özel). Web / Okuma / Sohbet birden fazla seçilebilir — seçilenlerin araçları birleşir.',
|
|
411
417
|
bot_skills: 'Skill erişimi',
|
|
418
|
+
bot_model: 'Model',
|
|
419
|
+
bot_model_global: 'Global seçim (üstteki picker)',
|
|
420
|
+
bot_model_note: 'Bu bot için farklı model seçebilirsin; boş bırakırsan üstteki global model kullanılır. Seçim sadece bu bota bağlı sohbetlerde geçerli.',
|
|
412
421
|
bot_browser: 'Tarayıcı ayarları',
|
|
413
422
|
bot_ext_browser: 'Dış tarayıcı yetkisi (kullanıcı isterse)',
|
|
414
423
|
bot_def_browser: 'Varsayılan tarayıcı',
|
|
@@ -727,6 +736,12 @@
|
|
|
727
736
|
it_tg_token_bad: 'Token could not be verified — check the token from @BotFather',
|
|
728
737
|
it_tg_id_ph: 'User ID or @username',
|
|
729
738
|
it_tg_note: 'Empty allow list = nobody gets a reply. Create a bot: @BotFather → /newbot. To learn your ID, message @userinfobot.',
|
|
739
|
+
it_dc_sub: 'Works with a bot token — replies on Discord to people in your allow list (mentions only in servers)',
|
|
740
|
+
it_dc_token_ph: 'Bot token (Developer Portal → Bot → Reset Token)',
|
|
741
|
+
it_dc_connecting: 'Connecting to Discord…',
|
|
742
|
+
it_dc_token_bad: 'Token could not be verified — check the token from the Developer Portal',
|
|
743
|
+
it_dc_id_ph: 'User ID or @username',
|
|
744
|
+
it_dc_note: 'Empty allow list = nobody gets a reply. Create a bot: discord.com/developers → New Application → Bot → Token. Turn ON "MESSAGE CONTENT INTENT". Invite via OAuth2 bot URL. To learn your ID: enable developer mode, right-click yourself → Copy ID.',
|
|
730
745
|
ev_h2: 'Event Center',
|
|
731
746
|
ev_sub: 'Live events without cron — open sources, the agent sets up subscriptions itself (event_subscribe). The triggered event drops into the relevant chat, the reply goes to WhatsApp.',
|
|
732
747
|
ev_on: 'Event center on',
|
|
@@ -985,6 +1000,9 @@
|
|
|
985
1000
|
bot_perm: 'Permission level',
|
|
986
1001
|
bot_perm_note: 'all alone grants every tool (exclusive). Web / Read / Chat can be multi-selected — their tool sets are combined.',
|
|
987
1002
|
bot_skills: 'Skill access',
|
|
1003
|
+
bot_model: 'Model',
|
|
1004
|
+
bot_model_global: 'Global selection (main picker)',
|
|
1005
|
+
bot_model_note: 'You can pick a different model for this bot; leave empty to use the global selection. Applies only to chats bound to this bot.',
|
|
988
1006
|
bot_browser: 'Browser settings',
|
|
989
1007
|
bot_ext_browser: 'External browser permission (when user asks)',
|
|
990
1008
|
bot_def_browser: 'Default browser',
|