beast-agent 0.20.0 → 0.22.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/README.md +1 -1
- package/bin/beast-agent.js +16 -6
- package/package.json +3 -2
- package/scripts/release.js +21 -34
- package/src/agent/bots.js +17 -3
- package/src/agent/engine.js +258 -57
- package/src/main.js +332 -20
- package/src/preload.js +10 -0
- package/src/renderer/i18n.js +28 -2
- package/src/renderer/index.html +46 -1
- package/src/renderer/renderer.js +855 -10
- package/src/renderer/style.css +524 -2
package/src/main.js
CHANGED
|
@@ -193,6 +193,7 @@ const BUILTIN_PROVIDERS = [
|
|
|
193
193
|
{ id: 'gemini', name: 'Google Gemini', baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai', hint: 'aistudio.google.com/apikey' },
|
|
194
194
|
{ id: 'zhipu', name: 'Zhipu AI', baseUrl: 'https://api.z.ai/api/paas/v4', hint: 'z.ai model konsolu' },
|
|
195
195
|
{ id: 'groq', name: 'Groq', baseUrl: 'https://api.groq.com/openai/v1', hint: 'console.groq.com/keys' },
|
|
196
|
+
{ id: 'nvidia', name: 'NVIDIA NIM', baseUrl: 'https://integrate.api.nvidia.com/v1', hint: 'build.nvidia.com — ücretsiz kredi veriyor, talep yüksek' },
|
|
196
197
|
];
|
|
197
198
|
const SESSIONS_DIR = path.join(APP_DIR, 'sessions');
|
|
198
199
|
const SETTINGS_FILE = path.join(APP_DIR, 'settings.json');
|
|
@@ -1333,13 +1334,37 @@ function scheduleReminder({ when, message, sessionId, repeat }) {
|
|
|
1333
1334
|
/* ---------------- BOT SİSTEMİ yardımcıları ----------------
|
|
1334
1335
|
Bot eşleştirme, izolasyon, whitelist.json aynası ve bot istatistikleri. */
|
|
1335
1336
|
|
|
1336
|
-
|
|
1337
|
+
/* İzin değerini araç kümesine çevirir; null = tüm araçlar ('all').
|
|
1338
|
+
'web' / ['web','read'] / 'web,read' biçimlerini kabul eder. */
|
|
1339
|
+
function permToToolSet(p) {
|
|
1340
|
+
const { PERM_TOOL_SETS } = require('./agent/engine');
|
|
1341
|
+
const set = new Set();
|
|
1342
|
+
for (const raw of Array.isArray(p) ? p : String(p == null ? 'all' : p).split(',')) {
|
|
1343
|
+
const k = String(raw).trim() || 'all';
|
|
1344
|
+
if (k === 'all' || !PERM_TOOL_SETS[k]) return null;
|
|
1345
|
+
for (const t of PERM_TOOL_SETS[k]) set.add(t);
|
|
1346
|
+
}
|
|
1347
|
+
return set;
|
|
1348
|
+
}
|
|
1337
1349
|
|
|
1350
|
+
/* Hangi izin daha kısıtlıysa o kazanır. Dizi (çoklu bot izni) destekler:
|
|
1351
|
+
araç kümesi diğerinin alt kümesiyse o geçer; ikisi de alt küme değilse
|
|
1352
|
+
daha küçük küme kazanır (eşitse kişi yetkisi). */
|
|
1338
1353
|
function moreRestrictivePerm(a, b) {
|
|
1339
|
-
const
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
|
|
1354
|
+
const sa = permToToolSet(a);
|
|
1355
|
+
const sb = permToToolSet(b);
|
|
1356
|
+
if (sa === null && sb === null) return 'all';
|
|
1357
|
+
if (sa === null) return b; // b kısıtlı
|
|
1358
|
+
if (sb === null) return a; // a kısıtlı
|
|
1359
|
+
const aSubB = [...sa].every((t) => sb.has(t));
|
|
1360
|
+
const bSubA = [...sb].every((t) => sa.has(t));
|
|
1361
|
+
if (aSubB && !bSubA) return a;
|
|
1362
|
+
if (bSubA && !aSubB) return b;
|
|
1363
|
+
return sa.size <= sb.size ? a : b;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
function fmtPerm(p) {
|
|
1367
|
+
return Array.isArray(p) ? '[' + p.join('+') + ']' : String(p);
|
|
1343
1368
|
}
|
|
1344
1369
|
|
|
1345
1370
|
/* Bot skill checkbox'ları → oturumun görebileceği araç adları.
|
|
@@ -1623,7 +1648,7 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
|
1623
1648
|
} else {
|
|
1624
1649
|
engine.setSessionTools(sid, null);
|
|
1625
1650
|
}
|
|
1626
|
-
waLog(`perm=${perm} bot=${botId} sid=${sid}`);
|
|
1651
|
+
waLog(`perm=${fmtPerm(perm)} bot=${botId} sid=${sid}`);
|
|
1627
1652
|
|
|
1628
1653
|
const participantName = payload.participant ? '+' + String(payload.participant).split('@')[0].split(':')[0] : '';
|
|
1629
1654
|
/* #v13.1 rol: SAHİP vs MİSAFİR — ajan kime konuştuğunu net bilsin */
|
|
@@ -2193,9 +2218,9 @@ function setBrowserOpen(v, forceVisible) {
|
|
|
2193
2218
|
}
|
|
2194
2219
|
|
|
2195
2220
|
// AÇMA — görünürlük: kullanıcı kendi açtıysa (forceVisible) MUTLAKA görünür;
|
|
2196
|
-
// ajan açtıysa göz
|
|
2221
|
+
// ajan açtıysa DA VARSAYILAN GÖRÜNÜR (göz ikonuyla gizli mod seçilmedikçe)
|
|
2197
2222
|
browser.open = true;
|
|
2198
|
-
browser.visible = forceVisible === true ? true : settings.browserHeadless
|
|
2223
|
+
browser.visible = forceVisible === true ? true : settings.browserHeadless !== true;
|
|
2199
2224
|
ensureBrowser();
|
|
2200
2225
|
if (!browser.started) {
|
|
2201
2226
|
browser.started = true;
|
|
@@ -2770,7 +2795,8 @@ function createWindow() {
|
|
|
2770
2795
|
icon: path.join(__dirname, '..', 'assets', 'app.ico'),
|
|
2771
2796
|
titleBarStyle: 'hidden',
|
|
2772
2797
|
titleBarOverlay: {
|
|
2773
|
-
|
|
2798
|
+
/* transparent Windows'ta beyaz buton arka planı veriyor — tema rengiyle başlat */
|
|
2799
|
+
color: dark ? '#0d0d0f' : '#f7f7f8',
|
|
2774
2800
|
symbolColor: dark ? '#9a9aa2' : '#707078',
|
|
2775
2801
|
height: 46,
|
|
2776
2802
|
},
|
|
@@ -3584,24 +3610,82 @@ ipcMain.handle('update:check', async () => {
|
|
|
3584
3610
|
});
|
|
3585
3611
|
|
|
3586
3612
|
/* npm kurulumunda KENDİ KENDİNİ GÜNCELLEME:
|
|
3587
|
-
|
|
3613
|
+
helper script %APPDATA%\beast'a yazılır (paket dizini değişse de yaşar) ve
|
|
3614
|
+
detached çalışır: 1) uygulama PID'i tamamen çıkana kadar bekler (en çok 30 sn,
|
|
3615
|
+
sonra zorla kapatır — EBUSY dosya kilidinin numarası), 2) npm install -g
|
|
3616
|
+
beast-agent@latest — kilide karşı 5 deneme, 3) tüm çıktı update.log'a yazılır,
|
|
3617
|
+
4) beast-agent komut shim'i üzerinden yeniden başlatır (electron sürümü
|
|
3618
|
+
değişse de yol bozulmaz). Sonra app.quit(). */
|
|
3588
3619
|
function npmSelfUpdate() {
|
|
3589
3620
|
try {
|
|
3590
|
-
|
|
3591
|
-
const
|
|
3621
|
+
fs.mkdirSync(APP_DIR, { recursive: true });
|
|
3622
|
+
const pid = String(process.pid);
|
|
3592
3623
|
if (process.platform === 'win32') {
|
|
3593
|
-
const ps =
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3624
|
+
const ps = [
|
|
3625
|
+
"param([int]$ProcId = 0)",
|
|
3626
|
+
"$ErrorActionPreference = 'Continue'",
|
|
3627
|
+
"$Log = Join-Path $env:APPDATA 'beast\\update.log'",
|
|
3628
|
+
"function L([string]$m) { try { Add-Content -LiteralPath $Log -Value (\"[\" + (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') + \"] \" + $m) } catch {} }",
|
|
3629
|
+
"L \"=== self-update basladi (app pid: $ProcId) ===\"",
|
|
3630
|
+
"if ($ProcId -gt 0) {",
|
|
3631
|
+
" $deadline = (Get-Date).AddSeconds(30)",
|
|
3632
|
+
" while ((Get-Date) -lt $deadline) {",
|
|
3633
|
+
" if (-not (Get-Process -Id $ProcId -ErrorAction SilentlyContinue)) { break }",
|
|
3634
|
+
" Start-Sleep -Milliseconds 500",
|
|
3635
|
+
" }",
|
|
3636
|
+
" if (Get-Process -Id $ProcId -ErrorAction SilentlyContinue) {",
|
|
3637
|
+
" L 'uygulama hala acik - zorla kapatiliyor'",
|
|
3638
|
+
" try { Stop-Process -Id $ProcId -Force } catch {}",
|
|
3639
|
+
" Start-Sleep -Seconds 2",
|
|
3640
|
+
" }",
|
|
3641
|
+
"}",
|
|
3642
|
+
"L 'uygulama kapandi, npm install basliyor'",
|
|
3643
|
+
"$npmCmd = (Get-Command 'npm.cmd' -ErrorAction SilentlyContinue).Source",
|
|
3644
|
+
"if (-not $npmCmd) { $npmCmd = Join-Path $env:APPDATA 'npm\\npm.cmd' }",
|
|
3645
|
+
"$ok = $false",
|
|
3646
|
+
"for ($i = 1; $i -le 5; $i++) {",
|
|
3647
|
+
" if ($ok) { break }",
|
|
3648
|
+
" L \"npm install -g beast-agent@latest (deneme $i)\"",
|
|
3649
|
+
" $out = & $npmCmd install -g beast-agent@latest 2>&1",
|
|
3650
|
+
" $code = $LASTEXITCODE",
|
|
3651
|
+
" foreach ($line in @($out)) { L \" npm: $line\" }",
|
|
3652
|
+
" if ($code -eq 0) { $ok = $true } else { Start-Sleep -Seconds 3 }",
|
|
3653
|
+
"}",
|
|
3654
|
+
"if (-not $ok) {",
|
|
3655
|
+
" L 'HATA: npm install 5 denemede basarisiz - uygulama yeniden baslatilmiyor'",
|
|
3656
|
+
" exit 1",
|
|
3657
|
+
"}",
|
|
3658
|
+
"L 'npm install tamam, yeniden baslatma'",
|
|
3659
|
+
"$shim = Get-Command 'beast-agent.cmd' -ErrorAction SilentlyContinue",
|
|
3660
|
+
"if ($shim) {",
|
|
3661
|
+
" L \"shim uzerinden: $($shim.Source)\"",
|
|
3662
|
+
" Start-Process -FilePath $shim.Source -WindowStyle Hidden",
|
|
3663
|
+
"} else {",
|
|
3664
|
+
" $prefix = (& $npmCmd prefix -g 2>$null)",
|
|
3665
|
+
" if (-not $prefix) { $prefix = Join-Path $env:APPDATA 'npm' }",
|
|
3666
|
+
" $exe = Join-Path $prefix 'node_modules\\electron\\dist\\electron.exe'",
|
|
3667
|
+
" if (-not (Test-Path $exe)) { $exe = Join-Path $prefix 'node_modules\\beast-agent\\node_modules\\electron\\dist\\electron.exe' }",
|
|
3668
|
+
" $appDir = Join-Path $prefix 'node_modules\\beast-agent'",
|
|
3669
|
+
" L \"shim bulunamadi - dogrudan: $exe\"",
|
|
3670
|
+
" Start-Process -FilePath $exe -ArgumentList \"`\"$appDir`\"\" -WindowStyle Hidden",
|
|
3671
|
+
"}",
|
|
3672
|
+
"L '=== self-update bitti ==='",
|
|
3673
|
+
].join('\r\n');
|
|
3674
|
+
const psFile = path.join(APP_DIR, 'update-helper.ps1');
|
|
3675
|
+
fs.writeFileSync(psFile, ps, 'utf8');
|
|
3676
|
+
spawn('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', psFile, '-ProcId', pid],
|
|
3677
|
+
{ detached: true, stdio: 'ignore', windowsHide: true }).unref();
|
|
3599
3678
|
} else {
|
|
3600
|
-
const sh =
|
|
3679
|
+
const sh =
|
|
3680
|
+
`i=0; while [ $i -lt 60 ] && kill -0 ${pid} 2>/dev/null; do i=$((i+1)); sleep 0.5; done; ` +
|
|
3681
|
+
'ok=0; for n in 1 2 3 4 5; do npm install -g beast-agent@latest && ok=1 && break; sleep 3; done; ' +
|
|
3682
|
+
'if [ $ok -eq 1 ]; then nohup beast-agent >/dev/null 2>&1 & fi';
|
|
3601
3683
|
spawn('sh', ['-c', sh], { detached: true, stdio: 'ignore' }).unref();
|
|
3602
3684
|
}
|
|
3603
3685
|
log.info('main', 'npm self-update: helper bırakıldı, uygulama kapatılıyor');
|
|
3604
|
-
} catch {
|
|
3686
|
+
} catch (e) {
|
|
3687
|
+
log.error('main', 'npm self-update hatası: ' + String((e && e.message) || e));
|
|
3688
|
+
}
|
|
3605
3689
|
setTimeout(() => { try { app.quit(); } catch {} }, 400);
|
|
3606
3690
|
}
|
|
3607
3691
|
|
|
@@ -3993,6 +4077,74 @@ ipcMain.handle('terminal:stop', () => {
|
|
|
3993
4077
|
return { ok: true };
|
|
3994
4078
|
});
|
|
3995
4079
|
|
|
4080
|
+
/* ---------------- Beast Code paneli (IDE modu ortası) ----------------
|
|
4081
|
+
IDE modunda ortadaki sohbet yerine Beast'in KENDİ ajanı çalışır:
|
|
4082
|
+
varsayılan model zinciri + soldaki dosya panelindeki klasör.
|
|
4083
|
+
Yazışma SOLDAKİ KLASÖRE BAĞLIDIR: her klasörün kendi gizli engine
|
|
4084
|
+
oturumu vardır (klasör değişince sohbet de değişir); sohbet geçmişi
|
|
4085
|
+
listesine karışmaz. Olayları renderer zaten agent:event ile alır,
|
|
4086
|
+
panele orada akıtılır. */
|
|
4087
|
+
const bcSessions = new Map(); /* klasör yolu → sessionId */
|
|
4088
|
+
|
|
4089
|
+
function bcGetSession(folder) {
|
|
4090
|
+
let sid = bcSessions.get(folder);
|
|
4091
|
+
if (sid) {
|
|
4092
|
+
try {
|
|
4093
|
+
const s = engine.cache.get(sid);
|
|
4094
|
+
if (s) return s;
|
|
4095
|
+
} catch {}
|
|
4096
|
+
bcSessions.delete(folder);
|
|
4097
|
+
}
|
|
4098
|
+
const s = engine._load(engine.createSession().id);
|
|
4099
|
+
s.messages = s.messages || [];
|
|
4100
|
+
s.bgTitle = 'Beast Code'; /* _view.isBg → sohbet geçmişi listesinde gizli */
|
|
4101
|
+
s.bcCode = true; /* engine: her işte todo planı çıkar (BEAST CODE MODU bloğu) */
|
|
4102
|
+
try {
|
|
4103
|
+
fs.appendFileSync(
|
|
4104
|
+
engine._file(s.id),
|
|
4105
|
+
JSON.stringify({ t: 'meta2', bgOf: '', title: 'Beast Code', at: new Date().toISOString() }) + '\n'
|
|
4106
|
+
);
|
|
4107
|
+
} catch {}
|
|
4108
|
+
engine.cache.set(s.id, s);
|
|
4109
|
+
bcSessions.set(folder, s.id);
|
|
4110
|
+
return s;
|
|
4111
|
+
}
|
|
4112
|
+
|
|
4113
|
+
ipcMain.handle('beastcode:send', (_e, payload) => {
|
|
4114
|
+
const text = String((payload && payload.msg) || '').trim();
|
|
4115
|
+
if (!text) return { ok: false, error: 'boş mesaj' };
|
|
4116
|
+
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
4117
|
+
if (!engine.publicState().hasModel) return { ok: false, error: 'model yok — Ayarlar → Provider sekmesinden ekle' };
|
|
4118
|
+
const ws = ideRoot();
|
|
4119
|
+
if (engine.isBusy(bcSessions.get(ws))) return { ok: false, busy: true, error: 'önceki mesaj sürüyor — ■ ile durdurabilirsin' };
|
|
4120
|
+
const s = bcGetSession(ws);
|
|
4121
|
+
s.workspace = ws; /* soldaki klasörde çalış */
|
|
4122
|
+
s.bcCode = true; /* todo disiplini + iş sonu hızlı kapanış (engine) */
|
|
4123
|
+
engine.cache.set(s.id, s);
|
|
4124
|
+
const ok = engine.send(s.id, text);
|
|
4125
|
+
if (!ok) return { ok: false, error: 'mesaj gönderilemedi' };
|
|
4126
|
+
return { ok: true, sessionId: s.id };
|
|
4127
|
+
});
|
|
4128
|
+
|
|
4129
|
+
ipcMain.handle('beastcode:stop', () => {
|
|
4130
|
+
const sid = bcSessions.get(ideRoot());
|
|
4131
|
+
if (!sid) return { ok: false };
|
|
4132
|
+
let r = false;
|
|
4133
|
+
try { r = engine.interrupt(sid); } catch {}
|
|
4134
|
+
return { ok: !!r };
|
|
4135
|
+
});
|
|
4136
|
+
|
|
4137
|
+
ipcMain.handle('beastcode:new', () => {
|
|
4138
|
+
const ws = ideRoot();
|
|
4139
|
+
const sid = bcSessions.get(ws);
|
|
4140
|
+
if (sid && engine.isBusy(sid)) return { ok: false, error: 'mesaj sürüyor — önce ■ ile durdur' };
|
|
4141
|
+
if (sid) {
|
|
4142
|
+
try { engine.deleteSession(sid); } catch {}
|
|
4143
|
+
bcSessions.delete(ws);
|
|
4144
|
+
}
|
|
4145
|
+
return { ok: true };
|
|
4146
|
+
});
|
|
4147
|
+
|
|
3996
4148
|
/* düşünme (reasoning) seviyesi */
|
|
3997
4149
|
ipcMain.handle('think:set', (_e, v) => {
|
|
3998
4150
|
setThinkLevel(v);
|
|
@@ -4110,6 +4262,166 @@ ipcMain.handle('store:remove', (_e, id) =>
|
|
|
4110
4262
|
|
|
4111
4263
|
ipcMain.handle('store:export', (_e, id) => storeMod.exportEntry(id));
|
|
4112
4264
|
|
|
4265
|
+
/* ---------- IDE MODU (sol: dosya gezgini · orta: chat · sağ: preview) ---------- */
|
|
4266
|
+
|
|
4267
|
+
const IDE_TEXT_EXT = new Set([
|
|
4268
|
+
'.html', '.htm', '.css', '.js', '.mjs', '.cjs', '.ts', '.jsx', '.tsx', '.json',
|
|
4269
|
+
'.md', '.txt', '.csv', '.py', '.ps1', '.bat', '.cmd', '.sh', '.yaml', '.yml',
|
|
4270
|
+
'.xml', '.ini', '.svg', '.gitignore',
|
|
4271
|
+
]);
|
|
4272
|
+
const IDE_MAX_BYTES = 400 * 1024;
|
|
4273
|
+
|
|
4274
|
+
function ideRoot() {
|
|
4275
|
+
/* kullanıcı panelde başka klasör seçtiyse o kök alınır; yoksa agent workspace'i */
|
|
4276
|
+
return path.resolve(settings.ideRoot || settings.workspace || app.getPath('home'));
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
ipcMain.handle('ide:setroot', async () => {
|
|
4280
|
+
try {
|
|
4281
|
+
const r = await dialog.showOpenDialog({
|
|
4282
|
+
title: 'Klasör seç — dosya paneli ve preview bu klasörü kullanır',
|
|
4283
|
+
defaultPath: ideRoot(),
|
|
4284
|
+
properties: ['openDirectory'],
|
|
4285
|
+
});
|
|
4286
|
+
if (r.canceled || !r.filePaths || !r.filePaths[0]) return { ok: false, canceled: true };
|
|
4287
|
+
settings.ideRoot = r.filePaths[0];
|
|
4288
|
+
saveSettings();
|
|
4289
|
+
return { ok: true, root: settings.ideRoot };
|
|
4290
|
+
} catch (e) {
|
|
4291
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
4292
|
+
}
|
|
4293
|
+
});
|
|
4294
|
+
|
|
4295
|
+
/* rel yol → workspace içinde kal (path traversal kilidi) */
|
|
4296
|
+
function ideSafe(rel) {
|
|
4297
|
+
const root = ideRoot();
|
|
4298
|
+
const p = path.resolve(root, String(rel || ''));
|
|
4299
|
+
if (p !== root && !p.startsWith(root + path.sep)) return null;
|
|
4300
|
+
return p;
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4303
|
+
ipcMain.handle('ide:tree', (_e, rel) => {
|
|
4304
|
+
const p = ideSafe(rel);
|
|
4305
|
+
if (!p) return { ok: false, error: 'geçersiz yol' };
|
|
4306
|
+
try {
|
|
4307
|
+
const entries = fs.readdirSync(p, { withFileTypes: true })
|
|
4308
|
+
.filter((e) => e.name !== 'node_modules' && e.name !== '.git')
|
|
4309
|
+
.map((e) => {
|
|
4310
|
+
let size = 0;
|
|
4311
|
+
try { if (e.isFile()) size = fs.statSync(path.join(p, e.name)).size; } catch {}
|
|
4312
|
+
return { name: e.name, dir: e.isDirectory(), size };
|
|
4313
|
+
})
|
|
4314
|
+
.sort((a, b) => (a.dir === b.dir ? a.name.localeCompare(b.name) : a.dir ? -1 : 1));
|
|
4315
|
+
return { ok: true, workspace: ideRoot(), entries };
|
|
4316
|
+
} catch (e) {
|
|
4317
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
4318
|
+
}
|
|
4319
|
+
});
|
|
4320
|
+
|
|
4321
|
+
ipcMain.handle('ide:read', (_e, rel) => {
|
|
4322
|
+
const p = ideSafe(rel);
|
|
4323
|
+
if (!p) return { ok: false, error: 'geçersiz yol' };
|
|
4324
|
+
const ext = path.extname(p).toLowerCase();
|
|
4325
|
+
if (ext && !IDE_TEXT_EXT.has(ext)) return { ok: false, error: 'metin dosyası değil — düzenlenemez' };
|
|
4326
|
+
try {
|
|
4327
|
+
const st = fs.statSync(p);
|
|
4328
|
+
if (st.isDirectory()) return { ok: false, error: 'klasör' };
|
|
4329
|
+
if (st.size > IDE_MAX_BYTES) return { ok: false, error: 'dosya çok büyük (max 400KB)' };
|
|
4330
|
+
const buf = fs.readFileSync(p);
|
|
4331
|
+
if (buf.slice(0, 4096).includes(0)) return { ok: false, error: 'ikili (binary) dosya' };
|
|
4332
|
+
return { ok: true, content: buf.toString('utf8') };
|
|
4333
|
+
} catch (e) {
|
|
4334
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
4335
|
+
}
|
|
4336
|
+
});
|
|
4337
|
+
|
|
4338
|
+
ipcMain.handle('ide:write', (_e, p) => {
|
|
4339
|
+
const target = ideSafe(p && p.rel);
|
|
4340
|
+
if (!target) return { ok: false, error: 'geçersiz yol' };
|
|
4341
|
+
const ext = path.extname(target).toLowerCase();
|
|
4342
|
+
if (ext && !IDE_TEXT_EXT.has(ext)) return { ok: false, error: 'metin dosyası değil — yazılamaz' };
|
|
4343
|
+
try {
|
|
4344
|
+
const body = String((p && p.content) ?? '');
|
|
4345
|
+
if (Buffer.byteLength(body, 'utf8') > IDE_MAX_BYTES) return { ok: false, error: 'içerik çok büyük (max 400KB)' };
|
|
4346
|
+
fs.writeFileSync(target, body, 'utf8');
|
|
4347
|
+
return { ok: true };
|
|
4348
|
+
} catch (e) {
|
|
4349
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
4350
|
+
}
|
|
4351
|
+
});
|
|
4352
|
+
|
|
4353
|
+
/* Sağ tık menüsü: dosya/klasör sil (onay diyaloglu) */
|
|
4354
|
+
ipcMain.handle('ide:delete', async (_e, rel) => {
|
|
4355
|
+
const p = ideSafe(rel);
|
|
4356
|
+
if (!p || p === ideRoot()) return { ok: false, error: 'geçersiz yol' };
|
|
4357
|
+
try {
|
|
4358
|
+
const st = fs.statSync(p);
|
|
4359
|
+
const isDir = st.isDirectory();
|
|
4360
|
+
const owner = win && !win.isDestroyed() ? win : undefined;
|
|
4361
|
+
const r = owner
|
|
4362
|
+
? await dialog.showMessageBox(owner, {
|
|
4363
|
+
type: 'warning',
|
|
4364
|
+
title: 'Sil',
|
|
4365
|
+
message: `"${rel}" ${isDir ? 'klasörünü (içi dahil)' : 'dosyasını'} silmek istiyor musun?`,
|
|
4366
|
+
buttons: ['Sil', 'Vazgeç'],
|
|
4367
|
+
defaultId: 1,
|
|
4368
|
+
cancelId: 1,
|
|
4369
|
+
})
|
|
4370
|
+
: { response: 1 };
|
|
4371
|
+
if (r.response !== 0) return { ok: false, canceled: true };
|
|
4372
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
4373
|
+
return { ok: true };
|
|
4374
|
+
} catch (e) {
|
|
4375
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
4376
|
+
}
|
|
4377
|
+
});
|
|
4378
|
+
|
|
4379
|
+
/* Sağ tık menüsü: HTML dosyasını dahili tarayıcıda GÖRÜNÜR aç */
|
|
4380
|
+
ipcMain.handle('ide:previewFile', (_e, rel) => {
|
|
4381
|
+
try {
|
|
4382
|
+
const p = ideSafe(rel);
|
|
4383
|
+
if (!p) return { ok: false, error: 'geçersiz yol' };
|
|
4384
|
+
if (!/\.html?$/i.test(p)) return { ok: false, error: 'önizleme yalnız .html/.htm dosyaları için' };
|
|
4385
|
+
setBrowserOpen(true, true);
|
|
4386
|
+
const url = 'file:///' + p.replace(/\\/g, '/');
|
|
4387
|
+
browser.view.webContents.loadURL(url).catch(() => {});
|
|
4388
|
+
browserEmit({ open: true, width: browser.width, url });
|
|
4389
|
+
return { ok: true };
|
|
4390
|
+
} catch (e) {
|
|
4391
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
4392
|
+
}
|
|
4393
|
+
});
|
|
4394
|
+
|
|
4395
|
+
/* PREVIEW: workspace kökündeki entry sayfayı (index.html → ilk *.html) sağdaki
|
|
4396
|
+
dahili tarayıcıda aç. file:// yükleme browserNavigate'i BYPASS eder — o https
|
|
4397
|
+
olmayan adresi aramaya çevirir. */
|
|
4398
|
+
ipcMain.handle('ide:preview', () => { try {
|
|
4399
|
+
const root = ideRoot();
|
|
4400
|
+
const pick = (name) => {
|
|
4401
|
+
const p = path.join(root, name);
|
|
4402
|
+
try { return fs.existsSync(p) ? p : null; } catch { return null; }
|
|
4403
|
+
};
|
|
4404
|
+
let entry = pick('index.html');
|
|
4405
|
+
if (!entry) {
|
|
4406
|
+
const htmls = fs
|
|
4407
|
+
.readdirSync(root, { withFileTypes: true })
|
|
4408
|
+
.filter((e) => e.isFile() && /\.html?$/i.test(e.name))
|
|
4409
|
+
.map((e) => e.name);
|
|
4410
|
+
entry = htmls.length ? path.join(root, htmls.sort()[0]) : null;
|
|
4411
|
+
}
|
|
4412
|
+
if (!entry) return { ok: false, error: 'workspace kökünde index.html yok — önce agent\'a siteyi yazdır' };
|
|
4413
|
+
/* forceVisible: preview'a basınca tarayıcı ikonuna basmaya gerek kalmasın —
|
|
4414
|
+
dahili tarayıcı otomatik ve GÖRÜNÜR açılır */
|
|
4415
|
+
setBrowserOpen(true, true);
|
|
4416
|
+
const url = 'file:///' + entry.replace(/\\/g, '/');
|
|
4417
|
+
browser.view.webContents.loadURL(url).catch(() => {});
|
|
4418
|
+
browserEmit({ open: true, width: browser.width, url });
|
|
4419
|
+
return { ok: true, url };
|
|
4420
|
+
} catch (e) {
|
|
4421
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
4422
|
+
}
|
|
4423
|
+
});
|
|
4424
|
+
|
|
4113
4425
|
ipcMain.handle('custom:set', (_e, list) => {
|
|
4114
4426
|
settings.customProviders = Array.isArray(list) ? list : [];
|
|
4115
4427
|
saveSettings();
|
package/src/preload.js
CHANGED
|
@@ -64,6 +64,9 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
64
64
|
terminalToggle: () => ipcRenderer.invoke('terminal:toggle'),
|
|
65
65
|
terminalRun: (cmd, shell) => ipcRenderer.invoke('terminal:run', { cmd, shell }),
|
|
66
66
|
terminalStop: () => ipcRenderer.invoke('terminal:stop'),
|
|
67
|
+
beastcodeSend: (msg) => ipcRenderer.invoke('beastcode:send', { msg }),
|
|
68
|
+
beastcodeStop: () => ipcRenderer.invoke('beastcode:stop'),
|
|
69
|
+
beastcodeNew: () => ipcRenderer.invoke('beastcode:new'),
|
|
67
70
|
thinkSet: (v) => ipcRenderer.invoke('think:set', v),
|
|
68
71
|
exaGet: () => ipcRenderer.invoke('exa:get'),
|
|
69
72
|
exaSet: (key) => ipcRenderer.invoke('exa:set', key),
|
|
@@ -144,6 +147,13 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
144
147
|
storeLike: (id) => ipcRenderer.invoke('store:like', id),
|
|
145
148
|
storeRemove: (id) => ipcRenderer.invoke('store:remove', id),
|
|
146
149
|
storeExport: (id) => ipcRenderer.invoke('store:export', id),
|
|
150
|
+
ideTree: (rel) => ipcRenderer.invoke('ide:tree', rel),
|
|
151
|
+
ideSetRoot: () => ipcRenderer.invoke('ide:setroot'),
|
|
152
|
+
ideRead: (rel) => ipcRenderer.invoke('ide:read', rel),
|
|
153
|
+
ideWrite: (rel, content) => ipcRenderer.invoke('ide:write', { rel, content }),
|
|
154
|
+
idePreview: () => ipcRenderer.invoke('ide:preview'),
|
|
155
|
+
idePreviewFile: (rel) => ipcRenderer.invoke('ide:previewFile', rel),
|
|
156
|
+
ideDelete: (rel) => ipcRenderer.invoke('ide:delete', rel),
|
|
147
157
|
onWaEvent: (cb) => ipcRenderer.on('wa:event', (_e, ev) => cb(ev)),
|
|
148
158
|
onEvent: (cb) => ipcRenderer.on('agent:event', (_e, ev) => cb(ev)),
|
|
149
159
|
});
|
package/src/renderer/i18n.js
CHANGED
|
@@ -73,7 +73,8 @@
|
|
|
73
73
|
up_downloading: 'İndiriliyor…',
|
|
74
74
|
up_downloaded: 'İndirildi — kurulum için hazır',
|
|
75
75
|
up_npm_mode: 'npm kurulumu — güncellemeler otomatik kontrol edilir',
|
|
76
|
-
up_npm_note: 'Yeni sürüm algılanınca buton aktifleşir: uygulama kapanır, güncellenir ve kendiliğinden yeniden açılır. Terminal komutu: beast
|
|
76
|
+
up_npm_note: 'Yeni sürüm algılanınca buton aktifleşir: uygulama kapanır, güncellenir ve kendiliğinden yeniden açılır. Terminal komutu: beast update',
|
|
77
|
+
up_npm_started: 'Güncelleme başladı — uygulama kapanacak, en son sürüm kurulup kendiliğinden açılacak…',
|
|
77
78
|
up_dev_note: 'Geliştirme modu — güncelleme buradan yapılmaz; dağıtım npm ve GitHub Releases üzerinden. Kaynak kod için: git pull',
|
|
78
79
|
up_auto_check: 'Otomatik sürüm kontrolü (açılış + 6 saatte bir)',
|
|
79
80
|
up_auto_dl: 'Yeni sürümü otomatik indir (kapanışta kurulur)',
|
|
@@ -374,6 +375,7 @@
|
|
|
374
375
|
bot_tab_plugin: 'Plugin',
|
|
375
376
|
bot_tab_watcher: 'Watcher',
|
|
376
377
|
bot_tab_stats: 'İstatistik',
|
|
378
|
+
bot_tab_notes: 'Notlar',
|
|
377
379
|
bot_name: 'Bot adı',
|
|
378
380
|
bot_icon: 'İkon',
|
|
379
381
|
bot_prompt: 'System prompt (kişilik/görev)',
|
|
@@ -396,6 +398,7 @@
|
|
|
396
398
|
bot_see: 'Görebilir (diğer botlar)',
|
|
397
399
|
bot_no_other: 'Başka bot yok.',
|
|
398
400
|
bot_perm: 'Yetki seviyesi',
|
|
401
|
+
bot_perm_note: 'all tek başına tüm araçları açar (özel). Web / Okuma / Sohbet birden fazla seçilebilir — seçilenlerin araçları birleşir.',
|
|
399
402
|
bot_skills: 'Skill erişimi',
|
|
400
403
|
bot_browser: 'Tarayıcı ayarları',
|
|
401
404
|
bot_ext_browser: 'Dış tarayıcı yetkisi (kullanıcı isterse)',
|
|
@@ -451,6 +454,16 @@
|
|
|
451
454
|
p_zen_fail: 'Model kurulumu başarısız',
|
|
452
455
|
bot_num_ro_hint: 'Numara ekleme/kişi adı Ayarlar → Entegrasyonlar (WhatsApp izin listesi) üzerinden yapılır; burada yalnızca bağlı numaralar görünür.',
|
|
453
456
|
tipStore: 'Skills Store',
|
|
457
|
+
tipIde: 'IDE Modu — dosyalar + chat + preview',
|
|
458
|
+
ide_files: 'DOSYALAR',
|
|
459
|
+
ide_pick: 'Klasör seç',
|
|
460
|
+
ide_refresh: 'Yenile',
|
|
461
|
+
ide_preview: '▶ Preview',
|
|
462
|
+
ide_saved: 'Kaydedildi — preview\u2019ı yenile',
|
|
463
|
+
bc_ph: 'Beast Code mesajı yaz — Enter ile gönder…',
|
|
464
|
+
bc_new: 'Yeni oturum',
|
|
465
|
+
bc_clear: 'Çıktıyı temizle',
|
|
466
|
+
bc_stop: 'Çalışan mesajı durdur',
|
|
454
467
|
store_title: 'SKILLS STORE',
|
|
455
468
|
store_tab_trending: 'Trending',
|
|
456
469
|
store_tab_stars: 'Stars',
|
|
@@ -561,7 +574,8 @@
|
|
|
561
574
|
up_downloading: 'Downloading…',
|
|
562
575
|
up_downloaded: 'Downloaded — ready to install',
|
|
563
576
|
up_npm_mode: 'npm install — updates are checked automatically',
|
|
564
|
-
up_npm_note: 'The button activates when a new version is detected: the app closes, updates and relaunches by itself. Terminal command: beast
|
|
577
|
+
up_npm_note: 'The button activates when a new version is detected: the app closes, updates and relaunches by itself. Terminal command: beast update',
|
|
578
|
+
up_npm_started: 'Update started — the app will close, install the latest version and relaunch…',
|
|
565
579
|
up_dev_note: 'Development mode — updates are distributed via npm and GitHub Releases. For source code: git pull',
|
|
566
580
|
up_auto_check: 'Automatic version check (on startup + every 6 hours)',
|
|
567
581
|
up_auto_dl: 'Auto-download new versions (installed on quit)',
|
|
@@ -920,6 +934,7 @@
|
|
|
920
934
|
bot_tab_plugin: 'Plugin',
|
|
921
935
|
bot_tab_watcher: 'Watcher',
|
|
922
936
|
bot_tab_stats: 'Stats',
|
|
937
|
+
bot_tab_notes: 'Notes',
|
|
923
938
|
bot_name: 'Bot name',
|
|
924
939
|
bot_icon: 'Icon',
|
|
925
940
|
bot_prompt: 'System prompt (personality/task)',
|
|
@@ -942,6 +957,7 @@
|
|
|
942
957
|
bot_see: 'Can see (other bots)',
|
|
943
958
|
bot_no_other: 'No other bots.',
|
|
944
959
|
bot_perm: 'Permission level',
|
|
960
|
+
bot_perm_note: 'all alone grants every tool (exclusive). Web / Read / Chat can be multi-selected — their tool sets are combined.',
|
|
945
961
|
bot_skills: 'Skill access',
|
|
946
962
|
bot_browser: 'Browser settings',
|
|
947
963
|
bot_ext_browser: 'External browser permission (when user asks)',
|
|
@@ -997,6 +1013,16 @@
|
|
|
997
1013
|
p_zen_fail: 'Model setup failed',
|
|
998
1014
|
bot_num_ro_hint: 'Add numbers / person names via Settings → Integrations (WhatsApp allow list); only linked numbers are shown here.',
|
|
999
1015
|
tipStore: 'Skills Store',
|
|
1016
|
+
tipIde: 'IDE Mode — files + chat + preview',
|
|
1017
|
+
ide_files: 'FILES',
|
|
1018
|
+
ide_pick: 'Choose folder',
|
|
1019
|
+
ide_refresh: 'Refresh',
|
|
1020
|
+
ide_preview: '▶ Preview',
|
|
1021
|
+
ide_saved: 'Saved — refresh the preview',
|
|
1022
|
+
bc_ph: 'Type a message for Beast Code — press Enter to send…',
|
|
1023
|
+
bc_new: 'New session',
|
|
1024
|
+
bc_clear: 'Clear output',
|
|
1025
|
+
bc_stop: 'Stop running message',
|
|
1000
1026
|
store_title: 'SKILLS STORE',
|
|
1001
1027
|
store_tab_trending: 'Trending',
|
|
1002
1028
|
store_tab_stars: 'Stars',
|
package/src/renderer/index.html
CHANGED
|
@@ -23,11 +23,23 @@
|
|
|
23
23
|
</div>
|
|
24
24
|
<div id="botList"></div>
|
|
25
25
|
</div>
|
|
26
|
+
<aside id="filePanel">
|
|
27
|
+
<div id="filePanelHead">
|
|
28
|
+
<span id="filePanelTitle" data-i18n="ide_files">DOSYALAR</span>
|
|
29
|
+
<span class="file-spacer"></span>
|
|
30
|
+
<button id="filePick" title="Klasör seç" data-i18n-title="ide_pick"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z"/></svg></button>
|
|
31
|
+
<button id="fileRefresh" title="Yenile" data-i18n-title="ide_refresh">⟳</button>
|
|
32
|
+
<button id="filePreview" title="Preview" data-i18n="ide_preview">▶ Preview</button>
|
|
33
|
+
</div>
|
|
34
|
+
<div id="filePanelPath" title="">—</div>
|
|
35
|
+
<div id="fileTree"></div>
|
|
36
|
+
</aside>
|
|
26
37
|
<div id="sideFoot">
|
|
27
38
|
<button id="gearBtn" title="Ayarlar" data-i18n-title="tipGear">⚙︎</button>
|
|
28
39
|
<button id="themeBtn" title="Koyu tema">☾</button>
|
|
29
40
|
<button id="langBtn" title="Dil değiştir / Switch language" data-i18n-title="tipLang"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="9"/><path d="M3 12h18"/><path d="M12 3c2.6 2.6 2.6 15.4 0 18M12 3c-2.6 2.6-2.6 15.4 0 18"/></svg></button>
|
|
30
41
|
<button id="storeBtn" title="Skills Store" data-i18n-title="tipStore"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16l-1.3 12.1a2 2 0 0 1-2 1.9H7.3a2 2 0 0 1-2-1.9L4 7z"/><path d="M8 10V6a4 4 0 0 1 8 0v4"/></svg></button>
|
|
42
|
+
<button id="ideBtn" title="IDE Modu" data-i18n-title="tipIde"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 6l-5 6 5 6"/><path d="M16 6l5 6-5 6"/><path d="M13.5 4l-3 16"/></svg></button>
|
|
31
43
|
</div>
|
|
32
44
|
</aside>
|
|
33
45
|
|
|
@@ -65,7 +77,7 @@
|
|
|
65
77
|
<button id="termGBtn" class="dd-btn dd-icon-btn" title="Git Bash terminali" data-i18n-title="tipTermG">◈</button>
|
|
66
78
|
<button id="termCBtn" class="dd-btn dd-icon-btn" title="CMD terminali" data-i18n-title="tipTermC">❯︎</button>
|
|
67
79
|
<button id="browserBtn" class="dd-btn dd-icon-btn" title="Dahili tarayıcı" data-i18n-title="tipBrowser">⧉</button>
|
|
68
|
-
<button id="eyeBtn" class="dd-btn dd-icon-btn" title="Ajan tarayıcısı görünür/gizli — göz açıkken aramalar panelde izlenir, kapalıyken gizli çalışır" data-i18n-title="tipEye"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></svg></button>
|
|
80
|
+
<button id="eyeBtn" class="dd-btn dd-icon-btn" title="Ajan tarayıcısı görünür/gizli — göz açıkken aramalar panelde izlenir, kapalıyken gizli çalışır" data-i18n-title="tipEye"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/><line class="eye-slash" x1="4" y1="4" x2="20" y2="20"/></svg></button>
|
|
69
81
|
<div class="drag-spacer"></div>
|
|
70
82
|
</header>
|
|
71
83
|
|
|
@@ -90,6 +102,36 @@
|
|
|
90
102
|
<button id="sendBtn" title="Gönder" data-i18n-title="tipSend">➤︎</button>
|
|
91
103
|
</div>
|
|
92
104
|
</div>
|
|
105
|
+
|
|
106
|
+
<div id="ideRow">
|
|
107
|
+
<section id="codePane">
|
|
108
|
+
<div id="codeTabsBar">
|
|
109
|
+
<div id="codeTabs"></div>
|
|
110
|
+
<code id="codePath" title="">—</code>
|
|
111
|
+
<button id="codeSave" title="Kaydet (Ctrl+S)">Kaydet</button>
|
|
112
|
+
</div>
|
|
113
|
+
<textarea id="codeTa" spellcheck="false"></textarea>
|
|
114
|
+
</section>
|
|
115
|
+
|
|
116
|
+
<div id="ideSplit" title="Sürükleyerek boyutlandır"></div>
|
|
117
|
+
|
|
118
|
+
<div id="bcPanel">
|
|
119
|
+
<div id="bcHead">
|
|
120
|
+
<span id="bcTitle">BEAST CODE</span>
|
|
121
|
+
<span id="bcCwd" title=""></span>
|
|
122
|
+
<span class="file-spacer"></span>
|
|
123
|
+
<button id="bcNew" title="Yeni oturum" data-i18n-title="bc_new">+</button>
|
|
124
|
+
<button id="bcClear" title="Çıktıyı temizle" data-i18n-title="bc_clear">Temizle</button>
|
|
125
|
+
</div>
|
|
126
|
+
<div id="bcOut"></div>
|
|
127
|
+
<div id="bcTodoWrap" hidden></div>
|
|
128
|
+
<div id="bcInputRow">
|
|
129
|
+
<span id="bcPrompt">code></span>
|
|
130
|
+
<input id="bcInput" spellcheck="false" autocomplete="off" placeholder="Beast Code mesajı yaz — Enter ile gönder…" data-i18n-ph="bc_ph" />
|
|
131
|
+
<button id="bcStop" title="Çalışan mesajı durdur" hidden data-i18n-title="bc_stop">■︎</button>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
93
135
|
</main>
|
|
94
136
|
|
|
95
137
|
<aside id="rail">
|
|
@@ -198,6 +240,7 @@
|
|
|
198
240
|
<button class="btab" data-btab="log" data-i18n="bot_tab_log">Log</button>
|
|
199
241
|
<button class="btab" data-btab="watcher" data-i18n="bot_tab_watcher">Watcher</button>
|
|
200
242
|
<button class="btab" data-btab="stats" data-i18n="bot_tab_stats">İstatistik</button>
|
|
243
|
+
<button class="btab" data-btab="notes" data-i18n="bot_tab_notes">Notlar</button>
|
|
201
244
|
</nav>
|
|
202
245
|
<div id="botPane"></div>
|
|
203
246
|
</div>
|
|
@@ -279,6 +322,8 @@
|
|
|
279
322
|
|
|
280
323
|
<div id="toast"></div>
|
|
281
324
|
|
|
325
|
+
<div id="fileCtxMenu" hidden></div>
|
|
326
|
+
|
|
282
327
|
<input type="file" id="fileInput" multiple hidden
|
|
283
328
|
accept="image/*,.txt,.md,.json,.csv,.log,.js,.ts,.py,.ps1,.bat,.cmd,.html,.css,.yaml,.yml,.xml,.ini" />
|
|
284
329
|
|