beast-agent 2.30.6 → 2.32.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/fsguard.js +46 -0
- package/src/agent/mcp.js +98 -18
- package/src/agent/tools.js +114 -4
- package/src/main.js +163 -22
- package/src/preload.js +5 -0
- package/src/renderer/i18n.js +121 -30
- package/src/renderer/index.html +40 -0
- package/src/renderer/renderer.js +293 -11
- package/src/renderer/style.css +13 -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, net: electronNet, clipboard } = require('electron');
|
|
3
|
+
const { app, BrowserWindow, WebContentsView, ipcMain, shell, dialog, Tray, Menu, nativeImage, desktopCapturer, session, net: electronNet, clipboard, Notification } = require('electron');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const http = require('http');
|
|
@@ -26,10 +26,15 @@ const watchers = require('./agent/watchers');
|
|
|
26
26
|
const usageMod = require('./agent/usage');
|
|
27
27
|
const bus = require('./agent/bus');
|
|
28
28
|
const computeruse = require('./agent/computeruse');
|
|
29
|
+
const fsguard = require('./agent/fsguard');
|
|
29
30
|
const log = require('./agent/logger');
|
|
30
31
|
const headroom = require('./agent/headroom');
|
|
31
32
|
const QRCode = require('qrcode'); /* Expo Go QR (bc-expurl) — whatsapp ile aynı paket */
|
|
32
33
|
|
|
34
|
+
/* Renderer'a sır gönderirken kullanılan maske; kaydederken aynen geri gelirse
|
|
35
|
+
gerçek değer korunur (anahtarlar UI'da düz metin dolaşmaz). */
|
|
36
|
+
const SECRET_MASK = '***';
|
|
37
|
+
|
|
33
38
|
/* #3 otomatik updater: sessiz — indirir, kapanışta kurar, kullanıcıya soru sormaz.
|
|
34
39
|
Paketlenmemiş (npm start) modda devre dışı; Update sekmesi ve /update komutu kontrol eder. */
|
|
35
40
|
let autoUpdater = null;
|
|
@@ -267,6 +272,8 @@ try {
|
|
|
267
272
|
try { nodemailer = require('nodemailer'); } catch {}
|
|
268
273
|
|
|
269
274
|
const APP_DIR = path.join(app.getPath('appData'), 'beast');
|
|
275
|
+
/* Windows toast bildirimleri için AppUserModelID erken ayarlanmalı */
|
|
276
|
+
try { app.setAppUserModelId('com.quantumalgo.beastagent'); } catch {}
|
|
270
277
|
/* mem0-native embedding modeli whisper ile AYNI cache'i kullanır (src/agent/mem0.js okur) */
|
|
271
278
|
process.env.BEAST_MODELS_DIR = path.join(APP_DIR, 'models');
|
|
272
279
|
|
|
@@ -296,6 +303,8 @@ for (const d of [APP_DIR, SESSIONS_DIR]) fs.mkdirSync(d, { recursive: true });
|
|
|
296
303
|
|
|
297
304
|
let win = null;
|
|
298
305
|
let engine = null;
|
|
306
|
+
/* Paralel ajan durum takibi: bitişte BİR kez toast bildirimi (spam yok) */
|
|
307
|
+
const bgNotifSeen = new Map(); // sid -> son görülen durum
|
|
299
308
|
let settings = loadSettings();
|
|
300
309
|
ensureBeastCode();
|
|
301
310
|
startHealthServer(); /* splash/boot aşamasından itibaren /health ayakta */
|
|
@@ -2337,20 +2346,34 @@ async function handleWaIncoming(jid, payload, senderNum) {
|
|
|
2337
2346
|
if (!engine) return;
|
|
2338
2347
|
if (typeof payload === 'string') payload = { text: payload };
|
|
2339
2348
|
const isGroup = !!payload.isGroup || jid.endsWith('@g.us');
|
|
2349
|
+
/* İzin listesi kaydı — grup kapıları ve doğal dil komutları bunu kullanır:
|
|
2350
|
+
izinli değilse yalnız sohbet; agentic iş yaptıramaz. */
|
|
2351
|
+
const senderHit = waFind(senderNum);
|
|
2340
2352
|
|
|
2341
|
-
/* slash komutları: DM'de her zaman; grupta sadece bot mention edildiyse
|
|
2353
|
+
/* slash komutları: DM'de her zaman; grupta sadece bot mention edildiyse.
|
|
2354
|
+
GRUP KAPISI: izin listesinde olmayan üye slash (agentic) komut kullanamaz —
|
|
2355
|
+
herkes sohbet eder ama işi yalnız izinli kişiler yaptırır. */
|
|
2342
2356
|
const txt0 = String(payload.text || '').trim();
|
|
2343
2357
|
if (txt0.startsWith('/') && !txt0.includes('\n') && (!isGroup || payload.mentioned)) {
|
|
2358
|
+
if (isGroup && !senderHit && !/^\/(help|version)\b/i.test(txt0)) {
|
|
2359
|
+
await sendWaSafe(
|
|
2360
|
+
jid,
|
|
2361
|
+
'Bu komutu yalnız izinli kişiler kullanabilir. Sohbet için beni @mention ile çağırabilirsin.'
|
|
2362
|
+
).catch(() => {});
|
|
2363
|
+
return;
|
|
2364
|
+
}
|
|
2344
2365
|
if (await tryWaSlash(jid, txt0, senderNum, payload)) return;
|
|
2345
2366
|
}
|
|
2346
2367
|
|
|
2347
2368
|
/* "hepsini sil" / "tüm cron" / "cronları temizle" gibi doğal dil → cron temizle.
|
|
2369
|
+
Yalnız İZİNLİ gönderen: misafir grup üyesi cron silemez.
|
|
2348
2370
|
clearAll() bellekteki job'ları VE diski boşaltır, zamanlayıcıyı durdurur
|
|
2349
2371
|
(sadece dosya silmek yetmezdi: çalışan proses belleği geri yazıyordu). */
|
|
2350
2372
|
const lc = txt0.toLowerCase();
|
|
2351
2373
|
if (
|
|
2352
|
-
|
|
2353
|
-
|
|
2374
|
+
senderHit &&
|
|
2375
|
+
((lc.includes('cron') && /(sil|temizle|kaldır|hepsi|tüm|sıfırla)/.test(lc)) ||
|
|
2376
|
+
/^(hepsini|hepsını|tümünü|tümünü|hepsnı|hepsnı)\b.*(sil|sıl|temizle|kaldır)/.test(lc))
|
|
2354
2377
|
) {
|
|
2355
2378
|
const r = cron.clearAll();
|
|
2356
2379
|
cronEmit();
|
|
@@ -2364,8 +2387,10 @@ async function handleWaIncoming(jid, payload, senderNum) {
|
|
|
2364
2387
|
}
|
|
2365
2388
|
|
|
2366
2389
|
/* "hatırlatmaları sil" / "alarm temizle" / "reminders kaldır" → yalnızca
|
|
2367
|
-
otomatik hatırlatmalar silinir (cron görevlerine dokunulmaz).
|
|
2390
|
+
otomatik hatırlatmalar silinir (cron görevlerine dokunulmaz).
|
|
2391
|
+
Yalnız İZİNLİ gönderen tetikleyebilir. */
|
|
2368
2392
|
if (
|
|
2393
|
+
senderHit &&
|
|
2369
2394
|
/hat[iı]rlat|alarm|remind/.test(lc) &&
|
|
2370
2395
|
/(sil|sıl|temizle|kaldır|iptal|sıfırla|kapat)/.test(lc)
|
|
2371
2396
|
) {
|
|
@@ -2505,7 +2530,10 @@ async function mqueueTick() {
|
|
|
2505
2530
|
|
|
2506
2531
|
async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
2507
2532
|
const isGroup = !!payload.isGroup;
|
|
2508
|
-
|
|
2533
|
+
/* GRUP: gönderen izin listesinde mi? İzinli → kendi yetki seviyesi;
|
|
2534
|
+
değilse 'chat' (sohbet serbest, araç YOK — agentic işi yalnız izinliler
|
|
2535
|
+
yaptırabilir). DM'de izinsiz gönderen buraya gelemez. */
|
|
2536
|
+
const hit = waFind(senderNum);
|
|
2509
2537
|
if (!isGroup && !hit) {
|
|
2510
2538
|
waLog(`skip flush: izinli eşleşme yok (sender=+${senderNum || '?'})`);
|
|
2511
2539
|
return;
|
|
@@ -2522,8 +2550,8 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
|
2522
2550
|
waLastActiveJid = String(jid); // cevap/dosya hedefi: en son yazan sohbet
|
|
2523
2551
|
// Cevap verilecek — karşı telefonda "yazıyor…" göstergesi (medya işlenene dek sürer)
|
|
2524
2552
|
wa.setComposing(jid, true);
|
|
2525
|
-
// Kişi bazlı granül izin: all/web/read/chat
|
|
2526
|
-
let perm =
|
|
2553
|
+
// Kişi bazlı granül izin: all/web/read/chat — grup misafiri varsayılan 'chat'
|
|
2554
|
+
let perm = hit ? hit.perm || (hit.lockdown ? 'chat' : 'all') : 'chat';
|
|
2527
2555
|
engine.setSessionPerm(sid, perm);
|
|
2528
2556
|
|
|
2529
2557
|
/* BOT SİSTEMİ: numara → bot eşleştirme (izinli kayıtta bot_id yoksa beast'e düşer) */
|
|
@@ -3208,6 +3236,17 @@ function reloadBackend() {
|
|
|
3208
3236
|
},
|
|
3209
3237
|
emit: (ev) => {
|
|
3210
3238
|
if (win && !win.isDestroyed()) win.webContents.send('agent:event', ev);
|
|
3239
|
+
/* PARALEL AJAN BİTİŞİ: running → done/error/aborted geçişinde tek toast */
|
|
3240
|
+
if (ev.type === 'agents' && Array.isArray(ev.jobs)) {
|
|
3241
|
+
for (const j of ev.jobs) {
|
|
3242
|
+
const prev = bgNotifSeen.get(j.id);
|
|
3243
|
+
bgNotifSeen.set(j.id, j.status);
|
|
3244
|
+
if (prev === 'running' && j.status && j.status !== 'running' && j.status !== 'queued' && !j.continuous) {
|
|
3245
|
+
const durum = j.status === 'done' ? 'tamamlandı' : j.status === 'error' ? 'HATA ile bitti' : 'iptal edildi';
|
|
3246
|
+
toastNotify(`Ajan: ${j.title || j.code || 'görev'}`, durum, 'agents');
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3211
3250
|
/* opencode permission (BC): ask kanalı UI'a zaten yukarıda düştü — WA
|
|
3212
3251
|
sahibine de tek satır bildir gitsin (/approve · /approve always · /deny) */
|
|
3213
3252
|
if (ev.type === 'permission.asked' && ev.request && wa && wa.connected) {
|
|
@@ -3717,6 +3756,30 @@ function showWin() {
|
|
|
3717
3756
|
}
|
|
3718
3757
|
}
|
|
3719
3758
|
|
|
3759
|
+
/* ---------- Windows toast bildirimi ----------
|
|
3760
|
+
watcher/cron/paralel ajan bitişinde sistem bildirimi; tıklanınca pencere
|
|
3761
|
+
açılır ve renderer'a notify-click eventi gider (ilgili panel açılır).
|
|
3762
|
+
Ayarlardan kapatılabilir: settings.notifyToast === false */
|
|
3763
|
+
function toastNotify(title, body, target) {
|
|
3764
|
+
try {
|
|
3765
|
+
if (settings.notifyToast === false) return;
|
|
3766
|
+
if (typeof Notification === 'undefined' || !Notification.isSupported()) return;
|
|
3767
|
+
const n = new Notification({
|
|
3768
|
+
title: String(title || 'Beast Agent').slice(0, 120),
|
|
3769
|
+
body: String(body || '').slice(0, 240),
|
|
3770
|
+
icon: path.join(__dirname, '..', 'assets', 'tray.png'),
|
|
3771
|
+
silent: false,
|
|
3772
|
+
});
|
|
3773
|
+
n.on('click', () => {
|
|
3774
|
+
showWin();
|
|
3775
|
+
if (win && !win.isDestroyed()) {
|
|
3776
|
+
win.webContents.send('agent:event', { type: 'notify-click', target: String(target || '') });
|
|
3777
|
+
}
|
|
3778
|
+
});
|
|
3779
|
+
n.show();
|
|
3780
|
+
} catch {}
|
|
3781
|
+
}
|
|
3782
|
+
|
|
3720
3783
|
/* ---------------- dahili tarayıcı ---------------- */
|
|
3721
3784
|
|
|
3722
3785
|
const BROWSER_TOOLBAR_H = 46;
|
|
@@ -6111,13 +6174,36 @@ ipcMain.handle('wa:groups:set', (_e, cfg) => {
|
|
|
6111
6174
|
ipcMain.handle('settings:get', () => {
|
|
6112
6175
|
const out = JSON.parse(JSON.stringify(settings));
|
|
6113
6176
|
/* Sırların renderera düz metin gitmesini engelle */
|
|
6114
|
-
if (out.email && out.email.pass) out.email.pass =
|
|
6115
|
-
if (out.waTts && out.waTts.key) out.waTts.key =
|
|
6177
|
+
if (out.email && out.email.pass) out.email.pass = SECRET_MASK;
|
|
6178
|
+
if (out.waTts && out.waTts.key) out.waTts.key = SECRET_MASK;
|
|
6179
|
+
if (out.supermemory && out.supermemory.apiKey) out.supermemory.apiKey = SECRET_MASK;
|
|
6180
|
+
if (out.fallout && Array.isArray(out.fallout.slots)) {
|
|
6181
|
+
for (const s of out.fallout.slots) if (s && s.key) s.key = SECRET_MASK;
|
|
6182
|
+
}
|
|
6183
|
+
if (Array.isArray(out.customProviders)) {
|
|
6184
|
+
for (const p of out.customProviders) if (p && p.key) p.key = SECRET_MASK;
|
|
6185
|
+
}
|
|
6116
6186
|
return out;
|
|
6117
6187
|
});
|
|
6118
6188
|
|
|
6119
6189
|
/* ---------------- MCP IPC ---------------- */
|
|
6120
6190
|
|
|
6191
|
+
/* UI'a giden raw JSON'da env sırlarını maskeler (düz metin anahtar sızmasın) */
|
|
6192
|
+
function maskMcpRaw(raw) {
|
|
6193
|
+
try {
|
|
6194
|
+
const obj = JSON.parse(String(raw || ''));
|
|
6195
|
+
if (!obj || typeof obj !== 'object' || !obj.servers || typeof obj.servers !== 'object') return raw;
|
|
6196
|
+
for (const s of Object.values(obj.servers)) {
|
|
6197
|
+
if (s && typeof s === 'object' && s.env && typeof s.env === 'object') {
|
|
6198
|
+
for (const k of Object.keys(s.env)) if (String(s.env[k] || '')) s.env[k] = SECRET_MASK;
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
6201
|
+
return JSON.stringify(obj, null, 2);
|
|
6202
|
+
} catch {
|
|
6203
|
+
return raw;
|
|
6204
|
+
}
|
|
6205
|
+
}
|
|
6206
|
+
|
|
6121
6207
|
ipcMain.handle('mcp:status', () => {
|
|
6122
6208
|
try { return mcpMod.status(); } catch { return { path: '', servers: [] }; }
|
|
6123
6209
|
});
|
|
@@ -6127,15 +6213,17 @@ ipcMain.handle('mcp:config:get', () => {
|
|
|
6127
6213
|
const p = mcpMod.configPath();
|
|
6128
6214
|
let raw = '';
|
|
6129
6215
|
try { raw = require('fs').readFileSync(p, 'utf8'); } catch {}
|
|
6130
|
-
return { path: p, raw };
|
|
6216
|
+
return { path: p, raw: maskMcpRaw(raw) };
|
|
6131
6217
|
} catch { return { path: '', raw: '' }; }
|
|
6132
6218
|
});
|
|
6133
6219
|
|
|
6134
6220
|
ipcMain.handle('mcp:config:set', (_e, raw) => {
|
|
6135
6221
|
try {
|
|
6136
6222
|
const text = String(raw || '');
|
|
6137
|
-
|
|
6138
|
-
|
|
6223
|
+
const cfg = text.trim() ? JSON.parse(text) : { servers: {} };
|
|
6224
|
+
/* maskeli env sırlarını mevcut diskteki değerlerle geri koy */
|
|
6225
|
+
try { mcpMod.restoreMaskedSecrets(cfg); } catch {}
|
|
6226
|
+
mcpMod.saveConfig(cfg);
|
|
6139
6227
|
return { ok: true };
|
|
6140
6228
|
} catch (e) {
|
|
6141
6229
|
return { ok: false, error: String((e && e.message) || e) };
|
|
@@ -6163,31 +6251,45 @@ ipcMain.handle('fallout:get', () => {
|
|
|
6163
6251
|
out.enabled = !!f.enabled;
|
|
6164
6252
|
out.autoResume = f.autoResume !== false;
|
|
6165
6253
|
if (Array.isArray(f.slots)) {
|
|
6166
|
-
for (let i = 0; i < Math.min(10, f.slots.length); i++)
|
|
6254
|
+
for (let i = 0; i < Math.min(10, f.slots.length); i++) {
|
|
6255
|
+
const s = f.slots[i];
|
|
6256
|
+
/* kayıtlı anahtarlar UI'a maskeli gider */
|
|
6257
|
+
out.slots[i] = s && s.key ? { ...s, key: SECRET_MASK } : s || null;
|
|
6258
|
+
}
|
|
6167
6259
|
}
|
|
6168
6260
|
return out;
|
|
6169
6261
|
});
|
|
6170
6262
|
|
|
6171
6263
|
ipcMain.handle('fallout:set', (_e, cfg) => {
|
|
6172
6264
|
const cur = settings.fallout || defaultFallout();
|
|
6265
|
+
const prevSlots = Array.isArray(cur.slots) ? cur.slots : [];
|
|
6173
6266
|
const next = defaultFallout();
|
|
6174
6267
|
next.enabled = !!(cfg && cfg.enabled);
|
|
6175
6268
|
next.autoResume = !cfg || cfg.autoResume !== false;
|
|
6176
6269
|
const slots = Array.isArray(cfg && cfg.slots) ? cfg.slots : [];
|
|
6177
6270
|
for (let i = 0; i < Math.min(10, slots.length); i++) {
|
|
6178
6271
|
const s = slots[i];
|
|
6179
|
-
if (!s || typeof s !== 'object' || !s.providerId || !s.model
|
|
6272
|
+
if (!s || typeof s !== 'object' || !s.providerId || !s.model) continue;
|
|
6273
|
+
let key = String(s.key || '');
|
|
6274
|
+
/* maskeli/boş anahtar: aynı provider'ın önceki kayıtlı anahtarı korunur */
|
|
6275
|
+
if (key === SECRET_MASK || !key) {
|
|
6276
|
+
const prev = prevSlots[i];
|
|
6277
|
+
key = prev && prev.providerId === String(s.providerId) && prev.key ? String(prev.key) : '';
|
|
6278
|
+
}
|
|
6279
|
+
if (!key) continue;
|
|
6180
6280
|
next.slots[i] = {
|
|
6181
6281
|
providerId: String(s.providerId),
|
|
6182
6282
|
providerName: String(s.providerName || s.providerId),
|
|
6183
6283
|
model: String(s.model),
|
|
6184
|
-
key
|
|
6284
|
+
key,
|
|
6185
6285
|
};
|
|
6186
6286
|
}
|
|
6187
6287
|
settings.fallout = next;
|
|
6188
6288
|
saveSettings();
|
|
6189
6289
|
if (engine) engine.setFallout(next);
|
|
6190
|
-
|
|
6290
|
+
const out = JSON.parse(JSON.stringify(next));
|
|
6291
|
+
for (const s of out.slots) if (s && s.key) s.key = SECRET_MASK;
|
|
6292
|
+
return out;
|
|
6191
6293
|
});
|
|
6192
6294
|
|
|
6193
6295
|
/* #Limit: provider bazlı max input token limiti + bağlam sıkıştırma */
|
|
@@ -6217,10 +6319,13 @@ ipcMain.handle('sec:set', (_e, cfg) => {
|
|
|
6217
6319
|
ipcMain.handle('supermemory:set', (_e, cfg) => {
|
|
6218
6320
|
try {
|
|
6219
6321
|
const base = String((cfg && cfg.baseUrl) || '').trim() || 'http://localhost:6767';
|
|
6322
|
+
const incomingKey = String((cfg && cfg.apiKey) || '').trim();
|
|
6323
|
+
const prevKey = settings.supermemory && settings.supermemory.apiKey ? String(settings.supermemory.apiKey) : '';
|
|
6220
6324
|
settings.supermemory = {
|
|
6221
6325
|
enabled: !!(cfg && cfg.enabled),
|
|
6222
6326
|
baseUrl: base.replace(/\/+$/, ''),
|
|
6223
|
-
|
|
6327
|
+
/* maskeli anahtar geri gelirse mevcut korunur */
|
|
6328
|
+
apiKey: incomingKey === SECRET_MASK ? prevKey : incomingKey,
|
|
6224
6329
|
containerTag: String((cfg && cfg.containerTag) || 'beast').trim() || 'beast',
|
|
6225
6330
|
};
|
|
6226
6331
|
saveSettings();
|
|
@@ -6230,7 +6335,7 @@ ipcMain.handle('supermemory:set', (_e, cfg) => {
|
|
|
6230
6335
|
engine._smCheckedAt = 0;
|
|
6231
6336
|
engine._smWarned = false;
|
|
6232
6337
|
}
|
|
6233
|
-
return { ok: true, supermemory: settings.supermemory };
|
|
6338
|
+
return { ok: true, supermemory: { ...settings.supermemory, apiKey: settings.supermemory.apiKey ? SECRET_MASK : '' } };
|
|
6234
6339
|
} catch (e) {
|
|
6235
6340
|
return { ok: false, error: String((e && e.message) || e) };
|
|
6236
6341
|
}
|
|
@@ -6240,7 +6345,7 @@ ipcMain.handle('supermemory:get', () => {
|
|
|
6240
6345
|
return {
|
|
6241
6346
|
enabled: sm.enabled !== false,
|
|
6242
6347
|
baseUrl: sm.baseUrl || 'http://localhost:6767',
|
|
6243
|
-
apiKey: sm.apiKey
|
|
6348
|
+
apiKey: sm.apiKey ? SECRET_MASK : '',
|
|
6244
6349
|
containerTag: sm.containerTag || 'beast',
|
|
6245
6350
|
up: engine ? !!engine._smUp : null,
|
|
6246
6351
|
};
|
|
@@ -6809,6 +6914,11 @@ function cronFire(job) {
|
|
|
6809
6914
|
const sent = engine.send(sid, {
|
|
6810
6915
|
text: `[cron: ${job.name}]\n${job.prompt}`,
|
|
6811
6916
|
});
|
|
6917
|
+
toastNotify(
|
|
6918
|
+
`Cron: ${job.name}`,
|
|
6919
|
+
isReminderJob(job) ? 'Hatırlatma zamanı geldi' : String(job.prompt || '').slice(0, 160),
|
|
6920
|
+
'cron'
|
|
6921
|
+
);
|
|
6812
6922
|
if (!sent) cronAnswerPendingDrop(sid); // gönderilemedi — bayat bekleme bırakma
|
|
6813
6923
|
} catch {}
|
|
6814
6924
|
cronEmit();
|
|
@@ -6826,6 +6936,7 @@ function watcherFire(w, value) {
|
|
|
6826
6936
|
w.op === 'changed'
|
|
6827
6937
|
? 'izlenen değer değişti'
|
|
6828
6938
|
: `kural sağlandı (son değer ${value}, koşul ${w.op} ${w.value ?? ''})`;
|
|
6939
|
+
toastNotify(`İzleyici: ${w.name}`, target, 'watchers');
|
|
6829
6940
|
engine.send(sid, {
|
|
6830
6941
|
text:
|
|
6831
6942
|
`[IZLEYICI: ${w.name}] ${target}. ` +
|
|
@@ -7317,7 +7428,10 @@ ipcMain.handle('providers:keys', () => {
|
|
|
7317
7428
|
}
|
|
7318
7429
|
} catch {}
|
|
7319
7430
|
}
|
|
7320
|
-
|
|
7431
|
+
/* UI'a anahtarlar DÜZ METİN gitmez — yalnız "kayıtlı var mı" bilgisi taşınır */
|
|
7432
|
+
const out = {};
|
|
7433
|
+
for (const [k, v] of Object.entries(map)) out[k] = String(v || '') ? SECRET_MASK : '';
|
|
7434
|
+
return out;
|
|
7321
7435
|
});
|
|
7322
7436
|
/* #22 izleyici paneli IPC */
|
|
7323
7437
|
ipcMain.handle('watchers:list', () => watchers.list());
|
|
@@ -7357,6 +7471,13 @@ ipcMain.handle('cron:runNow', (_e, id) => {
|
|
|
7357
7471
|
cronEmit();
|
|
7358
7472
|
return r;
|
|
7359
7473
|
});
|
|
7474
|
+
/* Masaüstü (toast) bildirimleri aç/kapa */
|
|
7475
|
+
ipcMain.handle('notify:get', () => ({ toast: settings.notifyToast !== false }));
|
|
7476
|
+
ipcMain.handle('notify:set', (_e, on) => {
|
|
7477
|
+
settings.notifyToast = !!on;
|
|
7478
|
+
saveSettings();
|
|
7479
|
+
return { toast: settings.notifyToast };
|
|
7480
|
+
});
|
|
7360
7481
|
|
|
7361
7482
|
/* ---------------- tarayıcı IPC ---------------- */
|
|
7362
7483
|
ipcMain.handle('browser:toggle', () => {
|
|
@@ -10266,7 +10387,17 @@ ipcMain.handle('ide:previewUrl', async (_e, url) => {
|
|
|
10266
10387
|
});
|
|
10267
10388
|
|
|
10268
10389
|
ipcMain.handle('custom:set', (_e, list) => {
|
|
10269
|
-
|
|
10390
|
+
const prev = Array.isArray(settings.customProviders) ? settings.customProviders : [];
|
|
10391
|
+
const byId = new Map(prev.filter((p) => p && p.id).map((p) => [p.id, p]));
|
|
10392
|
+
settings.customProviders = (Array.isArray(list) ? list : []).map((p) => {
|
|
10393
|
+
if (!p || typeof p !== 'object') return p;
|
|
10394
|
+
const item = { ...p };
|
|
10395
|
+
const old = byId.get(item.id);
|
|
10396
|
+
const k = String(item.key == null ? '' : item.key).trim();
|
|
10397
|
+
/* UI'dan maskeli/boş anahtar geldiyse aynı id'nin kayıtlı anahtarı korunur */
|
|
10398
|
+
if ((k === SECRET_MASK || !k) && old && old.key) item.key = String(old.key);
|
|
10399
|
+
return item;
|
|
10400
|
+
});
|
|
10270
10401
|
saveSettings();
|
|
10271
10402
|
engine.setCustomProviders(settings.customProviders);
|
|
10272
10403
|
headroom.syncProviders(); /* headroom açıksa proxy setini yeni listeye uydur */
|
|
@@ -10823,6 +10954,10 @@ ipcMain.handle('email:set', (_e, cfg) => {
|
|
|
10823
10954
|
saveSettings();
|
|
10824
10955
|
return { ...settings.email, pass: settings.email.pass ? '***' : '' };
|
|
10825
10956
|
});
|
|
10957
|
+
/* E-posta paneli: gelen kutusu + okuma + gönderim (engine köprüsüyle aynı fonksiyonlar) */
|
|
10958
|
+
ipcMain.handle('email:list', (_e, opts) => emailList(opts || {}));
|
|
10959
|
+
ipcMain.handle('email:read', (_e, uid) => emailRead(uid));
|
|
10960
|
+
ipcMain.handle('email:send', (_e, msg) => emailSend(msg || {}));
|
|
10826
10961
|
|
|
10827
10962
|
/* ---------- ekran görüntüsü ---------- */
|
|
10828
10963
|
|
|
@@ -10895,6 +11030,12 @@ async function deliverFile(sessionId, filePath, caption) {
|
|
|
10895
11030
|
try {
|
|
10896
11031
|
const abs = path.isAbsolute(filePath) ? filePath : path.join(engine.workspace || process.cwd(), filePath);
|
|
10897
11032
|
if (!fs.existsSync(abs)) return { ok: false, error: 'dosya bulunamadı: ' + abs };
|
|
11033
|
+
/* GÜVENLİK: sır/anahtar dosyaları dışa gönderilmez (settings.json,
|
|
11034
|
+
wa-auth, .ssh, .env, *.pem...). Ajan bunu kullanıcıya söylesin. */
|
|
11035
|
+
if (fsguard.isSensitiveSendPath(abs)) {
|
|
11036
|
+
log.warn('sec', `send_file engellendi (hassas yol): ${abs}`);
|
|
11037
|
+
return { ok: false, error: 'güvenlik: hassas dosya (sır/anahtar) dışa gönderilmedi: ' + path.basename(abs) };
|
|
11038
|
+
}
|
|
10898
11039
|
const name = path.basename(abs);
|
|
10899
11040
|
const ext = path.extname(abs).toLowerCase();
|
|
10900
11041
|
let sid = String(sessionId || '');
|
package/src/preload.js
CHANGED
|
@@ -147,6 +147,8 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
147
147
|
cronDelete: (id) => ipcRenderer.invoke('cron:delete', id),
|
|
148
148
|
cronToggle: (id) => ipcRenderer.invoke('cron:toggle', id),
|
|
149
149
|
cronRunNow: (id) => ipcRenderer.invoke('cron:runNow', id),
|
|
150
|
+
notifyGet: () => ipcRenderer.invoke('notify:get'),
|
|
151
|
+
notifySet: (on) => ipcRenderer.invoke('notify:set', on),
|
|
150
152
|
|
|
151
153
|
watchersList: () => ipcRenderer.invoke('watchers:list'),
|
|
152
154
|
|
|
@@ -222,6 +224,9 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
222
224
|
whereWasISet: (cfg) => ipcRenderer.invoke('wherewasi:set', cfg),
|
|
223
225
|
getEmail: () => ipcRenderer.invoke('email:get'),
|
|
224
226
|
setEmail: (cfg) => ipcRenderer.invoke('email:set', cfg),
|
|
227
|
+
emailList: (opts) => ipcRenderer.invoke('email:list', opts),
|
|
228
|
+
emailRead: (uid) => ipcRenderer.invoke('email:read', uid),
|
|
229
|
+
emailSend: (msg) => ipcRenderer.invoke('email:send', msg),
|
|
225
230
|
captureScreen: () => ipcRenderer.invoke('screen:capture'),
|
|
226
231
|
waListSessions: () => ipcRenderer.invoke('wa:sessions'),
|
|
227
232
|
botsList: () => ipcRenderer.invoke('bots:list'),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -166,6 +166,7 @@
|
|
|
166
166
|
em_mem_clear: 'Hafızayı Temizle',
|
|
167
167
|
em_mem_cleared: 'Empati hafızası temizlendi',
|
|
168
168
|
tab_cron: 'Cron',
|
|
169
|
+
tab_agenda: 'Ajanda',
|
|
169
170
|
tab_usage: 'Maliyet',
|
|
170
171
|
tab_logs: 'Log',
|
|
171
172
|
tab_dash: 'Dashboard',
|
|
@@ -656,6 +657,81 @@
|
|
|
656
657
|
store_install_fail: 'Kurulum başarısız',
|
|
657
658
|
store_like_ok: 'Beğeni kaydedildi',
|
|
658
659
|
store_login_note: 'Beast Kimliği anonimdir; gerçek Beast Kodun asla paylaşılmaz.',
|
|
660
|
+
/* Cron paneli + WhatsApp kişi/yetki editörü */
|
|
661
|
+
cr_empty: 'Henüz görev yok — yukarıdan ekle.',
|
|
662
|
+
cr_dot_on: 'aktif — tıkla: forma doldur',
|
|
663
|
+
cr_dot_off: 'kapalı — tıkla: forma doldur',
|
|
664
|
+
cr_next: 'sıradaki: ',
|
|
665
|
+
cr_last: ' · son: ',
|
|
666
|
+
cr_run: 'Şimdi çalıştır',
|
|
667
|
+
cr_pause: 'Durdur',
|
|
668
|
+
cr_start: 'Başlat',
|
|
669
|
+
cr_del: 'Sil',
|
|
670
|
+
cr_modal_empty: 'Kayıtlı cron görev yok — Ayarlar → Cron sekmesinden ekle.',
|
|
671
|
+
cr_ran: 'Çalıştırıldı',
|
|
672
|
+
cr_toggle_pause: 'Duraklat',
|
|
673
|
+
wa_vazgec: 'Vazgeç',
|
|
674
|
+
wa_kaydet: 'kaydet',
|
|
675
|
+
wa_updated: 'Güncellendi',
|
|
676
|
+
wa_isim_ph: 'isim',
|
|
677
|
+
wa_numara_ph: 'numara',
|
|
678
|
+
wa_no_name_title: 'Bu kişiye cevap VERİLMEYECEK — tıklayıp isim ver',
|
|
679
|
+
wa_edit_title: 'Tıkla: isim/numara düzenle',
|
|
680
|
+
wa_perm_title: 'Yetki seviyesi — bu kişinin agent erişimini belirler',
|
|
681
|
+
wa_owner_title_on: 'Bu kişi BEAST\u2019İN SAHİBİ — tıklayıp başka kişiye devret',
|
|
682
|
+
wa_owner_title_off: 'Tıkla: bu kişiyi SAHİP yap (yalnız biri olabilir)',
|
|
683
|
+
wa_owner_on: '👑 sahip',
|
|
684
|
+
wa_owner_off: 'sahip yap',
|
|
685
|
+
wa_perm_all: 'serbest',
|
|
686
|
+
wa_perm_read: 'okuma',
|
|
687
|
+
wa_perm_chat: 'kısıtlı',
|
|
688
|
+
wa_queue_info: 'Mesaj kuyruğu: ${n} bekleyen',
|
|
689
|
+
wa_queue_failed: '${n} gönderilemedi',
|
|
690
|
+
wa_queue_empty_ok: 'Kuyruk temiz',
|
|
691
|
+
/* Cron düzenleme */
|
|
692
|
+
cr_update: 'Güncelle',
|
|
693
|
+
cr_cancel_edit: 'Vazgeç',
|
|
694
|
+
cr_edit_hint: 'tıkla: düzenle',
|
|
695
|
+
cr_edit_loaded: 'Görev forma yüklendi — düzenleyip Güncelle',
|
|
696
|
+
cr_updated: 'Görev güncellendi',
|
|
697
|
+
cr_added: 'Görev eklendi',
|
|
698
|
+
cr_edit_cancelled: 'Düzenleme iptal edildi',
|
|
699
|
+
/* İzleyici ekleme formu */
|
|
700
|
+
w_add: '+',
|
|
701
|
+
w_added: 'İzleyici eklendi',
|
|
702
|
+
w_add_name: 'İsim',
|
|
703
|
+
w_add_url: 'https://...',
|
|
704
|
+
w_add_path: 'json path (örn: data.price)',
|
|
705
|
+
w_add_value: 'eşik değer',
|
|
706
|
+
w_add_re: 'regex (opsiyonel)',
|
|
707
|
+
w_kind_web: 'Web',
|
|
708
|
+
w_kind_battery: 'Pil',
|
|
709
|
+
w_kind_logs: 'Log',
|
|
710
|
+
w_op_changed: 'değişti',
|
|
711
|
+
/* Ajanda */
|
|
712
|
+
aj_h2: 'Ajanda',
|
|
713
|
+
aj_sub: 'Yaklaşan cron görevleri, hatırlatıcılar ve izleyiciler — en yakın üstte.',
|
|
714
|
+
aj_empty: 'Planlanmış bir şey yok.',
|
|
715
|
+
aj_now: 'şimdi',
|
|
716
|
+
aj_later: 'sonra',
|
|
717
|
+
aj_ago: 'önce',
|
|
718
|
+
aj_notify: 'Masaüstü bildirimleri (toast) — cron, hatırlatıcı, izleyici ve ajan bitişinde',
|
|
719
|
+
aj_notify_on: 'Bildirimler açık',
|
|
720
|
+
aj_notify_off: 'Bildirimler kapalı',
|
|
721
|
+
/* E-posta paneli */
|
|
722
|
+
mail_inbox: 'Gelen Kutusu',
|
|
723
|
+
mail_refresh: 'Yenile',
|
|
724
|
+
mail_compose: 'Yeni Mail',
|
|
725
|
+
mail_to: 'alıcı@ornek.com',
|
|
726
|
+
mail_subject: 'Konu',
|
|
727
|
+
mail_body: 'Mesaj…',
|
|
728
|
+
mail_send: 'Gönder',
|
|
729
|
+
mail_sent: 'Mail gönderildi',
|
|
730
|
+
mail_send_fail: 'Mail gönderilemedi',
|
|
731
|
+
mail_empty: 'Gelen kutusu boş.',
|
|
732
|
+
mail_reply: 'Yanıtla',
|
|
733
|
+
mail_read_fail: 'Mail okunamadı',
|
|
734
|
+
mail_need_to: 'Alıcı adresi gerekli',
|
|
659
735
|
},
|
|
660
736
|
en: {
|
|
661
737
|
newChat: '+ New Chat',
|
|
@@ -811,6 +887,7 @@
|
|
|
811
887
|
em_mem_clear: 'Clear Memory',
|
|
812
888
|
em_mem_cleared: 'Empathy memory cleared',
|
|
813
889
|
tab_cron: 'Cron',
|
|
890
|
+
tab_agenda: 'Agenda',
|
|
814
891
|
tab_usage: 'Cost',
|
|
815
892
|
tab_logs: 'Logs',
|
|
816
893
|
tab_dash: 'Dashboard',
|
|
@@ -1076,36 +1153,50 @@
|
|
|
1076
1153
|
wa_queue_info: 'Message queue: ${n} pending',
|
|
1077
1154
|
wa_queue_failed: '${n} could not be sent',
|
|
1078
1155
|
wa_queue_empty_ok: 'Queue clear',
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1156
|
+
/* Cron edit */
|
|
1157
|
+
cr_update: 'Update',
|
|
1158
|
+
cr_cancel_edit: 'Cancel',
|
|
1159
|
+
cr_edit_hint: 'click: edit',
|
|
1160
|
+
cr_edit_loaded: 'Task loaded into form — edit and Update',
|
|
1161
|
+
cr_updated: 'Task updated',
|
|
1162
|
+
cr_added: 'Task added',
|
|
1163
|
+
cr_edit_cancelled: 'Edit cancelled',
|
|
1164
|
+
/* Watcher add form */
|
|
1165
|
+
w_add: '+',
|
|
1166
|
+
w_added: 'Watcher added',
|
|
1167
|
+
w_add_name: 'Name',
|
|
1168
|
+
w_add_url: 'https://...',
|
|
1169
|
+
w_add_path: 'json path (e.g. data.price)',
|
|
1170
|
+
w_add_value: 'threshold value',
|
|
1171
|
+
w_add_re: 'regex (optional)',
|
|
1172
|
+
w_kind_web: 'Web',
|
|
1173
|
+
w_kind_battery: 'Battery',
|
|
1174
|
+
w_kind_logs: 'Log',
|
|
1175
|
+
w_op_changed: 'changed',
|
|
1176
|
+
/* Agenda */
|
|
1177
|
+
aj_h2: 'Agenda',
|
|
1178
|
+
aj_sub: 'Upcoming cron jobs, reminders and watchers — nearest first.',
|
|
1179
|
+
aj_empty: 'Nothing scheduled.',
|
|
1180
|
+
aj_now: 'now',
|
|
1181
|
+
aj_later: 'from now',
|
|
1182
|
+
aj_ago: 'ago',
|
|
1183
|
+
aj_notify: 'Desktop notifications (toast) — on cron, reminder, watcher and agent finish',
|
|
1184
|
+
aj_notify_on: 'Notifications on',
|
|
1185
|
+
aj_notify_off: 'Notifications off',
|
|
1186
|
+
/* Email panel */
|
|
1187
|
+
mail_inbox: 'Inbox',
|
|
1188
|
+
mail_refresh: 'Refresh',
|
|
1189
|
+
mail_compose: 'New Mail',
|
|
1190
|
+
mail_to: 'to@example.com',
|
|
1191
|
+
mail_subject: 'Subject',
|
|
1192
|
+
mail_body: 'Message…',
|
|
1193
|
+
mail_send: 'Send',
|
|
1194
|
+
mail_sent: 'Mail sent',
|
|
1195
|
+
mail_send_fail: 'Could not send mail',
|
|
1196
|
+
mail_empty: 'Inbox is empty.',
|
|
1197
|
+
mail_reply: 'Reply',
|
|
1198
|
+
mail_read_fail: 'Could not read mail',
|
|
1199
|
+
mail_need_to: 'Recipient address required',
|
|
1109
1200
|
us_reset_toast: 'Usage counters reset',
|
|
1110
1201
|
us_backup_ok: 'Backup taken: Desktop\\Beast-Backups',
|
|
1111
1202
|
us_where_on_toast: 'Startup summary on',
|
package/src/renderer/index.html
CHANGED
|
@@ -455,6 +455,7 @@
|
|
|
455
455
|
<button class="tab" data-tab="events" data-i18n="tab_events">Olay Merkezi</button>
|
|
456
456
|
<button class="tab" data-tab="empati" data-i18n="tab_empati">Empati Loop</button>
|
|
457
457
|
<button class="tab" data-tab="cron" data-i18n="tab_cron">Cron</button>
|
|
458
|
+
<button class="tab" data-tab="agenda" data-i18n="tab_agenda">Ajanda</button>
|
|
458
459
|
<button class="tab" data-tab="usage" data-i18n="tab_usage">Maliyet · Limit</button>
|
|
459
460
|
<button class="tab" data-tab="headroom" data-i18n="tab_headroom">Token Sıkıştırma</button>
|
|
460
461
|
<button class="tab" data-tab="logs" data-i18n="tab_logs">Log</button>
|
|
@@ -506,9 +507,11 @@
|
|
|
506
507
|
</div>
|
|
507
508
|
<textarea id="cronPrompt" rows="3" placeholder="Agent'a verilecek görev…" data-i18n-ph="cron_prompt_ph"></textarea>
|
|
508
509
|
<button id="cronAddBtn" data-i18n="cron_add">+ Görev Ekle</button>
|
|
510
|
+
<button id="cronCancel" class="ghost" hidden data-i18n="cr_cancel_edit">Vazgeç</button>
|
|
509
511
|
</div>
|
|
510
512
|
<div id="cronList"></div>
|
|
511
513
|
</div>
|
|
514
|
+
<div id="tab-agenda" class="pane" hidden></div>
|
|
512
515
|
</section>
|
|
513
516
|
</div>
|
|
514
517
|
</div>
|
|
@@ -612,6 +615,43 @@
|
|
|
612
615
|
<button class="mini-close" id="watchClose" title="Kapat" data-i18n-title="btn_close">×</button>
|
|
613
616
|
</div>
|
|
614
617
|
<div class="mini-body">
|
|
618
|
+
<div id="wAddBox">
|
|
619
|
+
<div class="mini-add-row">
|
|
620
|
+
<input id="wAddName" class="inp" data-i18n-ph="w_add_name" placeholder="İsim" maxlength="80" />
|
|
621
|
+
<select id="wAddKind" class="inp mini-select">
|
|
622
|
+
<option value="web" data-i18n="w_kind_web">Web</option>
|
|
623
|
+
<option value="battery" data-i18n="w_kind_battery">Pil</option>
|
|
624
|
+
<option value="logs" data-i18n="w_kind_logs">Log</option>
|
|
625
|
+
</select>
|
|
626
|
+
<input id="wAddEvery" class="inp" type="number" min="1" max="1440" value="15" title="dakika" style="width:70px" />
|
|
627
|
+
<button id="wAddBtn" data-i18n="w_add">+</button>
|
|
628
|
+
</div>
|
|
629
|
+
<div class="mini-add-row" id="wAddWeb">
|
|
630
|
+
<input id="wAddUrl" class="inp" data-i18n-ph="w_add_url" placeholder="https://..." />
|
|
631
|
+
<input id="wAddPath" class="inp" data-i18n-ph="w_add_path" placeholder="json path (örn: data.price)" />
|
|
632
|
+
</div>
|
|
633
|
+
<div class="mini-add-row" id="wAddCond">
|
|
634
|
+
<select id="wAddOp" class="inp mini-select">
|
|
635
|
+
<option value="lte"><=</option>
|
|
636
|
+
<option value="lt"><</option>
|
|
637
|
+
<option value="gte">>=</option>
|
|
638
|
+
<option value="gt">></option>
|
|
639
|
+
<option value="eq">=</option>
|
|
640
|
+
<option value="neq">≠</option>
|
|
641
|
+
<option value="changed" data-i18n="w_op_changed">değişti</option>
|
|
642
|
+
</select>
|
|
643
|
+
<input id="wAddValue" class="inp" data-i18n-ph="w_add_value" placeholder="eşik değer" />
|
|
644
|
+
</div>
|
|
645
|
+
<div class="mini-add-row" id="wAddLogs" hidden>
|
|
646
|
+
<select id="wAddLevel" class="inp mini-select">
|
|
647
|
+
<option value="error">error</option>
|
|
648
|
+
<option value="warn">warn</option>
|
|
649
|
+
<option value="info">info</option>
|
|
650
|
+
</select>
|
|
651
|
+
<input id="wAddWindow" class="inp" type="number" min="1" max="720" value="10" title="pencere (dk)" style="width:80px" />
|
|
652
|
+
<input id="wAddRe" class="inp" data-i18n-ph="w_add_re" placeholder="regex (opsiyonel)" />
|
|
653
|
+
</div>
|
|
654
|
+
</div>
|
|
615
655
|
<div id="watchList" class="mini-list"></div>
|
|
616
656
|
</div>
|
|
617
657
|
</div>
|