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.
Files changed (41) hide show
  1. package/package.json +1 -1
  2. package/src/agent/engine.js +499 -154
  3. package/src/agent/financetools.js +307 -0
  4. package/src/agent/opencode/agents.js +161 -0
  5. package/src/agent/opencode/askperm.js +135 -0
  6. package/src/agent/opencode/index.js +51 -0
  7. package/src/agent/opencode/permission.js +108 -0
  8. package/src/agent/opencode/prompts/anthropic.txt +105 -0
  9. package/src/agent/opencode/prompts/beast.txt +147 -0
  10. package/src/agent/opencode/prompts/build-switch.txt +5 -0
  11. package/src/agent/opencode/prompts/codex.txt +79 -0
  12. package/src/agent/opencode/prompts/default.txt +95 -0
  13. package/src/agent/opencode/prompts/edit.txt +10 -0
  14. package/src/agent/opencode/prompts/explore.txt +18 -0
  15. package/src/agent/opencode/prompts/gemini.txt +155 -0
  16. package/src/agent/opencode/prompts/glob.txt +6 -0
  17. package/src/agent/opencode/prompts/gpt.txt +107 -0
  18. package/src/agent/opencode/prompts/grep.txt +8 -0
  19. package/src/agent/opencode/prompts/kimi.txt +95 -0
  20. package/src/agent/opencode/prompts/max-steps.txt +16 -0
  21. package/src/agent/opencode/prompts/meta.txt +65 -0
  22. package/src/agent/opencode/prompts/plan.txt +26 -0
  23. package/src/agent/opencode/prompts/read.txt +14 -0
  24. package/src/agent/opencode/prompts/skill.txt +5 -0
  25. package/src/agent/opencode/prompts/task.txt +19 -0
  26. package/src/agent/opencode/prompts/todowrite.txt +44 -0
  27. package/src/agent/opencode/prompts/trinity.txt +97 -0
  28. package/src/agent/opencode/prompts/webfetch.txt +13 -0
  29. package/src/agent/opencode/prompts/websearch.txt +14 -0
  30. package/src/agent/opencode/prompts/write.txt +8 -0
  31. package/src/agent/opencode/systemprompt.js +157 -0
  32. package/src/agent/opencode/toolmap.js +316 -0
  33. package/src/agent/scripts/mt5_bridge.py +381 -0
  34. package/src/agent/tools.js +10 -3
  35. package/src/main.js +503 -69
  36. package/src/mt5bridge.js +215 -0
  37. package/src/preload.js +13 -3
  38. package/src/renderer/index.html +67 -0
  39. package/src/renderer/renderer.js +552 -53
  40. package/src/renderer/style.css +328 -1
  41. package/src/agent/android.js +0 -265
