beast-agent 2.14.0 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/engine.js +499 -154
- package/src/agent/financetools.js +307 -0
- package/src/agent/opencode/agents.js +161 -0
- package/src/agent/opencode/askperm.js +135 -0
- package/src/agent/opencode/index.js +51 -0
- package/src/agent/opencode/permission.js +108 -0
- package/src/agent/opencode/prompts/anthropic.txt +105 -0
- package/src/agent/opencode/prompts/beast.txt +147 -0
- package/src/agent/opencode/prompts/build-switch.txt +5 -0
- package/src/agent/opencode/prompts/codex.txt +79 -0
- package/src/agent/opencode/prompts/default.txt +95 -0
- package/src/agent/opencode/prompts/edit.txt +10 -0
- package/src/agent/opencode/prompts/explore.txt +18 -0
- package/src/agent/opencode/prompts/gemini.txt +155 -0
- package/src/agent/opencode/prompts/glob.txt +6 -0
- package/src/agent/opencode/prompts/gpt.txt +107 -0
- package/src/agent/opencode/prompts/grep.txt +8 -0
- package/src/agent/opencode/prompts/kimi.txt +95 -0
- package/src/agent/opencode/prompts/max-steps.txt +16 -0
- package/src/agent/opencode/prompts/meta.txt +65 -0
- package/src/agent/opencode/prompts/plan.txt +26 -0
- package/src/agent/opencode/prompts/read.txt +14 -0
- package/src/agent/opencode/prompts/skill.txt +5 -0
- package/src/agent/opencode/prompts/task.txt +19 -0
- package/src/agent/opencode/prompts/todowrite.txt +44 -0
- package/src/agent/opencode/prompts/trinity.txt +97 -0
- package/src/agent/opencode/prompts/webfetch.txt +13 -0
- package/src/agent/opencode/prompts/websearch.txt +14 -0
- package/src/agent/opencode/prompts/write.txt +8 -0
- package/src/agent/opencode/systemprompt.js +157 -0
- package/src/agent/opencode/toolmap.js +316 -0
- package/src/agent/scripts/mt5_bridge.py +381 -0
- package/src/agent/tools.js +10 -3
- package/src/main.js +503 -69
- package/src/mt5bridge.js +215 -0
- package/src/preload.js +13 -3
- package/src/renderer/index.html +67 -0
- package/src/renderer/renderer.js +552 -53
- package/src/renderer/style.css +328 -1
- package/src/agent/android.js +0 -265
package/src/main.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
3
|
const { app, BrowserWindow, WebContentsView, ipcMain, shell, dialog, Tray, Menu, nativeImage, desktopCapturer, session, net: electronNet, clipboard } = require('electron');
|
|
4
4
|
const path = require('path');
|
|
@@ -26,7 +26,6 @@ const watchers = require('./agent/watchers');
|
|
|
26
26
|
const usageMod = require('./agent/usage');
|
|
27
27
|
const bus = require('./agent/bus');
|
|
28
28
|
const computeruse = require('./agent/computeruse');
|
|
29
|
-
const android = require('./agent/android');
|
|
30
29
|
const log = require('./agent/logger');
|
|
31
30
|
const headroom = require('./agent/headroom');
|
|
32
31
|
const QRCode = require('qrcode'); /* Expo Go QR (bc-expurl) — whatsapp ile aynı paket */
|
|
@@ -1336,6 +1335,18 @@ function resolveApproval(id, ok, always) {
|
|
|
1336
1335
|
}
|
|
1337
1336
|
|
|
1338
1337
|
function resolveFirstApproval(ok, always) {
|
|
1338
|
+
/* opencode permission (BC): önce opencode izin istekleri yanıtlanır —
|
|
1339
|
+
cevap 'once' | 'always' | 'reject' üçlüsüdür (permission/reply portu) */
|
|
1340
|
+
if (engine && typeof engine.replyPermission === 'function') {
|
|
1341
|
+
try {
|
|
1342
|
+
const pend = engine._perm && engine._perm.list ? engine._perm.list() : [];
|
|
1343
|
+
if (pend.length) {
|
|
1344
|
+
const action = ok ? (always ? 'always' : 'once') : 'reject';
|
|
1345
|
+
const r = engine.replyPermission(pend[0].id, action);
|
|
1346
|
+
if (r && r.ok) return { ok: true, tool: pend[0].permission };
|
|
1347
|
+
}
|
|
1348
|
+
} catch {}
|
|
1349
|
+
}
|
|
1339
1350
|
const first = pendingApprovals.keys().next();
|
|
1340
1351
|
if (first.done) return { ok: false, error: 'bekleyen onay yok' };
|
|
1341
1352
|
return resolveApproval(first.value, ok, always);
|
|
@@ -1678,17 +1689,17 @@ async function tryWaSlash(jid, rawText, senderNum, payload0) {
|
|
|
1678
1689
|
`Tekrar kodlamak için: /beastcode`;
|
|
1679
1690
|
}
|
|
1680
1691
|
} else if (waBcMode.has(jid) && (cmd === 'plan' || cmd === 'build' || cmd === 'auto')) {
|
|
1681
|
-
/*
|
|
1692
|
+
/* opencode ajan değişimi (WA): /plan → plan ajanı (salt-okur),
|
|
1693
|
+
/build · /auto → build ajanı (varsayılan; auto opencode'da yoktur) */
|
|
1682
1694
|
const sid = waChats.get(jid);
|
|
1683
1695
|
const s = sid ? engine.cache.get(sid) : null;
|
|
1684
1696
|
if (s && s.bcCode) {
|
|
1685
|
-
s.
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
? '*🔍 PLAN MODU* — dosyaları inceler, kod YAZMAZ; adım adım uygulama planı verir.'
|
|
1697
|
+
const agent = engine.setBcAgent(s.id, cmd === 'plan' ? 'plan' : 'build');
|
|
1698
|
+
out = agent === 'plan'
|
|
1699
|
+
? '*🔍 PLAN AJANI* (opencode plan) — salt-okur: dosyaları okur/inceler, kod YAZMAZ; adım adım uygulama planı verir. Uygulamak için /build yaz.'
|
|
1689
1700
|
: cmd === 'build'
|
|
1690
|
-
? '*🛠 BUILD
|
|
1691
|
-
: '*⚡
|
|
1701
|
+
? '*🛠 BUILD AJANI* (opencode build) — bağlamı inceler, todowrite ile planlar, uygular, doğrular.'
|
|
1702
|
+
: '*⚡ BUILD AJANI* (opencode build) — kısa plan + hemen uygulama + doğrulama.';
|
|
1692
1703
|
} else {
|
|
1693
1704
|
out = 'Bu komut yalnız BeastCode modunda çalışır — önce /beastcode yaz.';
|
|
1694
1705
|
}
|
|
@@ -2442,14 +2453,12 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
|
2442
2453
|
}
|
|
2443
2454
|
engine.setSessionModel(sid, botCfg && !botCfg.admin ? (botCfg.model || null) : null);
|
|
2444
2455
|
/* BEASTCODE MODU: sohbet uzaktan kodlama modundaysa oturumu Beast Code
|
|
2445
|
-
olarak tazele — restart sonrası bcCode/workspace bayrakları dosyadan
|
|
2456
|
+
olarak tazele — restart sonrası bcCode/bcAgent/workspace bayrakları dosyadan
|
|
2446
2457
|
yüklenmediği için masaüstü bcFlush disipliniyle her mesajda bindirilir */
|
|
2447
2458
|
if (waBcMode.has(jid)) {
|
|
2448
2459
|
const bs = engine.cache.get(sid);
|
|
2449
2460
|
if (bs) {
|
|
2450
|
-
bs
|
|
2451
|
-
bs.workspace = waBcWorkspace();
|
|
2452
|
-
engine.cache.set(sid, bs);
|
|
2461
|
+
bcBindOc(bs, waBcWorkspace());
|
|
2453
2462
|
}
|
|
2454
2463
|
}
|
|
2455
2464
|
waLog(`perm=${fmtPerm(perm)} bot=${botId} sid=${sid}${waBcMode.has(jid) ? ' beastcode=1' : ''}`);
|
|
@@ -3101,12 +3110,28 @@ function reloadBackend() {
|
|
|
3101
3110
|
},
|
|
3102
3111
|
emit: (ev) => {
|
|
3103
3112
|
if (win && !win.isDestroyed()) win.webContents.send('agent:event', ev);
|
|
3113
|
+
/* opencode permission (BC): ask kanalı UI'a zaten yukarıda düştü — WA
|
|
3114
|
+
sahibine de tek satır bildir gitsin (/approve · /approve always · /deny) */
|
|
3115
|
+
if (ev.type === 'permission.asked' && ev.request && wa && wa.connected) {
|
|
3116
|
+
try {
|
|
3117
|
+
const own = waOwnerNum();
|
|
3118
|
+
if (own) {
|
|
3119
|
+
const pats = Array.isArray(ev.request.patterns) ? ev.request.patterns.join(', ') : '*';
|
|
3120
|
+
sendWaSafe(
|
|
3121
|
+
own + '@s.whatsapp.net',
|
|
3122
|
+
'\u26A0\uFE0F *\u0130zin bekleniyor* \u2014 `' + String(ev.request.permission) + '`\n' +
|
|
3123
|
+
String(pats).slice(0, 160) + '\n\nOnayla: `/approve`\nBu desen i\u00e7in bir daha sorma: `/approve always`\nReddet: `/deny`'
|
|
3124
|
+
).catch(() => {});
|
|
3125
|
+
}
|
|
3126
|
+
} catch {}
|
|
3127
|
+
}
|
|
3104
3128
|
try { empatiRememberFromEvent(ev); } catch {} /* empati hafızası: sohbet → kayıt + ilgi öğrenme */
|
|
3105
3129
|
empatiFlushInjectsOnDone(ev); /* tur ortasında kuyruğa düşen proaktif bildirimleri iş bitince geçmişe bas */
|
|
3106
3130
|
flushDesktopOnDone(ev); /* biriken desktop mesajlarını sıraya bas */
|
|
3107
3131
|
bcFlushOnDone(ev); /* Beast Code kuyruğunu iş bitiminde boşalt */
|
|
3108
3132
|
stFlushOnDone(ev); /* Beast Studio kuyruğunu iş bitiminde boşalt */
|
|
3109
3133
|
sbFlushOnDone(ev); /* Sandbox kuyruğunu iş bitiminde boşalt */
|
|
3134
|
+
finFlushOnDone(ev); /* Finance trader döngüsü: iş bitince sıradaki turu planla */
|
|
3110
3135
|
/* BC canlı önizleme: ajan bir dev server başlattıysa adresi yakala —
|
|
3111
3136
|
preview butonu ve otomatik açılış DAİMA bu sunucuyu öncelikli kullanır */
|
|
3112
3137
|
if (ev.type === 'bc-preview' && /^https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d{2,5})?/i.test(String(ev.url || ''))) {
|
|
@@ -4904,6 +4929,13 @@ ipcMain.handle('sessions:create', () => {
|
|
|
4904
4929
|
engine.setSessionTools(v.id, null);
|
|
4905
4930
|
}
|
|
4906
4931
|
engine.setSessionModel(v.id, b && !b.admin ? (b.model || null) : null);
|
|
4932
|
+
/* Beast Finance modu açıkken açılan sohbet finance izolasyonuna girer */
|
|
4933
|
+
if (financeState.mode) {
|
|
4934
|
+
try {
|
|
4935
|
+
engine.markFinance(v.id, false);
|
|
4936
|
+
v.finance = true;
|
|
4937
|
+
} catch {}
|
|
4938
|
+
}
|
|
4907
4939
|
return v;
|
|
4908
4940
|
});
|
|
4909
4941
|
ipcMain.handle('sessions:open', (_e, id) => engine.openSession(id));
|
|
@@ -4932,6 +4964,25 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
|
|
|
4932
4964
|
}
|
|
4933
4965
|
}
|
|
4934
4966
|
} catch {}
|
|
4967
|
+
/* BEAST FINANCE: finance modu açıkken bu sohbet finance listesine girer ve
|
|
4968
|
+
mt5_* araçlarını görür (botlar hariç); mod kapalıysa etiket kaldırılır */
|
|
4969
|
+
try {
|
|
4970
|
+
if (sessionId) {
|
|
4971
|
+
const fsid = String(sessionId);
|
|
4972
|
+
let fsess = engine.cache.get(fsid);
|
|
4973
|
+
if (!fsess) { try { fsess = engine._load(fsid); } catch {} }
|
|
4974
|
+
if (fsess && !fsess.botId && !fsess.bgJob) {
|
|
4975
|
+
if (financeState.mode) {
|
|
4976
|
+
if (!fsess.finance) engine.markFinance(fsid, false);
|
|
4977
|
+
finApplyTraderFields(fsess);
|
|
4978
|
+
fsess.financeTrader = false; /* chat copilot'ı — trader değil */
|
|
4979
|
+
engine.cache.set(fsid, fsess);
|
|
4980
|
+
} else if (fsess.finance) {
|
|
4981
|
+
engine.unmarkFinance(fsid); /* normal modda yazınca normal sohbete döner */
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
}
|
|
4985
|
+
} catch {}
|
|
4935
4986
|
if (t === '/stop' || t === '/start') {
|
|
4936
4987
|
handleGlobalStopStart(sessionId, t);
|
|
4937
4988
|
return true;
|
|
@@ -5979,7 +6030,21 @@ ipcMain.handle('sec:set', (_e, cfg) => {
|
|
|
5979
6030
|
log.info('sec', `güvenlik: onay kapısı ${settings.security.approvals ? 'AÇIK' : 'KAPALI (her şey serbest)'}`);
|
|
5980
6031
|
return { approvals: settings.security.approvals, alwaysAllow: settings.security.alwaysAllow };
|
|
5981
6032
|
});
|
|
5982
|
-
ipcMain.handle('approval:respond', (_e, { id, ok, always }) => resolveApproval(id, ok, always));
|
|
6033
|
+
ipcMain.handle('approval:respond', (_e, { id, ok, always }) => resolveApproval(id, ok, always));
|
|
6034
|
+
/* opencode permission reply (BC): UI kartı üçlü cevap verir —
|
|
6035
|
+
'once' | 'always' | 'reject' (+ opsiyonel geri bildirim) */
|
|
6036
|
+
ipcMain.handle('permission:reply', (_e, payload) => {
|
|
6037
|
+
try {
|
|
6038
|
+
if (!engine || typeof engine.replyPermission !== 'function') return { ok: false, error: 'ajan hazır değil' };
|
|
6039
|
+
return engine.replyPermission(
|
|
6040
|
+
String((payload && payload.requestId) || ''),
|
|
6041
|
+
String((payload && payload.action) || 'once'),
|
|
6042
|
+
String((payload && payload.message) || '')
|
|
6043
|
+
);
|
|
6044
|
+
} catch (e) {
|
|
6045
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
6046
|
+
}
|
|
6047
|
+
});
|
|
5983
6048
|
|
|
5984
6049
|
/* ---------------- #Update IPC ---------------- */
|
|
5985
6050
|
ipcMain.handle('update:status', async () => {
|
|
@@ -6310,29 +6375,6 @@ ipcMain.handle('install:status', async () => {
|
|
|
6310
6375
|
});
|
|
6311
6376
|
}
|
|
6312
6377
|
|
|
6313
|
-
/* 10) Android Emülatörü — opsiyonel; varsa Beast Code mobil uygulamaları emülatörde açar */
|
|
6314
|
-
{
|
|
6315
|
-
const st = android.probe();
|
|
6316
|
-
let state = 'optional';
|
|
6317
|
-
if (st.sdk) state = 'ok';
|
|
6318
|
-
else if (st.installing) state = 'loading';
|
|
6319
|
-
if (st.failed) state = 'failed';
|
|
6320
|
-
rows.push({
|
|
6321
|
-
id: 'android',
|
|
6322
|
-
name: 'Android Emülatörü — mobil uygulamalar emülatörde çalışır',
|
|
6323
|
-
state,
|
|
6324
|
-
canInstall: true,
|
|
6325
|
-
detail: st.installing
|
|
6326
|
-
? (st.stage || 'arka planda kuruluyor — JDK + SDK + sistem imajı (~1.5GB, internete göre 5-20 dk)')
|
|
6327
|
-
: st.failed
|
|
6328
|
-
? (st.error || 'kurulum başarısız')
|
|
6329
|
-
: st.sdk
|
|
6330
|
-
? 'hazır — SDK: ' + st.sdkPath
|
|
6331
|
-
: 'kurulu değil — KUR ile arka planda kurulur (istekge bağlı)',
|
|
6332
|
-
...(st.installing ? pctFields('android') : {}),
|
|
6333
|
-
});
|
|
6334
|
-
}
|
|
6335
|
-
|
|
6336
6378
|
return rows;
|
|
6337
6379
|
});
|
|
6338
6380
|
|
|
@@ -6349,10 +6391,7 @@ ipcMain.handle('headroom:toggle', async (_e, on) => {
|
|
|
6349
6391
|
ipcMain.handle('headroom:status', () => headroom.probe());
|
|
6350
6392
|
ipcMain.handle('headroom:stats', () => headroom.stats());
|
|
6351
6393
|
|
|
6352
|
-
/* ANDROID EMÜLATÖR
|
|
6353
|
-
ipcMain.handle('android:status', async () => ({ ok: true, ...(await android.probe()), avds: await android.listAvds() }));
|
|
6354
|
-
ipcMain.handle('android:install', () => android.autoInstall());
|
|
6355
|
-
ipcMain.handle('android:startAvd', async (_e, name) => android.startAvd(name));
|
|
6394
|
+
/* ANDROID EMÜLATÖR: kaldırıldı — mobil önizleme tunnel (telefon QR) yoluyla yapılır */
|
|
6356
6395
|
|
|
6357
6396
|
/* embedding modelini şimdi indir (mem0 arama yolu ısıtılır) */
|
|
6358
6397
|
ipcMain.handle('embed:prefetch', () => {
|
|
@@ -7419,6 +7458,23 @@ ipcMain.handle('terminal:stop', () => {
|
|
|
7419
7458
|
panele orada akıtılır. */
|
|
7420
7459
|
const bcSessions = new Map(); /* klasör yolu → sessionId */
|
|
7421
7460
|
|
|
7461
|
+
/* opencode bayrak bindirme (bcCode disiplini): bcCode/bcAgent runtime
|
|
7462
|
+
bayrakları restart sonrası jsonl'den gelmez — her mesajda yeniden bindirilir.
|
|
7463
|
+
Ajan: 'plan' | 'build' | 'sandbox' (bcMode:'plan' oturumları geriye uyumlu
|
|
7464
|
+
plan'a düşer; Sandbox oturumları sandbox ajanına sabitlenir) */
|
|
7465
|
+
function bcBindOc(s, folder, agent) {
|
|
7466
|
+
s.workspace = folder;
|
|
7467
|
+
s.bcCode = true;
|
|
7468
|
+
const known = s.bcAgent === 'plan' || s.bcAgent === 'build' || s.bcAgent === 'sandbox';
|
|
7469
|
+
if (agent) {
|
|
7470
|
+
s.bcAgent = agent;
|
|
7471
|
+
} else if (!known) {
|
|
7472
|
+
s.bcAgent = String(s.bcMode || '').toLowerCase() === 'plan' ? 'plan' : 'build';
|
|
7473
|
+
}
|
|
7474
|
+
engine.cache.set(s.id, s);
|
|
7475
|
+
return s;
|
|
7476
|
+
}
|
|
7477
|
+
|
|
7422
7478
|
function bcGetSession(folder) {
|
|
7423
7479
|
let sid = bcSessions.get(folder);
|
|
7424
7480
|
if (sid) {
|
|
@@ -7431,7 +7487,8 @@ function bcGetSession(folder) {
|
|
|
7431
7487
|
const s = engine._load(engine.createSession().id);
|
|
7432
7488
|
s.messages = s.messages || [];
|
|
7433
7489
|
s.bgTitle = 'Beast Code'; /* _view.isBg → sohbet geçmişi listesinde gizli */
|
|
7434
|
-
s.bcCode = true; /*
|
|
7490
|
+
s.bcCode = true; /* opencode ajan sistemi: build/plan ajanları engine'de koşar */
|
|
7491
|
+
s.bcAgent = 'build'; /* opencode varsayılan ajanı (agent.ts:141) */
|
|
7435
7492
|
try {
|
|
7436
7493
|
fs.appendFileSync(
|
|
7437
7494
|
engine._file(s.id),
|
|
@@ -7500,9 +7557,7 @@ function bcFlush(folder) {
|
|
|
7500
7557
|
clearTimeout(q.timer);
|
|
7501
7558
|
return;
|
|
7502
7559
|
}
|
|
7503
|
-
s
|
|
7504
|
-
s.bcCode = true;
|
|
7505
|
-
engine.cache.set(s.id, s);
|
|
7560
|
+
bcBindOc(s, folder);
|
|
7506
7561
|
const payload = mergedAtts ? { text: merged, attachments: mergedAtts } : merged;
|
|
7507
7562
|
if (engine.send(s.id, payload, { userAction: true })) {
|
|
7508
7563
|
bcQueue.delete(folder);
|
|
@@ -7543,25 +7598,21 @@ ipcMain.handle('beastcode:send', async (_e, payload) => {
|
|
|
7543
7598
|
|
|
7544
7599
|
if (!engine.publicState().hasModel) return { ok: false, error: 'model yok — Ayarlar → Provider sekmesinden ekle' };
|
|
7545
7600
|
/* oturumu HEMEN aç: cevap daima gerçek sessionId taşır — renderer paneli bu id ile
|
|
7546
|
-
|
|
7547
|
-
const s = bcGetSession(ws);
|
|
7548
|
-
s.workspace = ws; /* soldaki klasörde çalış */
|
|
7549
|
-
s.bcCode = true; /* todo disiplini + iş sonu hızlı kapanış (engine) */
|
|
7550
|
-
engine.cache.set(s.id, s);
|
|
7601
|
+
eşler; boşta/kuyrukta olsa bile id yoksa panel olayları eşleyemez ve ölü kalır */
|
|
7602
|
+
const s = bcBindOc(bcGetSession(ws), ws);
|
|
7551
7603
|
const busy = engine.isBusy(s.id);
|
|
7552
7604
|
if (modeM) {
|
|
7553
|
-
/*
|
|
7554
|
-
|
|
7605
|
+
/* opencode ajan değişimi (agent.ts: plan/build primary ajanlar) — anında
|
|
7606
|
+
uygulanır, meşgulken de (yalnız ajan bayrağı; koşan tur çakışmaz) */
|
|
7607
|
+
const agent = engine.setBcAgent(s.id, modeM[1].toLowerCase() === 'plan' ? 'plan' : 'build');
|
|
7555
7608
|
if (win && !win.isDestroyed()) {
|
|
7556
|
-
const body =
|
|
7557
|
-
? 'PLAN
|
|
7558
|
-
:
|
|
7559
|
-
|
|
7560
|
-
: 'OTOMATİK MOD — önce kısa plan, sonra uygulama + doğrulama (OpenCode disiplini).';
|
|
7561
|
-
win.webContents.send('agent:event', { sessionId: s.id, type: 'bc-mode', mode: s.bcMode, body });
|
|
7609
|
+
const body = agent === 'plan'
|
|
7610
|
+
? 'PLAN AJANI (opencode plan) — salt-okur: dosyaları okur/inceler, KOD YAZMAZ; adım adım uygulama planı verir. Uygulamak için /build.'
|
|
7611
|
+
: 'BUILD AJANI (opencode build) — bağlamı inceler, todowrite ile planlar, uygular, doğrular.';
|
|
7612
|
+
win.webContents.send('agent:event', { sessionId: s.id, type: 'bc-agent', agent, body });
|
|
7562
7613
|
}
|
|
7563
|
-
return { ok: true, sessionId: s.id, mode:
|
|
7564
|
-
}
|
|
7614
|
+
return { ok: true, sessionId: s.id, agent, mode: agent };
|
|
7615
|
+
}
|
|
7565
7616
|
if (busy) {
|
|
7566
7617
|
/* OPENCODE STEER: koşan tur varken mesaj kuyrukta BEKLEMEZ — engine.send
|
|
7567
7618
|
konuşmaya ekler, koşan tur sonraki istekte görür (ana chat ile aynı) */
|
|
@@ -7655,8 +7706,7 @@ ipcMain.handle('bc:open', async (_e, payload) => {
|
|
|
7655
7706
|
if (s.bgTitle && s.bgTitle !== 'Beast Code') return { ok: false, error: 'bu oturum Beast Code oturumu değil' };
|
|
7656
7707
|
/* mevcut klasöre bağlan: sonraki mesajlar BU oturumda sürer */
|
|
7657
7708
|
const ws = ideRoot();
|
|
7658
|
-
s
|
|
7659
|
-
s.bcCode = true;
|
|
7709
|
+
bcBindOc(s, ws);
|
|
7660
7710
|
bcMarkWs(s, ws);
|
|
7661
7711
|
engine.cache.set(s.id, s);
|
|
7662
7712
|
bcSessions.set(ws, s.id);
|
|
@@ -7671,7 +7721,8 @@ ipcMain.handle('bc:open', async (_e, payload) => {
|
|
|
7671
7721
|
ok: true,
|
|
7672
7722
|
sessionId: s.id,
|
|
7673
7723
|
busy: !!engine.isBusy(s.id),
|
|
7674
|
-
|
|
7724
|
+
agent: s.bcAgent || 'build',
|
|
7725
|
+
mode: s.bcAgent || 'build',
|
|
7675
7726
|
messages: msgs.slice(-200),
|
|
7676
7727
|
};
|
|
7677
7728
|
});
|
|
@@ -7987,6 +8038,388 @@ function studioWatchStop() {
|
|
|
7987
8038
|
if (studioWatchTimer) { clearTimeout(studioWatchTimer); studioWatchTimer = null; }
|
|
7988
8039
|
}
|
|
7989
8040
|
|
|
8041
|
+
/* ---------------- Beast Finance (MT5) ----------------
|
|
8042
|
+
Studio/Sandbox ile AYRI dünya: solda sohbet geçmişi + izleyiciler AYNI kalır,
|
|
8043
|
+
ortada chat, sağda MT5 işlem paneli. İki ajan: (1) chat copilot'ı — aktif
|
|
8044
|
+
sohbet finance bayrağıyla mt5_* araçlarını görür; (2) TRADER — gizli engine
|
|
8045
|
+
oturumu, tur tur piyasa tarayıp (farklı API/model seçilebilir) işlem kovalar. */
|
|
8046
|
+
const mt5bridge = require('./mt5bridge');
|
|
8047
|
+
const financetools = require('./agent/financetools');
|
|
8048
|
+
|
|
8049
|
+
const financeState = {
|
|
8050
|
+
mode: false, /* chat oturumları finance bayrağı alıyor mu */
|
|
8051
|
+
traderSid: null,
|
|
8052
|
+
traderOn: false,
|
|
8053
|
+
traderTimer: null,
|
|
8054
|
+
traderRounds: 0,
|
|
8055
|
+
lastRoundAt: 0,
|
|
8056
|
+
installing: false,
|
|
8057
|
+
installTried: false,
|
|
8058
|
+
};
|
|
8059
|
+
|
|
8060
|
+
function financeDir() {
|
|
8061
|
+
const d = path.join(APP_DIR, 'finance');
|
|
8062
|
+
try { fs.mkdirSync(d, { recursive: true }); } catch {}
|
|
8063
|
+
return d;
|
|
8064
|
+
}
|
|
8065
|
+
|
|
8066
|
+
function finCfg() {
|
|
8067
|
+
if (!settings.finance || typeof settings.finance !== 'object') settings.finance = {};
|
|
8068
|
+
const f = settings.finance;
|
|
8069
|
+
if (!Array.isArray(f.symbols) || !f.symbols.length) f.symbols = ['EURUSD', 'XAUUSD', 'GBPUSD', 'BTCUSD'];
|
|
8070
|
+
if (!Number(f.intervalSec)) f.intervalSec = 120;
|
|
8071
|
+
if (!Number(f.maxLot)) f.maxLot = 0.1;
|
|
8072
|
+
if (f.maxPositions == null) f.maxPositions = 3;
|
|
8073
|
+
if (typeof f.allowTrading !== 'boolean') f.allowTrading = false;
|
|
8074
|
+
return f;
|
|
8075
|
+
}
|
|
8076
|
+
|
|
8077
|
+
function financeLog(line) {
|
|
8078
|
+
try {
|
|
8079
|
+
fs.appendFileSync(path.join(financeDir(), 'finance.log'), `[${new Date().toISOString()}] ${line}\n`);
|
|
8080
|
+
} catch {}
|
|
8081
|
+
finPush('log', { line });
|
|
8082
|
+
}
|
|
8083
|
+
|
|
8084
|
+
function finPush(fn, data) {
|
|
8085
|
+
try {
|
|
8086
|
+
if (win && !win.isDestroyed()) win.webContents.send('agent:event', { type: 'finance', fn, ...data });
|
|
8087
|
+
} catch {}
|
|
8088
|
+
}
|
|
8089
|
+
|
|
8090
|
+
/* financetools ayar + bildirim kancası */
|
|
8091
|
+
try {
|
|
8092
|
+
financetools.setConfig(() => finCfg());
|
|
8093
|
+
financetools.setNotify((entry) => {
|
|
8094
|
+
const k = String((entry && entry.kind) || '');
|
|
8095
|
+
const d = entry && entry.data ? entry.data : {};
|
|
8096
|
+
let line = '';
|
|
8097
|
+
if (k === 'trade') line = `İŞLEM AÇILDI: ${d.side} ${d.volume} ${d.symbol}`;
|
|
8098
|
+
else if (k === 'close') line = `POZİSYON KAPANDI: ticket ${d.ticket}${d.volume ? ' (' + d.volume + ' lot)' : ''}`;
|
|
8099
|
+
else if (k === 'modify') line = `SL/TP GÜNCELLENDİ: ticket ${d.ticket} (SL ${d.sl || 0} / TP ${d.tp || 0})`;
|
|
8100
|
+
else if (k === 'pending') line = `BEKLEYEN EMİR: ${d.type} ${d.volume} ${d.symbol}`;
|
|
8101
|
+
else if (k === 'cancel') line = `EMİR İPTAL: ticket ${d.ticket}`;
|
|
8102
|
+
if (line) {
|
|
8103
|
+
financeLog('[ajan] ' + line);
|
|
8104
|
+
finPush('trade', { line });
|
|
8105
|
+
}
|
|
8106
|
+
});
|
|
8107
|
+
} catch {}
|
|
8108
|
+
|
|
8109
|
+
function financeEnsureBridge() {
|
|
8110
|
+
const f = finCfg();
|
|
8111
|
+
const candidates = [String(f.pythonPath || '').trim(), 'python', 'py -3'].filter(Boolean);
|
|
8112
|
+
/* Beast gömülü Python runtime kuruluysa en başa ekle (makinede Python olmasa da köprü çalışır) */
|
|
8113
|
+
try {
|
|
8114
|
+
const t = require('./agent/tools');
|
|
8115
|
+
const emb = t.embeddedPythonExe();
|
|
8116
|
+
if (emb && fs.existsSync(emb) && !candidates.includes(emb)) candidates.unshift(emb);
|
|
8117
|
+
} catch {}
|
|
8118
|
+
mt5bridge.start({
|
|
8119
|
+
candidates,
|
|
8120
|
+
terminal: String(f.terminalPath || '').trim(),
|
|
8121
|
+
});
|
|
8122
|
+
return mt5bridge.status();
|
|
8123
|
+
}
|
|
8124
|
+
|
|
8125
|
+
/* MetaTrader5 paketi yoksa bir kez pip ile kurmayı dene */
|
|
8126
|
+
function financeTryInstall() {
|
|
8127
|
+
if (financeState.installing || (financeState.installTried && finCfg().autoInstall === false)) return;
|
|
8128
|
+
financeState.installing = true;
|
|
8129
|
+
financeState.installTried = true;
|
|
8130
|
+
financeLog('[MT5] MetaTrader5 python paketi kuruluyor…');
|
|
8131
|
+
const py = mt5bridge.status().python || 'python';
|
|
8132
|
+
/* 'py -3' gibi çok kelimeli kısa komutlar bölünür; tam yol tek exe'dir */
|
|
8133
|
+
const parts = /[\\/]/.test(py) ? [py] : py.split(/\s+/);
|
|
8134
|
+
const p = spawn(parts[0], [...parts.slice(1), '-m', 'pip', 'install', '--user', 'MetaTrader5'], { windowsHide: true });
|
|
8135
|
+
let tail = '';
|
|
8136
|
+
p.stdout.on('data', (c) => { tail = (tail + c).slice(-800); });
|
|
8137
|
+
p.stderr.on('data', (c) => { tail = (tail + c).slice(-800); });
|
|
8138
|
+
p.on('error', (e) => {
|
|
8139
|
+
financeState.installing = false;
|
|
8140
|
+
financeLog('[MT5] pip başlatılamadı: ' + String((e && e.message) || e));
|
|
8141
|
+
});
|
|
8142
|
+
p.on('exit', (code) => {
|
|
8143
|
+
financeState.installing = false;
|
|
8144
|
+
if (code === 0) {
|
|
8145
|
+
financeLog('[MT5] MetaTrader5 paketi kuruldu — köprü yeniden başlatılıyor');
|
|
8146
|
+
mt5bridge.stop();
|
|
8147
|
+
financeEnsureBridge();
|
|
8148
|
+
} else {
|
|
8149
|
+
financeLog('[MT5] kurulum başarısız (kod ' + code + ') ' + tail.slice(-300));
|
|
8150
|
+
}
|
|
8151
|
+
finPush('install', { installing: false, code });
|
|
8152
|
+
});
|
|
8153
|
+
}
|
|
8154
|
+
|
|
8155
|
+
mt5bridge.on('bridge', (m) => {
|
|
8156
|
+
if (m && m.connected) {
|
|
8157
|
+
financeLog('[MT5] terminal bağlı: ' + ((m.terminal && (m.terminal.name + ' @ ' + m.terminal.company)) || 'MT5'));
|
|
8158
|
+
if (m.account) financeLog(`[MT5] hesap ${m.account.login} · bakiye ${m.account.balance} ${m.account.currency}`);
|
|
8159
|
+
} else if (m && m.error) {
|
|
8160
|
+
financeLog('[MT5] bağlantı yok — ' + m.error);
|
|
8161
|
+
if (/paket/i.test(m.error)) financeTryInstall();
|
|
8162
|
+
}
|
|
8163
|
+
finPush('bridge', { connected: !!(m && m.connected), error: (m && m.error) || '' });
|
|
8164
|
+
});
|
|
8165
|
+
mt5bridge.on('log', (m) => {
|
|
8166
|
+
const line = String((m && m.line) || '');
|
|
8167
|
+
if (!line) return;
|
|
8168
|
+
financeLog('[mt5] ' + line.slice(0, 300));
|
|
8169
|
+
});
|
|
8170
|
+
|
|
8171
|
+
/* ---------- TRADER ajanı (gizli engine oturumu + tur döngüsü) ---------- */
|
|
8172
|
+
|
|
8173
|
+
function finTraderSession() {
|
|
8174
|
+
let sid = financeState.traderSid;
|
|
8175
|
+
if (sid) {
|
|
8176
|
+
try {
|
|
8177
|
+
const s = engine.cache.get(sid);
|
|
8178
|
+
if (s) return s;
|
|
8179
|
+
} catch {}
|
|
8180
|
+
financeState.traderSid = null;
|
|
8181
|
+
}
|
|
8182
|
+
const s = engine._load(engine.createSession().id);
|
|
8183
|
+
s.messages = s.messages || [];
|
|
8184
|
+
s.bgTitle = 'Beast Finance · Trader'; /* _view.isBg → sohbet geçmişinde gizli */
|
|
8185
|
+
try {
|
|
8186
|
+
fs.appendFileSync(
|
|
8187
|
+
engine._file(s.id),
|
|
8188
|
+
JSON.stringify({ t: 'meta2', bgOf: '', title: 'Beast Finance · Trader', at: new Date().toISOString() }) + '\n'
|
|
8189
|
+
);
|
|
8190
|
+
} catch {}
|
|
8191
|
+
engine.cache.set(s.id, s);
|
|
8192
|
+
engine.markFinance(s.id, true); /* kalıcı finance etiketi (trader rolüyle) */
|
|
8193
|
+
s.workspace = financeDir(); /* trader kendi klasöründe çalışır */
|
|
8194
|
+
financeState.traderSid = s.id;
|
|
8195
|
+
return s;
|
|
8196
|
+
}
|
|
8197
|
+
|
|
8198
|
+
function finApplyTraderFields(s) {
|
|
8199
|
+
const f = finCfg();
|
|
8200
|
+
s.finance = true;
|
|
8201
|
+
s.financeTrader = true;
|
|
8202
|
+
s.financeAuto = f.allowTrading === true;
|
|
8203
|
+
s.financeSymbols = f.symbols;
|
|
8204
|
+
s.financeStrategy = String(f.strategy || '');
|
|
8205
|
+
s.financeLimits = { maxLot: f.maxLot, maxPositions: f.maxPositions };
|
|
8206
|
+
engine.cache.set(String(s.id), s);
|
|
8207
|
+
}
|
|
8208
|
+
|
|
8209
|
+
function finTraderBrief() {
|
|
8210
|
+
const f = finCfg();
|
|
8211
|
+
return [
|
|
8212
|
+
'Beast Finance TRADER başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.',
|
|
8213
|
+
`İzleme listesi: ${(f.symbols || []).join(', ')}`,
|
|
8214
|
+
`Tur aralığı: ${f.intervalSec} sn · Max lot: ${f.maxLot} · Max eşzamanlı pozisyon: ${f.maxPositions}`,
|
|
8215
|
+
f.allowTrading
|
|
8216
|
+
? 'Otomatik işlem AÇIK: mt5_trade/mt5_close/mt5_modify/mt5_pending kullanabilirsin (limitler sistemce zorlanır).'
|
|
8217
|
+
: 'Otomatik işlem KAPALI: SADECE analiz + net işlem önerileri yaz (sembol, yön, giriş, SL, TP, sebep); işlem açma.',
|
|
8218
|
+
f.strategy ? `Sahibinin strateji notu: ${f.strategy}` : 'Strateji notu yok: trend + destek/direnç + momentum ile temel okuma yap.',
|
|
8219
|
+
'Bu turda: mt5_status → hesap/pozisyon/fiyat verisi → değerlendirme → kararlar (veya BEKLE: sebep) → kısa rapor.',
|
|
8220
|
+
].join('\n');
|
|
8221
|
+
}
|
|
8222
|
+
|
|
8223
|
+
function finTraderRound() {
|
|
8224
|
+
if (!financeState.traderOn) return;
|
|
8225
|
+
if (!engine) return;
|
|
8226
|
+
const s = finTraderSession();
|
|
8227
|
+
finApplyTraderFields(s);
|
|
8228
|
+
if (engine.isBusy(s.id)) {
|
|
8229
|
+
/* hâlâ çalışıyor — done eventinde tekrar planlanır */
|
|
8230
|
+
return;
|
|
8231
|
+
}
|
|
8232
|
+
financeState.traderRounds += 1;
|
|
8233
|
+
const f = finCfg();
|
|
8234
|
+
const auto = f.allowTrading
|
|
8235
|
+
? 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.'
|
|
8236
|
+
: 'Otomatik işlem KAPALI — sadece analiz + öneri.';
|
|
8237
|
+
const ok = engine.send(
|
|
8238
|
+
s.id,
|
|
8239
|
+
`FINANCE TUR #${financeState.traderRounds}: hesap + pozisyonlar + izleme listesi fiyatlarını çek; açık pozisyonları yönet (SL/TP güncelle, hedefe ulaşanı kapat); stratejine göre yeni fırsatları değerlendir. ${auto} Kısa rapor ver.`,
|
|
8240
|
+
{ userAction: false }
|
|
8241
|
+
);
|
|
8242
|
+
if (ok) {
|
|
8243
|
+
financeState.lastRoundAt = Date.now();
|
|
8244
|
+
finPush('trader', { state: 'running', round: financeState.traderRounds });
|
|
8245
|
+
} else {
|
|
8246
|
+
/* stop kapısı/yoğunluk — yarım dakika sonra sessizce tekrar dene */
|
|
8247
|
+
clearTimeout(financeState.traderTimer);
|
|
8248
|
+
financeState.traderTimer = setTimeout(() => { try { finTraderRound(); } catch {} }, 30000);
|
|
8249
|
+
}
|
|
8250
|
+
}
|
|
8251
|
+
|
|
8252
|
+
function finFlushOnDone(ev) {
|
|
8253
|
+
if (!ev || (ev.type !== 'done' && ev.type !== 'error')) return;
|
|
8254
|
+
if (String(ev.sessionId || '') !== String(financeState.traderSid || '')) return;
|
|
8255
|
+
if (!financeState.traderOn) return;
|
|
8256
|
+
clearTimeout(financeState.traderTimer);
|
|
8257
|
+
const f = finCfg();
|
|
8258
|
+
const iv = Math.max(30, Number(f.intervalSec) || 120) * 1000;
|
|
8259
|
+
financeState.lastRoundAt = Date.now();
|
|
8260
|
+
finPush('trader', { state: 'idle', round: financeState.traderRounds, nextInSec: iv / 1000 });
|
|
8261
|
+
financeState.traderTimer = setTimeout(() => { try { finTraderRound(); } catch {} }, iv);
|
|
8262
|
+
}
|
|
8263
|
+
|
|
8264
|
+
ipcMain.handle('finance:state', async () => {
|
|
8265
|
+
const f = finCfg();
|
|
8266
|
+
const busy = financeState.traderSid && engine ? engine.isBusy(financeState.traderSid) : false;
|
|
8267
|
+
return {
|
|
8268
|
+
ok: true,
|
|
8269
|
+
mode: financeState.mode,
|
|
8270
|
+
cfg: f,
|
|
8271
|
+
bridge: mt5bridge.status(),
|
|
8272
|
+
trader: { on: financeState.traderOn, busy, rounds: financeState.traderRounds, lastAt: financeState.lastRoundAt, sid: financeState.traderSid },
|
|
8273
|
+
models: engine ? engine.publicState().models : [],
|
|
8274
|
+
};
|
|
8275
|
+
});
|
|
8276
|
+
|
|
8277
|
+
ipcMain.handle('finance:snapshot', async () => {
|
|
8278
|
+
const f = finCfg();
|
|
8279
|
+
const st = financeEnsureBridge();
|
|
8280
|
+
let account = st.account || null;
|
|
8281
|
+
let positions = [];
|
|
8282
|
+
let symbols = [];
|
|
8283
|
+
if (st.running) {
|
|
8284
|
+
const [a, p, sy] = await Promise.all([
|
|
8285
|
+
mt5bridge.call('account', {}, 8000).catch(() => null),
|
|
8286
|
+
mt5bridge.call('positions', {}, 8000).catch(() => null),
|
|
8287
|
+
mt5bridge.call('symbols', { symbols: f.symbols || [] }, 10000).catch(() => null),
|
|
8288
|
+
]);
|
|
8289
|
+
if (a && a.ok) account = a.data && a.data.account;
|
|
8290
|
+
if (p && p.ok) positions = (p.data && p.data.positions) || [];
|
|
8291
|
+
if (sy && sy.ok) symbols = (sy.data && sy.data.symbols) || [];
|
|
8292
|
+
}
|
|
8293
|
+
const busy = financeState.traderSid && engine ? engine.isBusy(financeState.traderSid) : false;
|
|
8294
|
+
return {
|
|
8295
|
+
ok: true,
|
|
8296
|
+
bridge: { running: st.running, connected: st.connected, error: st.error, terminal: st.terminal, python: st.python },
|
|
8297
|
+
account,
|
|
8298
|
+
positions,
|
|
8299
|
+
symbols,
|
|
8300
|
+
cfg: f,
|
|
8301
|
+
trader: { on: financeState.traderOn, busy, rounds: financeState.traderRounds, lastAt: financeState.lastRoundAt },
|
|
8302
|
+
};
|
|
8303
|
+
});
|
|
8304
|
+
|
|
8305
|
+
ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
8306
|
+
financeState.mode = !!(payload && payload.on);
|
|
8307
|
+
const sid = String((payload && payload.sessionId) || '');
|
|
8308
|
+
if (sid && engine) {
|
|
8309
|
+
try {
|
|
8310
|
+
let s = engine.cache.get(sid);
|
|
8311
|
+
if (!s) { try { s = engine._load(sid); } catch {} }
|
|
8312
|
+
if (s && !s.botId && !s.bgJob) {
|
|
8313
|
+
if (financeState.mode) {
|
|
8314
|
+
engine.markFinance(sid, false); /* kalıcı etiket: finance geçmişine girer */
|
|
8315
|
+
finApplyTraderFields(s);
|
|
8316
|
+
s.financeTrader = false; /* chat copilot'ı — trader değil */
|
|
8317
|
+
} else {
|
|
8318
|
+
engine.unmarkFinance(sid); /* normal sohbete döner */
|
|
8319
|
+
}
|
|
8320
|
+
engine.cache.set(sid, s);
|
|
8321
|
+
}
|
|
8322
|
+
} catch {}
|
|
8323
|
+
}
|
|
8324
|
+
return { ok: true, mode: financeState.mode };
|
|
8325
|
+
});
|
|
8326
|
+
|
|
8327
|
+
/* MT5 sembol seçici: terminaldeki tüm semboller (isteğe bağlı *filter*) */
|
|
8328
|
+
ipcMain.handle('finance:symbols:list', async (_e, filter) => {
|
|
8329
|
+
if (!mt5bridge.running) return { ok: false, error: 'MT5 köprüsü bağlı değil — terminal açık mı?' };
|
|
8330
|
+
return await mt5bridge.call('all_symbols', { filter: String(filter || '').trim() }, 20000);
|
|
8331
|
+
});
|
|
8332
|
+
|
|
8333
|
+
ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
8334
|
+
const f = finCfg();
|
|
8335
|
+
const p = patch || {};
|
|
8336
|
+
if (p.symbols !== undefined) {
|
|
8337
|
+
const arr = Array.isArray(p.symbols) ? p.symbols : String(p.symbols).split(/[,\s]+/);
|
|
8338
|
+
f.symbols = arr.map((s) => String(s).trim().toUpperCase()).filter(Boolean).slice(0, 20);
|
|
8339
|
+
}
|
|
8340
|
+
if (p.intervalSec !== undefined) f.intervalSec = Math.max(30, Math.min(3600, Math.round(Number(p.intervalSec) || 120)));
|
|
8341
|
+
if (p.maxLot !== undefined) f.maxLot = Math.max(0.01, Math.min(100, Number(p.maxLot) || 0.1));
|
|
8342
|
+
if (p.maxPositions !== undefined) f.maxPositions = Math.max(1, Math.min(20, Math.round(Number(p.maxPositions) || 3)));
|
|
8343
|
+
if (p.allowTrading !== undefined) f.allowTrading = !!p.allowTrading;
|
|
8344
|
+
if (p.strategy !== undefined) f.strategy = String(p.strategy || '').slice(0, 2000);
|
|
8345
|
+
if (p.pythonPath !== undefined) f.pythonPath = String(p.pythonPath || '').trim();
|
|
8346
|
+
if (p.terminalPath !== undefined) f.terminalPath = String(p.terminalPath || '').trim();
|
|
8347
|
+
if (p.traderSel !== undefined) {
|
|
8348
|
+
f.traderSel = String(p.traderSel || '').trim();
|
|
8349
|
+
if (financeState.traderSid && engine) {
|
|
8350
|
+
try { engine.setSessionModel(financeState.traderSid, f.traderSel || null); } catch {}
|
|
8351
|
+
}
|
|
8352
|
+
}
|
|
8353
|
+
saveSettings();
|
|
8354
|
+
/* trader oturumu alanlarını tazele (oturum yoksa oluşturma — start'ta kurulur) */
|
|
8355
|
+
if (financeState.traderSid && engine) {
|
|
8356
|
+
try {
|
|
8357
|
+
const ts = engine.cache.get(financeState.traderSid);
|
|
8358
|
+
if (ts) finApplyTraderFields(ts);
|
|
8359
|
+
} catch {}
|
|
8360
|
+
}
|
|
8361
|
+
return { ok: true, cfg: f };
|
|
8362
|
+
});
|
|
8363
|
+
|
|
8364
|
+
ipcMain.handle('finance:trader:start', async () => {
|
|
8365
|
+
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
8366
|
+
const f = finCfg();
|
|
8367
|
+
if (!engine.publicState().hasModel && !f.traderSel) return { ok: false, error: 'model yok — Ayarlar → Provider' };
|
|
8368
|
+
const s = finTraderSession();
|
|
8369
|
+
finApplyTraderFields(s);
|
|
8370
|
+
try { engine.setSessionModel(s.id, f.traderSel || null); } catch {}
|
|
8371
|
+
financeState.traderOn = true;
|
|
8372
|
+
financeState.traderRounds = 0;
|
|
8373
|
+
try { engine.clearStop(); } catch {}
|
|
8374
|
+
const ok = engine.send(s.id, finTraderBrief(), { userAction: true });
|
|
8375
|
+
if (!ok) {
|
|
8376
|
+
financeState.traderOn = false;
|
|
8377
|
+
return { ok: false, error: 'trader oturumu meşgul — birkaç saniye sonra tekrar dene' };
|
|
8378
|
+
}
|
|
8379
|
+
financeState.lastRoundAt = Date.now();
|
|
8380
|
+
financeLog('[trader] başlatıldı (model: ' + (f.traderSel || 'genel aktif model') + ')');
|
|
8381
|
+
finPush('trader', { state: 'running', round: 0 });
|
|
8382
|
+
return { ok: true, sid: s.id };
|
|
8383
|
+
});
|
|
8384
|
+
|
|
8385
|
+
ipcMain.handle('finance:trader:stop', async () => {
|
|
8386
|
+
financeState.traderOn = false;
|
|
8387
|
+
clearTimeout(financeState.traderTimer);
|
|
8388
|
+
financeState.traderTimer = null;
|
|
8389
|
+
let interrupted = false;
|
|
8390
|
+
if (financeState.traderSid && engine && engine.isBusy(financeState.traderSid)) {
|
|
8391
|
+
try { interrupted = engine.interrupt(financeState.traderSid, 'kullanıcı Beast Finance trader ajanını durdurdu'); } catch {}
|
|
8392
|
+
}
|
|
8393
|
+
financeLog('[trader] durduruldu');
|
|
8394
|
+
finPush('trader', { state: 'stopped' });
|
|
8395
|
+
return { ok: true, interrupted };
|
|
8396
|
+
});
|
|
8397
|
+
|
|
8398
|
+
ipcMain.handle('finance:connect', async () => {
|
|
8399
|
+
mt5bridge.stop();
|
|
8400
|
+
const st = financeEnsureBridge();
|
|
8401
|
+
return { ok: true, bridge: st };
|
|
8402
|
+
});
|
|
8403
|
+
|
|
8404
|
+
ipcMain.handle('finance:close', async (_e, payload) => {
|
|
8405
|
+
const ticket = Number(payload && payload.ticket);
|
|
8406
|
+
if (!ticket) return { ok: false, error: 'ticket gerekli' };
|
|
8407
|
+
if (!mt5bridge.running) return { ok: false, error: 'MT5 köprüsü bağlı değil' };
|
|
8408
|
+
const r = await mt5bridge.call('close', { ticket, volume: Number(payload && payload.volume) || 0 }, 20000);
|
|
8409
|
+
if (r && r.ok) {
|
|
8410
|
+
financeLog('[panel] pozisyon kapatıldı: ticket ' + ticket);
|
|
8411
|
+
finPush('trade', { line: 'POZİSYON KAPANDI (panel): ticket ' + ticket });
|
|
8412
|
+
}
|
|
8413
|
+
return r;
|
|
8414
|
+
});
|
|
8415
|
+
|
|
8416
|
+
ipcMain.handle('finance:install', async () => {
|
|
8417
|
+
financeState.installTried = false;
|
|
8418
|
+
financeTryInstall();
|
|
8419
|
+
return { ok: true, installing: true };
|
|
8420
|
+
});
|
|
8421
|
+
|
|
8422
|
+
|
|
7990
8423
|
/* düşünme (reasoning) seviyesi */
|
|
7991
8424
|
ipcMain.handle('think:set', (_e, v) => {
|
|
7992
8425
|
setThinkLevel(v);
|
|
@@ -8391,8 +8824,9 @@ function sandboxGetSession(folder) {
|
|
|
8391
8824
|
const s = engine._load(engine.createSession().id);
|
|
8392
8825
|
s.messages = s.messages || [];
|
|
8393
8826
|
s.bgTitle = 'Beast Sandbox'; /* _view.isBg → sohbet geçmişinde gizli */
|
|
8394
|
-
s.bcCode = true; /*
|
|
8395
|
-
s.sbSandbox = true; /* engine:
|
|
8827
|
+
s.bcCode = true; /* opencode ajan sistemi: sandbox ajanı engine'de koşar */
|
|
8828
|
+
s.sbSandbox = true; /* engine: 'sandbox' ajanı çözümlemesi */
|
|
8829
|
+
s.bcAgent = 'sandbox'; /* opencode custom-agent: repo disiplini + panel_run */
|
|
8396
8830
|
try {
|
|
8397
8831
|
fs.appendFileSync(
|
|
8398
8832
|
engine._file(s.id),
|
|
@@ -8435,10 +8869,10 @@ function sbFlush(folder) {
|
|
|
8435
8869
|
clearTimeout(q.timer);
|
|
8436
8870
|
return;
|
|
8437
8871
|
}
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
s.sbSandbox = true;
|
|
8441
|
-
|
|
8872
|
+
/* opencode mantığı: Sandbox da ajan sistemiyle koşar — 'sandbox' ajanı
|
|
8873
|
+
(repo disiplini + panel_run izni, opencode/agents.js'te tanımlı) */
|
|
8874
|
+
s.sbSandbox = true; /* engine: sandbox ajanı çözümlemesi + ÇALIŞTIR paneli köprüsü */
|
|
8875
|
+
bcBindOc(s, folder, 'sandbox');
|
|
8442
8876
|
const payload = mergedAtts ? { text: merged, attachments: mergedAtts } : merged;
|
|
8443
8877
|
engine.send(s.id, payload, { userAction: true });
|
|
8444
8878
|
sbQueue.delete(folder);
|