beast-agent 2.19.1 → 2.20.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/customtools.js +242 -0
- package/src/agent/engine.js +34 -5
- package/src/main.js +47 -6
- package/src/preload.js +5 -0
- package/src/renderer/index.html +41 -2
- package/src/renderer/renderer.js +226 -1
- package/src/renderer/style.css +219 -0
package/package.json
CHANGED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* KİŞİSEL TOOLLAR: %APPDATA%\beast\tools\<slug>\tool.json + run.js
|
|
4
|
+
Kullanıcı (ya da ajan) Node ile koşan kendi araçlarını yazar; TÜM oturumlar
|
|
5
|
+
— BEAST FINANCE dahil — bunları model'e açılan gerçek araç olarak çağırır
|
|
6
|
+
(tool__<slug>). Sözleşme:
|
|
7
|
+
- stdin : JSON argümanlar
|
|
8
|
+
- stdout : JSON sonuç (son satır JSON'a çevrilmeye çalışılır)
|
|
9
|
+
- çalışma klasörü: tool'un kendi klasörü (%APPDATA%\beast\tools\<slug>)
|
|
10
|
+
Sonuç model'e { ok, ... } olarak döner; çocuk proses izoledir (uygulama
|
|
11
|
+
çökmez), 90 sn zaman aşımı vardır. */
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { spawn } = require('child_process');
|
|
16
|
+
const { beastRoot } = require('./memory');
|
|
17
|
+
|
|
18
|
+
let notify = () => {};
|
|
19
|
+
function setNotify(fn) {
|
|
20
|
+
notify = typeof fn === 'function' ? fn : () => {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function dir() {
|
|
24
|
+
return path.join(beastRoot(), 'tools');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function slugify(name) {
|
|
28
|
+
return String(name || '')
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/[^a-z0-9_-]+/g, '_')
|
|
31
|
+
.replace(/^_+|_+$/g, '')
|
|
32
|
+
.slice(0, 24);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let cache = null;
|
|
36
|
+
let dirty = true;
|
|
37
|
+
|
|
38
|
+
function invalidate() {
|
|
39
|
+
dirty = true;
|
|
40
|
+
cache = null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readTool(d) {
|
|
44
|
+
try {
|
|
45
|
+
const json = JSON.parse(fs.readFileSync(path.join(d, 'tool.json'), 'utf8'));
|
|
46
|
+
if (!json || typeof json !== 'object' || !json.name) return null;
|
|
47
|
+
const runJs = path.join(d, 'run.js');
|
|
48
|
+
if (!fs.existsSync(runJs)) return null;
|
|
49
|
+
return {
|
|
50
|
+
id: path.basename(d),
|
|
51
|
+
name: String(json.name).slice(0, 40),
|
|
52
|
+
description: String(json.description || '').slice(0, 300),
|
|
53
|
+
parameters:
|
|
54
|
+
json.parameters && typeof json.parameters === 'object'
|
|
55
|
+
? json.parameters
|
|
56
|
+
: { type: 'object', properties: {} },
|
|
57
|
+
script: runJs,
|
|
58
|
+
code: fs.readFileSync(runJs, 'utf8').slice(0, 20000),
|
|
59
|
+
updatedAt: json.updatedAt || null,
|
|
60
|
+
};
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function scan() {
|
|
67
|
+
const out = [];
|
|
68
|
+
try {
|
|
69
|
+
for (const e of fs.readdirSync(dir(), { withFileTypes: true })) {
|
|
70
|
+
if (!e.isDirectory()) continue;
|
|
71
|
+
const t = readTool(path.join(dir(), e.name));
|
|
72
|
+
if (t) out.push(t);
|
|
73
|
+
}
|
|
74
|
+
} catch {}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function list() {
|
|
79
|
+
return scan();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* engine _chatTurn'e OpenAI function formatında şemalar */
|
|
83
|
+
function definitions() {
|
|
84
|
+
if (dirty || !cache) {
|
|
85
|
+
cache = scan();
|
|
86
|
+
dirty = false;
|
|
87
|
+
}
|
|
88
|
+
return cache.map((t) => ({
|
|
89
|
+
type: 'function',
|
|
90
|
+
function: {
|
|
91
|
+
name: 'tool__' + t.id,
|
|
92
|
+
description:
|
|
93
|
+
(t.description || 'kişisel tool') + ` (kişisel tool — args stdin JSON, sonuç stdout JSON)`,
|
|
94
|
+
parameters: t.parameters,
|
|
95
|
+
},
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function names() {
|
|
100
|
+
if (dirty || !cache) {
|
|
101
|
+
cache = scan();
|
|
102
|
+
dirty = false;
|
|
103
|
+
}
|
|
104
|
+
return cache.map((t) => 'tool__' + t.id);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const TOOL_TIMEOUT_MS = 90 * 1000;
|
|
108
|
+
|
|
109
|
+
/* Tool'u çocuk node prosesinde koşturur — uygulama asla çökmez */
|
|
110
|
+
function call(name, args) {
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
const id = String(name || '').replace(/^tool__/, '');
|
|
113
|
+
if (!id) return resolve({ ok: false, error: 'tool adı gerekli' });
|
|
114
|
+
const t = scan().find((x) => x.id === id);
|
|
115
|
+
if (!t) return resolve({ ok: false, error: 'tool bulunamadı: ' + id });
|
|
116
|
+
const t0 = Date.now();
|
|
117
|
+
let child = null;
|
|
118
|
+
let out = '';
|
|
119
|
+
let err = '';
|
|
120
|
+
let done = false;
|
|
121
|
+
const finish = (r) => {
|
|
122
|
+
if (done) return;
|
|
123
|
+
done = true;
|
|
124
|
+
clearTimeout(killer);
|
|
125
|
+
try {
|
|
126
|
+
notify({
|
|
127
|
+
at: new Date().toISOString(),
|
|
128
|
+
tool: t.name,
|
|
129
|
+
toolId: id,
|
|
130
|
+
ok: !!r.ok,
|
|
131
|
+
ms: Date.now() - t0,
|
|
132
|
+
error: r && r.error ? String(r.error).slice(0, 200) : null,
|
|
133
|
+
});
|
|
134
|
+
} catch {}
|
|
135
|
+
resolve(r);
|
|
136
|
+
};
|
|
137
|
+
let killer = setTimeout(() => {
|
|
138
|
+
try { child && child.kill(); } catch {}
|
|
139
|
+
finish({ ok: false, error: 'tool zaman aşımı (' + Math.round(TOOL_TIMEOUT_MS / 1000) + ' sn)' });
|
|
140
|
+
}, TOOL_TIMEOUT_MS);
|
|
141
|
+
try {
|
|
142
|
+
child = spawn(process.execPath, [t.script], { cwd: t.path, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return finish({ ok: false, error: String((e && e.message) || e) });
|
|
145
|
+
}
|
|
146
|
+
child.stdout.on('data', (d) => { out += d; });
|
|
147
|
+
child.stderr.on('data', (d) => { err += d; });
|
|
148
|
+
child.on('error', (e) => finish({ ok: false, error: String((e && e.message) || e) }));
|
|
149
|
+
child.on('close', (code) => {
|
|
150
|
+
const raw = String(out || '').trim();
|
|
151
|
+
let data = null;
|
|
152
|
+
try { data = raw ? JSON.parse(raw) : null; } catch {}
|
|
153
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data) || typeof data.ok !== 'boolean') {
|
|
154
|
+
data = { ok: code === 0, result: data === null ? raw.slice(0, 4000) : data };
|
|
155
|
+
}
|
|
156
|
+
if (code !== 0) {
|
|
157
|
+
if (data.ok !== false) data.ok = false;
|
|
158
|
+
if (!data.error) data.error = String(err || '').slice(0, 400) || 'çıkış kodu ' + code;
|
|
159
|
+
}
|
|
160
|
+
finish(data);
|
|
161
|
+
});
|
|
162
|
+
try {
|
|
163
|
+
child.stdin.write(JSON.stringify(args || {}));
|
|
164
|
+
child.stdin.end();
|
|
165
|
+
} catch {}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function save({ id, name, description, parameters, code }) {
|
|
170
|
+
const slug = id && /^[a-z0-9_-]{2,24}$/.test(String(id)) ? String(id) : slugify(name);
|
|
171
|
+
if (!slug) return { ok: false, error: 'tool adı gerekli (a-z 0-9 _ -)' };
|
|
172
|
+
const d = path.join(dir(), slug);
|
|
173
|
+
try {
|
|
174
|
+
fs.mkdirSync(d, { recursive: true });
|
|
175
|
+
fs.writeFileSync(
|
|
176
|
+
path.join(d, 'tool.json'),
|
|
177
|
+
JSON.stringify(
|
|
178
|
+
{
|
|
179
|
+
name: String(name || slug).slice(0, 40),
|
|
180
|
+
description: String(description || '').slice(0, 300),
|
|
181
|
+
parameters:
|
|
182
|
+
parameters && typeof parameters === 'object' && !Array.isArray(parameters)
|
|
183
|
+
? parameters
|
|
184
|
+
: { type: 'object', properties: {} },
|
|
185
|
+
updatedAt: new Date().toISOString(),
|
|
186
|
+
},
|
|
187
|
+
null,
|
|
188
|
+
2
|
|
189
|
+
)
|
|
190
|
+
);
|
|
191
|
+
const js = String(code || '').trim();
|
|
192
|
+
if (!js) return { ok: false, error: 'run.js kodu boş olamaz' };
|
|
193
|
+
fs.writeFileSync(path.join(d, 'run.js'), js.slice(0, 20000));
|
|
194
|
+
invalidate();
|
|
195
|
+
return { ok: true, id: slug };
|
|
196
|
+
} catch (e) {
|
|
197
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function remove(id) {
|
|
202
|
+
const slug = String(id || '');
|
|
203
|
+
if (!/^[a-z0-9_-]{2,24}$/.test(slug)) return { ok: false, error: 'geçersiz tool kimliği' };
|
|
204
|
+
try {
|
|
205
|
+
fs.rmSync(path.join(dir(), slug), { recursive: true, force: true });
|
|
206
|
+
invalidate();
|
|
207
|
+
return { ok: true };
|
|
208
|
+
} catch (e) {
|
|
209
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Örnek tool: klasör boşsa biçimi göstermek için bir adet kur */
|
|
214
|
+
function seedIfEmpty() {
|
|
215
|
+
try {
|
|
216
|
+
if (scan().length) return;
|
|
217
|
+
save({
|
|
218
|
+
name: 'ornek_echo',
|
|
219
|
+
description:
|
|
220
|
+
'Örnek kişisel tool — gelen argümanları geri döndürür. Bu klasörü kopyalayarak kendi toollarını yaz; agent finance dahil tüm oturumlarda tool__<ad> olarak çağırır.',
|
|
221
|
+
parameters: {
|
|
222
|
+
type: 'object',
|
|
223
|
+
properties: {
|
|
224
|
+
mesaj: { type: 'string', description: 'geri döndürülecek deneme mesajı' },
|
|
225
|
+
},
|
|
226
|
+
required: ['mesaj'],
|
|
227
|
+
},
|
|
228
|
+
code:
|
|
229
|
+
"'use strict';\n" +
|
|
230
|
+
"// KİŞİSEL TOOL — stdin: JSON args · stdout: JSON sonuç\n" +
|
|
231
|
+
"// Bu klasörü kopyalayıp kendi araçlarını yaz (örn. MT5 emir köprüsü, özel API çağrısı…)\n" +
|
|
232
|
+
"const fs = require('fs');\n" +
|
|
233
|
+
"let raw = '';\n" +
|
|
234
|
+
"try { raw = fs.readFileSync(0, 'utf8'); } catch {}\n" +
|
|
235
|
+
"let args = {};\n" +
|
|
236
|
+
"try { args = JSON.parse(raw || '{}'); } catch {}\n" +
|
|
237
|
+
"console.log(JSON.stringify({ ok: true, echo: args, not: 'run.js ve tool.json düzenlenebilir' }));\n",
|
|
238
|
+
});
|
|
239
|
+
} catch {}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = { setNotify, list, scan, definitions, names, call, save, remove, seedIfEmpty, invalidate, dir, slugify };
|
package/src/agent/engine.js
CHANGED
|
@@ -13,6 +13,7 @@ const tools = require('./tools');
|
|
|
13
13
|
let financetools = null;
|
|
14
14
|
try { financetools = require('./financetools'); } catch {}
|
|
15
15
|
const FINANCE_TOOL_SET = new Set(financetools ? financetools.NAMES : []);
|
|
16
|
+
const customtools = require('./customtools');
|
|
16
17
|
const research = require('./research');
|
|
17
18
|
const agentdefs = require('./agentdefs');
|
|
18
19
|
const memory = require('./memory');
|
|
@@ -1232,7 +1233,14 @@ class Engine {
|
|
|
1232
1233
|
let r;
|
|
1233
1234
|
try { r = JSON.parse(l); } catch { continue; }
|
|
1234
1235
|
if (r.t === 'meta') out.push(JSON.stringify({ t: 'meta', id: s.id, code: s.code, createdAt: s.createdAt }));
|
|
1235
|
-
|
|
1236
|
+
/* KALICİ ETİKETLER compact'ta ASLA düşmez — özellikle 'fin'/'finoff':
|
|
1237
|
+
silinirse finance oturumu etiketini KAYBEDER ve mt5_* araçları
|
|
1238
|
+
"unknown tool" olur (bayat compact kaybı bu yüzden) */
|
|
1239
|
+
else if (
|
|
1240
|
+
r.t === 'meta2' || r.t === 'bot' || r.t === 'todo' ||
|
|
1241
|
+
r.t === 'fin' || r.t === 'finoff' ||
|
|
1242
|
+
r.t === 'agent' || r.t === 'bcws' || r.t === 'botdm'
|
|
1243
|
+
) out.push(l);
|
|
1236
1244
|
}
|
|
1237
1245
|
if (lastNotesLine) out.push(lastNotesLine);
|
|
1238
1246
|
/* korunan pencere: son N mesaj */
|
|
@@ -1419,7 +1427,12 @@ class Engine {
|
|
|
1419
1427
|
const kept = lines.filter((l) => {
|
|
1420
1428
|
try {
|
|
1421
1429
|
const r = JSON.parse(l);
|
|
1422
|
-
|
|
1430
|
+
/* kalıcı etiketler korunur (fin/finoff dahil — finance bayrağı kaybolmasın) */
|
|
1431
|
+
return (
|
|
1432
|
+
r.t === 'meta' || r.t === 'meta2' || r.t === 'bot' || r.t === 'todo' ||
|
|
1433
|
+
r.t === 'fin' || r.t === 'finoff' ||
|
|
1434
|
+
r.t === 'agent' || r.t === 'bcws' || r.t === 'botdm'
|
|
1435
|
+
);
|
|
1423
1436
|
} catch { return false; }
|
|
1424
1437
|
});
|
|
1425
1438
|
const tmp = file + '.tmp';
|
|
@@ -3453,6 +3466,9 @@ class Engine {
|
|
|
3453
3466
|
toolsList = await mcp.mergeTools(toolsList);
|
|
3454
3467
|
/* Beast Apps: kurulu app'lerin araçları (app__<id>__<tool>) modele açılır */
|
|
3455
3468
|
toolsList = apps.mergeTools(toolsList);
|
|
3469
|
+
/* KİŞİSEL TOOLLAR: %APPDATA%\beast\tools\<ad>\ — kullanıcı yazmışsa
|
|
3470
|
+
TÜM oturumlara (BEAST FINANCE dahil) tool__<ad> olarak açılır */
|
|
3471
|
+
toolsList = [...toolsList, ...customtools.definitions()];
|
|
3456
3472
|
/* Beast Finance: skill aracı da açık — kataloğun SKILL.md gövdesi
|
|
3457
3473
|
modele açılır (skill handler'ı zaten tüm oturumlar için çalışır,
|
|
3458
3474
|
definition yalnız BC'de vardı; finance chat copilot + trader ikisi de)
|
|
@@ -3464,8 +3480,13 @@ class Engine {
|
|
|
3464
3480
|
if (skillDef) toolsList = [...toolsList, skillDef];
|
|
3465
3481
|
}
|
|
3466
3482
|
/* AJAN DM: koşan ajan oturumlarına (arka plan işleri + finance
|
|
3467
|
-
ajanları)
|
|
3468
|
-
|
|
3483
|
+
ajanları) VE ana sohbete verilir — chat Beast'i de ajana DM atabilir;
|
|
3484
|
+
müşteri bot oturumları (botId ≠ beast) ve botlar arası DM hariç */
|
|
3485
|
+
if (
|
|
3486
|
+
session &&
|
|
3487
|
+
!session.isBotDm &&
|
|
3488
|
+
(session.bgJob || session.finance || !session.botId || session.botId === 'beast')
|
|
3489
|
+
) {
|
|
3469
3490
|
toolsList = [...toolsList, AGENT_DM_DEF];
|
|
3470
3491
|
}
|
|
3471
3492
|
/* panel_run yalnız SANDBOX oturumlarında görünsün — diğer panellerde
|
|
@@ -3890,6 +3911,8 @@ class Engine {
|
|
|
3890
3911
|
// Paralel yürütme — bağımsız çağrılar beklemesin
|
|
3891
3912
|
session.toolsSinceReflect = (session.toolsSinceReflect || 0) + res.toolCalls.length;
|
|
3892
3913
|
if (!this._knownToolNames) this._knownToolNames = new Set([...TOOLS.map((t) => t.function.name), ...opencode.toolmap.names()]);
|
|
3914
|
+
/* kişisel tool sonradan yazılabilir — isim havuzunu canlı tazele */
|
|
3915
|
+
for (const n of customtools.names()) this._knownToolNames.add(n);
|
|
3893
3916
|
await Promise.all(
|
|
3894
3917
|
res.toolCalls.map(async (tc) => {
|
|
3895
3918
|
let name = tc.function && tc.function.name;
|
|
@@ -4765,6 +4788,10 @@ const skills = require('./skills');
|
|
|
4765
4788
|
if (name === 'agent_dm') {
|
|
4766
4789
|
return JSON.stringify(this._agentDmSend(sessionId, args));
|
|
4767
4790
|
}
|
|
4791
|
+
/* KİŞİSEL TOOL: %APPDATA%\beast\tools\ — çocuk node prosesinde izole koşar */
|
|
4792
|
+
if (String(name).startsWith('tool__')) {
|
|
4793
|
+
return JSON.stringify(await customtools.call(name, args));
|
|
4794
|
+
}
|
|
4768
4795
|
if (isBc) {
|
|
4769
4796
|
/* opencode permission akışı (tool/external-directory.ts + her aracın
|
|
4770
4797
|
ctx.ask'ı): önce workspace DIŞI erişim izni, sonra araç izni.
|
|
@@ -4796,7 +4823,9 @@ const skills = require('./skills');
|
|
|
4796
4823
|
"always" onaylı araçlar doğrudan geçer. (BC dışı oturumlar — opencode
|
|
4797
4824
|
izin sistemi yalnız Beast Code oturumlarında koşar) */
|
|
4798
4825
|
if (
|
|
4799
|
-
(Engine.RISKY_TOOLS.has(name) ||
|
|
4826
|
+
(Engine.RISKY_TOOLS.has(name) ||
|
|
4827
|
+
String(name).startsWith('mcp__') ||
|
|
4828
|
+
String(name).startsWith('tool__')) && /* kişisel tool = kullanıcı kodu koşturur */
|
|
4800
4829
|
!this.alwaysAllowTools.has(name) &&
|
|
4801
4830
|
this.approvals && typeof this.approvals.request === 'function'
|
|
4802
4831
|
) {
|
package/src/main.js
CHANGED
|
@@ -3605,6 +3605,7 @@ app.whenReady().then(() => {
|
|
|
3605
3605
|
reloadBackend();
|
|
3606
3606
|
syncWhitelist(); // bot sistemi: whitelist.json aynası ilk açılışta garanti
|
|
3607
3607
|
try { bots.ensureBotCodes(); } catch {} // her bota benzersiz 5 haneli kod garanti
|
|
3608
|
+
try { customtools.seedIfEmpty(); } catch {} // kişisel toollar klasörü boşsa örnek tool kur
|
|
3608
3609
|
createSplash();
|
|
3609
3610
|
createWindow();
|
|
3610
3611
|
log.info('main', 'Beast Agent başlatıldı');
|
|
@@ -5892,6 +5893,27 @@ ipcMain.handle('agents:cancel', (_e, id) => {
|
|
|
5892
5893
|
/* AJAN DM paneli: ajanlar arası mesaj arşivi */
|
|
5893
5894
|
ipcMain.handle('agent-dms:list', () => (engine ? engine.agentDmsList() : []));
|
|
5894
5895
|
ipcMain.handle('agent-dms:clear', () => (engine ? engine.agentDmsClear() : { ok: false }));
|
|
5896
|
+
|
|
5897
|
+
/* ---------- KİŞİSEL TOOLLAR (%APPDATA%\beast\tools\) ---------- */
|
|
5898
|
+
ipcMain.handle('tools:list', () => customtools.list());
|
|
5899
|
+
ipcMain.handle('tools:save', (_e, tool) => customtools.save(tool || {}));
|
|
5900
|
+
ipcMain.handle('tools:delete', (_e, id) => customtools.remove(id));
|
|
5901
|
+
ipcMain.handle('tools:run', async (_e, payload) => {
|
|
5902
|
+
const id = String((payload && payload.id) || '');
|
|
5903
|
+
const args = (payload && payload.args) || {};
|
|
5904
|
+
const r = await customtools.call('tool__' + id, args);
|
|
5905
|
+
return r;
|
|
5906
|
+
});
|
|
5907
|
+
ipcMain.handle('tools:openFolder', () => {
|
|
5908
|
+
try {
|
|
5909
|
+
const d = customtools.dir();
|
|
5910
|
+
fs.mkdirSync(d, { recursive: true });
|
|
5911
|
+
require('electron').shell.openPath(d);
|
|
5912
|
+
return { ok: true };
|
|
5913
|
+
} catch (e) {
|
|
5914
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
5915
|
+
}
|
|
5916
|
+
});
|
|
5895
5917
|
ipcMain.handle('ceo:get', () => !!engine.ceoMode);
|
|
5896
5918
|
ipcMain.handle('ceo:set', (_e, v) => {
|
|
5897
5919
|
settings.ceoMode = !!v;
|
|
@@ -8273,6 +8295,7 @@ function studioWatchStop() {
|
|
|
8273
8295
|
oturumu, tur tur piyasa tarayıp (farklı API/model seçilebilir) işlem kovalar. */
|
|
8274
8296
|
const mt5bridge = require('./mt5bridge');
|
|
8275
8297
|
const financetools = require('./agent/financetools');
|
|
8298
|
+
const customtools = require('./agent/customtools');
|
|
8276
8299
|
|
|
8277
8300
|
const financeState = {
|
|
8278
8301
|
mode: false, /* chat oturumları finance bayrağı alıyor mu */
|
|
@@ -8337,6 +8360,15 @@ try {
|
|
|
8337
8360
|
});
|
|
8338
8361
|
} catch {}
|
|
8339
8362
|
|
|
8363
|
+
/* KİŞİSEL TOOLLAR: çalışma kaydı renderer'a (TOOLS konsolu buradan beslenir) */
|
|
8364
|
+
try {
|
|
8365
|
+
customtools.setNotify((entry) => {
|
|
8366
|
+
try {
|
|
8367
|
+
if (win && !win.isDestroyed()) win.webContents.send('agent:event', { type: 'tool-log', ...entry });
|
|
8368
|
+
} catch {}
|
|
8369
|
+
});
|
|
8370
|
+
} catch {}
|
|
8371
|
+
|
|
8340
8372
|
function financeEnsureBridge() {
|
|
8341
8373
|
const f = finCfg();
|
|
8342
8374
|
const candidates = [String(f.pythonPath || '').trim(), 'python', 'py -3'].filter(Boolean);
|
|
@@ -8695,23 +8727,32 @@ ipcMain.handle('finance:snapshot', async () => {
|
|
|
8695
8727
|
ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
8696
8728
|
financeState.mode = !!(payload && payload.on);
|
|
8697
8729
|
const sid = String((payload && payload.sessionId) || '');
|
|
8730
|
+
let needNew = false;
|
|
8698
8731
|
if (sid && engine) {
|
|
8699
8732
|
try {
|
|
8700
8733
|
let s = engine.cache.get(sid);
|
|
8701
8734
|
if (!s) { try { s = engine._load(sid); } catch {} }
|
|
8702
|
-
if (s && !s.
|
|
8735
|
+
if (s && !s.bgJob && (!s.botId || s.botId === 'beast')) {
|
|
8703
8736
|
if (financeState.mode) {
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8737
|
+
if (s.finance) {
|
|
8738
|
+
/* zaten finance oturumu — aynen sürdür */
|
|
8739
|
+
finApplyTraderFields(s);
|
|
8740
|
+
s.financeTrader = false; /* chat copilot'ı — trader değil */
|
|
8741
|
+
} else {
|
|
8742
|
+
/* NORMAL oturum (örn. WhatsApp sohbeti) finance modundayken ASLA
|
|
8743
|
+
finance'e çevrilmez — ayrı bir finance oturumu açılır.
|
|
8744
|
+
(eski davranış WhatsApp oturumunu da finance listesine karıştırıyordu) */
|
|
8745
|
+
needNew = true;
|
|
8746
|
+
}
|
|
8707
8747
|
} else {
|
|
8708
|
-
|
|
8748
|
+
/* mod kapandı: aktif finance oturumu normal sohbete döner */
|
|
8749
|
+
if (s.finance) engine.unmarkFinance(sid);
|
|
8709
8750
|
}
|
|
8710
8751
|
engine.cache.set(sid, s);
|
|
8711
8752
|
}
|
|
8712
8753
|
} catch {}
|
|
8713
8754
|
}
|
|
8714
|
-
return { ok: true, mode: financeState.mode };
|
|
8755
|
+
return { ok: true, mode: financeState.mode, needNew };
|
|
8715
8756
|
});
|
|
8716
8757
|
|
|
8717
8758
|
/* MT5 sembol seçici: terminaldeki tüm semboller (isteğe bağlı *filter*) */
|
package/src/preload.js
CHANGED
|
@@ -22,6 +22,11 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
22
22
|
agentsCancel: (id) => ipcRenderer.invoke('agents:cancel', id),
|
|
23
23
|
agentDmsList: () => ipcRenderer.invoke('agent-dms:list'),
|
|
24
24
|
agentDmsClear: () => ipcRenderer.invoke('agent-dms:clear'),
|
|
25
|
+
toolsList: () => ipcRenderer.invoke('tools:list'),
|
|
26
|
+
toolSave: (t) => ipcRenderer.invoke('tools:save', t),
|
|
27
|
+
toolDelete: (id) => ipcRenderer.invoke('tools:delete', id),
|
|
28
|
+
toolRun: (id, args) => ipcRenderer.invoke('tools:run', { id, args }),
|
|
29
|
+
toolsOpenFolder: () => ipcRenderer.invoke('tools:openFolder'),
|
|
25
30
|
getCeoMode: () => ipcRenderer.invoke('ceo:get'),
|
|
26
31
|
setCeoMode: (v) => ipcRenderer.invoke('ceo:set', v),
|
|
27
32
|
listNotes: () => ipcRenderer.invoke('notes:list'),
|
package/src/renderer/index.html
CHANGED
|
@@ -22,6 +22,16 @@
|
|
|
22
22
|
</div>
|
|
23
23
|
<div id="watchPaneList"></div>
|
|
24
24
|
</div>
|
|
25
|
+
<div id="toolBox">
|
|
26
|
+
<div id="toolBoxHead">
|
|
27
|
+
<span id="toolBoxTitle">TOOLLAR</span>
|
|
28
|
+
<span class="bot-bar-btns">
|
|
29
|
+
<button id="toolFolderBtn" title="Toollar klasörünü aç (%APPDATA%\beast\tools)"><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>
|
|
30
|
+
<button id="toolAdd" title="Yeni tool yaz">+</button>
|
|
31
|
+
</span>
|
|
32
|
+
</div>
|
|
33
|
+
<div id="toolList"></div>
|
|
34
|
+
</div>
|
|
25
35
|
<div id="botBar">
|
|
26
36
|
<div id="botBarHead">
|
|
27
37
|
<span id="botBarTitle" data-i18n="bot_head">BOTLAR</span>
|
|
@@ -363,10 +373,39 @@
|
|
|
363
373
|
</aside>
|
|
364
374
|
</div>
|
|
365
375
|
|
|
376
|
+
<div id="toolOverlay" hidden>
|
|
377
|
+
<div id="toolDialog">
|
|
378
|
+
<div id="toolDialogHead">
|
|
379
|
+
<span id="toolDialogTitle">YENİ TOOL</span>
|
|
380
|
+
<button id="toolClose" title="Kapat">✕︎</button>
|
|
381
|
+
</div>
|
|
382
|
+
<div class="tool-form">
|
|
383
|
+
<label>Tool adı (ajan tool__<ad> olarak çağırır)</label>
|
|
384
|
+
<input id="toolName" placeholder="örn: gold_emir" spellcheck="false" autocomplete="off" />
|
|
385
|
+
<label>Açıklama — ajan bu metni görüp ne zaman çağıracağını anlar</label>
|
|
386
|
+
<input id="toolDesc" placeholder="örn: GOLD için özel emir açar (side, lot, sl, tp)" spellcheck="false" autocomplete="off" />
|
|
387
|
+
<label>Parametre şeması (JSON)</label>
|
|
388
|
+
<textarea id="toolParams" rows="6" spellcheck="false" placeholder='{"type":"object","properties":{"side":{"type":"string"},"lot":{"type":"number"}},"required":["side"]}'></textarea>
|
|
389
|
+
<label>run.js kodu — stdin: JSON args · stdout: JSON sonuç (console.log)</label>
|
|
390
|
+
<textarea id="toolCode" rows="12" spellcheck="false" placeholder="const args = JSON.parse(...); console.log(JSON.stringify({ ok: true }));"></textarea>
|
|
391
|
+
<div id="toolFormErr" class="sub" style="color:var(--err)" hidden></div>
|
|
392
|
+
</div>
|
|
393
|
+
<div id="toolTestRow">
|
|
394
|
+
<input id="toolTestArgs" placeholder='deneme argümanları (JSON): {"mesaj":"merhaba"}' spellcheck="false" autocomplete="off" />
|
|
395
|
+
<button id="toolTestBtn">▶︎ Çalıştır</button>
|
|
396
|
+
</div>
|
|
397
|
+
<pre id="toolTestOut" hidden></pre>
|
|
398
|
+
<div id="toolDialogBtns">
|
|
399
|
+
<button id="toolDeleteBtn" class="tool-danger" hidden>Sil</button>
|
|
400
|
+
<span class="file-spacer"></span>
|
|
401
|
+
<button id="toolSaveBtn" class="tool-primary">Kaydet</button>
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
|
|
366
406
|
<div id="settingsOverlay" hidden>
|
|
367
407
|
<div id="settingsDialog">
|
|
368
|
-
<aside id="setTabs">
|
|
369
|
-
<div class="set-tabs-title" data-i18n="set_title">AYARLAR</div>
|
|
408
|
+
<aside id="setTabs"> <div class="set-tabs-title" data-i18n="set_title">AYARLAR</div>
|
|
370
409
|
<nav class="set-tabs-list">
|
|
371
410
|
<button class="tab active" data-tab="lang" data-i18n="tab_lang">Dil</button>
|
|
372
411
|
<button class="tab" data-tab="provider" data-i18n="tab_provider">Provider</button>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -48,6 +48,22 @@ const els = {
|
|
|
48
48
|
watchBtn: $('#watchBtn'),
|
|
49
49
|
watchPaneList: $('#watchPaneList'),
|
|
50
50
|
watchPaneOpen: $('#watchPaneOpen'),
|
|
51
|
+
toolBoxAdd: $('#toolAdd'),
|
|
52
|
+
toolList: $('#toolList'),
|
|
53
|
+
toolFolderBtn: $('#toolFolderBtn'),
|
|
54
|
+
toolOverlay: $('#toolOverlay'),
|
|
55
|
+
toolClose: $('#toolClose'),
|
|
56
|
+
toolDialogTitle: $('#toolDialogTitle'),
|
|
57
|
+
toolName: $('#toolName'),
|
|
58
|
+
toolDesc: $('#toolDesc'),
|
|
59
|
+
toolParams: $('#toolParams'),
|
|
60
|
+
toolCode: $('#toolCode'),
|
|
61
|
+
toolFormErr: $('#toolFormErr'),
|
|
62
|
+
toolTestArgs: $('#toolTestArgs'),
|
|
63
|
+
toolTestBtn: $('#toolTestBtn'),
|
|
64
|
+
toolTestOut: $('#toolTestOut'),
|
|
65
|
+
toolSaveBtn: $('#toolSaveBtn'),
|
|
66
|
+
toolDeleteBtn: $('#toolDeleteBtn'),
|
|
51
67
|
watchPaneLog: $('#watchPaneLog'),
|
|
52
68
|
cronBtn: $('#cronBtn'),
|
|
53
69
|
watchOverlay: $('#watchOverlay'),
|
|
@@ -5110,6 +5126,192 @@ if (els.dmClear) {
|
|
|
5110
5126
|
});
|
|
5111
5127
|
}
|
|
5112
5128
|
|
|
5129
|
+
/* ---------------- TOOLS konsolu (kişisel araçlar) ----------------
|
|
5130
|
+
%APPDATA%\beast\tools\<ad>\tool.json + run.js — kullanıcı kendi araçlarını
|
|
5131
|
+
yazar; finance dahil TÜM oturumlar tool__<ad> olarak çağırır. Sol panelde
|
|
5132
|
+
buradan görüntülenir, yazılır, test edilir, silinir. */
|
|
5133
|
+
|
|
5134
|
+
const toolUi = {
|
|
5135
|
+
tools: [],
|
|
5136
|
+
editId: null,
|
|
5137
|
+
logRows: [],
|
|
5138
|
+
};
|
|
5139
|
+
|
|
5140
|
+
const TOOL_EXAMPLE_CODE =
|
|
5141
|
+
"'use strict';\n" +
|
|
5142
|
+
"// KİŞİSEL TOOL — stdin: JSON args · stdout: JSON sonuç\n" +
|
|
5143
|
+
"const fs = require('fs');\n" +
|
|
5144
|
+
"let raw = '';\n" +
|
|
5145
|
+
"try { raw = fs.readFileSync(0, 'utf8'); } catch {}\n" +
|
|
5146
|
+
"let args = {};\n" +
|
|
5147
|
+
"try { args = JSON.parse(raw || '{}'); } catch {}\n" +
|
|
5148
|
+
"// Buraya kendi işini yaz (MT5 köprüsü, özel API, hesap, sinyal…)\n" +
|
|
5149
|
+
"console.log(JSON.stringify({ ok: true, echo: args }));\n";
|
|
5150
|
+
|
|
5151
|
+
let toolRenderTimer = null;
|
|
5152
|
+
function scheduleToolRender() {
|
|
5153
|
+
if (toolRenderTimer) return;
|
|
5154
|
+
toolRenderTimer = setTimeout(() => {
|
|
5155
|
+
toolRenderTimer = null;
|
|
5156
|
+
renderToolBox();
|
|
5157
|
+
}, 500);
|
|
5158
|
+
}
|
|
5159
|
+
|
|
5160
|
+
async function renderToolBox() {
|
|
5161
|
+
const list = els.toolList;
|
|
5162
|
+
if (!list) return;
|
|
5163
|
+
try { toolUi.tools = (await beast.toolsList()) || []; } catch { toolUi.tools = []; }
|
|
5164
|
+
list.textContent = '';
|
|
5165
|
+
if (!toolUi.tools.length) {
|
|
5166
|
+
const d = document.createElement('div');
|
|
5167
|
+
d.className = 'tool-empty';
|
|
5168
|
+
d.textContent = 'Tool yok — + ile kendi aracını yaz (Finance dahil tüm oturumlarda çalışır)';
|
|
5169
|
+
list.appendChild(d);
|
|
5170
|
+
return;
|
|
5171
|
+
}
|
|
5172
|
+
for (const t of toolUi.tools) {
|
|
5173
|
+
const row = document.createElement('div');
|
|
5174
|
+
row.className = 'tool-row';
|
|
5175
|
+
const last = toolUi.logRows.filter((l) => l.toolId === t.id).slice(-1)[0];
|
|
5176
|
+
const st = last ? (last.ok ? 'ok' : 'err') : 'idle';
|
|
5177
|
+
const tip = last
|
|
5178
|
+
? 'son çalışma ' + dmTime(last.at) + (last.error ? ' · hata: ' + last.error : ' · ' + last.ms + ' ms')
|
|
5179
|
+
: 'hiç çalışmadı';
|
|
5180
|
+
row.innerHTML =
|
|
5181
|
+
'<span class="tool-dot ' + st + '" title="' + escapeHtml(tip) + '"></span>' +
|
|
5182
|
+
'<span class="tool-name" title="' + escapeHtml(t.description || '') + '">' + escapeHtml(t.name) + '</span>' +
|
|
5183
|
+
'<span class="tool-btns">' +
|
|
5184
|
+
'<button class="tb-run" title="Aç & test çalıştır">▶︎</button>' +
|
|
5185
|
+
'<button class="tb-edit" title="Düzenle">✎︎</button>' +
|
|
5186
|
+
'<button class="tb-del" title="Sil">✕︎</button>' +
|
|
5187
|
+
'</span>';
|
|
5188
|
+
row.querySelector('.tb-run').addEventListener('click', () => openToolDialog(t));
|
|
5189
|
+
row.querySelector('.tb-edit').addEventListener('click', () => openToolDialog(t));
|
|
5190
|
+
row.querySelector('.tb-del').addEventListener('click', async () => {
|
|
5191
|
+
if (!(await uiConfirm('"' + t.name + '" toolu silinsin mi? (ajanlar artık çağıramaz)', 'Sil'))) return;
|
|
5192
|
+
const r = await beast.toolDelete(t.id).catch(() => ({ ok: false }));
|
|
5193
|
+
if (r && r.ok) {
|
|
5194
|
+
toast('Tool silindi');
|
|
5195
|
+
renderToolBox();
|
|
5196
|
+
} else toast((r && r.error) || 'Silinemedi');
|
|
5197
|
+
});
|
|
5198
|
+
list.appendChild(row);
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
|
|
5202
|
+
function openToolDialog(t) {
|
|
5203
|
+
if (!els.toolOverlay) return;
|
|
5204
|
+
toolUi.editId = t ? t.id : null;
|
|
5205
|
+
els.toolDialogTitle.textContent = t ? 'TOOL · ' + t.name : 'YENİ TOOL';
|
|
5206
|
+
els.toolName.value = t ? t.name : '';
|
|
5207
|
+
els.toolDesc.value = t ? t.description || '' : '';
|
|
5208
|
+
els.toolParams.value = t
|
|
5209
|
+
? JSON.stringify(t.parameters || { type: 'object', properties: {} }, null, 2)
|
|
5210
|
+
: '{\n "type": "object",\n "properties": {},\n "required": []\n}';
|
|
5211
|
+
els.toolCode.value = t ? t.code || TOOL_EXAMPLE_CODE : TOOL_EXAMPLE_CODE;
|
|
5212
|
+
els.toolTestArgs.value = t && t.parameters && Array.isArray(t.parameters.required) && t.parameters.required.length
|
|
5213
|
+
? JSON.stringify(Object.fromEntries(t.parameters.required.map((k) => [k, ''])), null, 1)
|
|
5214
|
+
: '';
|
|
5215
|
+
els.toolTestOut.hidden = true;
|
|
5216
|
+
els.toolTestOut.textContent = '';
|
|
5217
|
+
els.toolFormErr.hidden = true;
|
|
5218
|
+
els.toolDeleteBtn.hidden = !t;
|
|
5219
|
+
els.toolOverlay.hidden = false;
|
|
5220
|
+
els.toolName.focus();
|
|
5221
|
+
}
|
|
5222
|
+
|
|
5223
|
+
function closeToolDialog() {
|
|
5224
|
+
if (els.toolOverlay) els.toolOverlay.hidden = true;
|
|
5225
|
+
}
|
|
5226
|
+
|
|
5227
|
+
function toolErr(msg) {
|
|
5228
|
+
if (els.toolFormErr) {
|
|
5229
|
+
els.toolFormErr.textContent = msg;
|
|
5230
|
+
els.toolFormErr.hidden = !msg;
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
|
|
5234
|
+
if (els.toolBoxAdd) els.toolBoxAdd.addEventListener('click', () => openToolDialog(null));
|
|
5235
|
+
if (els.toolFolderBtn) els.toolFolderBtn.addEventListener('click', () => beast.toolsOpenFolder());
|
|
5236
|
+
if (els.toolClose) els.toolClose.addEventListener('click', closeToolDialog);
|
|
5237
|
+
if (els.toolOverlay) {
|
|
5238
|
+
els.toolOverlay.addEventListener('click', (e) => {
|
|
5239
|
+
if (e.target === els.toolOverlay) closeToolDialog();
|
|
5240
|
+
});
|
|
5241
|
+
els.toolOverlay.addEventListener('keydown', (e) => {
|
|
5242
|
+
if (e.key === 'Escape') { e.stopPropagation(); closeToolDialog(); }
|
|
5243
|
+
});
|
|
5244
|
+
}
|
|
5245
|
+
if (els.toolSaveBtn) {
|
|
5246
|
+
els.toolSaveBtn.addEventListener('click', async () => {
|
|
5247
|
+
toolErr('');
|
|
5248
|
+
const name = String(els.toolName.value || '').trim();
|
|
5249
|
+
if (!name) return toolErr('Tool adı gerekli');
|
|
5250
|
+
let parameters = null;
|
|
5251
|
+
try {
|
|
5252
|
+
parameters = JSON.parse(els.toolParams.value || '{}');
|
|
5253
|
+
} catch {
|
|
5254
|
+
return toolErr('Parametre şeması geçerli JSON değil');
|
|
5255
|
+
}
|
|
5256
|
+
const code = String(els.toolCode.value || '').trim();
|
|
5257
|
+
if (!code) return toolErr('run.js kodu boş olamaz');
|
|
5258
|
+
const r = await beast
|
|
5259
|
+
.toolSave({ id: toolUi.editId, name, description: String(els.toolDesc.value || ''), parameters, code })
|
|
5260
|
+
.catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
|
|
5261
|
+
if (r && r.ok) {
|
|
5262
|
+
closeToolDialog();
|
|
5263
|
+
toast('Tool kaydedildi — ajanlar tool__' + r.id + ' olarak çağırabilir (Finance dahil)');
|
|
5264
|
+
renderToolBox();
|
|
5265
|
+
} else {
|
|
5266
|
+
toolErr((r && r.error) || 'Kaydedilemedi');
|
|
5267
|
+
}
|
|
5268
|
+
});
|
|
5269
|
+
}
|
|
5270
|
+
if (els.toolDeleteBtn) {
|
|
5271
|
+
els.toolDeleteBtn.addEventListener('click', async () => {
|
|
5272
|
+
if (!toolUi.editId) return;
|
|
5273
|
+
const t = toolUi.tools.find((x) => x.id === toolUi.editId);
|
|
5274
|
+
if (!(await uiConfirm('"' + ((t && t.name) || toolUi.editId) + '" toolu silinsin mi?', 'Sil'))) return;
|
|
5275
|
+
const r = await beast.toolDelete(toolUi.editId).catch(() => ({ ok: false }));
|
|
5276
|
+
if (r && r.ok) {
|
|
5277
|
+
closeToolDialog();
|
|
5278
|
+
toast('Tool silindi');
|
|
5279
|
+
renderToolBox();
|
|
5280
|
+
} else toast((r && r.error) || 'Silinemedi');
|
|
5281
|
+
});
|
|
5282
|
+
}
|
|
5283
|
+
if (els.toolTestBtn) {
|
|
5284
|
+
els.toolTestBtn.addEventListener('click', async () => {
|
|
5285
|
+
toolErr('');
|
|
5286
|
+
let args = {};
|
|
5287
|
+
try {
|
|
5288
|
+
args = JSON.parse(els.toolTestArgs.value || '{}');
|
|
5289
|
+
} catch {
|
|
5290
|
+
return toolErr('Test argümanları geçerli JSON değil');
|
|
5291
|
+
}
|
|
5292
|
+
const id = toolUi.editId || String(els.toolName.value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '_').replace(/^_+|_+$/g, '');
|
|
5293
|
+
if (!id) return toolErr('Önce tool adı gir');
|
|
5294
|
+
/* önce kaydet (yeni tool ilk testte de diskte olsun) */
|
|
5295
|
+
const sr = await beast
|
|
5296
|
+
.toolSave({
|
|
5297
|
+
id: toolUi.editId,
|
|
5298
|
+
name: String(els.toolName.value || id),
|
|
5299
|
+
description: String(els.toolDesc.value || ''),
|
|
5300
|
+
parameters: (() => {
|
|
5301
|
+
try { return JSON.parse(els.toolParams.value || '{}'); } catch { return null; }
|
|
5302
|
+
})() || { type: 'object', properties: {} },
|
|
5303
|
+
code: String(els.toolCode.value || ''),
|
|
5304
|
+
})
|
|
5305
|
+
.catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
|
|
5306
|
+
if (!sr || !sr.ok) return toolErr((sr && sr.error) || 'Tool kaydedilemedi');
|
|
5307
|
+
els.toolTestOut.hidden = false;
|
|
5308
|
+
els.toolTestOut.textContent = 'çalışıyor…';
|
|
5309
|
+
const r = await beast.toolRun(sr.id, args).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
|
|
5310
|
+
els.toolTestOut.textContent = JSON.stringify(r, null, 2).slice(0, 8000);
|
|
5311
|
+
renderToolBox();
|
|
5312
|
+
});
|
|
5313
|
+
}
|
|
5314
|
+
|
|
5113
5315
|
function renderAgentsPane() {
|
|
5114
5316
|
const pane = $('#tab-agents');
|
|
5115
5317
|
if (!pane) return;
|
|
@@ -5268,6 +5470,20 @@ function onEvent(ev) {
|
|
|
5268
5470
|
if ($('#dmRail') && !$('#dmRail').classList.contains('dm-hidden')) renderDmRail();
|
|
5269
5471
|
return;
|
|
5270
5472
|
}
|
|
5473
|
+
/* KİŞİSEL TOOL çalışma kaydı — TOOLS konsolunun nokta göstergeleri */
|
|
5474
|
+
if (ev.type === 'tool-log') {
|
|
5475
|
+
toolUi.logRows.push({
|
|
5476
|
+
at: ev.at,
|
|
5477
|
+
tool: ev.tool,
|
|
5478
|
+
toolId: ev.toolId,
|
|
5479
|
+
ok: ev.ok,
|
|
5480
|
+
ms: ev.ms,
|
|
5481
|
+
error: ev.error,
|
|
5482
|
+
});
|
|
5483
|
+
if (toolUi.logRows.length > 60) toolUi.logRows.splice(0, toolUi.logRows.length - 60);
|
|
5484
|
+
scheduleToolRender();
|
|
5485
|
+
return;
|
|
5486
|
+
}
|
|
5271
5487
|
/* OFFLINE MESAJ KUYRUĞU: bağlantı + kuyruk olayları (sessionId filtresinden önce) */
|
|
5272
5488
|
if (ev.type === 'net' || ev.type === 'netQueue') { onNetEvent(ev); return; }
|
|
5273
5489
|
/* Sandbox klonlama progress'i (sessionId'siz global olay) */
|
|
@@ -6415,6 +6631,7 @@ async function init() {
|
|
|
6415
6631
|
};
|
|
6416
6632
|
|
|
6417
6633
|
refreshAgentsPane();
|
|
6634
|
+
renderToolBox(); // TOOLS konsolu: kişisel araçlar sol panelde
|
|
6418
6635
|
|
|
6419
6636
|
els.sendBtn.addEventListener('click', sendCurrent);
|
|
6420
6637
|
els.stopBtn.addEventListener('click', () => {
|
|
@@ -8616,7 +8833,15 @@ async function setFinanceMode(on) {
|
|
|
8616
8833
|
const brandSub = document.querySelector('#brand .brand-sub');
|
|
8617
8834
|
if (brandSub) brandSub.textContent = on ? 'Finance' : 'Agent';
|
|
8618
8835
|
try { localStorage.setItem('beast.financeMode', on ? '1' : '0'); } catch {}
|
|
8619
|
-
beast.financeMode(!!on, activeId).catch(() => {});
|
|
8836
|
+
const fm = await beast.financeMode(!!on, activeId).catch(() => ({}));
|
|
8837
|
+
/* finance modu açıldı ama aktif oturum NORMAL (örn. WhatsApp sohbeti) ise
|
|
8838
|
+
ASLA o oturumu finance'e çevirme — kendine ÖZEL finance oturumu aç */
|
|
8839
|
+
if (on && fm && fm.needNew) {
|
|
8840
|
+
try {
|
|
8841
|
+
const created = await beast.createSession();
|
|
8842
|
+
if (created && created.id) await openSession(created.id);
|
|
8843
|
+
} catch {}
|
|
8844
|
+
}
|
|
8620
8845
|
/* sohbet geçmişi listesi değişir: finance modunda yalnız finance sohbetleri */
|
|
8621
8846
|
try {
|
|
8622
8847
|
await refreshSessions();
|
package/src/renderer/style.css
CHANGED
|
@@ -2274,6 +2274,225 @@ body.rail-hidden #dmRailHead { padding-right: 148px; }
|
|
|
2274
2274
|
letter-spacing: 1.5px;
|
|
2275
2275
|
color: var(--muted);
|
|
2276
2276
|
}
|
|
2277
|
+
|
|
2278
|
+
/* ---------- sol panel: TOOLS konsolu (kişisel araçlar) ---------- */
|
|
2279
|
+
#toolBox {
|
|
2280
|
+
flex: none;
|
|
2281
|
+
max-height: 32%;
|
|
2282
|
+
display: none;
|
|
2283
|
+
flex-direction: column;
|
|
2284
|
+
border-top: 1px solid var(--border);
|
|
2285
|
+
padding: 6px 8px 6px;
|
|
2286
|
+
}
|
|
2287
|
+
body:not(.ide-mode):not(.studio-mode):not(.sandbox-mode) #toolBox { display: flex; }
|
|
2288
|
+
#toolBoxHead {
|
|
2289
|
+
display: flex;
|
|
2290
|
+
align-items: center;
|
|
2291
|
+
justify-content: space-between;
|
|
2292
|
+
padding: 2px 2px 6px;
|
|
2293
|
+
font-size: 10.5px;
|
|
2294
|
+
font-weight: 800;
|
|
2295
|
+
letter-spacing: 1.5px;
|
|
2296
|
+
color: var(--muted);
|
|
2297
|
+
}
|
|
2298
|
+
#toolList {
|
|
2299
|
+
min-height: 40px;
|
|
2300
|
+
max-height: 200px;
|
|
2301
|
+
overflow-y: auto;
|
|
2302
|
+
display: flex;
|
|
2303
|
+
flex-direction: column;
|
|
2304
|
+
gap: 2px;
|
|
2305
|
+
}
|
|
2306
|
+
.tool-empty {
|
|
2307
|
+
color: var(--muted);
|
|
2308
|
+
font-size: 10.5px;
|
|
2309
|
+
line-height: 1.5;
|
|
2310
|
+
padding: 6px 2px;
|
|
2311
|
+
}
|
|
2312
|
+
.tool-row {
|
|
2313
|
+
display: flex;
|
|
2314
|
+
align-items: center;
|
|
2315
|
+
gap: 6px;
|
|
2316
|
+
min-width: 0;
|
|
2317
|
+
padding: 3px 4px;
|
|
2318
|
+
border-radius: 6px;
|
|
2319
|
+
cursor: pointer;
|
|
2320
|
+
color: var(--text);
|
|
2321
|
+
}
|
|
2322
|
+
.tool-row:hover { background: var(--panel2); }
|
|
2323
|
+
.tool-dot {
|
|
2324
|
+
flex: none;
|
|
2325
|
+
width: 7px;
|
|
2326
|
+
height: 7px;
|
|
2327
|
+
border-radius: 50%;
|
|
2328
|
+
background: var(--border);
|
|
2329
|
+
}
|
|
2330
|
+
.tool-dot.ok { background: #43c463; }
|
|
2331
|
+
.tool-dot.err { background: var(--err); }
|
|
2332
|
+
.tool-name {
|
|
2333
|
+
flex: 1;
|
|
2334
|
+
min-width: 0;
|
|
2335
|
+
font-size: 12px;
|
|
2336
|
+
font-weight: 600;
|
|
2337
|
+
white-space: nowrap;
|
|
2338
|
+
overflow: hidden;
|
|
2339
|
+
text-overflow: ellipsis;
|
|
2340
|
+
}
|
|
2341
|
+
.tool-btns {
|
|
2342
|
+
flex: none;
|
|
2343
|
+
display: flex;
|
|
2344
|
+
gap: 2px;
|
|
2345
|
+
visibility: hidden;
|
|
2346
|
+
}
|
|
2347
|
+
.tool-row:hover .tool-btns { visibility: visible; }
|
|
2348
|
+
.tool-btns button {
|
|
2349
|
+
border: none;
|
|
2350
|
+
background: none;
|
|
2351
|
+
color: var(--muted);
|
|
2352
|
+
font-size: 11px;
|
|
2353
|
+
cursor: pointer;
|
|
2354
|
+
padding: 2px 5px;
|
|
2355
|
+
border-radius: 4px;
|
|
2356
|
+
}
|
|
2357
|
+
.tool-btns button:hover { background: var(--accent-dim); color: var(--accent); }
|
|
2358
|
+
.tool-btns .tb-del:hover { color: var(--err); background: rgba(220, 60, 60, 0.12); }
|
|
2359
|
+
|
|
2360
|
+
/* ---------- TOOL editör dialogu ---------- */
|
|
2361
|
+
#toolOverlay {
|
|
2362
|
+
position: fixed;
|
|
2363
|
+
inset: 0;
|
|
2364
|
+
background: rgba(0, 0, 0, 0.45);
|
|
2365
|
+
z-index: 300;
|
|
2366
|
+
display: flex;
|
|
2367
|
+
align-items: center;
|
|
2368
|
+
justify-content: center;
|
|
2369
|
+
}
|
|
2370
|
+
#toolOverlay[hidden] { display: none; }
|
|
2371
|
+
#toolDialog {
|
|
2372
|
+
width: min(760px, 92vw);
|
|
2373
|
+
max-height: 88vh;
|
|
2374
|
+
overflow-y: auto;
|
|
2375
|
+
background: var(--panel);
|
|
2376
|
+
border: 1px solid var(--border);
|
|
2377
|
+
border-radius: 12px;
|
|
2378
|
+
padding: 14px 16px;
|
|
2379
|
+
display: flex;
|
|
2380
|
+
flex-direction: column;
|
|
2381
|
+
gap: 10px;
|
|
2382
|
+
}
|
|
2383
|
+
#toolDialogHead {
|
|
2384
|
+
display: flex;
|
|
2385
|
+
align-items: center;
|
|
2386
|
+
justify-content: space-between;
|
|
2387
|
+
font-size: 13px;
|
|
2388
|
+
font-weight: 800;
|
|
2389
|
+
letter-spacing: 1px;
|
|
2390
|
+
color: var(--text);
|
|
2391
|
+
}
|
|
2392
|
+
#toolDialogHead button {
|
|
2393
|
+
border: none;
|
|
2394
|
+
background: none;
|
|
2395
|
+
color: var(--muted);
|
|
2396
|
+
font-size: 13px;
|
|
2397
|
+
cursor: pointer;
|
|
2398
|
+
padding: 2px 6px;
|
|
2399
|
+
border-radius: 6px;
|
|
2400
|
+
}
|
|
2401
|
+
#toolDialogHead button:hover { color: var(--err); background: rgba(220, 60, 60, 0.1); }
|
|
2402
|
+
.tool-form {
|
|
2403
|
+
display: flex;
|
|
2404
|
+
flex-direction: column;
|
|
2405
|
+
gap: 4px;
|
|
2406
|
+
}
|
|
2407
|
+
.tool-form label {
|
|
2408
|
+
font-size: 10.5px;
|
|
2409
|
+
font-weight: 700;
|
|
2410
|
+
letter-spacing: 0.5px;
|
|
2411
|
+
color: var(--muted);
|
|
2412
|
+
margin-top: 4px;
|
|
2413
|
+
}
|
|
2414
|
+
.tool-form input,
|
|
2415
|
+
.tool-form textarea {
|
|
2416
|
+
width: 100%;
|
|
2417
|
+
background: var(--bg);
|
|
2418
|
+
color: var(--text);
|
|
2419
|
+
border: 1px solid var(--border);
|
|
2420
|
+
border-radius: 8px;
|
|
2421
|
+
padding: 7px 9px;
|
|
2422
|
+
font-size: 12px;
|
|
2423
|
+
font-family: Consolas, monospace;
|
|
2424
|
+
}
|
|
2425
|
+
.tool-form input { font-family: inherit; }
|
|
2426
|
+
.tool-form textarea { resize: vertical; min-height: 60px; }
|
|
2427
|
+
.tool-form input:focus,
|
|
2428
|
+
.tool-form textarea:focus { outline: none; border-color: var(--accent); }
|
|
2429
|
+
#toolTestRow {
|
|
2430
|
+
display: flex;
|
|
2431
|
+
gap: 6px;
|
|
2432
|
+
}
|
|
2433
|
+
#toolTestArgs {
|
|
2434
|
+
flex: 1;
|
|
2435
|
+
background: var(--bg);
|
|
2436
|
+
color: var(--text);
|
|
2437
|
+
border: 1px solid var(--border);
|
|
2438
|
+
border-radius: 8px;
|
|
2439
|
+
padding: 7px 9px;
|
|
2440
|
+
font-size: 12px;
|
|
2441
|
+
font-family: Consolas, monospace;
|
|
2442
|
+
}
|
|
2443
|
+
#toolTestArgs:focus { outline: none; border-color: var(--accent); }
|
|
2444
|
+
#toolTestBtn {
|
|
2445
|
+
border: none;
|
|
2446
|
+
background: var(--accent-dim);
|
|
2447
|
+
color: var(--accent);
|
|
2448
|
+
font-weight: 800;
|
|
2449
|
+
font-size: 12px;
|
|
2450
|
+
border-radius: 8px;
|
|
2451
|
+
padding: 7px 12px;
|
|
2452
|
+
cursor: pointer;
|
|
2453
|
+
}
|
|
2454
|
+
#toolTestBtn:hover { filter: brightness(1.1); }
|
|
2455
|
+
#toolTestOut {
|
|
2456
|
+
margin: 0;
|
|
2457
|
+
max-height: 220px;
|
|
2458
|
+
overflow: auto;
|
|
2459
|
+
background: var(--bg);
|
|
2460
|
+
border: 1px solid var(--border);
|
|
2461
|
+
border-radius: 8px;
|
|
2462
|
+
padding: 8px 10px;
|
|
2463
|
+
font-size: 11px;
|
|
2464
|
+
line-height: 1.5;
|
|
2465
|
+
white-space: pre-wrap;
|
|
2466
|
+
word-break: break-word;
|
|
2467
|
+
color: var(--text);
|
|
2468
|
+
}
|
|
2469
|
+
#toolDialogBtns {
|
|
2470
|
+
display: flex;
|
|
2471
|
+
align-items: center;
|
|
2472
|
+
gap: 8px;
|
|
2473
|
+
}
|
|
2474
|
+
.tool-primary {
|
|
2475
|
+
border: none;
|
|
2476
|
+
background: var(--accent);
|
|
2477
|
+
color: #fff;
|
|
2478
|
+
font-weight: 800;
|
|
2479
|
+
font-size: 12.5px;
|
|
2480
|
+
border-radius: 8px;
|
|
2481
|
+
padding: 8px 16px;
|
|
2482
|
+
cursor: pointer;
|
|
2483
|
+
}
|
|
2484
|
+
.tool-primary:hover { filter: brightness(1.08); }
|
|
2485
|
+
.tool-danger {
|
|
2486
|
+
border: none;
|
|
2487
|
+
background: rgba(220, 60, 60, 0.12);
|
|
2488
|
+
color: var(--err);
|
|
2489
|
+
font-weight: 700;
|
|
2490
|
+
font-size: 12px;
|
|
2491
|
+
border-radius: 8px;
|
|
2492
|
+
padding: 8px 14px;
|
|
2493
|
+
cursor: pointer;
|
|
2494
|
+
}
|
|
2495
|
+
.tool-danger:hover { background: rgba(220, 60, 60, 0.2); }
|
|
2277
2496
|
.bot-bar-btns { display: flex; gap: 4px; }
|
|
2278
2497
|
.bot-bar-btns button {
|
|
2279
2498
|
border: none; background: none; color: var(--muted);
|