beast-agent 0.23.9 → 0.24.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 +40 -27
- package/package.json +1 -1
- package/src/agent/bots.js +43 -1
- package/src/agent/engine.js +190 -3
- package/src/main.js +57 -147
- package/src/preload.js +3 -0
- package/src/renderer/i18n.js +8 -0
- package/src/renderer/index.html +7 -2
- package/src/renderer/renderer.js +99 -6
- package/src/renderer/style.css +27 -0
package/bin/beast-agent.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
const { spawn, spawnSync } = require('child_process');
|
|
9
9
|
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
10
12
|
|
|
11
13
|
/* kaldırma modu: uygulamayı kaldırır ama KİŞİSEL VERİLERİ korur
|
|
12
14
|
(%APPDATA%\beast: config.yaml, .env, oturumlar, hafıza, WhatsApp eşlemesi, yedekler) */
|
|
@@ -47,43 +49,54 @@ if (process.argv[2] === 'uninstall') {
|
|
|
47
49
|
process.exit(ur.status || 0);
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
/* güncelleme modu: çalışan Beast'i kapat (dosya kilidi EBUSY vermesin) → npm güncelle → yeniden başlat
|
|
52
|
+
/* güncelleme modu: çalışan Beast'i kapat (dosya kilidi EBUSY vermesin) → npm güncelle → yeniden başlat.
|
|
53
|
+
npm install DETACHED çalışır: terminali/uygulamayı kapatmak update'i boğmaz. */
|
|
51
54
|
if (process.argv[2] === 'update') {
|
|
52
55
|
const isWin = process.platform === 'win32';
|
|
53
|
-
console.log('\u27F3 beast-agent g\u00FCncelleniyor\u2026');
|
|
56
|
+
console.log('\u27F3 beast-agent g\u00FCncelleniyor\u2026 (arka planda s\u00FCrer — bu pencereyi kapabilirsin)');
|
|
54
57
|
if (isWin) {
|
|
55
58
|
try {
|
|
56
59
|
spawnSync('powershell.exe', ['-NoProfile', '-Command',
|
|
57
60
|
"Get-Process electron -ErrorAction SilentlyContinue | Where-Object { $_.Path -like '*node_modules*beast-agent*' } | Stop-Process -Force"],
|
|
58
61
|
{ stdio: 'ignore' });
|
|
59
62
|
console.log('\u2022 \u00E7al\u0131\u015Fan Beast kapat\u0131ld\u0131 (varsa)');
|
|
60
|
-
spawnSync('powershell.exe', ['-NoProfile', '-Command', 'Start-Sleep -Seconds 2'], { stdio: 'ignore' });
|
|
61
63
|
} catch {}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
/* detached PS helper: npm install (5 deneme) + electron.exe ile yeniden başlatma */
|
|
65
|
+
const ps = [
|
|
66
|
+
"$ErrorActionPreference = 'Continue'",
|
|
67
|
+
"$Log = Join-Path $env:APPDATA 'beast\\update.log'",
|
|
68
|
+
"function L([string]$m) { try { Add-Content -LiteralPath $Log -Value (\"[\" + (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') + \"] \" + $m) } catch {} }",
|
|
69
|
+
"L '=== beast update (terminal) basladi ==='",
|
|
70
|
+
"$ok = $false",
|
|
71
|
+
"for ($i = 1; $i -le 5 -and -not $ok; $i++) {",
|
|
72
|
+
" L \"npm install -g beast-agent@latest (deneme $i)\"",
|
|
73
|
+
" & npm.cmd install -g beast-agent@latest 2>&1 | ForEach-Object { L \" npm: $_\" }",
|
|
74
|
+
" if ($LASTEXITCODE -eq 0) { $ok = $true } else { Start-Sleep -Seconds 3 }",
|
|
75
|
+
"}",
|
|
76
|
+
"if (-not $ok) { L 'HATA: npm install basarisiz'; exit 1 }",
|
|
77
|
+
"L 'npm install tamam - yeniden baslatma'",
|
|
78
|
+
"$prefix = Join-Path $env:APPDATA 'npm'",
|
|
79
|
+
"$appDir = Join-Path $prefix 'node_modules\\beast-agent'",
|
|
80
|
+
"$exe = Join-Path $appDir 'node_modules\\electron\\dist\\electron.exe'",
|
|
81
|
+
"if (-not (Test-Path $exe)) { $exe = Join-Path $prefix 'node_modules\\electron\\dist\\electron.exe' }",
|
|
82
|
+
"Start-Process -FilePath $exe -ArgumentList ('\"' + $appDir + '\"')",
|
|
83
|
+
"L '=== beast update bitti ==='",
|
|
84
|
+
].join('\r\n');
|
|
85
|
+
const os = require('os');
|
|
86
|
+
const psFile = path.join(os.tmpdir(), 'beast-update-helper.ps1');
|
|
87
|
+
fs.writeFileSync(psFile, ps, 'utf8');
|
|
88
|
+
spawn('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', psFile],
|
|
89
|
+
{ detached: true, stdio: 'ignore', windowsHide: true }).unref();
|
|
90
|
+
console.log('\u2022 npm install arka planda s\u00FCr\u00FCyor (2-4 dk) \u2014 bitince uygulama kendili\u011Finden a\u00E7\u0131l\u0131r');
|
|
91
|
+
console.log(' durum: %APPDATA%\\beast\\update.log');
|
|
92
|
+
process.exit(0);
|
|
79
93
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
} catch {}
|
|
94
|
+
try { spawnSync('pkill', ['-f', 'node_modules/beast-agent'], { stdio: 'ignore' }); } catch {}
|
|
95
|
+
const sh =
|
|
96
|
+
'ok=0; for n in 1 2 3 4 5; do npm install -g beast-agent@latest && ok=1 && break; sleep 3; done; ' +
|
|
97
|
+
'if [ $ok -eq 1 ]; then nohup beast-agent >/dev/null 2>&1 & fi';
|
|
98
|
+
spawn('sh', ['-c', sh], { detached: true, stdio: 'ignore' }).unref();
|
|
99
|
+
console.log('\u2022 arka planda s\u00FCr\u00FCyor \u2014 bitince uygulama a\u00E7\u0131l\u0131r');
|
|
87
100
|
process.exit(0);
|
|
88
101
|
}
|
|
89
102
|
|
package/package.json
CHANGED
package/src/agent/bots.js
CHANGED
|
@@ -84,8 +84,10 @@ function adminBot() {
|
|
|
84
84
|
name: 'Beast',
|
|
85
85
|
icon: '🦁',
|
|
86
86
|
admin: true,
|
|
87
|
+
code: '', // 5 haneli benzersiz bot kodu (ensureBotCodes atar)
|
|
87
88
|
prompt: '',
|
|
88
89
|
perm: 'all',
|
|
90
|
+
vis: true, // sohbet görünürlüğü: ana sohbete davet edilebilir
|
|
89
91
|
skills: { ...DEFAULT_SKILLS, run_command: true },
|
|
90
92
|
seeBots: [], // admin her şeyi görür
|
|
91
93
|
extBrowser: true, // dış tarayıcı yetkisi
|
|
@@ -153,6 +155,7 @@ function botConfigOf(b) {
|
|
|
153
155
|
name: b.name,
|
|
154
156
|
icon: b.icon,
|
|
155
157
|
admin: !!b.admin,
|
|
158
|
+
code: b.code || '',
|
|
156
159
|
prompt: b.prompt || '',
|
|
157
160
|
browser: { default: b.browserDefault || 'dahili', extCommand: b.extCommand || '' },
|
|
158
161
|
plugins: b.plugins || [],
|
|
@@ -204,8 +207,10 @@ function add({ name, icon, prompt }) {
|
|
|
204
207
|
name: n,
|
|
205
208
|
icon: ICONS.includes(icon) ? icon : '🤖',
|
|
206
209
|
admin: false,
|
|
210
|
+
code: '', // ensureBotCodes hemen altında benzersiz 5 hane atar
|
|
207
211
|
prompt: String(prompt || '').slice(0, 4000),
|
|
208
212
|
perm: 'all',
|
|
213
|
+
vis: true, // sohbet görünürlüğü (admin matristen kapatır)
|
|
209
214
|
skills: { ...DEFAULT_SKILLS },
|
|
210
215
|
seeBots: [], // varsayılan: hiçbir bot baka botu göremez
|
|
211
216
|
extBrowser: false, // dış tarayıcı sadece yetkilide
|
|
@@ -215,12 +220,43 @@ function add({ name, icon, prompt }) {
|
|
|
215
220
|
createdAt: nowIso(),
|
|
216
221
|
};
|
|
217
222
|
REG.bots.push(bot);
|
|
223
|
+
ensureBotCodes();
|
|
218
224
|
saveRegistry();
|
|
219
225
|
ensureDirs();
|
|
220
|
-
logChange(id, `bot oluşturuldu (name="${n}", icon=${bot.icon})`);
|
|
226
|
+
logChange(id, `bot oluşturuldu (name="${n}", icon=${bot.icon}, code=${bot.code})`);
|
|
221
227
|
return { ok: true, bot: { ...bot } };
|
|
222
228
|
}
|
|
223
229
|
|
|
230
|
+
/* ---------- 5 HANELİ BENZERSİZ BOT KODU ----------
|
|
231
|
+
Her bot oluşurken 10000-99999 arası rastgele kod alır; duplicate kontrolü
|
|
232
|
+
zorunlu (tüm botlara karşı). Açılışta kodu eksik/çakışık botlara da atanır. */
|
|
233
|
+
function ensureBotCodes() {
|
|
234
|
+
loadRegistry();
|
|
235
|
+
let dirty = false;
|
|
236
|
+
const used = () => new Set(REG.bots.map((b) => String(b.code || '')).filter((c) => /^\d{5}$/.test(c)));
|
|
237
|
+
const seen = new Set();
|
|
238
|
+
for (const b of REG.bots) {
|
|
239
|
+
const cur = String(b.code || '');
|
|
240
|
+
if (/^\d{5}$/.test(cur) && !seen.has(cur)) { seen.add(cur); continue; }
|
|
241
|
+
const u = used();
|
|
242
|
+
let c;
|
|
243
|
+
do { c = String(10000 + Math.floor(Math.random() * 90000)); } while (u.has(c));
|
|
244
|
+
logChange(b.id, `bot kodu atandı: ${c}${cur ? ` (eskisi geçersizdi: ${cur})` : ''}`);
|
|
245
|
+
b.code = c;
|
|
246
|
+
seen.add(c);
|
|
247
|
+
dirty = true;
|
|
248
|
+
}
|
|
249
|
+
if (dirty) saveRegistry();
|
|
250
|
+
return dirty;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function byCode(code) {
|
|
254
|
+
const c = String(code || '').replace(/\D/g, '');
|
|
255
|
+
if (!/^\d{5}$/.test(c)) return null;
|
|
256
|
+
loadRegistry();
|
|
257
|
+
return REG.bots.find((b) => String(b.code || '') === c) || null;
|
|
258
|
+
}
|
|
259
|
+
|
|
224
260
|
function update(id, patch) {
|
|
225
261
|
loadRegistry();
|
|
226
262
|
const b = get(id);
|
|
@@ -279,6 +315,10 @@ function update(id, patch) {
|
|
|
279
315
|
changes.push(`dış tarayıcı yetkisi: ${b.extBrowser} → ${patch.extBrowser}`);
|
|
280
316
|
b.extBrowser = patch.extBrowser;
|
|
281
317
|
}
|
|
318
|
+
if (typeof patch.vis === 'boolean' && patch.vis !== (b.vis !== false)) {
|
|
319
|
+
changes.push(`sohbet görünürlüğü: ${b.vis !== false} → ${patch.vis}`);
|
|
320
|
+
b.vis = patch.vis;
|
|
321
|
+
}
|
|
282
322
|
if (['dahili', 'dis'].includes(patch.browserDefault) && patch.browserDefault !== b.browserDefault) {
|
|
283
323
|
changes.push(`varsayılan tarayıcı: ${b.browserDefault} → ${patch.browserDefault}`);
|
|
284
324
|
b.browserDefault = patch.browserDefault;
|
|
@@ -497,6 +537,8 @@ module.exports = {
|
|
|
497
537
|
update,
|
|
498
538
|
remove,
|
|
499
539
|
canSee,
|
|
540
|
+
ensureBotCodes,
|
|
541
|
+
byCode,
|
|
500
542
|
readMemory,
|
|
501
543
|
readMemoryFiles,
|
|
502
544
|
writeMemoryFile,
|
package/src/agent/engine.js
CHANGED
|
@@ -333,6 +333,41 @@ class Engine {
|
|
|
333
333
|
else this.sessionTools.delete(id);
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
+
/* ANA SOHBETE DAVETLİ BOTLAR: oturuma guest bot ekle/çıkar (persist) */
|
|
337
|
+
setSessionGuests(sessionId, guests) {
|
|
338
|
+
const s = this._load(String(sessionId || ''));
|
|
339
|
+
if (!s) return false;
|
|
340
|
+
const items = (Array.isArray(guests) ? guests : [])
|
|
341
|
+
.slice(0, 4)
|
|
342
|
+
.map((g) => ({ id: String((g && g.id) || ''), code: String((g && g.code) || ''), name: String((g && g.name) || '').slice(0, 40) }))
|
|
343
|
+
.filter((g) => g.id && g.code);
|
|
344
|
+
s.guests = items;
|
|
345
|
+
try {
|
|
346
|
+
const file = this._file(s.id);
|
|
347
|
+
const lines = fs.readFileSync(file, 'utf8').split('\n').filter((l) => l.trim());
|
|
348
|
+
const kept = lines.filter((l) => {
|
|
349
|
+
try { return JSON.parse(l).t !== 'guests'; } catch { return true; }
|
|
350
|
+
});
|
|
351
|
+
const out = kept.join('\n') + (items.length ? '\n' + JSON.stringify({ t: 'guests', items }) : '') + '\n';
|
|
352
|
+
const tmp = file + '.tmp';
|
|
353
|
+
fs.writeFileSync(tmp, out);
|
|
354
|
+
fs.renameSync(tmp, file);
|
|
355
|
+
} catch {}
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* Davetli botlar system prompt bloğu — ajan bot_dm ile onlara danışır */
|
|
360
|
+
_guestsBlock(session) {
|
|
361
|
+
const g = Array.isArray(session && session.guests) ? session.guests : [];
|
|
362
|
+
if (!g.length) return '';
|
|
363
|
+
const lines = g.map((x) => `- ${x.name} (kod: ${x.code})`).join('\n');
|
|
364
|
+
return (
|
|
365
|
+
`# DAVETLİ BOTLAR\n` +
|
|
366
|
+
`Bu sohbete şu botlar davet edildi. Kullanıcı onlara hitap ederse veya uzmanlıkları gereken bir konu olursa ` +
|
|
367
|
+
`bot_dm aracıyla (to=5 haneli kod) onlara yaz; aldığın cevabı [BotAdı] etiketiyle kullanıcıya aktar:\n${lines}`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
336
371
|
/* Oturumun bağlı olduğu MÜŞTERİ botu (admin/seasız → null → global hafıza) */
|
|
337
372
|
_sessionBotCtx(session) {
|
|
338
373
|
if (!session || !session.botId || typeof this.resolveBot !== 'function') return null;
|
|
@@ -907,6 +942,14 @@ class Engine {
|
|
|
907
942
|
} else if (rec.t === 'notes') {
|
|
908
943
|
session.notes = String(rec.text || '');
|
|
909
944
|
session.notesAt = Number(rec.at) || 0;
|
|
945
|
+
} else if (rec.t === 'botdm') {
|
|
946
|
+
/* botlar arası DM oturumu — admin izleyebilir, sidebar'da görünmez */
|
|
947
|
+
session.isBotDm = true;
|
|
948
|
+
session.dmA = String(rec.a || '');
|
|
949
|
+
session.dmB = String(rec.b || '');
|
|
950
|
+
} else if (rec.t === 'guests') {
|
|
951
|
+
/* ana sohbete davetli botlar */
|
|
952
|
+
session.guests = Array.isArray(rec.items) ? rec.items : [];
|
|
910
953
|
} else if (rec.t === 'msg') {
|
|
911
954
|
delete rec.t;
|
|
912
955
|
session.messages.push(rec);
|
|
@@ -1042,6 +1085,8 @@ class Engine {
|
|
|
1042
1085
|
updatedAt: s.updatedAt,
|
|
1043
1086
|
count: s.messages.length,
|
|
1044
1087
|
isBg,
|
|
1088
|
+
isBotDm: !!s.isBotDm,
|
|
1089
|
+
guests: Array.isArray(s.guests) ? s.guests : [],
|
|
1045
1090
|
bgStatus: isBg ? (job ? job.status : null) : null,
|
|
1046
1091
|
};
|
|
1047
1092
|
}
|
|
@@ -1058,6 +1103,7 @@ class Engine {
|
|
|
1058
1103
|
const id = f.replace(/\.jsonl$/, '');
|
|
1059
1104
|
const v = this._view(this._load(id));
|
|
1060
1105
|
if (v.isBg) continue;
|
|
1106
|
+
if (v.isBotDm) continue; // botlar arası DM — yalnız admin DM Log ekranında
|
|
1061
1107
|
out.push(v);
|
|
1062
1108
|
}
|
|
1063
1109
|
out.sort((a, b) => String(b.updatedAt).localeCompare(String(a.updatedAt)));
|
|
@@ -2150,6 +2196,8 @@ class Engine {
|
|
|
2150
2196
|
/* bot skill kısıtı: bota verilen yetkiye göre araç seti daraltılır */
|
|
2151
2197
|
const toolLimit = this.sessionTools.get(String(session.id));
|
|
2152
2198
|
if (toolLimit) activeTools = activeTools.filter((t) => toolLimit.has(t.function.name));
|
|
2199
|
+
/* DM oturumlarında bot_dm KAPALI — botlar botlara DM açıp döngü kuramaz */
|
|
2200
|
+
if (session.isBotDm) activeTools = activeTools.filter((t) => t.function.name !== 'bot_dm');
|
|
2153
2201
|
if (session.bgJob) {
|
|
2154
2202
|
/* arka plan ajanı: yönetim araçlarını görmez — işini bitirsin */
|
|
2155
2203
|
activeTools = activeTools.filter((t) => !BG_HIDDEN_TOOLS.has(t.function.name));
|
|
@@ -2172,6 +2220,8 @@ class Engine {
|
|
|
2172
2220
|
/* bot kimliği: bota bağlı oturumlarda kişilik + izolasyon kuralları */
|
|
2173
2221
|
const botBlock = this._botSystemBlock(session);
|
|
2174
2222
|
if (botBlock) system += '\n\n' + botBlock;
|
|
2223
|
+
const guestBlock = this._guestsBlock(session);
|
|
2224
|
+
if (guestBlock) system += '\n\n' + guestBlock;
|
|
2175
2225
|
if (session.notes) {
|
|
2176
2226
|
system +=
|
|
2177
2227
|
`\n\n# OTURUM NOTLARI (oturum ${session.code || '?'} — bu oturumun önceki konuşma özeti; birebir geçmişi buradan hatırla)\n` +
|
|
@@ -2270,7 +2320,7 @@ class Engine {
|
|
|
2270
2320
|
/* #21 bg ajanı: görev sonu ekstra LLM çağrıları YOK — hızlı kapansın.
|
|
2271
2321
|
Beast Code (bcCode) oturumları da aynı şekilde ANINDA kapansın:
|
|
2272
2322
|
not/hafıza/skill yansıtma çağrıları yapılmaz → done hemen gelir */
|
|
2273
|
-
if (!session.bgJob && !session.bcCode) {
|
|
2323
|
+
if (!session.bgJob && !session.bcCode && !session.isBotDm) {
|
|
2274
2324
|
const r = await this._updateSessionNotes(session, ctrl.signal);
|
|
2275
2325
|
if (r && r.ok) {
|
|
2276
2326
|
emitSafe(this, sid, { type: 'status', status: `oturum notları güncellendi (${session.code || ''})` });
|
|
@@ -2279,13 +2329,13 @@ class Engine {
|
|
|
2279
2329
|
}
|
|
2280
2330
|
}
|
|
2281
2331
|
/* otomatik memory döngüsü: kalıcı değerli bilgiyi MEMORY.md'ye düşür */
|
|
2282
|
-
if (!ctrl.signal.aborted && !session.bgJob && !session.bcCode) {
|
|
2332
|
+
if (!ctrl.signal.aborted && !session.bgJob && !session.bcCode && !session.isBotDm) {
|
|
2283
2333
|
try {
|
|
2284
2334
|
await this._autoMemory(session, ctrl.signal);
|
|
2285
2335
|
} catch {}
|
|
2286
2336
|
}
|
|
2287
2337
|
/* deneyimden skill doğurma (#2): 5+ yeni araç çağrısı biriktiyse yansıt */
|
|
2288
|
-
if (this.reflection.enabled && !ctrl.signal.aborted && !session.bgJob && !session.bcCode) {
|
|
2338
|
+
if (this.reflection.enabled && !ctrl.signal.aborted && !session.bgJob && !session.bcCode && !session.isBotDm) {
|
|
2289
2339
|
try {
|
|
2290
2340
|
const made = await this._maybeReflectSkill(session);
|
|
2291
2341
|
if (made) emitSafe(this, sid, { type: 'status', status: `skill taslağı doğdu: ${made}` });
|
|
@@ -2541,6 +2591,119 @@ class Engine {
|
|
|
2541
2591
|
return r.ok ? skills.slugify(draft.name) : null;
|
|
2542
2592
|
}
|
|
2543
2593
|
|
|
2594
|
+
/* ---------- BOTLAR ARASI DM (FEATURE) ----------
|
|
2595
|
+
Admin bot, 5 haneli kodla başka bota özel mesaj atar; hedef botun cevabı
|
|
2596
|
+
senkron döner. İzolasyon: DM turları gizli pair oturumunda yürür, bot_dm
|
|
2597
|
+
DM oturumlarında KAPALI (döngü koruması). Tüm trafik admin DM Log'ta. */
|
|
2598
|
+
async _botDm(args, sessionId, signal) {
|
|
2599
|
+
const bots = require('./bots');
|
|
2600
|
+
const code = String((args && args.to) || '').replace(/\D/g, '');
|
|
2601
|
+
const message = String((args && args.message) || '').slice(0, 6000);
|
|
2602
|
+
if (!/^\d{5}$/.test(code)) return { ok: false, error: 'geçersiz kod — 5 haneli bot kodu gir' };
|
|
2603
|
+
if (!message.trim()) return { ok: false, error: 'mesaj boş' };
|
|
2604
|
+
|
|
2605
|
+
const session = this.cache.get(String(sessionId)) || this._load(String(sessionId));
|
|
2606
|
+
const senderBotId = (session && session.botId) || 'beast';
|
|
2607
|
+
const senderBot = (typeof this.resolveBot === 'function' && senderBotId) ? this.resolveBot(senderBotId) : null;
|
|
2608
|
+
const senderIsAdmin = !session.botId || !!(senderBot && senderBot.admin);
|
|
2609
|
+
if (!senderIsAdmin) {
|
|
2610
|
+
return { ok: false, error: 'yetki yok — botlar arası DM yalnız yönetici (admin) bot tarafından açılabilir' };
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
const target = bots.byCode(code);
|
|
2614
|
+
if (!target) return { ok: false, error: `bu kotta bot yok: ${code}` };
|
|
2615
|
+
if (target.id === senderBotId) return { ok: false, error: 'kendine DM atılamaz' };
|
|
2616
|
+
|
|
2617
|
+
/* deterministik pair oturumu: dm + (küçük kod + büyük kod) — aynı çift aynı oturum */
|
|
2618
|
+
const codes = [String(senderBot ? senderBot.code || '' : ''), String(target.code || '')]
|
|
2619
|
+
.map((c) => (/^\d{5}$/.test(c) ? c : '00000'))
|
|
2620
|
+
.sort();
|
|
2621
|
+
const pairId = 'dm' + codes[0] + codes[1];
|
|
2622
|
+
|
|
2623
|
+
let pair = this.cache.get(pairId) || this._load(pairId);
|
|
2624
|
+
const fresh = !pair.isBotDm && !pair.messages.length;
|
|
2625
|
+
if (!pair.isBotDm) {
|
|
2626
|
+
try {
|
|
2627
|
+
fs.appendFileSync(this._file(pairId), JSON.stringify({ t: 'botdm', a: senderBotId, b: target.id, at: nowIso() }) + '\n');
|
|
2628
|
+
} catch {}
|
|
2629
|
+
pair.isBotDm = true;
|
|
2630
|
+
pair.dmA = senderBotId;
|
|
2631
|
+
pair.dmB = target.id;
|
|
2632
|
+
}
|
|
2633
|
+
/* hedef botun kimliğiyle çalışsın; izolasyon _botSystemBlock'tan gelir */
|
|
2634
|
+
this.setSessionBot(pairId, target.id);
|
|
2635
|
+
this.setSessionPerm(pairId, target.perm || 'all');
|
|
2636
|
+
|
|
2637
|
+
const senderName = senderBot ? senderBot.name : 'Beast';
|
|
2638
|
+
const senderCode = senderBot && /^\d{5}$/.test(String(senderBot.code || '')) ? senderBot.code : codes[0];
|
|
2639
|
+
const prefix = `[BOT DM — gönderen bot: ${senderName} (kod ${senderCode}) — cevabını kısa ve net ver, araç kullanman gerekmiyorsa kullanma]`;
|
|
2640
|
+
const before = pair.messages.length;
|
|
2641
|
+
const sent = this.send(pairId, { text: prefix + '\n' + message });
|
|
2642
|
+
if (!sent) return { ok: false, error: 'DM oturumu başlatılamadı' };
|
|
2643
|
+
|
|
2644
|
+
/* tur bitene kadar bekle (ctrl kaydı düşer), en fazla 120 sn */
|
|
2645
|
+
const t0 = Date.now();
|
|
2646
|
+
while (Date.now() - t0 < 120000) {
|
|
2647
|
+
if (signal && signal.aborted) return { ok: false, error: 'iptal edildi' };
|
|
2648
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
2649
|
+
if (!this.ctrls.has(pairId)) break;
|
|
2650
|
+
}
|
|
2651
|
+
if (this.ctrls.has(pairId)) {
|
|
2652
|
+
try { this.interrupt(pairId); } catch {}
|
|
2653
|
+
return { ok: false, error: 'hedef bot zaman aşımına uğradı (120 sn)' };
|
|
2654
|
+
}
|
|
2655
|
+
const after = this.cache.get(pairId) || pair;
|
|
2656
|
+
const newMsgs = after.messages.slice(before);
|
|
2657
|
+
const lastA = [...newMsgs].reverse().find((m) => m.role === 'assistant' && m.content && !(m.tool_calls && m.tool_calls.length));
|
|
2658
|
+
const reply = lastA ? String(typeof lastA.content === 'string' ? lastA.content : '(medya içerikli cevap)').slice(0, 12000) : '';
|
|
2659
|
+
if (!reply.trim()) return { ok: false, error: 'hedef bot cevap vermedi' };
|
|
2660
|
+
return { ok: true, from: target.name, code: target.code, reply };
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
/* ADMIN İZLEME: tüm botlar arası DM oturumlarının listesi */
|
|
2664
|
+
listBotDmSessions() {
|
|
2665
|
+
const bots = require('./bots');
|
|
2666
|
+
let files = [];
|
|
2667
|
+
try {
|
|
2668
|
+
files = fs.readdirSync(this.sessionsDir).filter((f) => f.startsWith('dm') && f.endsWith('.jsonl'));
|
|
2669
|
+
} catch {}
|
|
2670
|
+
const out = [];
|
|
2671
|
+
for (const f of files) {
|
|
2672
|
+
const id = f.replace(/\.jsonl$/, '');
|
|
2673
|
+
const s = this._load(id);
|
|
2674
|
+
if (!s.isBotDm || !s.messages.length) continue;
|
|
2675
|
+
const a = bots.get(s.dmA);
|
|
2676
|
+
const b = bots.get(s.dmB);
|
|
2677
|
+
out.push({
|
|
2678
|
+
id,
|
|
2679
|
+
a: s.dmA,
|
|
2680
|
+
b: s.dmB,
|
|
2681
|
+
aName: a ? a.name : s.dmA,
|
|
2682
|
+
bName: b ? b.name : s.dmB,
|
|
2683
|
+
aCode: (a && a.code) || '—',
|
|
2684
|
+
bCode: (b && b.code) || '—',
|
|
2685
|
+
count: s.messages.length,
|
|
2686
|
+
updatedAt: s.updatedAt,
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2689
|
+
out.sort((x, y) => String(y.updatedAt).localeCompare(String(x.updatedAt)));
|
|
2690
|
+
return out;
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
/* ADMIN İZLEME: bir DM oturumunun tam dökümü */
|
|
2694
|
+
readBotDm(id) {
|
|
2695
|
+
const s = this._load(String(id || ''));
|
|
2696
|
+
if (!s || !s.isBotDm) return { ok: false, error: 'DM oturumu yok' };
|
|
2697
|
+
return {
|
|
2698
|
+
ok: true,
|
|
2699
|
+
id: s.id,
|
|
2700
|
+
messages: s.messages.map((m) => ({
|
|
2701
|
+
role: m.role,
|
|
2702
|
+
content: typeof m.content === 'string' ? m.content : '[ek/medya]',
|
|
2703
|
+
})),
|
|
2704
|
+
};
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2544
2707
|
/* ---------- alt-agent ---------- */
|
|
2545
2708
|
|
|
2546
2709
|
async _subagent(task, context, parentSignal, sessionId) {
|
|
@@ -2781,6 +2944,14 @@ class Engine {
|
|
|
2781
2944
|
const result = await this._subagent(task, String(args.context || ''), signal, sessionId);
|
|
2782
2945
|
return JSON.stringify({ ok: true, task, result: String(result).slice(0, 12000) });
|
|
2783
2946
|
}
|
|
2947
|
+
if (name === 'bot_dm') {
|
|
2948
|
+
const dmS = this.cache.get(String(sessionId));
|
|
2949
|
+
if (dmS && dmS.isBotDm) {
|
|
2950
|
+
return JSON.stringify({ ok: false, error: 'DM oturumunda bot_dm kullanılamaz (döngü koruması)' });
|
|
2951
|
+
}
|
|
2952
|
+
const r = await this._botDm(args, sessionId, signal);
|
|
2953
|
+
return JSON.stringify(r);
|
|
2954
|
+
}
|
|
2784
2955
|
if (name === 'send_file') {
|
|
2785
2956
|
if (typeof this.fileSend !== 'function') {
|
|
2786
2957
|
return JSON.stringify({ ok: false, error: 'dosya gönderim köprüsü yok' });
|
|
@@ -3151,6 +3322,22 @@ const TOOLS = [
|
|
|
3151
3322
|
},
|
|
3152
3323
|
},
|
|
3153
3324
|
},
|
|
3325
|
+
{
|
|
3326
|
+
type: 'function',
|
|
3327
|
+
function: {
|
|
3328
|
+
name: 'bot_dm',
|
|
3329
|
+
description:
|
|
3330
|
+
'Send a direct message to ANOTHER BOT by its 5-digit code and receive its answer. Use when the user asks another bot something, invites bots into this chat, or a specialist bot should weigh in. Admin bot only.',
|
|
3331
|
+
parameters: {
|
|
3332
|
+
type: 'object',
|
|
3333
|
+
properties: {
|
|
3334
|
+
to: { type: 'string', description: "Target bot's 5-digit code, e.g. 48213" },
|
|
3335
|
+
message: { type: 'string', description: 'Message to send to that bot' },
|
|
3336
|
+
},
|
|
3337
|
+
required: ['to', 'message'],
|
|
3338
|
+
},
|
|
3339
|
+
},
|
|
3340
|
+
},
|
|
3154
3341
|
{
|
|
3155
3342
|
type: 'function',
|
|
3156
3343
|
function: {
|
package/src/main.js
CHANGED
|
@@ -1015,16 +1015,9 @@ async function tryWaSlash(jid, rawText, senderNum) {
|
|
|
1015
1015
|
? `*${cmd === 'deny' ? 'Reddedildi' : 'Onaylandı'}:* ${r.tool}${always ? ' — bu araç için bir daha sorulmayacak' : ''}`
|
|
1016
1016
|
: 'Bekleyen onay yok.';
|
|
1017
1017
|
} else if (cmd === 'update') {
|
|
1018
|
-
/* /update — sürüm kontrol
|
|
1018
|
+
/* /update — sürüm kontrol. TEK DAĞITIM npm: uygulama içi kurulum YOK. */
|
|
1019
1019
|
if (String(arg || '').toLowerCase() === 'now') {
|
|
1020
|
-
|
|
1021
|
-
npmUpdateNow(async (text) => { out = text; });
|
|
1022
|
-
} else if (updateState.downloaded && autoUpdater) {
|
|
1023
|
-
out = `*v${updateState.version} kuruluyor* — uygulama yeniden başlayacak.`;
|
|
1024
|
-
setTimeout(() => { try { autoUpdater.quitAndInstall(); } catch {} }, 1200);
|
|
1025
|
-
} else {
|
|
1026
|
-
out = 'İndirilmiş sürüm yok — önce `/update` yaz.';
|
|
1027
|
-
}
|
|
1020
|
+
out = NPM_ONLY_TEXT;
|
|
1028
1021
|
} else {
|
|
1029
1022
|
updateReplies.jids.add(jid);
|
|
1030
1023
|
await runUpdateCommand(async (text) => { out = text; });
|
|
@@ -2309,18 +2302,16 @@ function ensureDesktopShortcut() {
|
|
|
2309
2302
|
app.whenReady().then(() => {
|
|
2310
2303
|
// Tailscale modu: paketli uygulamada Windows ile otomatik başlat (sessiz, tepside)
|
|
2311
2304
|
if (app.isPackaged) {
|
|
2305
|
+
/* DAĞITIM KARARI: EXE/portable kurulum desteklenmiyor — tek yol npm.
|
|
2306
|
+
EXE kendini startup'a YAZMAZ (eski portable kayıtları da temizlenir). */
|
|
2312
2307
|
try {
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
app.
|
|
2316
|
-
|
|
2317
|
-
path: exe,
|
|
2318
|
-
args: ['--hidden'],
|
|
2308
|
+
const k = 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';
|
|
2309
|
+
spawn('reg.exe', ['delete', k, '/v', 'electron.app.Beast Agent', '/f'], { stdio: 'ignore', windowsHide: true }).unref();
|
|
2310
|
+
spawn('reg.exe', ['query', k, '/v', 'electron.app.Beast Agent'], { stdio: 'ignore', windowsHide: true }).on('exit', (code) => {
|
|
2311
|
+
if (code !== 0) log.info('main', 'EXE modunda çalışıyor — startup kaydı temizlendi (npm kurulumuna geçin)');
|
|
2319
2312
|
});
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
log.error('main', 'Startup kaydı başarısız: ' + String((e && e.message) || e));
|
|
2323
|
-
}
|
|
2313
|
+
} catch {}
|
|
2314
|
+
log.info('main', '⚠ EXE/portable mod desteklenmiyor — tek dağıtım: npm i -g beast-agent');
|
|
2324
2315
|
} else if (!app.isPackaged && /node_modules[\\/]beast-agent/i.test(String(app.getAppPath()))) {
|
|
2325
2316
|
/* npm (global) kurulum modu: startup kaydı + masaüstü kısayolu */
|
|
2326
2317
|
try {
|
|
@@ -2337,6 +2328,7 @@ app.whenReady().then(() => {
|
|
|
2337
2328
|
}
|
|
2338
2329
|
reloadBackend();
|
|
2339
2330
|
syncWhitelist(); // bot sistemi: whitelist.json aynası ilk açılışta garanti
|
|
2331
|
+
try { bots.ensureBotCodes(); } catch {} // her bota benzersiz 5 haneli kod garanti
|
|
2340
2332
|
createSplash();
|
|
2341
2333
|
createWindow();
|
|
2342
2334
|
log.info('main', 'Beast Agent başlatıldı');
|
|
@@ -3333,14 +3325,7 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
|
|
|
3333
3325
|
const a = t.slice(7).trim().toLowerCase();
|
|
3334
3326
|
updateReplies.sids.add(String(sessionId || ''));
|
|
3335
3327
|
if (a === 'now') {
|
|
3336
|
-
|
|
3337
|
-
npmUpdateNow((text) => desktopEcho(sessionId, t, text));
|
|
3338
|
-
} else if (updateState.downloaded && autoUpdater) {
|
|
3339
|
-
desktopEcho(sessionId, t, `*v${updateState.version} kuruluyor* — uygulama yeniden başlayacak.`);
|
|
3340
|
-
setTimeout(() => { try { autoUpdater.quitAndInstall(); } catch {} }, 1200);
|
|
3341
|
-
} else {
|
|
3342
|
-
desktopEcho(sessionId, t, 'İndirilmiş sürüm yok — önce `/update` yaz.');
|
|
3343
|
-
}
|
|
3328
|
+
desktopEcho(sessionId, t, NPM_ONLY_TEXT);
|
|
3344
3329
|
} else {
|
|
3345
3330
|
runUpdateCommand((text) => desktopEcho(sessionId, t, text));
|
|
3346
3331
|
}
|
|
@@ -4087,100 +4072,13 @@ ipcMain.handle('update:check', async () => {
|
|
|
4087
4072
|
return out;
|
|
4088
4073
|
});
|
|
4089
4074
|
|
|
4090
|
-
/*
|
|
4091
|
-
|
|
4092
|
-
detached çalışır: 1) uygulama PID'i tamamen çıkana kadar bekler (en çok 30 sn,
|
|
4093
|
-
sonra zorla kapatır — EBUSY dosya kilidinin numarası), 2) npm install -g
|
|
4094
|
-
beast-agent@latest — kilide karşı 5 deneme, 3) tüm çıktı update.log'a yazılır,
|
|
4095
|
-
4) beast-agent komut shim'i üzerinden yeniden başlatır (electron sürümü
|
|
4096
|
-
değişse de yol bozulmaz). Sonra app.quit(). */
|
|
4097
|
-
function npmSelfUpdate() {
|
|
4098
|
-
try {
|
|
4099
|
-
fs.mkdirSync(APP_DIR, { recursive: true });
|
|
4100
|
-
const pid = String(process.pid);
|
|
4101
|
-
if (process.platform === 'win32') {
|
|
4102
|
-
const ps = [
|
|
4103
|
-
"param([int]$ProcId = 0)",
|
|
4104
|
-
"$ErrorActionPreference = 'Continue'",
|
|
4105
|
-
"$Log = Join-Path $env:APPDATA 'beast\\update.log'",
|
|
4106
|
-
"function L([string]$m) { try { Add-Content -LiteralPath $Log -Value (\"[\" + (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') + \"] \" + $m) } catch {} }",
|
|
4107
|
-
"L \"=== self-update basladi (app pid: $ProcId) ===\"",
|
|
4108
|
-
"if ($ProcId -gt 0) {",
|
|
4109
|
-
" $deadline = (Get-Date).AddSeconds(30)",
|
|
4110
|
-
" while ((Get-Date) -lt $deadline) {",
|
|
4111
|
-
" if (-not (Get-Process -Id $ProcId -ErrorAction SilentlyContinue)) { break }",
|
|
4112
|
-
" Start-Sleep -Milliseconds 500",
|
|
4113
|
-
" }",
|
|
4114
|
-
" if (Get-Process -Id $ProcId -ErrorAction SilentlyContinue) {",
|
|
4115
|
-
" L 'uygulama hala acik - zorla kapatiliyor'",
|
|
4116
|
-
" try { Stop-Process -Id $ProcId -Force } catch {}",
|
|
4117
|
-
" Start-Sleep -Seconds 2",
|
|
4118
|
-
" }",
|
|
4119
|
-
"}",
|
|
4120
|
-
"L 'uygulama kapandi, npm install basliyor'",
|
|
4121
|
-
"$npmCmd = (Get-Command 'npm.cmd' -ErrorAction SilentlyContinue).Source",
|
|
4122
|
-
"if (-not $npmCmd) { $npmCmd = Join-Path $env:APPDATA 'npm\\npm.cmd' }",
|
|
4123
|
-
"$ok = $false",
|
|
4124
|
-
"for ($i = 1; $i -le 5; $i++) {",
|
|
4125
|
-
" if ($ok) { break }",
|
|
4126
|
-
" L \"npm install -g beast-agent@latest (deneme $i)\"",
|
|
4127
|
-
" $out = & $npmCmd install -g beast-agent@latest 2>&1",
|
|
4128
|
-
" $code = $LASTEXITCODE",
|
|
4129
|
-
" foreach ($line in @($out)) { L \" npm: $line\" }",
|
|
4130
|
-
" if ($code -eq 0) { $ok = $true } else { Start-Sleep -Seconds 3 }",
|
|
4131
|
-
"}",
|
|
4132
|
-
"if (-not $ok) {",
|
|
4133
|
-
" L 'HATA: npm install 5 denemede basarisiz - uygulama yeniden baslatilmiyor'",
|
|
4134
|
-
" exit 1",
|
|
4135
|
-
"}",
|
|
4136
|
-
"L 'npm install tamam, yeniden baslatma'",
|
|
4137
|
-
"$prefix = (& $npmCmd prefix -g 2>$null)",
|
|
4138
|
-
"if (-not $prefix) { $prefix = Join-Path $env:APPDATA 'npm' }",
|
|
4139
|
-
"$appDir = Join-Path $prefix 'node_modules\\beast-agent'",
|
|
4140
|
-
"$exe = Join-Path $appDir 'node_modules\\electron\\dist\\electron.exe'",
|
|
4141
|
-
"if (-not (Test-Path $exe)) { $exe = Join-Path $prefix 'node_modules\\electron\\dist\\electron.exe' }",
|
|
4142
|
-
"if (Test-Path $exe) {",
|
|
4143
|
-
" L \"dogrudan electron: $exe\"",
|
|
4144
|
-
" # WindowStyle Hidden KULLANMA: gizli bayragi Chromium miras alir, ilk pencere tray'de kaybolur",
|
|
4145
|
-
" Start-Process -FilePath $exe -ArgumentList \"`\"$appDir`\"\"",
|
|
4146
|
-
"} else {",
|
|
4147
|
-
" $shim = Get-Command 'beast-agent.cmd' -ErrorAction SilentlyContinue",
|
|
4148
|
-
" if ($shim) {",
|
|
4149
|
-
" L \"shim uzerinden: $($shim.Source)\"",
|
|
4150
|
-
" Start-Process -FilePath $shim.Source",
|
|
4151
|
-
" } else {",
|
|
4152
|
-
" L 'HATA: electron.exe ve shim bulunamadi - yeniden baslatma yapilamadi'",
|
|
4153
|
-
" exit 1",
|
|
4154
|
-
" }",
|
|
4155
|
-
"}",
|
|
4156
|
-
"L '=== self-update bitti ==='",
|
|
4157
|
-
].join('\r\n');
|
|
4158
|
-
const psFile = path.join(APP_DIR, 'update-helper.ps1');
|
|
4159
|
-
fs.writeFileSync(psFile, ps, 'utf8');
|
|
4160
|
-
spawn('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', psFile, '-ProcId', pid],
|
|
4161
|
-
{ detached: true, stdio: 'ignore', windowsHide: true }).unref();
|
|
4162
|
-
} else {
|
|
4163
|
-
const sh =
|
|
4164
|
-
`i=0; while [ $i -lt 60 ] && kill -0 ${pid} 2>/dev/null; do i=$((i+1)); sleep 0.5; done; ` +
|
|
4165
|
-
'ok=0; for n in 1 2 3 4 5; do npm install -g beast-agent@latest && ok=1 && break; sleep 3; done; ' +
|
|
4166
|
-
'if [ $ok -eq 1 ]; then nohup beast-agent >/dev/null 2>&1 & fi';
|
|
4167
|
-
spawn('sh', ['-c', sh], { detached: true, stdio: 'ignore' }).unref();
|
|
4168
|
-
}
|
|
4169
|
-
log.info('main', 'npm self-update: helper bırakıldı, uygulama kapatılıyor');
|
|
4170
|
-
} catch (e) {
|
|
4171
|
-
log.error('main', 'npm self-update hatası: ' + String((e && e.message) || e));
|
|
4172
|
-
}
|
|
4173
|
-
setTimeout(() => { try { app.quit(); } catch {} }, 400);
|
|
4174
|
-
}
|
|
4075
|
+
/* TEK DAĞITIM POLİTİKASI: uygulama içi self-update KALDIRILDI (v0.24.0).
|
|
4076
|
+
Güncelleme yalnız: uygulamayı kapat → "npm i -g beast-agent@latest" → tekrar aç. */
|
|
4175
4077
|
|
|
4176
|
-
ipcMain.handle('update:install', () => {
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
try { autoUpdater.quitAndInstall(); return { ok: true }; } catch (e) {
|
|
4181
|
-
return { ok: false, error: String((e && e.message) || e) };
|
|
4182
|
-
}
|
|
4183
|
-
});
|
|
4078
|
+
ipcMain.handle('update:install', () => ({
|
|
4079
|
+
ok: false,
|
|
4080
|
+
error: NPM_ONLY_TEXT,
|
|
4081
|
+
}));
|
|
4184
4082
|
|
|
4185
4083
|
ipcMain.handle('update:setAuto', (_e, cfg) => {
|
|
4186
4084
|
if (cfg && typeof cfg.autoCheck === 'boolean') settings.autoCheckUpdate = cfg.autoCheck;
|
|
@@ -4194,14 +4092,19 @@ ipcMain.handle('update:setAuto', (_e, cfg) => {
|
|
|
4194
4092
|
|
|
4195
4093
|
/* /update komutu (masaüstü + WA): hedefi kaydet, kontrol başlat */
|
|
4196
4094
|
/* npm modunda güncelle-şimdi: sürüm kontrolü + numaralarıyla bildir + kendi kendini güncelle */
|
|
4095
|
+
/* TEK DAĞITIM POLİTİKASI: exe/installer YOK, uygulama içi self-update YOK.
|
|
4096
|
+
Güncelleme yalnız: uygulamayı kapat → "npm i -g beast-agent@latest" → tekrar aç. */
|
|
4097
|
+
const NPM_ONLY_TEXT =
|
|
4098
|
+
'Tek dağıtım npm\u2019dir — uygulama içi güncelleme yok.\n' +
|
|
4099
|
+
'Güncellemek için:\n1) Uygulamayı kapat\n2) Terminalde: npm i -g beast-agent@latest\n3) Tekrar aç';
|
|
4100
|
+
|
|
4197
4101
|
function npmUpdateNow(reply /* fn(text) */) {
|
|
4198
4102
|
const current = app.getVersion();
|
|
4199
4103
|
getNpmLatest(true).then((v) => {
|
|
4200
4104
|
if (v && isNewerVersion(v, current)) {
|
|
4201
4105
|
updateState.available = true;
|
|
4202
4106
|
updateState.version = v;
|
|
4203
|
-
reply(`🔄 *
|
|
4204
|
-
setTimeout(() => npmSelfUpdate(), 1500);
|
|
4107
|
+
reply(`🔄 *Yeni sürüm var*\nMevcut: v${current}\nYeni: v${v}\n\n${NPM_ONLY_TEXT}`);
|
|
4205
4108
|
} else {
|
|
4206
4109
|
reply(`✅ *Güncelsin* — v${current} zaten en son sürüm.`);
|
|
4207
4110
|
}
|
|
@@ -4209,30 +4112,8 @@ function npmUpdateNow(reply /* fn(text) */) {
|
|
|
4209
4112
|
}
|
|
4210
4113
|
|
|
4211
4114
|
async function runUpdateCommand(reply /* fn(text) */) {
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
if (!autoUpdater) {
|
|
4215
|
-
reply('Updater bu modda kullanılamıyor. Yeni sürüm: github.com/algokodcom/beast-agent/releases');
|
|
4216
|
-
return;
|
|
4217
|
-
}
|
|
4218
|
-
/* önce npm registry'den sürüm bilgisi — kullanıcıya numaraları söyle */
|
|
4219
|
-
const v = await getNpmLatest(true);
|
|
4220
|
-
if (v && isNewerVersion(v, current)) {
|
|
4221
|
-
updateState.available = true;
|
|
4222
|
-
updateState.version = v;
|
|
4223
|
-
emitUpdateEvent();
|
|
4224
|
-
reply(`🔄 *Yeni sürüm bulundu*\nMevcut: v${current}\nYeni: v${v}\nİndiriliyor… (kurulum için: /update now)`);
|
|
4225
|
-
try { await autoUpdater.checkForUpdates(); } catch (e) {
|
|
4226
|
-
reply('İndirme başlatılamadı: ' + String((e && e.message) || e));
|
|
4227
|
-
}
|
|
4228
|
-
} else if (v) {
|
|
4229
|
-
reply(`✅ *Güncelsin* — v${current} en son sürüm.`);
|
|
4230
|
-
} else {
|
|
4231
|
-
reply(`🔍 Kontrol ediliyor (mevcut sürüm v${current})…`);
|
|
4232
|
-
try { await autoUpdater.checkForUpdates(); } catch (e) {
|
|
4233
|
-
reply('Kontrol başarısız: ' + String((e && e.message) || e));
|
|
4234
|
-
}
|
|
4235
|
-
}
|
|
4115
|
+
/* yalnız KONTROL + yol gösterme — kurulum yapmaz */
|
|
4116
|
+
return npmUpdateNow(reply);
|
|
4236
4117
|
}
|
|
4237
4118
|
|
|
4238
4119
|
/* #STT: sohbet mikrofonu — MediaRecorder sesini (webm/opus) yerel whisper'a çevir */
|
|
@@ -5482,10 +5363,39 @@ ipcMain.handle('bots:list', () => botListWithNumbers());
|
|
|
5482
5363
|
|
|
5483
5364
|
ipcMain.handle('bots:add', (_e, input) => {
|
|
5484
5365
|
const r = bots.add(input || {});
|
|
5485
|
-
if (r.ok)
|
|
5366
|
+
if (r.ok) {
|
|
5367
|
+
log.info('main', `yeni bot: ${r.bot.name} (${r.bot.id}) kod=${r.bot.code}`);
|
|
5368
|
+
/* BOT KODU MAİL BİLDİRİMİ: bot oluşur oluşmez sahibine mail atılır */
|
|
5369
|
+
try {
|
|
5370
|
+
const cfg = emailCfg();
|
|
5371
|
+
if (cfg.host && cfg.user && cfg.pass) {
|
|
5372
|
+
emailSend({
|
|
5373
|
+
to: cfg.user,
|
|
5374
|
+
subject: `Beast Agent — yeni bot kuruldu: ${r.bot.name} (kod ${r.bot.code})`,
|
|
5375
|
+
body:
|
|
5376
|
+
`Yeni bot oluşturuldu.\n\n` +
|
|
5377
|
+
`Ad: ${r.bot.name}\n` +
|
|
5378
|
+
`Bot kodu: ${r.bot.code}\n` +
|
|
5379
|
+
`Zaman: ${new Date().toLocaleString('tr-TR')}\n\n` +
|
|
5380
|
+
`Bu 5 haneli kod botlar arası DM adresidir — bot_dm aracında 'to' olarak kullanılır.\n` +
|
|
5381
|
+
`Sol alttaki bot listesinde de görüntülenir.`,
|
|
5382
|
+
}).catch(() => {});
|
|
5383
|
+
}
|
|
5384
|
+
} catch {}
|
|
5385
|
+
}
|
|
5486
5386
|
return { ...r, list: r.ok ? botListWithNumbers() : null };
|
|
5487
5387
|
});
|
|
5488
5388
|
|
|
5389
|
+
/* Botlar arası DM izleme (admin) */
|
|
5390
|
+
ipcMain.handle('bots:dm:list', () => engine.listBotDmSessions());
|
|
5391
|
+
ipcMain.handle('bots:dm:read', (_e, id) => engine.readBotDm(id));
|
|
5392
|
+
|
|
5393
|
+
/* Ana sohbete davetli botlar */
|
|
5394
|
+
ipcMain.handle('sessions:guests:set', (_e, { sessionId, guests }) => {
|
|
5395
|
+
try { engine.setSessionGuests(sessionId, guests); } catch {}
|
|
5396
|
+
return { ok: true };
|
|
5397
|
+
});
|
|
5398
|
+
|
|
5489
5399
|
ipcMain.handle('bots:update', (_e, { id, patch }) => {
|
|
5490
5400
|
const p = patch || {};
|
|
5491
5401
|
if (Array.isArray(p.numbers)) reassignBotNumbers(String(id || ''), p.numbers);
|
package/src/preload.js
CHANGED
|
@@ -141,6 +141,9 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
141
141
|
waListSessions: () => ipcRenderer.invoke('wa:sessions'),
|
|
142
142
|
botsList: () => ipcRenderer.invoke('bots:list'),
|
|
143
143
|
botsAdd: (input) => ipcRenderer.invoke('bots:add', input),
|
|
144
|
+
botsDmList: () => ipcRenderer.invoke('bots:dm:list'),
|
|
145
|
+
botsDmRead: (id) => ipcRenderer.invoke('bots:dm:read', id),
|
|
146
|
+
sessionSetGuests: (sessionId, guests) => ipcRenderer.invoke('sessions:guests:set', { sessionId, guests }),
|
|
144
147
|
botsUpdate: (id, patch) => ipcRenderer.invoke('bots:update', { id, patch }),
|
|
145
148
|
botsRemove: (id) => ipcRenderer.invoke('bots:remove', id),
|
|
146
149
|
botsStats: () => ipcRenderer.invoke('bots:stats'),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -165,6 +165,7 @@
|
|
|
165
165
|
sk_h2: 'Skills',
|
|
166
166
|
sk_sub: 'SKILL.md indeksi — tam metin model tarafından okunur',
|
|
167
167
|
sk_auto: 'Otomatik skill: ajan öğrendiği yeni yöntemleri taslak onayı beklemeden kurar; bir skillin daha kolay/daha iyi yolunu bulursa mevcut skilli kendisi günceller',
|
|
168
|
+
up_npm_only: 'Tek dağıtım npm\u2019dir — uygulama içi kurulum butonu yoktur. Güncellemek için: uygulamayı kapat → terminalde "npm i -g beast-agent@latest" → tekrar aç. (İpucu: sohbete "beni güncelle" yazarsan adımları söyler.)',
|
|
168
169
|
sk_rules_h2: 'Kalıcı Kurallar',
|
|
169
170
|
sk_rules_sub: 'Agent\u2019ın her sohbette uyduğu talimatlar — WA\u2019dan /rule ile de eklenir',
|
|
170
171
|
sk_no_rule: 'Henüz kural yok.',
|
|
@@ -443,6 +444,9 @@
|
|
|
443
444
|
bot_msg_user: 'KULLANICI',
|
|
444
445
|
bot_bind_title: 'Bu numarayı hangi bot karşılasın?',
|
|
445
446
|
bot_sel_empty: '— bot seçme —',
|
|
447
|
+
bot_code_title: 'Botun benzersiz 5 haneli kodu — botlar arası DM adresi',
|
|
448
|
+
bot_vis: 'Ana sohbete davet edilebilir (sohbet görünürlüğü)',
|
|
449
|
+
tipGuestBot: 'Konuşmaya bot davet et — davetli botlara ajan bot_dm ile danışır',
|
|
446
450
|
bot_switch_hint: 'Tıkla: bu botta çalış · ⚙: yönet',
|
|
447
451
|
bot_switched: 'bottasın — sohbetler bu botun kimliğiyle gider',
|
|
448
452
|
bot_manage: 'Botu yönet',
|
|
@@ -680,6 +684,7 @@
|
|
|
680
684
|
sk_h2: 'Skills',
|
|
681
685
|
sk_sub: 'SKILL.md index — full text is read by the model',
|
|
682
686
|
sk_auto: 'Auto skills: the agent installs learned procedures without draft approval and updates existing skills when it finds an easier or better way',
|
|
687
|
+
up_npm_only: 'npm is the only distribution — there is no in-app install button. To update: close the app → run "npm i -g beast-agent@latest" in a terminal → reopen.',
|
|
683
688
|
sk_rules_h2: 'Persistent Rules',
|
|
684
689
|
sk_rules_sub: 'Instructions the agent follows in every chat — also addable via /rule on WA',
|
|
685
690
|
sk_no_rule: 'No rules yet.',
|
|
@@ -1016,6 +1021,9 @@
|
|
|
1016
1021
|
bot_msg_user: 'USER',
|
|
1017
1022
|
bot_bind_title: 'Which bot should answer this number?',
|
|
1018
1023
|
bot_sel_empty: '— no bot —',
|
|
1024
|
+
bot_code_title: "This bot's unique 5-digit code — the inter-bot DM address",
|
|
1025
|
+
bot_vis: 'Invitable to the main chat (chat visibility)',
|
|
1026
|
+
tipGuestBot: 'Invite a bot to this chat — the agent consults invited bots via bot_dm',
|
|
1019
1027
|
bot_switch_hint: 'Click: work as this bot · ⚙: manage',
|
|
1020
1028
|
bot_switched: 'active — chats run with this bot\u2019s identity',
|
|
1021
1029
|
bot_manage: 'Manage bot',
|
package/src/renderer/index.html
CHANGED
|
@@ -95,8 +95,12 @@
|
|
|
95
95
|
<div id="chips" hidden></div>
|
|
96
96
|
<div id="slashMenu" hidden></div>
|
|
97
97
|
<div id="composer">
|
|
98
|
-
<
|
|
99
|
-
|
|
98
|
+
<div id="composerLeft" class="dd">
|
|
99
|
+
<button id="attachBtn" title="Dosya / resim ekle" data-i18n-title="tipAttach">+</button>
|
|
100
|
+
<button id="micBtn" title="Sesli komut — konuş, bırakınca yazıya çevirsin" data-i18n-title="tipMic"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><path d="M12 19v3"/></svg></button>
|
|
101
|
+
<button id="guestBotBtn" title="Konuşmaya bot davet et — davetli botlara ajan bot_dm ile danışır" data-i18n-title="tipGuestBot" hidden><span style="filter:grayscale(1)">🤖</span></button>
|
|
102
|
+
<div id="guestBotMenu" class="dd-menu" hidden></div>
|
|
103
|
+
</div>
|
|
100
104
|
<textarea id="input" rows="1" placeholder="Bir şey sor ya da görev ver… (Enter: gönder · Shift+Enter: satır · /: komutlar)" data-i18n-ph="composerPh"></textarea>
|
|
101
105
|
<button id="stopBtn" title="Durdur" hidden data-i18n-title="tipStop">■</button>
|
|
102
106
|
<button id="sendBtn" title="Gönder" data-i18n-title="tipSend">➤︎</button>
|
|
@@ -241,6 +245,7 @@
|
|
|
241
245
|
<button class="btab" data-btab="watcher" data-i18n="bot_tab_watcher">Watcher</button>
|
|
242
246
|
<button class="btab" data-btab="stats" data-i18n="bot_tab_stats">İstatistik</button>
|
|
243
247
|
<button class="btab" data-btab="notes" data-i18n="bot_tab_notes">Notlar</button>
|
|
248
|
+
<button class="btab" data-btab="dmlog" hidden>DM Log</button>
|
|
244
249
|
</nav>
|
|
245
250
|
<div id="botPane"></div>
|
|
246
251
|
</div>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -497,12 +497,15 @@ async function renderSessions(list) {
|
|
|
497
497
|
async function refreshSessions() {
|
|
498
498
|
await renderSessions(await beast.listSessions());
|
|
499
499
|
renderBotCards(); // bot kartlarındaki numara/sayı etiketleri de tazelensin
|
|
500
|
+
renderGuestBotMenu(); // davet menüsü (aktif bot/bot listesi değişmiş olabilir)
|
|
500
501
|
}
|
|
501
502
|
|
|
502
503
|
async function openSession(id) {
|
|
503
504
|
activeId = id;
|
|
504
505
|
streamEl = null;
|
|
505
506
|
const s = await beast.openSession(id);
|
|
507
|
+
activeGuests = Array.isArray(s.guests) ? s.guests : [];
|
|
508
|
+
renderGuestBotMenu();
|
|
506
509
|
els.msgs.innerHTML = '';
|
|
507
510
|
showEmpty(s.messages.length === 0);
|
|
508
511
|
renderTodos(s.todos || []);
|
|
@@ -1810,14 +1813,15 @@ async function renderUpdatePane(autoCheck) {
|
|
|
1810
1813
|
if (!st) return;
|
|
1811
1814
|
clearInterval(updatePaneTimer);
|
|
1812
1815
|
|
|
1813
|
-
/* mod bazlı butonlar: npm →
|
|
1816
|
+
/* mod bazlı butonlar: npm → kontrol, installer → kontrol, dev → sadece kontrol.
|
|
1817
|
+
TEK DAĞITIM npm — uygulama içi kurulum butonu KALDIRILDI. */
|
|
1814
1818
|
const isDev = !st.packaged && !st.npm;
|
|
1815
1819
|
let actions;
|
|
1816
1820
|
if (st.npm) {
|
|
1817
1821
|
actions = `<div class="form-grid" style="grid-template-columns:auto auto;gap:8px;margin-top:12px">
|
|
1818
|
-
<button id="
|
|
1822
|
+
<button id="upCheck" class="btn ghost">${_t('up_check_now')}</button>
|
|
1819
1823
|
</div>
|
|
1820
|
-
<div class="sub" style="margin-top:8px">${_t('
|
|
1824
|
+
<div class="sub" style="margin-top:8px">${_t('up_npm_only')}</div>`;
|
|
1821
1825
|
} else if (isDev) {
|
|
1822
1826
|
actions = `<div class="form-grid" style="grid-template-columns:auto auto;gap:8px;margin-top:12px">
|
|
1823
1827
|
<button id="upCheck" class="btn ghost">${_t('up_check_now')}</button>
|
|
@@ -1826,9 +1830,8 @@ async function renderUpdatePane(autoCheck) {
|
|
|
1826
1830
|
} else {
|
|
1827
1831
|
actions = `<div class="form-grid" style="grid-template-columns:auto auto;gap:8px;margin-top:12px">
|
|
1828
1832
|
<button id="upCheck" class="btn ghost">${_t('up_check_now')}</button>
|
|
1829
|
-
<button id="upInstall" class="btn ghost" ${st.downloaded ? '' : 'disabled style="opacity:.45;cursor:default"'}>${_t('up_install_now')}</button>
|
|
1830
1833
|
</div>
|
|
1831
|
-
<div class="sub" style="margin-top:8px">${_t('
|
|
1834
|
+
<div class="sub" style="margin-top:8px">${_t('up_npm_only')}</div>`;
|
|
1832
1835
|
}
|
|
1833
1836
|
|
|
1834
1837
|
pane.innerHTML =
|
|
@@ -1864,6 +1867,7 @@ async function renderUpdatePane(autoCheck) {
|
|
|
1864
1867
|
if (r.ok && r.npm) toast(_t('up_npm_started'));
|
|
1865
1868
|
else if (!r.ok && r.error) toast(r.error);
|
|
1866
1869
|
});
|
|
1870
|
+
/* #upInstall butonu kaldırıldı — tek dağıtım npm (buton geri gelirse çalışsın diye koruma duruyor) */
|
|
1867
1871
|
|
|
1868
1872
|
/* indirme ilerlemesi için sekme açıkken canlı tazele — YALNIZ durum kutusu */
|
|
1869
1873
|
updatePaneTimer = setInterval(() => {
|
|
@@ -2457,6 +2461,40 @@ let botsCache = [];
|
|
|
2457
2461
|
let botPageId = null; // null = genel bakış (Tüm Botlar)
|
|
2458
2462
|
let botPageStats = [];
|
|
2459
2463
|
let activeBotId = 'beast'; // masaüstü UI'ının şu an hangi botta olduğu (varsayılan: ilk bot/Beast)
|
|
2464
|
+
let activeGuests = []; // aktif ana sohbete davetli botlar [{id, code, name}]
|
|
2465
|
+
|
|
2466
|
+
/* ANA SOHBETE BOT DAVETİ: yalnız admin bottayken görünür (composer'daki 🤖 ikonu);
|
|
2467
|
+
davetli botlar session'a kaydedilir, ajan bot_dm aracıyla onlara danışır */
|
|
2468
|
+
function renderGuestBotMenu() {
|
|
2469
|
+
const btn = $('#guestBotBtn');
|
|
2470
|
+
const menu = $('#guestBotMenu');
|
|
2471
|
+
if (!btn || !menu) return;
|
|
2472
|
+
const isAdmin = activeBotId === 'beast';
|
|
2473
|
+
const show = isAdmin && !!activeId;
|
|
2474
|
+
btn.style.display = show ? '' : 'none';
|
|
2475
|
+
if (!show) { menu.hidden = true; return; }
|
|
2476
|
+
const candidates = botsCache.filter((b) => !b.admin && b.vis !== false);
|
|
2477
|
+
menu.innerHTML = '';
|
|
2478
|
+
if (!candidates.length) {
|
|
2479
|
+
menu.innerHTML = '<div class="dd-item" style="pointer-events:none">Davet edilebilir bot yok</div>';
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
for (const b of candidates) {
|
|
2483
|
+
const invited = activeGuests.some((g) => g.id === b.id);
|
|
2484
|
+
const item = document.createElement('div');
|
|
2485
|
+
item.className = 'dd-item';
|
|
2486
|
+
item.innerHTML = `${b.icon} ${escapeHtml(b.name)} <span style="float:right;color:var(--muted)">${invited ? '✓ davetli' : '+ davet et'}</span>`;
|
|
2487
|
+
item.addEventListener('click', async () => {
|
|
2488
|
+
const next = invited ? activeGuests.filter((g) => g.id !== b.id) : [...activeGuests, { id: b.id, code: b.code || '', name: b.name }];
|
|
2489
|
+
activeGuests = next;
|
|
2490
|
+
await beast.sessionSetGuests(activeId, next).catch(() => {});
|
|
2491
|
+
renderGuestBotMenu();
|
|
2492
|
+
toast(invited ? `${b.name} davetten çıkarıldı` : `${b.name} sohbete davet edildi — ajan gerektiğinde ona danışacak`);
|
|
2493
|
+
menu.hidden = true;
|
|
2494
|
+
});
|
|
2495
|
+
menu.appendChild(item);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2460
2498
|
|
|
2461
2499
|
async function refreshBots() {
|
|
2462
2500
|
try {
|
|
@@ -2483,7 +2521,8 @@ function renderBotCards() {
|
|
|
2483
2521
|
`<span class="bot-ico">${b.icon || '🤖'}</span>` +
|
|
2484
2522
|
`<span class="bot-nm">${escapeHtml(b.name)}</span>` +
|
|
2485
2523
|
`<span class="bot-gear" title="${_t('bot_manage')}">⚙</span>` +
|
|
2486
|
-
`<span class="bot-tag">${b.admin ? 'ADMIN' : 'BOT'}</span
|
|
2524
|
+
`<span class="bot-tag">${b.admin ? 'ADMIN' : 'BOT'}</span>` +
|
|
2525
|
+
(b.code ? `<span class="bot-code" title="${_t('bot_code_title')}">${escapeHtml(b.code)}</span>` : '');
|
|
2487
2526
|
/* tıkla → UI o bota geçer; ⚙ → yönetim sayfası */
|
|
2488
2527
|
row.addEventListener('click', () => switchBot(b.id));
|
|
2489
2528
|
row.querySelector('.bot-gear').addEventListener('click', (e) => {
|
|
@@ -2576,6 +2615,9 @@ function renderBotPage() {
|
|
|
2576
2615
|
head.querySelector('#botHeadIcon').textContent = b.icon || '🤖';
|
|
2577
2616
|
head.querySelector('#botHeadName').textContent = b.name;
|
|
2578
2617
|
$('#botHeadTag').textContent = b.admin ? 'ADMIN' : 'BOT';
|
|
2618
|
+
/* DM Log sekmesi yalnız admin botta görünür — botlar arası tüm trafiği o izler */
|
|
2619
|
+
const dmTab = document.querySelector('.btab[data-btab="dmlog"]');
|
|
2620
|
+
if (dmTab) dmTab.hidden = !b.admin;
|
|
2579
2621
|
const active = document.querySelector('.btab.active');
|
|
2580
2622
|
const tab = active ? active.dataset.btab : 'settings';
|
|
2581
2623
|
if (tab === 'settings') renderBotSettings(pane, b);
|
|
@@ -2584,9 +2626,43 @@ function renderBotPage() {
|
|
|
2584
2626
|
else if (tab === 'watcher') renderBotWatcher(pane, b);
|
|
2585
2627
|
else if (tab === 'stats') renderBotStats(pane, b);
|
|
2586
2628
|
else if (tab === 'notes') renderBotNotes(pane, b);
|
|
2629
|
+
else if (tab === 'dmlog') renderBotDmLog(pane, b);
|
|
2587
2630
|
renderBotChats(b);
|
|
2588
2631
|
}
|
|
2589
2632
|
|
|
2633
|
+
/* --- DM Log sekmesi (yalnız admin): botlar arası TÜM özel mesaj trafiği.
|
|
2634
|
+
Botlar birbirini göremez; admin her çiftin konuşmasını burada okur. --- */
|
|
2635
|
+
async function renderBotDmLog(pane, b) {
|
|
2636
|
+
pane.innerHTML = `<h2>Bot DM Log</h2><div class="sub">Botlar arası tüm özel mesaj trafiği — hangi bot kiminle, ne zaman, ne konuştu. Bu ekranı yalnız yönetici görür.</div>`;
|
|
2637
|
+
const list = await beast.botsDmList().catch(() => []);
|
|
2638
|
+
if (!list.length) {
|
|
2639
|
+
pane.insertAdjacentHTML('beforeend', '<p class="sub">Henüz botlar arası DM yok — admin botda bot_dm aracını kullanın.</p>');
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
for (const d of list) {
|
|
2643
|
+
const row = document.createElement('div');
|
|
2644
|
+
row.className = 'skill-row';
|
|
2645
|
+
row.innerHTML =
|
|
2646
|
+
`<div class="skill-name">${escapeHtml(d.aName)} (${escapeHtml(d.aCode)}) ⇄ ${escapeHtml(d.bName)} (${escapeHtml(d.bCode)})</div>` +
|
|
2647
|
+
`<div class="skill-desc">${d.count} mesaj · son: ${new Date(d.updatedAt).toLocaleString('tr-TR')}</div>` +
|
|
2648
|
+
`<button class="btn ghost" style="margin-top:6px;padding:3px 12px;font-size:12px">Dökümü aç/kapat</button>`;
|
|
2649
|
+
row.querySelector('button').addEventListener('click', async () => {
|
|
2650
|
+
const old = row.nextElementSibling;
|
|
2651
|
+
if (old && old.dataset && old.dataset.dmread === d.id) { old.remove(); return; }
|
|
2652
|
+
const r = await beast.botsDmRead(d.id).catch(() => ({ ok: false, error: 'ipc' }));
|
|
2653
|
+
const pre = document.createElement('pre');
|
|
2654
|
+
pre.dataset.dmread = d.id;
|
|
2655
|
+
pre.className = 'notes-body';
|
|
2656
|
+
pre.style.cssText = 'white-space:pre-wrap;max-height:340px;overflow:auto;margin-top:8px;border:1px solid var(--border);border-radius:8px;padding:10px';
|
|
2657
|
+
pre.textContent = r.ok
|
|
2658
|
+
? r.messages.map((m) => `[${m.role === 'user' ? '→ hedef bot' : m.role === 'assistant' ? '← hedef bot' : m.role}] ${m.content}`).join('\n\n')
|
|
2659
|
+
: (r.error || 'okunamadı');
|
|
2660
|
+
row.after(pre);
|
|
2661
|
+
});
|
|
2662
|
+
pane.appendChild(row);
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2590
2666
|
/* --- Notlar sekmesi: oturum notları — konuşma kodu, başlık, tarih ve özet metni.
|
|
2591
2667
|
Admin bot (beast) tüm oturumları görür; müşteri botu yalnız kendi oturumlarını. --- */
|
|
2592
2668
|
async function renderBotNotes(pane, b) {
|
|
@@ -2729,6 +2805,7 @@ function renderBotSettings(pane, b) {
|
|
|
2729
2805
|
<div><label class="mem-label">${_t('bot_name')}</label><input id="bName" class="inp" value="${escapeHtml(b.name)}" maxlength="40"/></div>
|
|
2730
2806
|
<div><label class="mem-label">${_t('bot_icon')}</label><div class="icon-pick" id="bIconPick">${iconBtns}</div></div>
|
|
2731
2807
|
</div>
|
|
2808
|
+
<div class="sub" style="margin:2px 0 6px">BOT KODU: <b style="letter-spacing:2px">${escapeHtml(b.code || '—')}</b> — botlar arası DM adresi</div>
|
|
2732
2809
|
<label class="mem-label">${_t('bot_prompt')}</label>
|
|
2733
2810
|
<textarea id="bPrompt" class="mem-area" rows="5" placeholder="${_t('bot_prompt_ph')}">${escapeHtml(b.prompt || '')}</textarea>
|
|
2734
2811
|
<div class="sub" style="margin-top:4px">${_t('bot_tpl_hint')}</div>
|
|
@@ -2748,6 +2825,9 @@ function renderBotSettings(pane, b) {
|
|
|
2748
2825
|
<div class="bot-checks" style="margin-bottom:6px">
|
|
2749
2826
|
<label><input type="checkbox" id="bExtBrowser" ${b.extBrowser ? 'checked' : ''}/> ${_t('bot_ext_browser')}</label>
|
|
2750
2827
|
</div>
|
|
2828
|
+
<div class="bot-checks" style="margin-bottom:6px">
|
|
2829
|
+
<label><input type="checkbox" id="bVis" ${b.vis !== false ? 'checked' : ''}/> ${_t('bot_vis')}</label>
|
|
2830
|
+
</div>
|
|
2751
2831
|
<div class="form-grid">
|
|
2752
2832
|
<div>
|
|
2753
2833
|
<label class="mem-label" style="margin-top:0">${_t('bot_def_browser')}</label>
|
|
@@ -2808,6 +2888,7 @@ function renderBotSettings(pane, b) {
|
|
|
2808
2888
|
prompt: $('#bPrompt').value,
|
|
2809
2889
|
seeBots: [...f.querySelectorAll('#bSee input:checked')].map((c) => c.dataset.see),
|
|
2810
2890
|
extBrowser: $('#bExtBrowser').checked,
|
|
2891
|
+
vis: f.querySelector('#bVis') ? f.querySelector('#bVis').checked : true,
|
|
2811
2892
|
browserDefault: $('#bBrDef').value,
|
|
2812
2893
|
extCommand: $('#bBrCmd').value.trim(),
|
|
2813
2894
|
numbers: (b.numbers || []).map((n) => n.num),
|
|
@@ -4396,6 +4477,18 @@ async function init() {
|
|
|
4396
4477
|
if ($('#botClose')) $('#botClose').addEventListener('click', () => botOverlaySetOpen(false));
|
|
4397
4478
|
if ($('#botOverviewBack')) $('#botOverviewBack').addEventListener('click', () => openBotPage(null));
|
|
4398
4479
|
if ($('#botChip')) $('#botChip').addEventListener('click', () => openBotPage(activeBotId));
|
|
4480
|
+
|
|
4481
|
+
/* BOT DAVET MENÜSÜ: butona tıkla → aç/kapa; dışarı tık → kapa */
|
|
4482
|
+
if ($('#guestBotBtn') && $('#guestBotMenu')) {
|
|
4483
|
+
$('#guestBotBtn').addEventListener('click', (e) => {
|
|
4484
|
+
e.stopPropagation();
|
|
4485
|
+
renderGuestBotMenu();
|
|
4486
|
+
$('#guestBotMenu').hidden = !$('#guestBotMenu').hidden;
|
|
4487
|
+
});
|
|
4488
|
+
document.addEventListener('click', (e) => {
|
|
4489
|
+
if (!$('#guestBotMenu').hidden && !$('#guestBotMenu').contains(e.target)) $('#guestBotMenu').hidden = true;
|
|
4490
|
+
});
|
|
4491
|
+
}
|
|
4399
4492
|
/* kalıcı aktif bot: restart sonrası aynı botun UI'ı açılır */
|
|
4400
4493
|
beast.botsActiveGet().then((r) => { activeBotId = (r && r.id) || 'beast'; updateBotChip(); renderBotCards(); }).catch(() => {});
|
|
4401
4494
|
document.querySelectorAll('.btab').forEach((b) =>
|
package/src/renderer/style.css
CHANGED
|
@@ -998,6 +998,23 @@ body.term-open #settingsOverlay { right: var(--tw, 520px); }
|
|
|
998
998
|
}
|
|
999
999
|
#attachBtn:hover { background: var(--panel); color: var(--accent); }
|
|
1000
1000
|
|
|
1001
|
+
/* composer sol grubu: + mikrofon bot-davet — yanyana boşluksuz */
|
|
1002
|
+
#composerLeft { display: flex; align-items: center; gap: 0; flex-shrink: 0; }
|
|
1003
|
+
#guestBotBtn {
|
|
1004
|
+
width: 32px; height: 32px;
|
|
1005
|
+
border-radius: 9px;
|
|
1006
|
+
border: none;
|
|
1007
|
+
cursor: pointer;
|
|
1008
|
+
font-size: 15px;
|
|
1009
|
+
line-height: 1;
|
|
1010
|
+
flex-shrink: 0;
|
|
1011
|
+
background: none;
|
|
1012
|
+
color: var(--muted);
|
|
1013
|
+
padding: 0;
|
|
1014
|
+
}
|
|
1015
|
+
#guestBotBtn:hover { background: var(--panel); color: var(--accent); }
|
|
1016
|
+
#guestBotMenu { width: 260px; left: 0; }
|
|
1017
|
+
|
|
1001
1018
|
#micBtn {
|
|
1002
1019
|
width: 32px; height: 32px;
|
|
1003
1020
|
border-radius: 9px;
|
|
@@ -1725,6 +1742,16 @@ body.browser-open #toast { left: calc((100vw - var(--bw, 480px)) / 2); }
|
|
|
1725
1742
|
.bot-card:hover { background: var(--panel2); color: var(--text); }
|
|
1726
1743
|
.bot-card.active { background: var(--panel2); color: var(--text); box-shadow: inset 2px 0 0 var(--accent); }
|
|
1727
1744
|
.bot-card .bot-ico { font-size: 16px; line-height: 1; flex-shrink: 0; }
|
|
1745
|
+
/* 5 haneli benzersiz bot kodu — bot adının yanında */
|
|
1746
|
+
.bot-card .bot-code {
|
|
1747
|
+
font-family: ui-monospace, Consolas, monospace;
|
|
1748
|
+
font-size: 10px;
|
|
1749
|
+
font-weight: 700;
|
|
1750
|
+
letter-spacing: 1.5px;
|
|
1751
|
+
color: var(--accent);
|
|
1752
|
+
opacity: 0.9;
|
|
1753
|
+
margin-left: 2px;
|
|
1754
|
+
}
|
|
1728
1755
|
.bot-card .bot-gear {
|
|
1729
1756
|
visibility: hidden;
|
|
1730
1757
|
color: var(--muted);
|