beast-agent 2.62.0 → 2.62.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 +17 -12
- package/package.json +1 -1
- package/scripts/fix-electron.js +31 -3
- package/src/main.js +150 -1
- package/src/renderer/renderer.js +2 -0
package/bin/beast-agent.js
CHANGED
|
@@ -107,23 +107,28 @@ if (process.argv[2] === 'searxng') {
|
|
|
107
107
|
|
|
108
108
|
/* detached launcher + self-healing electron:
|
|
109
109
|
on fresh machines the electron binary download during npm postinstall
|
|
110
|
-
may have silently failed → it is repaired automatically here.
|
|
110
|
+
may have silently failed OR been truncated → it is repaired automatically here.
|
|
111
|
+
Varlık kontrolü YETMEZ: kesik dosya "var" görünür ama spawn EFTYPE verir —
|
|
112
|
+
fix-electron.status binary'yi `--version` ile çalıştırarak doğrular. */
|
|
111
113
|
function launchDetached(extraArgs) {
|
|
112
114
|
let electron = null;
|
|
113
115
|
try { electron = require('electron'); } catch {}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
let fix = null;
|
|
117
|
+
try { fix = require('../scripts/fix-electron'); } catch {}
|
|
118
|
+
const usable = () => {
|
|
119
|
+
if (typeof electron !== 'string') return false;
|
|
120
|
+
if (fix && typeof fix.status === 'function') return !!fix.status().ok;
|
|
121
|
+
try { return fs.existsSync(electron); } catch { return false; }
|
|
122
|
+
};
|
|
123
|
+
if (!usable() && fix) {
|
|
124
|
+
console.log('\u27F3 electron runtime is missing or corrupted \u2014 repairing automatically\u2026');
|
|
125
|
+
const r = fix.repair({ quiet: false });
|
|
126
|
+
if (r.ok && !r.skipped) console.log('\u2713 electron repaired');
|
|
127
|
+
if (r.ok) {
|
|
128
|
+
try { electron = require('electron'); } catch {}
|
|
124
129
|
}
|
|
125
130
|
}
|
|
126
|
-
if (
|
|
131
|
+
if (!usable()) {
|
|
127
132
|
console.log('\n\u2717 Electron runtime could not be installed.');
|
|
128
133
|
console.log(' Manual fix \u2014 run these 2 commands:');
|
|
129
134
|
console.log(' npm config set ignore-scripts false');
|
package/package.json
CHANGED
package/scripts/fix-electron.js
CHANGED
|
@@ -23,15 +23,43 @@ function electronDir() {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/* binary ÇALIŞIYOR mu? — kesik/bozuk indirmede dosya VAR görünür ama
|
|
27
|
+
CreateProcess "bad EXE format" (EFTYPE) verir; varlık kontrolü yetmez.
|
|
28
|
+
`electron --version` ile gerçekten çalıştığını doğrularız (kısa timeout).
|
|
29
|
+
Sonuç (yol+boyut+mtime) önbelleklenir — aynı süreçte tekrar spawn edilmez. */
|
|
30
|
+
let verifyCache = null;
|
|
31
|
+
function exeWorks(exeAbs) {
|
|
32
|
+
let key = '';
|
|
33
|
+
try {
|
|
34
|
+
const st = fs.statSync(exeAbs);
|
|
35
|
+
key = exeAbs + '|' + st.size + '|' + st.mtimeMs;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (verifyCache && verifyCache.key === key) return verifyCache.ok;
|
|
40
|
+
let ok = false;
|
|
41
|
+
try {
|
|
42
|
+
const r = spawnSync(exeAbs, ['--version'], { timeout: 20000, windowsHide: true, stdio: 'ignore' });
|
|
43
|
+
ok = !r.error && r.status === 0;
|
|
44
|
+
} catch {
|
|
45
|
+
ok = false;
|
|
46
|
+
}
|
|
47
|
+
verifyCache = { key, ok };
|
|
48
|
+
return ok;
|
|
49
|
+
}
|
|
50
|
+
|
|
26
51
|
/* binary sağlam mı? electron/index.js'in beklentisiyle aynı kontrol:
|
|
27
|
-
path.txt + dist/<exe> varlığı */
|
|
52
|
+
path.txt + dist/<exe> varlığı — ARTIK + exe'nin gerçekten çalışması */
|
|
28
53
|
function status() {
|
|
29
54
|
const dir = electronDir();
|
|
30
55
|
if (!dir) return { ok: false, missing: true };
|
|
31
56
|
const exe = process.platform === 'win32' ? 'electron.exe' : 'electron';
|
|
32
|
-
const
|
|
57
|
+
const exeAbs = path.join(dir, 'dist', exe);
|
|
58
|
+
const distOk = fs.existsSync(exeAbs);
|
|
33
59
|
const txtOk = fs.existsSync(path.join(dir, 'path.txt'));
|
|
34
|
-
|
|
60
|
+
if (!(distOk && txtOk)) return { ok: false, dir };
|
|
61
|
+
if (!exeWorks(exeAbs)) return { ok: false, dir, broken: true };
|
|
62
|
+
return { ok: true, dir };
|
|
35
63
|
}
|
|
36
64
|
|
|
37
65
|
/* platforma göre dist içindeki çalıştırılabilirin göreli yolu (electron/index.js ile aynı) */
|
package/src/main.js
CHANGED
|
@@ -3901,6 +3901,7 @@ app.whenReady().then(() => {
|
|
|
3901
3901
|
app.on('before-quit', () => {
|
|
3902
3902
|
app.isQuitting = true;
|
|
3903
3903
|
try { finWatchStop(); } catch {} // finance watchdog zamanlayıcısını kapat
|
|
3904
|
+
try { finHoursStop(); } catch {} // trade saatleri denetim zamanlayıcısını kapat
|
|
3904
3905
|
flushBrowserStorage(); // x.com/google oturumları (cookies) diske yazılsın
|
|
3905
3906
|
try { toolsMod.disposeShellSessions(); } catch {} // kalıcı shell oturumlarını kapat
|
|
3906
3907
|
try { require('./agent/mcp').stopAll(); } catch {} // MCP server süreçlerini kapat
|
|
@@ -9333,6 +9334,8 @@ const financeState = {
|
|
|
9333
9334
|
watchTimer: null,
|
|
9334
9335
|
watchBusy: false,
|
|
9335
9336
|
watchTickAt: 0,
|
|
9337
|
+
hoursTimer: null, /* trade saatleri otomatik duraklatma denetimi (10 sn) */
|
|
9338
|
+
hoursPaused: null, /* pencere kapanınca durdurulan ajanların planı — açılınca geri gelir */
|
|
9336
9339
|
alerts: [], /* fiyat alarmları (kalıcı: finance/alerts.json) */
|
|
9337
9340
|
stats: null, /* son performans özeti (kalıcı: finance/stats.json) */
|
|
9338
9341
|
statsAt: 0,
|
|
@@ -9476,6 +9479,130 @@ function finTradeHoursBlockedLog(what) {
|
|
|
9476
9479
|
return true;
|
|
9477
9480
|
}
|
|
9478
9481
|
|
|
9482
|
+
/* ---------- TRADE SAATLERİ OTOMATİK DURAKLATMA ----------
|
|
9483
|
+
Pencere kapanınca TÜM finance ajanları (trader + ekip + sembol işçileri)
|
|
9484
|
+
Durdur butonuna basılmış gibi otomatik durdurulur; pencere açılınca
|
|
9485
|
+
duraklatılanlar AYNEN geri başlatılır. Kullanıcı duraklama sırasında bir
|
|
9486
|
+
ajanı elle durdurursa o ajan plandan düşer; "Ajanı Durdur"a basılırsa
|
|
9487
|
+
plandan tamamen vazgeçilir (otomatik başlatma iptal). */
|
|
9488
|
+
function finHoursPlanSnapshot() {
|
|
9489
|
+
const plan = { main: false, roles: [], symbols: [] };
|
|
9490
|
+
try {
|
|
9491
|
+
for (const [, a] of financeState.agents) {
|
|
9492
|
+
if (!a) continue;
|
|
9493
|
+
if (a.main) plan.main = true;
|
|
9494
|
+
else if (a.role) plan.roles.push(String(a.role));
|
|
9495
|
+
else if (Array.isArray(a.symbols) && a.symbols.length) plan.symbols.push(a.symbols.slice(0, 8));
|
|
9496
|
+
}
|
|
9497
|
+
} catch {}
|
|
9498
|
+
return plan;
|
|
9499
|
+
}
|
|
9500
|
+
|
|
9501
|
+
function finHoursPlanEmpty(p) {
|
|
9502
|
+
return !p || (!p.main && !(p.roles || []).length && !(p.symbols || []).length);
|
|
9503
|
+
}
|
|
9504
|
+
|
|
9505
|
+
function finHoursPlanDrop(agent) {
|
|
9506
|
+
const p = financeState.hoursPaused;
|
|
9507
|
+
if (!p || !agent) return;
|
|
9508
|
+
if (agent.main) p.main = false;
|
|
9509
|
+
else if (agent.role) p.roles = (p.roles || []).filter((r) => r !== String(agent.role));
|
|
9510
|
+
else if (Array.isArray(agent.symbols) && agent.symbols.length) {
|
|
9511
|
+
const key = String(agent.symbols[0] || '').toUpperCase();
|
|
9512
|
+
p.symbols = (p.symbols || []).filter((s) => String((s && s[0]) || '').toUpperCase() !== key);
|
|
9513
|
+
}
|
|
9514
|
+
if (finHoursPlanEmpty(p)) financeState.hoursPaused = null;
|
|
9515
|
+
}
|
|
9516
|
+
|
|
9517
|
+
/* Pencere kapandı: koşan her finance ajanını durdur, planı sakla. */
|
|
9518
|
+
function finHoursPause(reason) {
|
|
9519
|
+
const plan = finHoursPlanSnapshot();
|
|
9520
|
+
if (finHoursPlanEmpty(plan)) return false;
|
|
9521
|
+
financeState.hoursPaused = plan;
|
|
9522
|
+
const sids = [];
|
|
9523
|
+
for (const [sid, a] of financeState.agents) if (a) sids.push(String(sid));
|
|
9524
|
+
for (const sid of sids) {
|
|
9525
|
+
try { finAgentStop(sid, reason, { keepHoursPlan: true }); } catch {}
|
|
9526
|
+
}
|
|
9527
|
+
financeState.traderOn = false;
|
|
9528
|
+
const th = finCfg().tradeHours || {};
|
|
9529
|
+
const n = (plan.main ? 1 : 0) + (plan.roles || []).length + (plan.symbols || []).length;
|
|
9530
|
+
financeLog(`[saat] trade saatleri kapandı (${th.start || '09:00'}-${th.end || '22:00'} yerel) — ${n} ajan otomatik durduruldu; pencere açılınca geri başlayacak`);
|
|
9531
|
+
finPush('trader', { state: 'hours-stop', count: n });
|
|
9532
|
+
return true;
|
|
9533
|
+
}
|
|
9534
|
+
|
|
9535
|
+
/* Duraklatılan tek ajanı geri aç (ekip rolü ya da sembol işçisi). */
|
|
9536
|
+
function finHoursSpawn(role, symbol) {
|
|
9537
|
+
if (!engine) return false;
|
|
9538
|
+
const f = finCfg();
|
|
9539
|
+
try {
|
|
9540
|
+
const created = role ? finAgentCreate([], false, role) : finAgentCreate([symbol], false);
|
|
9541
|
+
try { engine.setSessionModel(created.s.id, f.traderSel || null); } catch {}
|
|
9542
|
+
const ok = engine.send(created.s.id, finTraderBrief(created.agent), { userAction: true });
|
|
9543
|
+
if (!ok) {
|
|
9544
|
+
finAgentStop(String(created.s.id), 'saat açılışı — oturum meşgul');
|
|
9545
|
+
return false;
|
|
9546
|
+
}
|
|
9547
|
+
const roleDef = role ? finRoleDef(role) : null;
|
|
9548
|
+
financeLog('[saat] ' + (roleDef ? roleDef.label : symbol) + ' geri başlatıldı');
|
|
9549
|
+
return true;
|
|
9550
|
+
} catch {
|
|
9551
|
+
return false;
|
|
9552
|
+
}
|
|
9553
|
+
}
|
|
9554
|
+
|
|
9555
|
+
/* Pencere açıldı: duraklatılanları geri başlat. */
|
|
9556
|
+
function finHoursResume() {
|
|
9557
|
+
const p = financeState.hoursPaused;
|
|
9558
|
+
financeState.hoursPaused = null;
|
|
9559
|
+
if (finHoursPlanEmpty(p) || !engine) return false;
|
|
9560
|
+
const th = finCfg().tradeHours || {};
|
|
9561
|
+
financeLog(`[saat] trade saatleri açıldı (${th.start || '09:00'}-${th.end || '22:00'} yerel) — duraklatılan ajanlar geri başlatılıyor`);
|
|
9562
|
+
if (p.main) { try { financeTraderStart(); } catch {} }
|
|
9563
|
+
for (const role of p.roles || []) {
|
|
9564
|
+
if (!finRoleDef(role)) continue;
|
|
9565
|
+
let running = false;
|
|
9566
|
+
for (const [, a] of financeState.agents) if (a && a.role === role) { running = true; break; }
|
|
9567
|
+
if (!running) finHoursSpawn(role, '');
|
|
9568
|
+
}
|
|
9569
|
+
for (const syms of p.symbols || []) {
|
|
9570
|
+
const symbol = String((syms && syms[0]) || '').toUpperCase();
|
|
9571
|
+
if (!symbol) continue;
|
|
9572
|
+
let running = false;
|
|
9573
|
+
for (const [, a] of financeState.agents) {
|
|
9574
|
+
if (a && !a.main && !a.role && a.symbols.length === 1 && a.symbols[0] === symbol) { running = true; break; }
|
|
9575
|
+
}
|
|
9576
|
+
if (!running) finHoursSpawn('', symbol);
|
|
9577
|
+
}
|
|
9578
|
+
finPush('trader', { state: 'hours-resume' });
|
|
9579
|
+
return true;
|
|
9580
|
+
}
|
|
9581
|
+
|
|
9582
|
+
/* Saat geçişi denetimi: pencere dışındaysa duraklat, içindeyse geri başlat. */
|
|
9583
|
+
function finHoursSync() {
|
|
9584
|
+
if (!engine) return;
|
|
9585
|
+
if (!finTradeHoursOpen()) {
|
|
9586
|
+
if (financeState.agents.size) finHoursPause('trade saatleri kapandı — ajan otomatik durduruldu');
|
|
9587
|
+
return;
|
|
9588
|
+
}
|
|
9589
|
+
if (financeState.hoursPaused) finHoursResume();
|
|
9590
|
+
}
|
|
9591
|
+
|
|
9592
|
+
/* Otomatik saat denetimi zamanlayıcısı (10 sn; MT5 köprüsünden bağımsız) */
|
|
9593
|
+
function finHoursStart() {
|
|
9594
|
+
if (financeState.hoursTimer) return;
|
|
9595
|
+
financeState.hoursTimer = setInterval(() => { try { finHoursSync(); } catch {} }, 10000);
|
|
9596
|
+
try { finHoursSync(); } catch {}
|
|
9597
|
+
}
|
|
9598
|
+
|
|
9599
|
+
function finHoursStop() {
|
|
9600
|
+
if (financeState.hoursTimer) {
|
|
9601
|
+
clearInterval(financeState.hoursTimer);
|
|
9602
|
+
financeState.hoursTimer = null;
|
|
9603
|
+
}
|
|
9604
|
+
}
|
|
9605
|
+
|
|
9479
9606
|
/* ANALİZ EKİBİ: trader'ın yanında koşan uzman ajan rolleri — seçilen her rol
|
|
9480
9607
|
AYRI bir sürekli finance ajanı açar. Hepsi tüm SKILL'lere + mt5 okuma
|
|
9481
9608
|
araçlarına erişir (finance oturumu oldukları için), İŞLEM AÇMAZLAR.
|
|
@@ -11096,6 +11223,7 @@ async function financeEnsureBridge() {
|
|
|
11096
11223
|
terminal: String(f.terminalPath || '').trim(),
|
|
11097
11224
|
});
|
|
11098
11225
|
finWatchStart(); /* risk otomasyonu + bildirimler köprü açılırken başlar */
|
|
11226
|
+
try { finHoursStart(); } catch {} /* trade saatleri otomatik duraklatma denetimi */
|
|
11099
11227
|
return mt5bridge.status();
|
|
11100
11228
|
}
|
|
11101
11229
|
|
|
@@ -11290,6 +11418,7 @@ mt5bridge.on('bridge', (m) => {
|
|
|
11290
11418
|
financeLog('[MT5] terminal bağlı: ' + ((m.terminal && (m.terminal.name + ' @ ' + m.terminal.company)) || 'MT5'));
|
|
11291
11419
|
if (m.account) financeLog(`[MT5] hesap ${m.account.login} · bakiye ${m.account.balance} ${m.account.currency}`);
|
|
11292
11420
|
finWatchStart();
|
|
11421
|
+
try { finHoursStart(); } catch {} /* trade saatleri otomatik duraklatma denetimi */
|
|
11293
11422
|
finStatsRefresh(true).catch(() => {});
|
|
11294
11423
|
/* İLK ENTEGRASYON: EA + AutoTrading + grafik kurulumu otomatik */
|
|
11295
11424
|
finMt5SetupEnsure(false)
|
|
@@ -11444,10 +11573,13 @@ function finAgentCreate(symbols, isMain, role) {
|
|
|
11444
11573
|
}
|
|
11445
11574
|
|
|
11446
11575
|
/* Ajanı durdur: timer kapat + oturumu kes + paralel ajan kaydını 'aborted' yaz */
|
|
11447
|
-
function finAgentStop(sid, reason) {
|
|
11576
|
+
function finAgentStop(sid, reason, opts) {
|
|
11448
11577
|
sid = String(sid || '');
|
|
11449
11578
|
const agent = financeState.agents.get(sid);
|
|
11450
11579
|
if (!agent) return false;
|
|
11580
|
+
/* ELLE DURDURULDU: saat duraklama planından düş — pencere açılınca geri
|
|
11581
|
+
gelmesin (otomatik duraklatma keepHoursPlan ile planı korur) */
|
|
11582
|
+
if (!(opts && opts.keepHoursPlan)) { try { finHoursPlanDrop(agent); } catch {} }
|
|
11451
11583
|
clearTimeout(agent.timer);
|
|
11452
11584
|
if (agent.waitPoll) { clearTimeout(agent.waitPoll); agent.waitPoll = null; }
|
|
11453
11585
|
agent.waitingTeam = false;
|
|
@@ -11910,6 +12042,8 @@ ipcMain.handle('finance:alerts:remove', async (_e, id) => {
|
|
|
11910
12042
|
|
|
11911
12043
|
ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
11912
12044
|
financeState.mode = !!(payload && payload.on);
|
|
12045
|
+
/* SAAT DENETİMİ: pencere kapanınca ajanlar otomatik durur, açılınca geri başlar */
|
|
12046
|
+
try { finHoursStart(); } catch {}
|
|
11913
12047
|
/* BEAST FINANCE = MT5 KAPISI:
|
|
11914
12048
|
- mod AÇILINCA köprü + MT5 terminali OTOMATİK başlar (watchdog dahil) —
|
|
11915
12049
|
panel açılır açılmaz bağlantı kurulur, snapshot'ı beklemez.
|
|
@@ -12146,6 +12280,13 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
12146
12280
|
async function financeTraderStart() {
|
|
12147
12281
|
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
12148
12282
|
const f = finCfg();
|
|
12283
|
+
try { finHoursStart(); } catch {}
|
|
12284
|
+
/* TRADE SAATLERİ KAPISI: pencere dışında elle başlatma da reddedilir —
|
|
12285
|
+
pencere açılınca duraklatılan ajanlar otomatik geri başlar */
|
|
12286
|
+
if (!finTradeHoursOpen()) {
|
|
12287
|
+
const th = f.tradeHours || {};
|
|
12288
|
+
return { ok: false, error: `trade saatleri dışı (${th.start || '09:00'}-${th.end || '22:00'} yerel) — pencere açılınca ajanlar otomatik başlar` };
|
|
12289
|
+
}
|
|
12149
12290
|
if (!engine.publicState().hasModel && !f.traderSel) return { ok: false, error: 'model yok — Ayarlar → Provider' };
|
|
12150
12291
|
try { finSyncScheduleJobs(); } catch {} /* plan/review saatleri trader açılışında garantiye alınır */
|
|
12151
12292
|
/* ANA trader: varsa aynen sürdür, yoksa yeni sürekli ajan aç */
|
|
@@ -12222,6 +12363,8 @@ ipcMain.handle('finance:trader:start', () => financeTraderStart());
|
|
|
12222
12363
|
|
|
12223
12364
|
ipcMain.handle('finance:trader:stop', async () => {
|
|
12224
12365
|
financeState.traderOn = false;
|
|
12366
|
+
/* ELLE DURDURMA: saat planı iptal — pencere açılınca otomatik geri başlatma yok */
|
|
12367
|
+
financeState.hoursPaused = null;
|
|
12225
12368
|
/* ANA trader + ANALİZ EKİBİ durdurulur (sembol işçileri rail × ile ayrı durdurulur) */
|
|
12226
12369
|
let mainSid = '';
|
|
12227
12370
|
const teamSids = [];
|
|
@@ -12242,6 +12385,12 @@ ipcMain.handle('finance:trader:stop', async () => {
|
|
|
12242
12385
|
ipcMain.handle('finance:agent:spawn', async (_e, payload) => {
|
|
12243
12386
|
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
12244
12387
|
const f = finCfg();
|
|
12388
|
+
try { finHoursStart(); } catch {}
|
|
12389
|
+
/* TRADE SAATLERİ: pencere dışında sembol işçisi de başlatılamaz */
|
|
12390
|
+
if (!finTradeHoursOpen()) {
|
|
12391
|
+
const th = f.tradeHours || {};
|
|
12392
|
+
return { ok: false, error: `trade saatleri dışı (${th.start || '09:00'}-${th.end || '22:00'} yerel) — pencere açılınca ajanlar otomatik başlar` };
|
|
12393
|
+
}
|
|
12245
12394
|
if (!engine.publicState().hasModel && !f.traderSel) return { ok: false, error: 'model yok — Ayarlar → Provider' };
|
|
12246
12395
|
const symbol = String((payload && payload.symbol) || '').trim().toUpperCase();
|
|
12247
12396
|
if (!symbol) return { ok: false, error: 'sembol gerekli' };
|
package/src/renderer/renderer.js
CHANGED
|
@@ -10167,6 +10167,8 @@ function finOnEvent(ev) {
|
|
|
10167
10167
|
else if (ev.state === 'team') finLogLine('[trader] ekip raporları bekleniyor — ' + (ev.waiting || 0) + ' ajan turunu bitirsin (max ' + (ev.maxSec || '?') + ' sn)');
|
|
10168
10168
|
else if (ev.state === 'idle') finLogLine('[trader] tur #' + (ev.round || 0) + ' bitti — sıradaki tur ~' + (ev.nextInSec || '?') + ' sn');
|
|
10169
10169
|
else if (ev.state === 'hours') finLogLine('[trader] trade saatleri dışı — ajan askıda (kontrol ~' + (ev.nextInSec || 60) + ' sn)');
|
|
10170
|
+
else if (ev.state === 'hours-stop') finLogLine('[trader] trade saatleri kapandı — ' + (ev.count || 0) + ' finance ajanı otomatik durduruldu; pencere açılınca geri başlar');
|
|
10171
|
+
else if (ev.state === 'hours-resume') finLogLine('[trader] trade saatleri açıldı — duraklatılan ajanlar geri başlatıldı');
|
|
10170
10172
|
else if (ev.state === 'stopped') finLogLine('[trader] durduruldu');
|
|
10171
10173
|
} else if (ev.fn === 'install') {
|
|
10172
10174
|
finLogLine('[MT5] paket kurulumu tamamlandı (kod ' + ev.code + ')');
|