beast-agent 0.26.0 → 0.26.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/bin/beast-agent.js +34 -17
- package/package.json +2 -2
- package/scripts/fix-electron.js +126 -0
- package/src/agent/bots.js +8 -0
- package/src/agent/discord.js +234 -0
- package/src/agent/engine.js +135 -77
- package/src/agent/llm.js +46 -17
- package/src/agent/research.js +133 -0
- package/src/agent/skills.js +8 -1
- package/src/agent/tools.js +53 -14
- package/src/agent/watext.js +4 -0
- package/src/main.js +533 -351
- package/src/preload.js +8 -5
- package/src/renderer/i18n.js +19 -55
- package/src/renderer/index.html +1 -3
- package/src/renderer/renderer.js +336 -155
- package/src/renderer/style.css +1 -60
- package/tests/bg-jobs.test.js +5 -10
- package/tests/llm.test.js +81 -1
- package/tests/python.test.js +3 -2
- package/tests/research.test.js +116 -0
package/src/main.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { app, BrowserWindow, WebContentsView, ipcMain, shell, dialog, Tray, Menu, nativeImage, desktopCapturer, session } = require('electron');
|
|
3
|
+
const { app, BrowserWindow, WebContentsView, ipcMain, shell, dialog, Tray, Menu, nativeImage, desktopCapturer, session, net: electronNet } = require('electron');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const http = require('http');
|
|
@@ -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');
|
|
@@ -229,6 +230,11 @@ let tg = null;
|
|
|
229
230
|
let tgChats = new Map(); // telegram chatId -> aktif session id
|
|
230
231
|
let tgHistory = new Map(); // chatId -> [sid,...]
|
|
231
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');
|
|
232
238
|
let tray = null;
|
|
233
239
|
app.isQuitting = false;
|
|
234
240
|
|
|
@@ -1183,7 +1189,7 @@ async function waFlush(jid) {
|
|
|
1183
1189
|
if (payload.participant && !merged.participant) merged.participant = payload.participant;
|
|
1184
1190
|
if (payload.participantPn && !merged.participantPn) merged.participantPn = payload.participantPn;
|
|
1185
1191
|
if (payload.participantUsername && !merged.participantUsername) merged.participantUsername = payload.participantUsername;
|
|
1186
|
-
if (payload.mentioned) merged.mentioned = true;
|
|
1192
|
+
if (payload.mentioned) merged.mentioned = true;
|
|
1187
1193
|
if (!senderNum && sn) senderNum = sn;
|
|
1188
1194
|
}
|
|
1189
1195
|
try {
|
|
@@ -1442,7 +1448,7 @@ function botToolSet(cfg) {
|
|
|
1442
1448
|
'event_list', 'event_subscribe', 'event_unsubscribe',
|
|
1443
1449
|
'watcher_add', 'watcher_list', 'watcher_remove',
|
|
1444
1450
|
]);
|
|
1445
|
-
if (s.web_search) { set.add('web_search'); set.add('http_fetch'); }
|
|
1451
|
+
if (s.web_search) { set.add('web_search'); set.add('http_fetch'); set.add('deep_search'); }
|
|
1446
1452
|
if (s.browser) {
|
|
1447
1453
|
for (const t of ['browser_open', 'browser_read', 'browser_screenshot', 'browser_snapshot', 'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select', 'ocr_read']) set.add(t);
|
|
1448
1454
|
}
|
|
@@ -1711,6 +1717,7 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
|
1711
1717
|
} else {
|
|
1712
1718
|
engine.setSessionTools(sid, null);
|
|
1713
1719
|
}
|
|
1720
|
+
engine.setSessionModel(sid, botCfg && !botCfg.admin ? (botCfg.model || null) : null);
|
|
1714
1721
|
waLog(`perm=${fmtPerm(perm)} bot=${botId} sid=${sid}`);
|
|
1715
1722
|
|
|
1716
1723
|
const participantName = payload.participant ? '+' + String(payload.participant).split('@')[0].split(':')[0] : '';
|
|
@@ -1979,6 +1986,7 @@ async function processTgMessage(chatId, payload, requeues = 0) {
|
|
|
1979
1986
|
} else {
|
|
1980
1987
|
engine.setSessionTools(sid, null);
|
|
1981
1988
|
}
|
|
1989
|
+
engine.setSessionModel(sid, botCfg && !botCfg.admin ? (botCfg.model || null) : null);
|
|
1982
1990
|
tgLog(`perm=${fmtPerm(perm)} bot=${botId} sid=${sid}`);
|
|
1983
1991
|
|
|
1984
1992
|
/* #v13.1 rol: SAHİP vs MİSAFİR — ajan kime konuştuğunu net bilsin */
|
|
@@ -2037,6 +2045,224 @@ async function restartTg() {
|
|
|
2037
2045
|
}
|
|
2038
2046
|
}
|
|
2039
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
|
+
|
|
2040
2266
|
function reloadBackend() {
|
|
2041
2267
|
if (engine && typeof engine.dispose === 'function') {
|
|
2042
2268
|
try { engine.dispose(); } catch {}
|
|
@@ -2091,13 +2317,18 @@ function reloadBackend() {
|
|
|
2091
2317
|
ocr: (o) => ocrRead(o),
|
|
2092
2318
|
email: { list: emailList, read: emailRead, send: emailSend },
|
|
2093
2319
|
browser: {
|
|
2094
|
-
openUrl: (u, s) => browserNavigate(u, s),
|
|
2095
|
-
search: (q, s) => browserSearch(q, s),
|
|
2320
|
+
openUrl: (u, s, ctx) => browserNavigate(u, s, ctx),
|
|
2321
|
+
search: (q, s, ctx) => browserSearch(q, s, ctx),
|
|
2096
2322
|
readText: (s) => browserRead(s),
|
|
2097
2323
|
screenshot: (s) => browserScreenshot(s),
|
|
2098
2324
|
snapshot: (s) => browserSnapshot(s),
|
|
2099
2325
|
act: (k, a, s) => browserAct(k, a, s),
|
|
2100
2326
|
},
|
|
2327
|
+
research: {
|
|
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),
|
|
2331
|
+
},
|
|
2101
2332
|
emit: (ev) => {
|
|
2102
2333
|
if (win && !win.isDestroyed()) win.webContents.send('agent:event', ev);
|
|
2103
2334
|
flushDesktopOnDone(ev); /* biriken desktop mesajlarını sıraya bas */
|
|
@@ -2201,6 +2432,27 @@ function reloadBackend() {
|
|
|
2201
2432
|
})();
|
|
2202
2433
|
}
|
|
2203
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
|
+
}
|
|
2204
2456
|
},
|
|
2205
2457
|
});
|
|
2206
2458
|
return engine.publicState();
|
|
@@ -2327,10 +2579,6 @@ function ensureDesktopShortcut() {
|
|
|
2327
2579
|
}
|
|
2328
2580
|
|
|
2329
2581
|
app.whenReady().then(() => {
|
|
2330
|
-
try { applyProxy(); } catch {} // proxy ayarı yüklüyse tüm çıkışa uygula
|
|
2331
|
-
if (settings.warp && settings.warp.enabled) {
|
|
2332
|
-
startWarp().catch((e) => log.info('main', 'warp açılışta başlatılamadı: ' + String((e && e.message) || e)));
|
|
2333
|
-
}
|
|
2334
2582
|
// Tailscale modu: paketli uygulamada Windows ile otomatik başlat (sessiz, tepside)
|
|
2335
2583
|
if (app.isPackaged) {
|
|
2336
2584
|
/* DAĞITIM KARARI: EXE/portable kurulum desteklenmiyor — tek yol npm.
|
|
@@ -2395,6 +2643,11 @@ app.whenReady().then(() => {
|
|
|
2395
2643
|
ensureTg().start().catch((e) => tgLog('autostart failed: ' + String((e && e.message) || e)));
|
|
2396
2644
|
}
|
|
2397
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
|
+
|
|
2398
2651
|
app.on('activate', () => {
|
|
2399
2652
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
|
2400
2653
|
});
|
|
@@ -2402,7 +2655,6 @@ app.whenReady().then(() => {
|
|
|
2402
2655
|
app.on('before-quit', () => {
|
|
2403
2656
|
app.isQuitting = true;
|
|
2404
2657
|
flushBrowserStorage(); // x.com/google oturumları (cookies) diske yazılsın
|
|
2405
|
-
try { stopWarp(); } catch {} // warp-plus yardımcısını da kapat
|
|
2406
2658
|
});
|
|
2407
2659
|
|
|
2408
2660
|
if (process.argv.includes('--smoke')) {
|
|
@@ -2558,10 +2810,13 @@ function setBrowserOpen(v, forceVisible) {
|
|
|
2558
2810
|
return;
|
|
2559
2811
|
}
|
|
2560
2812
|
|
|
2561
|
-
// AÇMA — görünürlük:
|
|
2562
|
-
//
|
|
2813
|
+
// AÇMA — görünürlük: forceVisible true/false ise onu uygula;
|
|
2814
|
+
// belirtilmemişse kullanıcı tercihi (settings.browserHeadless) belirler.
|
|
2815
|
+
// PARALEL AJANLAR (bg oturum) her zaman forceVisible=false ile çağırır →
|
|
2816
|
+
// tarayıcı gizli modda çalışır, kullanıcı ekranı ve ajan konsolu rahatsız edilmez.
|
|
2563
2817
|
browser.open = true;
|
|
2564
|
-
browser.visible =
|
|
2818
|
+
browser.visible =
|
|
2819
|
+
forceVisible === true ? true : forceVisible === false ? false : settings.browserHeadless !== true;
|
|
2565
2820
|
ensureBrowser();
|
|
2566
2821
|
if (!browser.started) {
|
|
2567
2822
|
browser.started = true;
|
|
@@ -2590,14 +2845,23 @@ function browserGate(job) {
|
|
|
2590
2845
|
__trafficTail = p.catch(() => {});
|
|
2591
2846
|
return p;
|
|
2592
2847
|
}
|
|
2593
|
-
|
|
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);
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
async function browserNavigate(raw, signal, ctx) {
|
|
2594
2858
|
return browserGate(async () => {
|
|
2595
2859
|
await browserTrafficWait();
|
|
2596
|
-
return browserNavigateNow(raw, signal);
|
|
2860
|
+
return browserNavigateNow(raw, signal, ctx);
|
|
2597
2861
|
});
|
|
2598
2862
|
}
|
|
2599
2863
|
|
|
2600
|
-
async function browserNavigateNow(raw, signal) {
|
|
2864
|
+
async function browserNavigateNow(raw, signal, ctx) {
|
|
2601
2865
|
let url = String(raw || '').trim();
|
|
2602
2866
|
if (!url) return { ok: false, error: 'boş adres' };
|
|
2603
2867
|
if (!/^https?:\/\//i.test(url)) {
|
|
@@ -2606,7 +2870,7 @@ async function browserNavigateNow(raw, signal) {
|
|
|
2606
2870
|
? 'https://' + url
|
|
2607
2871
|
: 'https://duckduckgo.com/?q=' + encodeURIComponent(url);
|
|
2608
2872
|
}
|
|
2609
|
-
|
|
2873
|
+
setBrowserOpenForAgent();
|
|
2610
2874
|
const wc = browser.view.webContents;
|
|
2611
2875
|
await new Promise((resolve) => {
|
|
2612
2876
|
let settled = false;
|
|
@@ -2668,6 +2932,120 @@ function flushBrowserStorage() {
|
|
|
2668
2932
|
} catch {}
|
|
2669
2933
|
}
|
|
2670
2934
|
|
|
2935
|
+
/* ---------- GİZLİ ARAŞTIRMA TARAYICISI (deep_search) ----------
|
|
2936
|
+
deep_search'ün "sayfayı açıp oku" adımı burada çalışır: WebContentsView
|
|
2937
|
+
HİÇbir pencereye eklenmez (kullanıcı hiçbir şey görmez) ama gerçek Chromium
|
|
2938
|
+
çalışır — JS/SPA sayfalar render olur, innerText okunur. Panel (browser.view)
|
|
2939
|
+
hiç meşgul edilmez. Görsel/font/media indirme hız için iptal edilir.
|
|
2940
|
+
2 slot = en fazla 2 sayfa aynı anda okunur (ayrı view, çakışma yok). */
|
|
2941
|
+
const researchPool = { views: [null, null], queue: [], slots: [false, false] };
|
|
2942
|
+
const RESEARCH_PAGE_TIMEOUT = 25000;
|
|
2943
|
+
const RESEARCH_SETTLE_MS = 900;
|
|
2944
|
+
const RESEARCH_CONTENT_CAP = 4500;
|
|
2945
|
+
|
|
2946
|
+
function researchAcquire() {
|
|
2947
|
+
return new Promise((resolve) => {
|
|
2948
|
+
researchPool.queue.push(resolve);
|
|
2949
|
+
researchPump();
|
|
2950
|
+
});
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
function researchRelease(idx) {
|
|
2954
|
+
researchPool.slots[idx] = false;
|
|
2955
|
+
researchPump();
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
function researchPump() {
|
|
2959
|
+
for (let i = 0; i < researchPool.slots.length; i++) {
|
|
2960
|
+
if (!researchPool.slots[i] && researchPool.queue.length) {
|
|
2961
|
+
researchPool.slots[i] = true;
|
|
2962
|
+
researchPool.queue.shift()(i);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
function researchViewAt(idx) {
|
|
2968
|
+
let v = researchPool.views[idx];
|
|
2969
|
+
if (v && v.webContents && !v.webContents.isDestroyed()) return v;
|
|
2970
|
+
v = new WebContentsView({
|
|
2971
|
+
webPreferences: {
|
|
2972
|
+
partition: 'research', // kalıcı olmayan bölüm — çerez birikmez
|
|
2973
|
+
contextIsolation: true,
|
|
2974
|
+
nodeIntegration: false,
|
|
2975
|
+
sandbox: true,
|
|
2976
|
+
spellcheck: false,
|
|
2977
|
+
backgroundThrottling: false, // gizli çalışırken zamanlayıcılar yavaşlamasın
|
|
2978
|
+
},
|
|
2979
|
+
});
|
|
2980
|
+
const wc = v.webContents;
|
|
2981
|
+
try {
|
|
2982
|
+
/* Google/bazı siteler Electron UA'yı reddeder — gerçek Chrome kimliği */
|
|
2983
|
+
const chromeUA = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${process.versions.chrome} Safari/537.36`;
|
|
2984
|
+
wc.setUserAgent(chromeUA);
|
|
2985
|
+
const ses = session.fromPartition('research');
|
|
2986
|
+
if (ses && ses.setUserAgent) ses.setUserAgent(chromeUA, 'tr-TR,tr;q=0.9,en;q=0.8');
|
|
2987
|
+
} catch {}
|
|
2988
|
+
try { wc.setWindowOpenHandler(() => ({ action: 'deny' })); } catch {}
|
|
2989
|
+
try {
|
|
2990
|
+
session.fromPartition('research').setPermissionRequestHandler((_w, _p, cb) => cb(false));
|
|
2991
|
+
} catch {}
|
|
2992
|
+
try {
|
|
2993
|
+
/* hız: araştırma okuması için görsel/font/media gereksiz — iptal et */
|
|
2994
|
+
session.fromPartition('research').webRequest.onBeforeRequest({ urls: ['*://*/*'] }, (details, cb) => {
|
|
2995
|
+
cb({ cancel: ['image', 'media', 'font'].includes(details.resourceType) });
|
|
2996
|
+
});
|
|
2997
|
+
} catch {}
|
|
2998
|
+
try {
|
|
2999
|
+
session.fromPartition('research').on('will-download', (_e, item) => { try { item.cancel(); } catch {} });
|
|
3000
|
+
} catch {}
|
|
3001
|
+
wc.on('render-process-gone', () => { researchPool.views[idx] = null; });
|
|
3002
|
+
researchPool.views[idx] = v;
|
|
3003
|
+
return v;
|
|
3004
|
+
}
|
|
3005
|
+
|
|
3006
|
+
async function researchRead(rawUrl, signal) {
|
|
3007
|
+
const url = String(rawUrl || '').trim();
|
|
3008
|
+
if (!/^https?:\/\//i.test(url)) return { ok: false, url, error: 'geçersiz adres' };
|
|
3009
|
+
if (signal && signal.aborted) return { ok: false, url, error: 'iptal edildi' };
|
|
3010
|
+
const idx = await researchAcquire();
|
|
3011
|
+
try {
|
|
3012
|
+
const view = researchViewAt(idx);
|
|
3013
|
+
if (!view) return { ok: false, url, error: 'araştırma tarayıcısı oluşturulamadı' };
|
|
3014
|
+
const wc = view.webContents;
|
|
3015
|
+
const loaded = await new Promise((resolve) => {
|
|
3016
|
+
let settled = false;
|
|
3017
|
+
const finish = (v) => { if (!settled) { settled = true; clearTimeout(timer); wc.removeListener('did-finish-load', onDone); wc.removeListener('did-fail-load', onFail); resolve(v); } };
|
|
3018
|
+
const timer = setTimeout(() => finish(false), RESEARCH_PAGE_TIMEOUT);
|
|
3019
|
+
const onDone = () => finish(true);
|
|
3020
|
+
const onFail = (_e, code) => { if (code !== -3) finish(false); };
|
|
3021
|
+
wc.once('did-finish-load', onDone);
|
|
3022
|
+
wc.on('did-fail-load', onFail);
|
|
3023
|
+
wc.loadURL(url).catch(() => {});
|
|
3024
|
+
});
|
|
3025
|
+
/* SPA hidrasyonu için kısa settle; metin boşsa bir kez daha dene */
|
|
3026
|
+
await new Promise((r) => setTimeout(r, RESEARCH_SETTLE_MS));
|
|
3027
|
+
let title = '';
|
|
3028
|
+
let finalUrl = url;
|
|
3029
|
+
try { title = wc.getTitle() || ''; finalUrl = wc.getURL() || url; } catch {}
|
|
3030
|
+
const grab = async () => {
|
|
3031
|
+
try { return String((await wc.executeJavaScript('(document.body&&document.body.innerText)||""', true)) || ''); } catch { return ''; }
|
|
3032
|
+
};
|
|
3033
|
+
let text = (await grab()).replace(/\n{3,}/g, '\n\n').trim();
|
|
3034
|
+
if (!text) {
|
|
3035
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
3036
|
+
text = (await grab()).replace(/\n{3,}/g, '\n\n').trim();
|
|
3037
|
+
}
|
|
3038
|
+
if (!text) {
|
|
3039
|
+
return { ok: false, url: finalUrl, title, error: loaded ? 'sayfa metni boş (tam JS-görsel veya engelli sayfa olabilir)' : 'sayfa yüklenemedi (zaman aşımı/hata)' };
|
|
3040
|
+
}
|
|
3041
|
+
return { ok: true, url: finalUrl, title, truncated: text.length > RESEARCH_CONTENT_CAP, content: text.slice(0, RESEARCH_CONTENT_CAP) };
|
|
3042
|
+
} catch (e) {
|
|
3043
|
+
return { ok: false, url, error: String((e && e.message) || e) };
|
|
3044
|
+
} finally {
|
|
3045
|
+
researchRelease(idx);
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
|
|
2671
3049
|
/* Dahili OCR: görsel desteklemeyen modeller için tesseract.js ile metin okuma.
|
|
2672
3050
|
Dil verisi ilk kullanımda %APPDATA%\beast\tessdata'ya iner, sonra offline çalışır. */
|
|
2673
3051
|
const _ocrWorkers = new Map(); // lang -> worker (her çağrıda yeniden init olmasın)
|
|
@@ -2705,21 +3083,21 @@ async function ocrRead({ image, lang = 'tur+eng' } = {}) {
|
|
|
2705
3083
|
Numara: view'i google.com'a bir kez açıp aramaları SAYFA İÇİ fetch() ile
|
|
2706
3084
|
yapmak (kullanıcının console'da yaptığı gibi) — sayfa bile değişmeden
|
|
2707
3085
|
DOMParser ile sonuç çekilir. Fetch olmazsa direk gezinme fallback'i var. */
|
|
2708
|
-
async function browserSearch(query, signal) {
|
|
3086
|
+
async function browserSearch(query, signal, ctx) {
|
|
2709
3087
|
/* paralel ajan sorguları trafik kapısından sırayla geçer */
|
|
2710
3088
|
return browserGate(async () => {
|
|
2711
3089
|
await browserTrafficWait();
|
|
2712
|
-
return browserSearchNow(query, signal);
|
|
3090
|
+
return browserSearchNow(query, signal, ctx);
|
|
2713
3091
|
});
|
|
2714
3092
|
}
|
|
2715
3093
|
|
|
2716
|
-
async function browserSearchNow(query, signal) {
|
|
3094
|
+
async function browserSearchNow(query, signal, ctx) {
|
|
2717
3095
|
try {
|
|
2718
3096
|
if (!win || win.isDestroyed()) return null;
|
|
2719
3097
|
if (signal && signal.aborted) return null;
|
|
2720
3098
|
const q = String(query || '').trim();
|
|
2721
3099
|
if (!q) return null;
|
|
2722
|
-
|
|
3100
|
+
setBrowserOpenForAgent();
|
|
2723
3101
|
const wc = browser.view && browser.view.webContents;
|
|
2724
3102
|
if (!wc) return null;
|
|
2725
3103
|
|
|
@@ -3325,6 +3703,7 @@ ipcMain.handle('sessions:create', () => {
|
|
|
3325
3703
|
} else {
|
|
3326
3704
|
engine.setSessionTools(v.id, null);
|
|
3327
3705
|
}
|
|
3706
|
+
engine.setSessionModel(v.id, b && !b.admin ? (b.model || null) : null);
|
|
3328
3707
|
return v;
|
|
3329
3708
|
});
|
|
3330
3709
|
ipcMain.handle('sessions:open', (_e, id) => engine.openSession(id));
|
|
@@ -3344,7 +3723,13 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
|
|
|
3344
3723
|
if (!sess) {
|
|
3345
3724
|
try { sess = engine._load(sid); } catch {}
|
|
3346
3725
|
}
|
|
3347
|
-
if (sess && !sess.botId)
|
|
3726
|
+
if (sess && !sess.botId) {
|
|
3727
|
+
engine.setSessionBot(sid, actBot.id);
|
|
3728
|
+
/* tam bağlama: izin + araç seti + botun kendi modeli (sessions:create ile aynı) */
|
|
3729
|
+
engine.setSessionPerm(sid, actBot.perm || 'all');
|
|
3730
|
+
engine.setSessionTools(sid, botToolSet(actBot));
|
|
3731
|
+
engine.setSessionModel(sid, actBot.model || null);
|
|
3732
|
+
}
|
|
3348
3733
|
}
|
|
3349
3734
|
} catch {}
|
|
3350
3735
|
if (t === '/stop' || t === '/start') {
|
|
@@ -3748,27 +4133,21 @@ function flushDesktopOnDone(ev) {
|
|
|
3748
4133
|
- Bağlantı geri gelince (DNS kontrolü) sırayla otomatik gönderilir
|
|
3749
4134
|
- Renderer'a 'net' / 'netQueue' olayları gider: ⏳ kuyruk balonu + toast */
|
|
3750
4135
|
const NET_CHECK_HOSTS = ['one.one.one.one', 'dns.google'];
|
|
4136
|
+
const NET_LOOKUP_HOSTS = ['www.google.com', 'www.microsoft.com'];
|
|
4137
|
+
const NET_HTTP_PROBES = [
|
|
4138
|
+
'http://www.msftconnecttest.com/connecttest.txt',
|
|
4139
|
+
'http://cp.cloudflare.com/generate_204',
|
|
4140
|
+
'http://connectivitycheck.gstatic.com/generate_204',
|
|
4141
|
+
];
|
|
3751
4142
|
const NET_CHECK_MS = 8000;
|
|
3752
4143
|
const NET_CHECK_TIMEOUT = 4000;
|
|
3753
|
-
const
|
|
3754
|
-
let __netFailStreak = 0;
|
|
3755
|
-
|
|
3756
|
-
/* Gerçek HTTP yoklaması: DNS yerine sayfa çekmeyi dener — proxy açıksa proxy
|
|
3757
|
-
üzerinden gider (kullanıcı trafiğinin gördüğü interneti ölçer) */
|
|
3758
|
-
async function httpProbe() {
|
|
3759
|
-
for (const u of NET_PROBE_URLS) {
|
|
3760
|
-
try {
|
|
3761
|
-
const res = await (__origFetch || fetch)(u, { signal: AbortSignal.timeout(5000) });
|
|
3762
|
-
if (res.ok) return true;
|
|
3763
|
-
} catch {}
|
|
3764
|
-
}
|
|
3765
|
-
return false;
|
|
3766
|
-
}
|
|
4144
|
+
const NET_OFFLINE_STRIKES = 2; // üst üste bu kadar başarısız turda offline ilan edilir
|
|
3767
4145
|
const CHAT_QUEUE_MAX = 50; // kuyruk üst sınırı — taşarsa en eski düşer
|
|
3768
4146
|
|
|
3769
4147
|
let netOnline = true; // son bilinen bağlantı durumu (başlangıçta iyimser)
|
|
3770
4148
|
let netCheckedOnce = false;
|
|
3771
4149
|
let netCheckBusy = false;
|
|
4150
|
+
let netFailStreak = 0;
|
|
3772
4151
|
let chatQueueFlushing = false;
|
|
3773
4152
|
const chatOfflineQueue = []; // { key, sessionId, text, attachments, at }
|
|
3774
4153
|
|
|
@@ -3852,8 +4231,42 @@ async function flushChatQueue() {
|
|
|
3852
4231
|
}
|
|
3853
4232
|
}
|
|
3854
4233
|
|
|
3855
|
-
/* gerçek internet
|
|
3856
|
-
|
|
4234
|
+
/* gerçek internet kontrolü — TEK yöntem yanıltıcı olabilir:
|
|
4235
|
+
dns.resolve (c-ares) sistem çözümleyicisini atlar; mobil ağ/hotspot/VPN ve
|
|
4236
|
+
ISS DNS engellemelerinde (ör. 1.1.1.1, dns.google) internet VARken bile
|
|
4237
|
+
başarısız çıkar. Bu yüzden katmanlı deniyoruz; HERHANGİ bir katman başarılıysa
|
|
4238
|
+
internet VAR sayılır:
|
|
4239
|
+
1) HTTP connectivity endpoint'leri (http modülü dns.lookup = OS çözümleyicisi
|
|
4240
|
+
kullanır — tarayıcı gibi; captive portal/proxy/mobil ağ hepsinde çalışır)
|
|
4241
|
+
2) dns.lookup (Windows sistem çözümleyicisi — hosts dosyası/VPN/NRPT dahil)
|
|
4242
|
+
+ dns.resolve (doğrudan DNS sunucusu)
|
|
4243
|
+
3) OS'in kendi bağlantı durumu (Electron net.isOnline — Windows NCSI)
|
|
4244
|
+
Ayrıca tek başarısız tur offline ilan etmez (2 üst üste başarısız tur gerekir):
|
|
4245
|
+
geçici DNS gecikmesi mesajları gereksiz kuyruğa atmaz. */
|
|
4246
|
+
function httpProbe(url) {
|
|
4247
|
+
return new Promise((resolve) => {
|
|
4248
|
+
let done = false;
|
|
4249
|
+
const fin = (v) => { if (!done) { done = true; resolve(v); } };
|
|
4250
|
+
try {
|
|
4251
|
+
const req = http.get(url, { timeout: NET_CHECK_TIMEOUT }, (res) => {
|
|
4252
|
+
res.resume(); // gövdeyi tüket — soket serbest kalsın
|
|
4253
|
+
const ok = !!res.statusCode && res.statusCode < 500;
|
|
4254
|
+
try { res.destroy(); } catch {}
|
|
4255
|
+
fin(ok);
|
|
4256
|
+
});
|
|
4257
|
+
req.on('timeout', () => { try { req.destroy(); } catch {} fin(false); });
|
|
4258
|
+
req.on('error', () => fin(false));
|
|
4259
|
+
} catch { fin(false); }
|
|
4260
|
+
});
|
|
4261
|
+
}
|
|
4262
|
+
|
|
4263
|
+
function lookupProbe(host) {
|
|
4264
|
+
return new Promise((resolve) => {
|
|
4265
|
+
const t = setTimeout(() => resolve(false), NET_CHECK_TIMEOUT);
|
|
4266
|
+
dns.lookup(host, (err) => { clearTimeout(t); resolve(!err); });
|
|
4267
|
+
});
|
|
4268
|
+
}
|
|
4269
|
+
|
|
3857
4270
|
function dnsProbe(host) {
|
|
3858
4271
|
return new Promise((resolve) => {
|
|
3859
4272
|
const t = setTimeout(() => resolve(false), NET_CHECK_TIMEOUT);
|
|
@@ -3868,37 +4281,37 @@ async function netCheck() {
|
|
|
3868
4281
|
if (netCheckBusy) return;
|
|
3869
4282
|
netCheckBusy = true;
|
|
3870
4283
|
try {
|
|
3871
|
-
let ok =
|
|
3872
|
-
|
|
3873
|
-
|
|
4284
|
+
let ok = (await Promise.all(NET_HTTP_PROBES.map(httpProbe))).some(Boolean);
|
|
4285
|
+
if (!ok) {
|
|
4286
|
+
const lookups = [
|
|
4287
|
+
...NET_LOOKUP_HOSTS.map(lookupProbe),
|
|
4288
|
+
...NET_CHECK_HOSTS.map(dnsProbe),
|
|
4289
|
+
];
|
|
4290
|
+
ok = (await Promise.all(lookups)).some(Boolean);
|
|
3874
4291
|
}
|
|
3875
4292
|
if (!ok) {
|
|
3876
|
-
|
|
3877
|
-
HTTP yoklamasıyla DOĞRULA — tarayıcının gördüğü internete yakın sinyal */
|
|
3878
|
-
ok = await httpProbe();
|
|
4293
|
+
try { ok = electronNet.isOnline() === true; } catch {}
|
|
3879
4294
|
}
|
|
3880
4295
|
const first = !netCheckedOnce;
|
|
3881
4296
|
const was = netOnline;
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
if (netOnline && __netFailStreak < 3) { netCheckedOnce = true; return; }
|
|
4297
|
+
netCheckedOnce = true;
|
|
4298
|
+
if (ok) {
|
|
4299
|
+
netFailStreak = 0;
|
|
4300
|
+
netOnline = true;
|
|
3887
4301
|
} else {
|
|
3888
|
-
|
|
4302
|
+
netFailStreak++;
|
|
4303
|
+
if (!was || netFailStreak >= NET_OFFLINE_STRIKES) netOnline = false;
|
|
3889
4304
|
}
|
|
3890
|
-
netOnline
|
|
3891
|
-
netCheckedOnce = true;
|
|
3892
|
-
if (was !== ok || first) {
|
|
4305
|
+
if (netOnline !== was || first) {
|
|
3893
4306
|
try {
|
|
3894
|
-
if (win && !win.isDestroyed()) win.webContents.send('agent:event', { type: 'net', online:
|
|
4307
|
+
if (win && !win.isDestroyed()) win.webContents.send('agent:event', { type: 'net', online: netOnline });
|
|
3895
4308
|
} catch {}
|
|
3896
|
-
if (
|
|
4309
|
+
if (netOnline) {
|
|
3897
4310
|
log.info('main', 'bağlantı geri geldi — offline kuyruk kontrol ediliyor');
|
|
3898
4311
|
chatQueueEmit(); // renderer: pill/toast güncellensin
|
|
3899
4312
|
flushChatQueue().catch(() => {});
|
|
3900
4313
|
} else {
|
|
3901
|
-
log.info('main',
|
|
4314
|
+
log.info('main', `internet bağlantısı yok — mesajlar kuyruğa alınacak (streak=${netFailStreak})`);
|
|
3902
4315
|
chatQueueEmit();
|
|
3903
4316
|
}
|
|
3904
4317
|
}
|
|
@@ -3908,9 +4321,21 @@ async function netCheck() {
|
|
|
3908
4321
|
}
|
|
3909
4322
|
|
|
3910
4323
|
ipcMain.handle('model:set', (_e, sel) => {
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
4324
|
+
/* MÜŞTERİ botu aktifken picker seçimi O BOTUN modelini değiştirir;
|
|
4325
|
+
Beast (admin) aktifken global seçim değişir. */
|
|
4326
|
+
const act = settings.activeBotId ? bots.get(settings.activeBotId) : null;
|
|
4327
|
+
if (act && !act.admin) {
|
|
4328
|
+
try { bots.update(act.id, { model: String(sel || '') }); } catch {}
|
|
4329
|
+
try {
|
|
4330
|
+
for (const v of engine.listSessions()) {
|
|
4331
|
+
if (v.botId === act.id) engine.setSessionModel(v.id, sel || null);
|
|
4332
|
+
}
|
|
4333
|
+
} catch {}
|
|
4334
|
+
} else {
|
|
4335
|
+
settings.modelOverride = sel;
|
|
4336
|
+
saveSettings();
|
|
4337
|
+
engine.setModelOverride(sel);
|
|
4338
|
+
}
|
|
3914
4339
|
return engine.publicState();
|
|
3915
4340
|
});
|
|
3916
4341
|
|
|
@@ -4136,294 +4561,6 @@ ipcMain.handle('wa:groups:set', (_e, cfg) => {
|
|
|
4136
4561
|
return settings.waGroups;
|
|
4137
4562
|
});
|
|
4138
4563
|
|
|
4139
|
-
/* ---------------- Proxy (Ayarlar → Proxy) ----------------
|
|
4140
|
-
Kullanıcının verdiği proxy(ler) üzerinden çıkış: LLM API'leri, http_fetch,
|
|
4141
|
-
TinyFish/Exa, python motorları (env) ve dahili tarayıcı (Chromium proxyRules).
|
|
4142
|
-
Çoklu adres = Node fetch tarafında round-robin rotasyon. SOCKS5 yalnız tarayıcıda. */
|
|
4143
|
-
let __proxyIdx = 0;
|
|
4144
|
-
let __proxyAgents = null; // url -> undici.ProxyAgent
|
|
4145
|
-
let __origFetch = null;
|
|
4146
|
-
|
|
4147
|
-
function proxyUrlList() {
|
|
4148
|
-
return String((settings.proxy && settings.proxy.urls) || '')
|
|
4149
|
-
.split(/[\n,;]+/)
|
|
4150
|
-
.map((s) => s.trim())
|
|
4151
|
-
.filter((s) => /^(https?|socks5):\/\//i.test(s));
|
|
4152
|
-
}
|
|
4153
|
-
|
|
4154
|
-
function proxyAgentFor(url) {
|
|
4155
|
-
const undici = require('undici');
|
|
4156
|
-
if (!__proxyAgents) __proxyAgents = new Map();
|
|
4157
|
-
if (!__proxyAgents.has(url)) {
|
|
4158
|
-
if (/^socks5:/i.test(url)) __proxyAgents.set(url, new undici.Agent({ connect: socks5Connector(url), connections: 64 }));
|
|
4159
|
-
else __proxyAgents.set(url, new undici.ProxyAgent(url));
|
|
4160
|
-
}
|
|
4161
|
-
return __proxyAgents.get(url);
|
|
4162
|
-
}
|
|
4163
|
-
|
|
4164
|
-
/* Minimal SOCKS5 CONNECT connector (undici Agent.connect imzası).
|
|
4165
|
-
WARP (warp-plus) dahil tüm socks5 proxy'leri Node fetch'te çalıştırır. */
|
|
4166
|
-
function socks5Connector(socksUrl) {
|
|
4167
|
-
const net = require('net');
|
|
4168
|
-
const tls = require('tls');
|
|
4169
|
-
const u = new URL(socksUrl);
|
|
4170
|
-
const shost = u.hostname;
|
|
4171
|
-
const sport = Number(u.port) || 1080;
|
|
4172
|
-
const suser = decodeURIComponent(u.username || '');
|
|
4173
|
-
const spass = decodeURIComponent(u.password || '');
|
|
4174
|
-
return function connect(opts, cb) {
|
|
4175
|
-
let stage = 0; // 0 greet · 1 auth · 2 conn
|
|
4176
|
-
let buf = Buffer.alloc(0);
|
|
4177
|
-
const socket = net.connect({ host: shost, port: sport });
|
|
4178
|
-
const fail = (err) => { try { socket.destroy(); } catch {} cb(err); };
|
|
4179
|
-
const onData = (d) => {
|
|
4180
|
-
buf = Buffer.concat([buf, d]);
|
|
4181
|
-
if (stage === 0) {
|
|
4182
|
-
if (buf.length < 2) return;
|
|
4183
|
-
if (buf[0] !== 5) return fail(new Error('socks5: geçersiz yanıt'));
|
|
4184
|
-
const method = buf[1];
|
|
4185
|
-
buf = buf.subarray(2);
|
|
4186
|
-
if (method === 2) {
|
|
4187
|
-
stage = 1;
|
|
4188
|
-
const ub = Buffer.from(suser), pb = Buffer.from(spass);
|
|
4189
|
-
socket.write(Buffer.concat([Buffer.from([1, ub.length]), ub, Buffer.from([pb.length]), pb]));
|
|
4190
|
-
} else if (method === 0) sendReq();
|
|
4191
|
-
else return fail(new Error('socks5: desteklenmeyen kimlik doğrulama'));
|
|
4192
|
-
} else if (stage === 1) {
|
|
4193
|
-
if (buf.length < 2) return;
|
|
4194
|
-
buf = buf.subarray(2);
|
|
4195
|
-
sendReq();
|
|
4196
|
-
} else if (stage === 2) {
|
|
4197
|
-
if (buf.length < 5) return;
|
|
4198
|
-
if (buf[0] !== 5 || buf[1] !== 0) return fail(new Error('socks5: bağlantı reddedildi (' + buf[1] + ')'));
|
|
4199
|
-
const need = 6 + (buf[3] === 1 ? 4 : buf[3] === 4 ? 16 : 1 + buf[4]);
|
|
4200
|
-
if (buf.length < need) return;
|
|
4201
|
-
buf = Buffer.alloc(0);
|
|
4202
|
-
socket.off('data', onData);
|
|
4203
|
-
if (opts.protocol === 'https:') {
|
|
4204
|
-
const tlsSock = tls.connect({ socket, servername: opts.servername || opts.hostname });
|
|
4205
|
-
tlsSock.on('error', fail);
|
|
4206
|
-
tlsSock.once('secureConnect', () => cb(null, tlsSock));
|
|
4207
|
-
} else cb(null, socket);
|
|
4208
|
-
}
|
|
4209
|
-
};
|
|
4210
|
-
socket.on('error', fail);
|
|
4211
|
-
socket.on('connect', () => {
|
|
4212
|
-
/* SOCKS5 greeting: şifre varsa user/pass(2), yoksa no-auth(0) yöntemi öner */
|
|
4213
|
-
socket.write(suser ? Buffer.from([5, 2, 0, 2]) : Buffer.from([5, 1, 0]));
|
|
4214
|
-
});
|
|
4215
|
-
socket.on('data', onData);
|
|
4216
|
-
function sendReq() {
|
|
4217
|
-
stage = 2;
|
|
4218
|
-
const host = opts.hostname || opts.host;
|
|
4219
|
-
const port = Number(opts.port) || (opts.protocol === 'https:' ? 443 : 80);
|
|
4220
|
-
const hb = Buffer.from(String(host), 'utf8');
|
|
4221
|
-
socket.write(Buffer.concat([Buffer.from([5, 1, 0, 3, hb.length]), hb, Buffer.from([(port >> 8) & 255, port & 255])]));
|
|
4222
|
-
}
|
|
4223
|
-
};
|
|
4224
|
-
}
|
|
4225
|
-
|
|
4226
|
-
function applyProxy() {
|
|
4227
|
-
const enabled = !!(settings.proxy && settings.proxy.enabled);
|
|
4228
|
-
const allUrls = enabled ? proxyUrlList() : [];
|
|
4229
|
-
const httpUrls = allUrls.filter((u) => /^https?:\/\//i.test(u));
|
|
4230
|
-
|
|
4231
|
-
/* 1) Node fetch (LLM API + http_fetch + TinyFish/Exa + GitHub...) — global sarmalayıcı.
|
|
4232
|
-
http(s) VE socks5 (socks5Connector) destekli — http(s) proxy'lerde rotasyon var. */
|
|
4233
|
-
if (enabled && allUrls.length) {
|
|
4234
|
-
if (!__origFetch) {
|
|
4235
|
-
__origFetch = globalThis.fetch;
|
|
4236
|
-
globalThis.fetch = (input, init = {}) => {
|
|
4237
|
-
const list = proxyUrlList();
|
|
4238
|
-
if (!list.length) return __origFetch(input, init);
|
|
4239
|
-
const p = list[Math.abs(__proxyIdx++) % list.length];
|
|
4240
|
-
return __origFetch(input, { ...init, dispatcher: proxyAgentFor(p) });
|
|
4241
|
-
};
|
|
4242
|
-
}
|
|
4243
|
-
} else if (__origFetch) {
|
|
4244
|
-
globalThis.fetch = __origFetch;
|
|
4245
|
-
__origFetch = null;
|
|
4246
|
-
}
|
|
4247
|
-
|
|
4248
|
-
/* 2) python/cmd çocuk süreçleri (ddgs, requests... env'den okur) */
|
|
4249
|
-
if (enabled && httpUrls.length) {
|
|
4250
|
-
process.env.HTTP_PROXY = httpUrls[0];
|
|
4251
|
-
process.env.HTTPS_PROXY = httpUrls[0];
|
|
4252
|
-
process.env.ALL_PROXY = httpUrls[0];
|
|
4253
|
-
process.env.NO_PROXY = 'localhost,127.0.0.1';
|
|
4254
|
-
} else {
|
|
4255
|
-
delete process.env.HTTP_PROXY;
|
|
4256
|
-
delete process.env.HTTPS_PROXY;
|
|
4257
|
-
delete process.env.ALL_PROXY;
|
|
4258
|
-
delete process.env.NO_PROXY;
|
|
4259
|
-
}
|
|
4260
|
-
|
|
4261
|
-
/* 3) dahili tarayıcı (Chromium; socks5 destekler) */
|
|
4262
|
-
try {
|
|
4263
|
-
const ses = session.fromPartition('persist:browser');
|
|
4264
|
-
if (ses) {
|
|
4265
|
-
if (enabled && allUrls.length) ses.setProxy({ proxyRules: allUrls[0] }).catch(() => {});
|
|
4266
|
-
else ses.setProxy({ mode: 'direct' }).catch(() => {});
|
|
4267
|
-
}
|
|
4268
|
-
} catch {}
|
|
4269
|
-
}
|
|
4270
|
-
|
|
4271
|
-
ipcMain.handle('proxy:get', () => ({
|
|
4272
|
-
enabled: !!(settings.proxy && settings.proxy.enabled),
|
|
4273
|
-
urls: String((settings.proxy && settings.proxy.urls) || ''),
|
|
4274
|
-
}));
|
|
4275
|
-
|
|
4276
|
-
ipcMain.handle('proxy:set', (_e, cfg) => {
|
|
4277
|
-
settings.proxy = {
|
|
4278
|
-
enabled: !!(cfg && cfg.enabled),
|
|
4279
|
-
urls: String((cfg && cfg.urls) || '').slice(0, 2000),
|
|
4280
|
-
};
|
|
4281
|
-
saveSettings();
|
|
4282
|
-
applyProxy();
|
|
4283
|
-
return { ok: true, enabled: settings.proxy.enabled, urls: settings.proxy.urls };
|
|
4284
|
-
});
|
|
4285
|
-
|
|
4286
|
-
ipcMain.handle('proxy:test', async () => {
|
|
4287
|
-
const allUrls = proxyUrlList();
|
|
4288
|
-
if (!allUrls.length) return { ok: false, error: 'proxy adresi girilmemiş' };
|
|
4289
|
-
const u = allUrls[Math.abs(__proxyIdx++) % allUrls.length];
|
|
4290
|
-
const t0 = Date.now();
|
|
4291
|
-
try {
|
|
4292
|
-
const res = await (__origFetch || fetch)('https://api.ipify.org?format=json', {
|
|
4293
|
-
dispatcher: proxyAgentFor(u),
|
|
4294
|
-
signal: AbortSignal.timeout(15000),
|
|
4295
|
-
});
|
|
4296
|
-
const j = await res.json();
|
|
4297
|
-
return { ok: true, ip: j.ip, proxy: u, ms: Date.now() - t0 };
|
|
4298
|
-
} catch (e) {
|
|
4299
|
-
return { ok: false, error: String((e && e.message) || e).slice(0, 160), proxy: u };
|
|
4300
|
-
}
|
|
4301
|
-
});
|
|
4302
|
-
|
|
4303
|
-
/* ---------------- Cloudflare WARP (warp-plus) ----------------
|
|
4304
|
-
Açık kaynak warp-plus tek binary: yerel socks5 (127.0.0.1:8086) açar, o port
|
|
4305
|
-
Cloudflare WARP ağına tünel olur. Bedava, üyeliksiz (profil otomatik oluşur).
|
|
4306
|
-
Beast: indirme + başlatma + watchdog + kapatma. Sadece Beast trafiği geçer. */
|
|
4307
|
-
const WARP_PORT = 8086;
|
|
4308
|
-
const WARP_PROXY_LINE = 'socks5://127.0.0.1:' + WARP_PORT;
|
|
4309
|
-
let warpChild = null;
|
|
4310
|
-
let warpWatchdog = null;
|
|
4311
|
-
let warpStopping = false;
|
|
4312
|
-
|
|
4313
|
-
function warpBinPath() { return path.join(APP_DIR, 'bin', 'warp-plus.exe'); }
|
|
4314
|
-
function warpDir() { return path.join(APP_DIR, 'warp'); }
|
|
4315
|
-
function warpEnabled() { return !!(settings.warp && settings.warp.enabled); }
|
|
4316
|
-
|
|
4317
|
-
async function ensureWarpPlusBinary() {
|
|
4318
|
-
const bin = warpBinPath();
|
|
4319
|
-
if (fs.existsSync(bin)) return bin;
|
|
4320
|
-
fs.mkdirSync(path.dirname(bin), { recursive: true });
|
|
4321
|
-
log.info('main', 'warp-plus indiriliyor…');
|
|
4322
|
-
const rel = await (__origFetch || fetch)('https://api.github.com/repos/bepass-org/warp-plus/releases/latest', {
|
|
4323
|
-
headers: { 'User-Agent': 'beast-agent', Accept: 'application/vnd.github+json' },
|
|
4324
|
-
signal: AbortSignal.timeout(20000),
|
|
4325
|
-
});
|
|
4326
|
-
if (!rel.ok) throw new Error('sürüm bilgisi alınamadı (' + rel.status + ')');
|
|
4327
|
-
const j = await rel.json();
|
|
4328
|
-
const asset = (j.assets || []).find((a) => /warp-plus_windows-amd64\.zip$/i.test(a.name || ''));
|
|
4329
|
-
if (!asset) throw new Error('windows paketi bulunamadı');
|
|
4330
|
-
const zipPath = bin + '.zip';
|
|
4331
|
-
const res = await (__origFetch || fetch)(asset.browser_download_url, { signal: AbortSignal.timeout(300000) });
|
|
4332
|
-
if (!res.ok) throw new Error('indirme başarısız (' + res.status + ')');
|
|
4333
|
-
fs.writeFileSync(zipPath, Buffer.from(await res.arrayBuffer()));
|
|
4334
|
-
await new Promise((resolve, reject) => {
|
|
4335
|
-
spawn('powershell.exe', ['-NoProfile', '-Command', `Expand-Archive -Force -LiteralPath '${zipPath}' -DestinationPath '${path.dirname(bin)}'`])
|
|
4336
|
-
.on('exit', (c) => (c === 0 ? resolve() : reject(new Error('zip açılamadı'))))
|
|
4337
|
-
.on('error', reject);
|
|
4338
|
-
});
|
|
4339
|
-
try { fs.unlinkSync(zipPath); } catch {}
|
|
4340
|
-
if (!fs.existsSync(bin)) throw new Error('warp-plus.exe açılamadı');
|
|
4341
|
-
log.info('main', 'warp-plus indirildi: ' + bin);
|
|
4342
|
-
return bin;
|
|
4343
|
-
}
|
|
4344
|
-
|
|
4345
|
-
function warpPortAlive() {
|
|
4346
|
-
const net = require('net');
|
|
4347
|
-
return new Promise((resolve) => {
|
|
4348
|
-
const s = net.connect({ host: '127.0.0.1', port: WARP_PORT, timeout: 1200 }, () => { s.destroy(); resolve(true); });
|
|
4349
|
-
s.on('error', () => resolve(false));
|
|
4350
|
-
s.on('timeout', () => { s.destroy(); resolve(false); });
|
|
4351
|
-
});
|
|
4352
|
-
}
|
|
4353
|
-
|
|
4354
|
-
async function startWarp() {
|
|
4355
|
-
if (warpChild) return { ok: true };
|
|
4356
|
-
const bin = await ensureWarpPlusBinary();
|
|
4357
|
-
fs.mkdirSync(warpDir(), { recursive: true });
|
|
4358
|
-
try {
|
|
4359
|
-
warpChild = spawn(bin, [], { windowsHide: true, cwd: warpDir() });
|
|
4360
|
-
} catch (e) {
|
|
4361
|
-
return { ok: false, error: String((e && e.message) || e) };
|
|
4362
|
-
}
|
|
4363
|
-
warpChild.stdout.on('data', () => {});
|
|
4364
|
-
warpChild.stderr.on('data', () => {});
|
|
4365
|
-
warpChild.on('exit', () => {
|
|
4366
|
-
warpChild = null;
|
|
4367
|
-
if (warpEnabled() && !warpStopping) {
|
|
4368
|
-
/* watchdog: beklenmedik çıkışta 5 sn sonra yeniden başlat */
|
|
4369
|
-
clearTimeout(warpWatchdog);
|
|
4370
|
-
warpWatchdog = setTimeout(() => { startWarp().catch(() => {}); }, 5000);
|
|
4371
|
-
}
|
|
4372
|
-
});
|
|
4373
|
-
for (let i = 0; i < 180; i++) {
|
|
4374
|
-
if (await warpPortAlive()) { log.info('main', 'warp-plus hazır (127.0.0.1:' + WARP_PORT + ')'); return { ok: true }; }
|
|
4375
|
-
if (!warpChild) break;
|
|
4376
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
4377
|
-
}
|
|
4378
|
-
return { ok: false, error: 'warp-plus başlatılamadı (port açılmadı)' };
|
|
4379
|
-
}
|
|
4380
|
-
|
|
4381
|
-
function stopWarp() {
|
|
4382
|
-
warpStopping = true;
|
|
4383
|
-
clearTimeout(warpWatchdog);
|
|
4384
|
-
try { if (warpChild) spawn('taskkill', ['/pid', String(warpChild.pid), '/T', '/F'], { windowsHide: true }); } catch {}
|
|
4385
|
-
try { if (warpChild) warpChild.kill(); } catch {}
|
|
4386
|
-
warpChild = null;
|
|
4387
|
-
setTimeout(() => { warpStopping = false; }, 1500);
|
|
4388
|
-
}
|
|
4389
|
-
|
|
4390
|
-
ipcMain.handle('warp:get', () => ({
|
|
4391
|
-
enabled: warpEnabled(),
|
|
4392
|
-
running: !!warpChild,
|
|
4393
|
-
installed: fs.existsSync(warpBinPath()),
|
|
4394
|
-
line: WARP_PROXY_LINE,
|
|
4395
|
-
}));
|
|
4396
|
-
|
|
4397
|
-
ipcMain.handle('warp:set', async (_e, on) => {
|
|
4398
|
-
const want = !!on;
|
|
4399
|
-
settings.warp = { enabled: want };
|
|
4400
|
-
saveSettings();
|
|
4401
|
-
if (want) {
|
|
4402
|
-
const r = await startWarp();
|
|
4403
|
-
if (!r.ok) {
|
|
4404
|
-
settings.warp = { enabled: false };
|
|
4405
|
-
saveSettings();
|
|
4406
|
-
return { ok: false, error: r.error || 'başlatılamadı' };
|
|
4407
|
-
}
|
|
4408
|
-
if (!settings.proxy) settings.proxy = { enabled: true, urls: '' };
|
|
4409
|
-
const lines = proxyUrlList();
|
|
4410
|
-
if (!lines.includes(WARP_PROXY_LINE)) lines.push(WARP_PROXY_LINE);
|
|
4411
|
-
settings.proxy.urls = lines.join('\n');
|
|
4412
|
-
settings.proxy.enabled = true; // WARP açıldıysa trafik ondan geçsin
|
|
4413
|
-
saveSettings();
|
|
4414
|
-
applyProxy();
|
|
4415
|
-
return { ok: true, enabled: true };
|
|
4416
|
-
}
|
|
4417
|
-
/* kapat: satırı listeden çıkar, başka proxy yoksa master'ı da kapat */
|
|
4418
|
-
const rest = proxyUrlList().filter((u) => u !== WARP_PROXY_LINE);
|
|
4419
|
-
settings.proxy.urls = rest.join('\n');
|
|
4420
|
-
if (!rest.length) settings.proxy.enabled = false;
|
|
4421
|
-
saveSettings();
|
|
4422
|
-
applyProxy();
|
|
4423
|
-
stopWarp();
|
|
4424
|
-
return { ok: true, enabled: false };
|
|
4425
|
-
});
|
|
4426
|
-
|
|
4427
4564
|
ipcMain.handle('settings:get', () => {
|
|
4428
4565
|
const out = JSON.parse(JSON.stringify(settings));
|
|
4429
4566
|
/* Sırların renderera düz metin gitmesini engelle */
|
|
@@ -4546,7 +4683,7 @@ ipcMain.handle('update:install', () => {
|
|
|
4546
4683
|
if (!isNpmMode()) return { ok: false, error: NPM_ONLY_TEXT };
|
|
4547
4684
|
npmUpdateViaCmd();
|
|
4548
4685
|
return { ok: true, npm: true };
|
|
4549
|
-
});
|
|
4686
|
+
});
|
|
4550
4687
|
|
|
4551
4688
|
ipcMain.handle('update:setAuto', (_e, cfg) => {
|
|
4552
4689
|
if (cfg && typeof cfg.autoCheck === 'boolean') settings.autoCheckUpdate = cfg.autoCheck;
|
|
@@ -4781,7 +4918,12 @@ ipcMain.handle('cron:runNow', (_e, id) => {
|
|
|
4781
4918
|
|
|
4782
4919
|
/* ---------------- tarayıcı IPC ---------------- */
|
|
4783
4920
|
ipcMain.handle('browser:toggle', () => {
|
|
4784
|
-
/*
|
|
4921
|
+
/* gizli çalışan ajan paneli varsa düğme ONU GÖRÜNÜR yapar; değilse aç/kapa.
|
|
4922
|
+
(ajanlar tarayıcıyı hep gizli açar — kullanıcı izlemek isterse buradan gösterir) */
|
|
4923
|
+
if (browser.open && !browser.visible) {
|
|
4924
|
+
setBrowserOpen(true, true);
|
|
4925
|
+
return { open: browser.open, visible: browser.visible };
|
|
4926
|
+
}
|
|
4785
4927
|
setBrowserOpen(!browser.open, true);
|
|
4786
4928
|
return { open: browser.open, visible: browser.visible };
|
|
4787
4929
|
});
|
|
@@ -5438,12 +5580,15 @@ function runNpmGlobalInstall() {
|
|
|
5438
5580
|
});
|
|
5439
5581
|
}
|
|
5440
5582
|
|
|
5441
|
-
/* auth login etkileşimlidir (tarayıcı açar) — kullanıcı görsün diye ayrı pencere
|
|
5583
|
+
/* auth login etkileşimlidir (tarayıcı açar) — kullanıcı görsün diye ayrı pencere.
|
|
5584
|
+
-ExecutionPolicy Bypass: SADECE bu pencere için — Windows varsayılan "Restricted"
|
|
5585
|
+
politikası npm'in opencode.ps1 shim'ini bloklar ("running scripts is disabled")
|
|
5586
|
+
→ taze makinede tek tık kurulum patlamasın; sistem politikası DEĞİŞMEZ. */
|
|
5442
5587
|
function openZenAuthWindow() {
|
|
5443
5588
|
try {
|
|
5444
5589
|
spawn(
|
|
5445
5590
|
'cmd.exe',
|
|
5446
|
-
['/c', 'start', '', 'powershell.exe', '-NoExit', '-Command', 'opencode auth login'],
|
|
5591
|
+
['/c', 'start', '', 'powershell.exe', '-NoExit', '-ExecutionPolicy', 'Bypass', '-Command', 'opencode auth login'],
|
|
5447
5592
|
{ detached: true, windowsHide: false, stdio: 'ignore' }
|
|
5448
5593
|
).unref();
|
|
5449
5594
|
return true;
|
|
@@ -5668,6 +5813,42 @@ ipcMain.handle('tg:allow:set', (_e, list) => {
|
|
|
5668
5813
|
});
|
|
5669
5814
|
ipcMain.handle('tg:sessions', () => [...tgChats.values()]);
|
|
5670
5815
|
|
|
5816
|
+
/* ---------- Discord IPC ---------- */
|
|
5817
|
+
ipcMain.handle('dc:status:get', () => {
|
|
5818
|
+
if (!dc) return { configured: !!settings.dcToken, status: 'disconnected', user: null, connected: false };
|
|
5819
|
+
return { configured: true, ...dc.snapshot() };
|
|
5820
|
+
});
|
|
5821
|
+
|
|
5822
|
+
/* token kaydet + köprüyü (yeniden) başlat */
|
|
5823
|
+
ipcMain.handle('dc:set', async (_e, token) => {
|
|
5824
|
+
const t = String(token || '').trim();
|
|
5825
|
+
if (t) settings.dcToken = t;
|
|
5826
|
+
saveSettings();
|
|
5827
|
+
await restartDc();
|
|
5828
|
+
return { configured: !!settings.dcToken, ...(dc ? dc.snapshot() : { status: 'disconnected', user: null }) };
|
|
5829
|
+
});
|
|
5830
|
+
|
|
5831
|
+
ipcMain.handle('dc:start', async () => {
|
|
5832
|
+
if (!settings.dcToken) return { ok: false, error: 'token yok — önce bot tokenı gir' };
|
|
5833
|
+
await restartDc();
|
|
5834
|
+
return { ok: true, ...(dc ? dc.snapshot() : {}) };
|
|
5835
|
+
});
|
|
5836
|
+
|
|
5837
|
+
ipcMain.handle('dc:stop', async () => {
|
|
5838
|
+
if (dc) {
|
|
5839
|
+
try { await dc.stop(); } catch {}
|
|
5840
|
+
}
|
|
5841
|
+
return { ok: true };
|
|
5842
|
+
});
|
|
5843
|
+
|
|
5844
|
+
ipcMain.handle('dc:allow:get', () => settings.dcAllow || []);
|
|
5845
|
+
ipcMain.handle('dc:allow:set', (_e, list) => {
|
|
5846
|
+
settings.dcAllow = Array.isArray(list) ? list : [];
|
|
5847
|
+
saveSettings();
|
|
5848
|
+
return settings.dcAllow;
|
|
5849
|
+
});
|
|
5850
|
+
ipcMain.handle('dc:sessions', () => [...dcChats.values()]);
|
|
5851
|
+
|
|
5671
5852
|
/* ---------- e-posta IPC ---------- */
|
|
5672
5853
|
|
|
5673
5854
|
ipcMain.handle('email:get', () => {
|
|
@@ -5929,6 +6110,7 @@ ipcMain.handle('bots:update', (_e, { id, patch }) => {
|
|
|
5929
6110
|
if ((v.botId || 'beast') === String(id || '')) {
|
|
5930
6111
|
const cfg = bots.get(String(id));
|
|
5931
6112
|
engine.setSessionTools(v.id, cfg && !cfg.admin ? botToolSet(cfg) : null);
|
|
6113
|
+
engine.setSessionModel(v.id, cfg && !cfg.admin ? (cfg.model || null) : null);
|
|
5932
6114
|
}
|
|
5933
6115
|
}
|
|
5934
6116
|
} catch {}
|