@@ -0,0 +1,215 @@
1
+ 'use strict';
2
+
3
+ /* Beast Finance — MT5 köprü yöneticisi (main süreç).
4
+ Python stdio köprüsünü (mt5_bridge.py) spawn eder; JSON satır istek/yanıt
5
+ ve event akışını yönetir. Python bulunamazsa aday listesinde sıradakini
6
+ dener; süreç düşerse backoff ile yeniden başlatır. */
7
+
8
+ const { spawn } = require('child_process');
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+ const { EventEmitter } = require('events');
12
+
13
+ const BUNDLED_SCRIPT = path.join(__dirname, 'agent', 'scripts', 'mt5_bridge.py');
14
+
15
+ /* Paketli uygulamada köprü scripti app.asar İÇİNDE kalır — harici Python
16
+ onu OKUYAMAZ. Her başlatmada %APPDATA%\beast\finance altına güncel haliyle
17
+ yazılır (Node asar içini okuyabilir) ve oradan çalıştırılır. */
18
+ function scriptPath() {
19
+ try {
20
+ const dest = path.join(process.env.APPDATA || process.env.TMP || process.cwd(), 'beast', 'finance', 'mt5_bridge.py');
21
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
22
+ fs.copyFileSync(BUNDLED_SCRIPT, dest);
23
+ return dest;
24
+ } catch {
25
+ return BUNDLED_SCRIPT;
26
+ }
27
+ }
28
+
29
+ class MT5Bridge extends EventEmitter {
30
+ constructor() {
31
+ super();
32
+ this.proc = null;
33
+ this._buf = '';
34
+ this._reqId = 1;
35
+ this._pending = new Map();
36
+ this._stopped = true;
37
+ this._backoff = 1500;
38
+ this._lastError = '';
39
+ this._terminal = null;
40
+ this._account = null;
41
+ this._candidates = ['python', 'py -3'];
42
+ this._candIdx = 0;
43
+ this._terminalPath = '';
44
+ this._everConnected = false;
45
+ }
46
+
47
+ get running() {
48
+ return !!this.proc;
49
+ }
50
+
51
+ status() {
52
+ return {
53
+ running: this.running,
54
+ connected: !!this._terminal,
55
+ terminal: this._terminal,
56
+ account: this._account,
57
+ error: this._lastError,
58
+ python: this._candidates[this._candIdx] || '',
59
+ };
60
+ }
61
+
62
+ /* opts: { candidates: ['python','py -3'], terminal: 'C:\\...\\terminal64.exe' } */
63
+ start(opts = {}) {
64
+ if (Array.isArray(opts.candidates) && opts.candidates.length) {
65
+ const next = opts.candidates.map((c) => String(c).trim()).filter(Boolean);
66
+ if (next.join('|') !== this._candidates.join('|')) {
67
+ this._candidates = next;
68
+ this._candIdx = 0;
69
+ }
70
+ }
71
+ this._terminalPath = String(opts.terminal || '').trim();
72
+ this._stopped = false;
73
+ if (this.running) return;
74
+ this._spawn();
75
+ }
76
+
77
+ _spawn() {
78
+ if (this._stopped) return;
79
+ const cand = this._candidates[this._candIdx] || 'python';
80
+ /* 'py -3' gibi çok kelimeli kısa komutlar bölünür; tam yol tek exe'dir */
81
+ const parts = /[\\/]/.test(cand) ? [cand] : cand.split(/\s+/);
82
+ const exe = parts[0];
83
+ const preArgs = parts.slice(1);
84
+ let proc;
85
+ try {
86
+ proc = spawn(exe, [...preArgs, scriptPath(), ...(this._terminalPath ? ['--terminal', this._terminalPath] : [])], {
87
+ stdio: ['pipe', 'pipe', 'pipe'],
88
+ windowsHide: true,
89
+ });
90
+ } catch (e) {
91
+ this._lastError = 'Python başlatılamadı: ' + String((e && e.message) || e);
92
+ this.emit('log', { level: 'error', line: this._lastError });
93
+ this._nextCandidateOrRetry();
94
+ return;
95
+ }
96
+ this.proc = proc;
97
+ this._buf = '';
98
+ proc.stdout.setEncoding('utf8');
99
+ proc.stdout.on('data', (chunk) => {
100
+ this._buf += chunk;
101
+ let i;
102
+ while ((i = this._buf.indexOf('\n')) >= 0) {
103
+ const line = this._buf.slice(0, i).trim();
104
+ this._buf = this._buf.slice(i + 1);
105
+ if (line) this._onLine(line);
106
+ }
107
+ });
108
+ proc.stderr.setEncoding('utf8');
109
+ proc.stderr.on('data', (c) => {
110
+ const t = String(c).trim();
111
+ if (t) this.emit('log', { level: 'error', line: t.slice(0, 400) });
112
+ });
113
+ proc.on('error', (e) => {
114
+ this._lastError = `Python (${exe}) bulunamadı/başlatılamadı: ` + String((e && e.message) || e);
115
+ this.emit('log', { level: 'error', line: this._lastError });
116
+ try { this._failAll(this._lastError); } catch {}
117
+ if (this.proc === proc) this.proc = null;
118
+ this._nextCandidateOrRetry();
119
+ });
120
+ proc.on('exit', (code) => {
121
+ if (this.proc === proc) this.proc = null;
122
+ this._terminal = null;
123
+ this._account = null;
124
+ try { this._failAll('MT5 köprüsü kapandı (kod ' + code + ')'); } catch {}
125
+ this.emit('bridge', { connected: false, error: this._lastError || ('süreç kapandı (kod ' + code + ')') });
126
+ this._nextCandidateOrRetry();
127
+ });
128
+ }
129
+
130
+ /* aday python'ları sırayla dener; hepsi tükendiyse backoff ile yeniden dener */
131
+ _nextCandidateOrRetry() {
132
+ if (this._stopped) return;
133
+ if (this._candIdx < this._candidates.length - 1) {
134
+ this._candIdx += 1;
135
+ this.emit('log', { level: 'info', line: 'python adayı deneniyor: ' + this._candidates[this._candIdx] });
136
+ setTimeout(() => { try { this._spawn(); } catch {} }, 300);
137
+ return;
138
+ }
139
+ this._candIdx = 0;
140
+ setTimeout(() => { try { if (!this._stopped && !this.running) this._spawn(); } catch {} }, this._backoff);
141
+ this._backoff = Math.min(this._backoff * 2, 15000);
142
+ }
143
+
144
+ _onLine(line) {
145
+ let msg;
146
+ try { msg = JSON.parse(line); } catch {
147
+ this.emit('log', { level: 'info', line: line.slice(0, 240) });
148
+ return;
149
+ }
150
+ if (msg.event) {
151
+ if (msg.event === 'bridge') {
152
+ this._terminal = msg.terminal || null;
153
+ this._account = msg.account || null;
154
+ this._lastError = String(msg.error || '');
155
+ if (msg.connected) {
156
+ this._everConnected = true;
157
+ this._backoff = 1500;
158
+ }
159
+ this.emit('bridge', msg);
160
+ } else if (msg.event === 'log') {
161
+ this.emit('log', msg);
162
+ } else {
163
+ this.emit('event', msg);
164
+ }
165
+ return;
166
+ }
167
+ if (msg.id != null && this._pending.has(msg.id)) {
168
+ const en = this._pending.get(msg.id);
169
+ this._pending.delete(msg.id);
170
+ clearTimeout(en.timer);
171
+ en.resolve(msg);
172
+ }
173
+ }
174
+
175
+ call(method, params, timeoutMs = 12000) {
176
+ if (!this.running) return Promise.resolve({ ok: false, error: 'MT5 köprüsü çalışmıyor' });
177
+ const id = this._reqId++;
178
+ let resolveFn = null;
179
+ const p = new Promise((resolve) => { resolveFn = resolve; });
180
+ const timer = setTimeout(() => {
181
+ this._pending.delete(id);
182
+ resolveFn({ ok: false, error: 'MT5 zaman aşımı: ' + method });
183
+ }, Math.max(2000, Number(timeoutMs) || 12000));
184
+ this._pending.set(id, { timer, resolve: resolveFn });
185
+ try {
186
+ this.proc.stdin.write(JSON.stringify({ id, method, params: params || {} }) + '\n');
187
+ } catch (e) {
188
+ clearTimeout(timer);
189
+ this._pending.delete(id);
190
+ resolveFn({ ok: false, error: 'köprü yazma hatası: ' + String((e && e.message) || e) });
191
+ }
192
+ return p;
193
+ }
194
+
195
+ stop() {
196
+ this._stopped = true;
197
+ try { this._failAll('köprü kapatıldı'); } catch {}
198
+ if (this.proc) {
199
+ try { this.proc.kill(); } catch {}
200
+ this.proc = null;
201
+ }
202
+ this._terminal = null;
203
+ this._account = null;
204
+ }
205
+
206
+ _failAll(err) {
207
+ for (const [, en] of this._pending) {
208
+ clearTimeout(en.timer);
209
+ en.resolve({ ok: false, error: err });
210
+ }
211
+ this._pending.clear();
212
+ }
213
+ }
214
+
215
+ module.exports = new MT5Bridge();
package/src/preload.js CHANGED
@@ -87,6 +87,8 @@ contextBridge.exposeInMainWorld('beast', {
87
87
  bcOpen: (id) => ipcRenderer.invoke('bc:open', { id }),
88
88
  bcDelete: (id) => ipcRenderer.invoke('bc:delete', { id }),
89
89
  bcDeleteAll: () => ipcRenderer.invoke('bc:deleteAll'),
90
+ /* opencode permission (BC): UI kartı cevabı — 'once' | 'always' | 'reject' */
91
+ permissionReply: (requestId, action, message) => ipcRenderer.invoke('permission:reply', { requestId, action, message }),
90
92
  studioSend: (msg, attachments) => ipcRenderer.invoke('studio:send', { msg, attachments }),
91
93
  studioStop: () => ipcRenderer.invoke('studio:stop'),
92
94
  studioNew: () => ipcRenderer.invoke('studio:new'),
@@ -94,6 +96,17 @@ contextBridge.exposeInMainWorld('beast', {
94
96
  studioTree: (rel) => ipcRenderer.invoke('studio:tree', rel),
95
97
  studioPickVideo: () => ipcRenderer.invoke('studio:pickvideo'),
96
98
  studioDelete: (rel) => ipcRenderer.invoke('studio:delete', rel),
99
+
100
+ financeState: () => ipcRenderer.invoke('finance:state'),
101
+ financeSnapshot: () => ipcRenderer.invoke('finance:snapshot'),
102
+ financeMode: (on, sessionId) => ipcRenderer.invoke('finance:mode', { on, sessionId }),
103
+ financeSettings: (patch) => ipcRenderer.invoke('finance:settings', patch),
104
+ financeTraderStart: () => ipcRenderer.invoke('finance:trader:start'),
105
+ financeTraderStop: () => ipcRenderer.invoke('finance:trader:stop'),
106
+ financeConnect: () => ipcRenderer.invoke('finance:connect'),
107
+ financeClose: (ticket, volume) => ipcRenderer.invoke('finance:close', { ticket, volume }),
108
+ financeInstall: () => ipcRenderer.invoke('finance:install'),
109
+ financeSymbolsList: (filter) => ipcRenderer.invoke('finance:symbols:list', filter),
97
110
  clipRead: () => ipcRenderer.invoke('clip:read'),
98
111
  clipWrite: (t) => ipcRenderer.invoke('clip:write', t),
99
112
  thinkSet: (v) => ipcRenderer.invoke('think:set', v),
@@ -227,9 +240,6 @@ contextBridge.exposeInMainWorld('beast', {
227
240
  headroomToggle: (on) => ipcRenderer.invoke('headroom:toggle', !!on),
228
241
  headroomStatus: () => ipcRenderer.invoke('headroom:status'),
229
242
  headroomStats: () => ipcRenderer.invoke('headroom:stats'),
230
- androidStatus: () => ipcRenderer.invoke('android:status'),
231
- androidInstall: () => ipcRenderer.invoke('android:install'),
232
- androidStartAvd: (name) => ipcRenderer.invoke('android:startAvd', name),
233
243
  agentsClearAll: () => ipcRenderer.invoke('agents:clearAll'),
234
244
  ideDelete: (rel) => ipcRenderer.invoke('ide:delete', rel),
235
245
  onWaEvent: (cb) => ipcRenderer.on('wa:event', (_e, ev) => cb(ev)),
@@ -90,6 +90,7 @@
90
90
  </div>
91
91
  </div>
92
92
  <button id="studioBtn" class="dd-btn dd-icon-btn" title="Beast Studio — video yapma/düzenleme" data-i18n-title="tipStudio"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="6" width="13" height="12" rx="2"/><path d="M15 10l7-4v12l-7-4"/></svg></button>
93
+ <button id="finBtn" class="dd-btn dd-icon-btn" title="Beast Finance — MT5 trade paneli (bilgisayardaki MetaTrader 5 ile çalışır)"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M7 15l4-5 3 3 5-7"/></svg></button>
93
94
  <button id="sbBtn" class="dd-btn dd-icon-btn" title="Beast Sandbox — GitHub reposu indir &amp; içinde çalış" data-i18n-title="tipSandbox"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg></button>
94
95
  <button id="railBtn" class="dd-btn dd-icon-btn" title="Paralel Ajan Konsolu" data-i18n-title="tipRail">&#x25A4;</button>
95
96
  <button id="watchBtn" class="dd-btn dd-icon-btn" title="İzleyiciler (Watchers)" data-i18n-title="tipWatch">&#x25CE;</button>
@@ -130,6 +131,57 @@
130
131
  </div>
131
132
  </div>
132
133
 
134
+ <section id="finPanel">
135
+ <div id="finHead">
136
+ <span id="finTitle">BEAST FINANCE</span>
137
+ <span id="finDot" class="fin-dot off" title="MT5 köprüsü"></span>
138
+ <span id="finTerm" title="">MT5</span>
139
+ <span class="file-spacer"></span>
140
+ <button id="finReconnect" title="MT5'e yeniden bağlan">&#x27F3;</button>
141
+ </div>
142
+ <div id="finBody">
143
+ <div class="fin-card">
144
+ <div class="fin-card-title">HESAP</div>
145
+ <div class="fin-grid">
146
+ <div class="fin-kv"><span>Bakiye</span><b id="finBalance">—</b></div>
147
+ <div class="fin-kv"><span>Özkaynak</span><b id="finEquity">—</b></div>
148
+ <div class="fin-kv"><span>Kâr/Zarar</span><b id="finProfit">—</b></div>
149
+ <div class="fin-kv"><span>Serbest Marj</span><b id="finFree">—</b></div>
150
+ </div>
151
+ </div>
152
+ <div class="fin-card">
153
+ <div class="fin-card-title">TRADE AJANI <span id="finTraderDot" class="fin-dot off" title="Trader durumu"></span></div>
154
+ <div class="fin-row"><select id="finTraderModel" title="Trader ajanının modeli — farklı API/model seçilebilir"></select></div>
155
+ <div class="fin-row fin-mini">
156
+ <label>Tur (sn)</label><input id="finInterval" type="number" min="30" max="3600" step="10" />
157
+ <label>Max lot</label><input id="finMaxLot" type="number" min="0.01" max="100" step="0.01" />
158
+ </div>
159
+ <div class="fin-row fin-mini"><label>Semboller</label><button id="finSymBtn" class="fin-sym-select" title="MT5 sembol listesinden seç — tıkla">&#8230;</button></div>
160
+ <div class="fin-row">
161
+ <label class="fin-check"><input id="finAllow" type="checkbox" /> Otomatik işlem (ajan trade açabilir)</label>
162
+ <button id="finTraderBtn" class="fin-start">&#9654;&#xFE0E; Ajanı Başlat</button>
163
+ </div>
164
+ <div id="finTraderStatus">Kapalı</div>
165
+ </div>
166
+ <div class="fin-card">
167
+ <div class="fin-card-title">POZİSYONLAR <span id="finPosCount"></span></div>
168
+ <div id="finPosList"><div class="fin-empty">Açık pozisyon yok</div></div>
169
+ </div>
170
+ <div class="fin-card">
171
+ <div class="fin-card-title">
172
+ PİYASA
173
+ <span class="file-spacer"></span>
174
+ <button id="finSymAdd" title="Sembol ekle/çıkar — MT5 sembol listesinden seç">&#43;</button>
175
+ </div>
176
+ <div id="finSymList"><div class="fin-empty">—</div></div>
177
+ </div>
178
+ <div class="fin-card fin-grow">
179
+ <div class="fin-card-title">AKIŞ</div>
180
+ <div id="finLog"></div>
181
+ </div>
182
+ </div>
183
+ </section>
184
+
133
185
  <div id="ideRow">
134
186
  <section id="codePane">
135
187
  <div id="codeTabsBar">
@@ -476,6 +528,21 @@
476
528
  </div>
477
529
  </div>
478
530
 
531
+ <div id="finSymOverlay" class="mini-overlay" hidden>
532
+ <div class="mini-dialog fin-sym-dialog">
533
+ <div class="mini-head">
534
+ <span class="mini-title">MT5 SEMBOLLERİ</span>
535
+ <span id="finSymCount" class="fin-sym-count"></span>
536
+ <button class="mini-close" id="finSymClose" title="Kapat">&#x00D7;</button>
537
+ </div>
538
+ <div class="mini-body">
539
+ <input id="finSymSearch" placeholder="Sembol ara… (örn: XAU, BTC, EUR, US500)" spellcheck="false" autocomplete="off" />
540
+ <div id="finSymPick" class="mini-list"></div>
541
+ <div class="fin-sym-hint">Tıkla: izleme listesine ekle/çıkar — PİYASA kartına anında düşer</div>
542
+ </div>
543
+ </div>
544
+ </div>
545
+
479
546
  <div id="cronOverlay" class="mini-overlay" hidden>
480
547
  <div class="mini-dialog">
481
548
  <div class="mini-head">