beast-agent 2.38.4 → 2.39.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/bots.js +66 -0
- package/src/agent/customtools.js +1 -1
- package/src/agent/engine.js +222 -45
- package/src/agent/financetools.js +52 -0
- package/src/agent/mt5/BeastFinance.mq5 +290 -0
- package/src/agent/mt5setup.js +520 -0
- package/src/agent/scripts/mt5_bridge.py +112 -0
- package/src/agent/seeds/mql5/SKILL.md +25 -2
- package/src/agent/seeds/tool-yazma/SKILL.md +6 -4
- package/src/main.js +232 -12
- package/src/preload.js +1 -0
- package/src/renderer/index.html +18 -5
- package/src/renderer/renderer.js +159 -36
- package/src/renderer/style.css +30 -17
|
@@ -22,6 +22,7 @@ const NAMES = [
|
|
|
22
22
|
'mt5_history',
|
|
23
23
|
'mt5_alerts',
|
|
24
24
|
'mt5_note',
|
|
25
|
+
'mt5_ea',
|
|
25
26
|
'mt5_trade',
|
|
26
27
|
'mt5_close',
|
|
27
28
|
'mt5_modify',
|
|
@@ -226,6 +227,29 @@ const definitions = NAMES.map((name) => {
|
|
|
226
227
|
required: ['symbol', 'note'],
|
|
227
228
|
},
|
|
228
229
|
},
|
|
230
|
+
mt5_ea: {
|
|
231
|
+
description:
|
|
232
|
+
'BEASTFINANCE EA KÖPRÜSÜ (grafikte çalışan uzman danışman): action:"status" → EA heartbeat (yüklü mü, AutoTrading/EA izni, equity, pozisyon, son ack); "ping" → EA canlı yanıt; "chart" → aktif grafiğin sembol/periyot/fiyat/spread bilgisi; "note" → {symbol?, text, levels:[{price,label}]} grafik panosuna not + yatay seviye çizgileri yazar. NOT: yazdığın pano ve çizgiler görsel ajanın computer_look screenshot\'ında GÖRÜNÜR — grafik analizini/planını panoya yazıp screenshot ile doğrula; entegrasyon bildirimleriyle birlikte kullan.',
|
|
233
|
+
parameters: {
|
|
234
|
+
type: 'object',
|
|
235
|
+
properties: {
|
|
236
|
+
action: { type: 'string', enum: ['status', 'ping', 'chart', 'note'] },
|
|
237
|
+
symbol: { type: 'string', description: 'note: yalnız bu sembolün grafiğine uygulanır (boş = tüm grafikler)' },
|
|
238
|
+
text: { type: 'string', description: 'note: grafik panosuna yazılacak kısa analiz/plan metni' },
|
|
239
|
+
levels: {
|
|
240
|
+
type: 'array',
|
|
241
|
+
description: 'note: yatay seviye çizgileri (destek/direnç/SL/TP)',
|
|
242
|
+
items: {
|
|
243
|
+
type: 'object',
|
|
244
|
+
properties: { price: { type: 'number' }, label: { type: 'string' } },
|
|
245
|
+
required: ['price'],
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
timeoutSec: { type: 'number', description: 'ping/chart: EA yanıt bekleme süresi sn (varsayılan 8)' },
|
|
249
|
+
},
|
|
250
|
+
required: ['action'],
|
|
251
|
+
},
|
|
252
|
+
},
|
|
229
253
|
mt5_trade: {
|
|
230
254
|
description:
|
|
231
255
|
'PİYASA EMRİ AÇAR: mt5_trade {symbol, side:"buy"|"sell", volume, sl?, tp?, comment?, reason?}. Lot limiti ve max pozisyon sayısı sistem tarafından zorlanır. Otomatik işlem anahtarı kapalıysa reddedilir. SL/TP vermek ŞIDDETLİ önerilir. reason: kararın tek cümlelik tezi (günlüğe yazılır, performans değerlendirmesinde kullanılır).',
|
|
@@ -410,6 +434,34 @@ const handlers = {
|
|
|
410
434
|
}, ctx || {});
|
|
411
435
|
return { ok: true, saved: true };
|
|
412
436
|
},
|
|
437
|
+
async mt5_ea(args) {
|
|
438
|
+
if (!mt5.running) return notConnected();
|
|
439
|
+
const action = String(args.action || 'status').toLowerCase();
|
|
440
|
+
if (action === 'status') {
|
|
441
|
+
const data = await bcall('ea_status', {}, 10000);
|
|
442
|
+
return { ok: true, ...data };
|
|
443
|
+
}
|
|
444
|
+
if (action === 'note') {
|
|
445
|
+
const levels = Array.isArray(args.levels)
|
|
446
|
+
? args.levels
|
|
447
|
+
.slice(0, 20)
|
|
448
|
+
.map((x) => ({ price: Number(x && x.price) || 0, label: String((x && x.label) || '').slice(0, 40) }))
|
|
449
|
+
.filter((x) => x.price > 0)
|
|
450
|
+
: [];
|
|
451
|
+
const data = await bcall('ea_note', {
|
|
452
|
+
symbol: String(args.symbol || '').trim().toUpperCase(),
|
|
453
|
+
text: String(args.text || '').slice(0, 4000),
|
|
454
|
+
levels,
|
|
455
|
+
}, 10000);
|
|
456
|
+
return { ok: true, ...data, note: 'Grafik panosu güncellendi — computer_look screenshot\'ında görünür' };
|
|
457
|
+
}
|
|
458
|
+
if (action === 'ping' || action === 'chart') {
|
|
459
|
+
const timeoutSec = Math.max(2, Math.min(20, Number(args.timeoutSec) || 8));
|
|
460
|
+
const data = await bcall('ea_cmd', { cmd: action, timeoutSec }, (timeoutSec + 6) * 1000);
|
|
461
|
+
return { ok: true, ...data };
|
|
462
|
+
}
|
|
463
|
+
return { ok: false, error: 'action: status|ping|chart|note' };
|
|
464
|
+
},
|
|
413
465
|
async mt5_risksize(args) {
|
|
414
466
|
if (!mt5.running) return notConnected();
|
|
415
467
|
const cfg = getCfg();
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
//+------------------------------------------------------------------+
|
|
2
|
+
//| BeastFinance.mq5 — Beast Finance köprü EA'sı (v1.20) |
|
|
3
|
+
//| - Heartbeat: MQL5\Files\beast_ea.json (durum + izinler + equity) |
|
|
4
|
+
//| - Komut köprüsü: beast_cmd.json (Beast yazar) → beast_cmd_ack.json|
|
|
5
|
+
//| - Entegrasyon/Screenshot: grafik üstü pano (Comment) + seviye |
|
|
6
|
+
//| çizgileri — Beast beast_note.json ile besler; görsel ajan |
|
|
7
|
+
//| computer_look ile chart screenshot'ında bu panoyu görür. |
|
|
8
|
+
//+------------------------------------------------------------------+
|
|
9
|
+
#property copyright "Beast Agent"
|
|
10
|
+
#property link "https://github.com/algokodcom/beast-agent"
|
|
11
|
+
#property version "1.20"
|
|
12
|
+
#property description "Beast Finance köprü EA'sı — heartbeat + komut köprüsü + grafik panosu."
|
|
13
|
+
|
|
14
|
+
#include <Trade\Trade.mqh>
|
|
15
|
+
|
|
16
|
+
input string InpTag = "BeastFinance"; // Etiket
|
|
17
|
+
input int InpHeartbeat = 5; // Kalp atışı (sn)
|
|
18
|
+
input bool InpVerbose = true; // Grafik panosunu yaz
|
|
19
|
+
input long InpMagic = 20260910; // Magic number
|
|
20
|
+
|
|
21
|
+
CTrade gTrade;
|
|
22
|
+
|
|
23
|
+
string BeatFile = "beast_ea.json";
|
|
24
|
+
string CmdFile = "beast_cmd.json";
|
|
25
|
+
string AckFile = "beast_cmd_ack.json";
|
|
26
|
+
string NoteFile = "beast_note.json";
|
|
27
|
+
string LvlPrefix = "BeastLvl_";
|
|
28
|
+
|
|
29
|
+
string gNoteText = "";
|
|
30
|
+
string gNoteSymbol = "";
|
|
31
|
+
|
|
32
|
+
int OnInit()
|
|
33
|
+
{
|
|
34
|
+
gTrade.SetExpertMagicNumber(InpMagic);
|
|
35
|
+
gTrade.SetTypeFillingBySymbol(_Symbol);
|
|
36
|
+
EventSetTimer(MathMax(1, InpHeartbeat));
|
|
37
|
+
WriteBeat("init");
|
|
38
|
+
RefreshPanel();
|
|
39
|
+
Print("BeastFinance EA başlatıldı: ", _Symbol, " period=", (int)Period());
|
|
40
|
+
return(INIT_SUCCEEDED);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void OnDeinit(const int reason)
|
|
44
|
+
{
|
|
45
|
+
EventKillTimer();
|
|
46
|
+
WriteBeat("deinit");
|
|
47
|
+
ClearLevels();
|
|
48
|
+
Comment("");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
void OnTimer()
|
|
52
|
+
{
|
|
53
|
+
ReadNote();
|
|
54
|
+
WriteBeat("beat");
|
|
55
|
+
ProcessCommands();
|
|
56
|
+
RefreshPanel();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
void OnTick()
|
|
60
|
+
{
|
|
61
|
+
// Fiyat bazlı ek işler ileride buraya eklenecek (sinyal/uyarı üretimi).
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
string JStr(string s)
|
|
65
|
+
{
|
|
66
|
+
StringReplace(s, "\\", "\\\\");
|
|
67
|
+
StringReplace(s, "\"", "\\\"");
|
|
68
|
+
return s;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
void WriteBeat(const string stage)
|
|
72
|
+
{
|
|
73
|
+
int h = FileOpen(BeatFile, FILE_WRITE|FILE_TXT|FILE_UNICODE);
|
|
74
|
+
if(h == INVALID_HANDLE) return;
|
|
75
|
+
string js = StringFormat("{\"ok\":true,\"ea\":\"BeastFinance\",\"version\":\"1.20\",\"tag\":\"%s\",\"stage\":\"%s\",\"time\":%d,\"server_time\":\"%s\",\"symbol\":\"%s\",\"period\":%d,\"equity\":%.2f,\"balance\":%.2f,\"terminal_trade_allowed\":%s,\"mql_trade_allowed\":%s,\"positions\":%d,\"note\":\"%s\"}",
|
|
76
|
+
JStr(InpTag), stage, (int)TimeCurrent(), TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
|
|
77
|
+
_Symbol, (int)Period(),
|
|
78
|
+
AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE),
|
|
79
|
+
(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) ? "true" : "false"),
|
|
80
|
+
(MQLInfoInteger(MQL_TRADE_ALLOWED) ? "true" : "false"),
|
|
81
|
+
PositionsTotal(),
|
|
82
|
+
JStr(StringSubstr(gNoteText, 0, 120)));
|
|
83
|
+
FileWriteString(h, js);
|
|
84
|
+
FileClose(h);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* ---------- grafik panosu (screenshot'larda görünür) ---------- */
|
|
88
|
+
|
|
89
|
+
void RefreshPanel()
|
|
90
|
+
{
|
|
91
|
+
if(!InpVerbose)
|
|
92
|
+
{
|
|
93
|
+
Comment("");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
string lines = "Beast Finance — BeastFinance v1.20\n";
|
|
97
|
+
lines += _Symbol + " · " + EnumToString((ENUM_TIMEFRAMES)Period()) + " · " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + "\n";
|
|
98
|
+
lines += StringFormat("Equity: %.2f · Balance: %.2f · Acik pozisyon: %d\n",
|
|
99
|
+
AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE), PositionsTotal());
|
|
100
|
+
lines += "AutoTrading: " + (TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) ? "ACIK" : "KAPALI");
|
|
101
|
+
lines += " · EA izni: " + (MQLInfoInteger(MQL_TRADE_ALLOWED) ? "ACIK" : "KAPALI") + "\n";
|
|
102
|
+
if(StringLen(gNoteText) > 0)
|
|
103
|
+
{
|
|
104
|
+
lines += "------ BEAST NOTU ------\n";
|
|
105
|
+
lines += gNoteText;
|
|
106
|
+
}
|
|
107
|
+
Comment(lines);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Beast tarafından yazılan beast_note.json:
|
|
111
|
+
{"symbol":"XAUUSD","text":"...","levels":[{"price":2400,"label":"Direnc"}]}
|
|
112
|
+
symbol boş ya da bu grafiğin sembolüyse uygulanır. */
|
|
113
|
+
void ReadNote()
|
|
114
|
+
{
|
|
115
|
+
if(!FileIsExist(NoteFile)) return;
|
|
116
|
+
int h = FileOpen(NoteFile, FILE_READ|FILE_TXT|FILE_UNICODE);
|
|
117
|
+
if(h == INVALID_HANDLE) return;
|
|
118
|
+
string raw = "";
|
|
119
|
+
while(!FileIsEnding(h)) raw += FileReadString(h);
|
|
120
|
+
FileClose(h);
|
|
121
|
+
if(StringLen(raw) == 0) return;
|
|
122
|
+
|
|
123
|
+
string sym = JsonGet(raw, "symbol");
|
|
124
|
+
if(StringLen(sym) > 0 && StringCompare(sym, _Symbol) != 0) return;
|
|
125
|
+
gNoteSymbol = sym;
|
|
126
|
+
gNoteText = JsonUnescape(JsonGet(raw, "text"));
|
|
127
|
+
ApplyLevels(raw);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
string JsonGet(const string js, const string key)
|
|
131
|
+
{
|
|
132
|
+
string pat = "\"" + key + "\"";
|
|
133
|
+
int i = StringFind(js, pat);
|
|
134
|
+
if(i < 0) return "";
|
|
135
|
+
i = StringFind(js, ":", i + StringLen(pat));
|
|
136
|
+
if(i < 0) return "";
|
|
137
|
+
i++;
|
|
138
|
+
while(i < StringLen(js) && (StringGetCharacter(js, i) == ' ' || StringGetCharacter(js, i) == '\t')) i++;
|
|
139
|
+
if(i >= StringLen(js)) return "";
|
|
140
|
+
if(StringGetCharacter(js, i) == '"')
|
|
141
|
+
{
|
|
142
|
+
int j = StringFind(js, "\"", i + 1);
|
|
143
|
+
if(j < 0) return "";
|
|
144
|
+
return StringSubstr(js, i + 1, j - i - 1);
|
|
145
|
+
}
|
|
146
|
+
int e = i;
|
|
147
|
+
while(e < StringLen(js) && StringGetCharacter(js, e) != ',' && StringGetCharacter(js, e) != '}') e++;
|
|
148
|
+
string v = StringSubstr(js, i, e - i);
|
|
149
|
+
StringTrimLeft(v);
|
|
150
|
+
StringTrimRight(v);
|
|
151
|
+
return v;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
string JsonUnescape(string s)
|
|
155
|
+
{
|
|
156
|
+
StringReplace(s, "\\n", "\n");
|
|
157
|
+
StringReplace(s, "\\\"", "\"");
|
|
158
|
+
StringReplace(s, "\\\\", "\\");
|
|
159
|
+
return s;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* levels dizisindeki her {price,label} için yatay çizgi çizer; eskileri siler */
|
|
163
|
+
void ApplyLevels(const string raw)
|
|
164
|
+
{
|
|
165
|
+
int li = StringFind(raw, "\"levels\"");
|
|
166
|
+
if(li < 0) return;
|
|
167
|
+
int arr = StringFind(raw, "[", li);
|
|
168
|
+
if(arr < 0) return;
|
|
169
|
+
int end = StringFind(raw, "]", arr);
|
|
170
|
+
if(end < 0) return;
|
|
171
|
+
string body = StringSubstr(raw, arr + 1, end - arr - 1);
|
|
172
|
+
|
|
173
|
+
string names[];
|
|
174
|
+
int n = 0;
|
|
175
|
+
int pos = 0;
|
|
176
|
+
while(pos < StringLen(body))
|
|
177
|
+
{
|
|
178
|
+
int p = StringFind(body, "\"price\"", pos);
|
|
179
|
+
if(p < 0) break;
|
|
180
|
+
int c = StringFind(body, ":", p);
|
|
181
|
+
if(c < 0) break;
|
|
182
|
+
int pe = c + 1;
|
|
183
|
+
while(pe < StringLen(body) && StringGetCharacter(body, pe) != ',' && StringGetCharacter(body, pe) != '}') pe++;
|
|
184
|
+
string priceStr = StringSubstr(body, c + 1, pe - (c + 1));
|
|
185
|
+
StringTrimLeft(priceStr);
|
|
186
|
+
StringTrimRight(priceStr);
|
|
187
|
+
double price = StringToDouble(priceStr);
|
|
188
|
+
string label = "";
|
|
189
|
+
int lq = StringFind(body, "\"label\"", pe);
|
|
190
|
+
int nq = StringFind(body, "\"price\"", pe);
|
|
191
|
+
if(lq >= 0 && (nq < 0 || lq < nq))
|
|
192
|
+
{
|
|
193
|
+
int lc = StringFind(body, ":", lq);
|
|
194
|
+
int q1 = StringFind(body, "\"", lc + 1);
|
|
195
|
+
int q2 = q1 >= 0 ? StringFind(body, "\"", q1 + 1) : -1;
|
|
196
|
+
if(q1 >= 0 && q2 > q1) label = StringSubstr(body, q1 + 1, q2 - q1 - 1);
|
|
197
|
+
}
|
|
198
|
+
if(price > 0)
|
|
199
|
+
{
|
|
200
|
+
string name = LvlPrefix + IntegerToString(n);
|
|
201
|
+
names[n] = name;
|
|
202
|
+
if(ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
|
|
203
|
+
ObjectSetDouble(0, name, OBJPROP_PRICE, price);
|
|
204
|
+
ObjectSetInteger(0, name, OBJPROP_COLOR, clrDodgerBlue);
|
|
205
|
+
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);
|
|
206
|
+
ObjectSetString(0, name, OBJPROP_TEXT, StringLen(label) > 0 ? label : "Beast");
|
|
207
|
+
n++;
|
|
208
|
+
}
|
|
209
|
+
pos = pe;
|
|
210
|
+
}
|
|
211
|
+
/* bu turda üretilmeyen eski Beast çizgilerini sil */
|
|
212
|
+
for(int i = n; i < n + 20; i++)
|
|
213
|
+
{
|
|
214
|
+
string old = LvlPrefix + IntegerToString(i);
|
|
215
|
+
if(ObjectFind(0, old) >= 0) ObjectDelete(0, old);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
void ClearLevels()
|
|
220
|
+
{
|
|
221
|
+
for(int i = 0; i < 40; i++)
|
|
222
|
+
{
|
|
223
|
+
string old = LvlPrefix + IntegerToString(i);
|
|
224
|
+
if(ObjectFind(0, old) >= 0) ObjectDelete(0, old);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* ---------- komut köprüsü ---------- */
|
|
229
|
+
|
|
230
|
+
void WriteAck(const string id, const string cmd, const bool ok, const string err, const string result)
|
|
231
|
+
{
|
|
232
|
+
int h = FileOpen(AckFile, FILE_WRITE|FILE_TXT|FILE_UNICODE);
|
|
233
|
+
if(h == INVALID_HANDLE) return;
|
|
234
|
+
string js = "{\"ok\":" + (ok ? "true" : "false") + ",\"id\":\"" + JStr(id) + "\",\"cmd\":\"" + JStr(cmd) + "\",\"at\":" + (string)(int)TimeCurrent();
|
|
235
|
+
if(!ok) js += ",\"error\":\"" + JStr(err) + "\"";
|
|
236
|
+
if(StringLen(result) > 0) js += ",\"result\":" + result;
|
|
237
|
+
js += "}";
|
|
238
|
+
FileWriteString(h, js);
|
|
239
|
+
FileClose(h);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
void ProcessCommands()
|
|
243
|
+
{
|
|
244
|
+
if(!FileIsExist(CmdFile)) return;
|
|
245
|
+
int h = FileOpen(CmdFile, FILE_READ|FILE_TXT|FILE_UNICODE);
|
|
246
|
+
if(h == INVALID_HANDLE) return;
|
|
247
|
+
string raw = "";
|
|
248
|
+
while(!FileIsEnding(h)) raw += FileReadString(h);
|
|
249
|
+
FileClose(h);
|
|
250
|
+
if(StringLen(raw) == 0) return;
|
|
251
|
+
|
|
252
|
+
string id = JsonGet(raw, "id");
|
|
253
|
+
string cmd = JsonGet(raw, "cmd");
|
|
254
|
+
bool ok = true;
|
|
255
|
+
string err = "";
|
|
256
|
+
string result = "";
|
|
257
|
+
if(cmd == "ping")
|
|
258
|
+
{
|
|
259
|
+
result = "{\"pong\":true}";
|
|
260
|
+
}
|
|
261
|
+
else if(cmd == "chart")
|
|
262
|
+
{
|
|
263
|
+
result = StringFormat("{\"symbol\":\"%s\",\"period\":%d,\"bid\":%.5f,\"ask\":%.5f,\"spread\":%d}",
|
|
264
|
+
_Symbol, (int)Period(),
|
|
265
|
+
SymbolInfoDouble(_Symbol, SYMBOL_BID), SymbolInfoDouble(_Symbol, SYMBOL_ASK),
|
|
266
|
+
(int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD));
|
|
267
|
+
}
|
|
268
|
+
else if(cmd == "status")
|
|
269
|
+
{
|
|
270
|
+
result = StringFormat("{\"equity\":%.2f,\"balance\":%.2f,\"positions\":%d,\"terminal_trade_allowed\":%s,\"mql_trade_allowed\":%s}",
|
|
271
|
+
AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE), PositionsTotal(),
|
|
272
|
+
(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) ? "true" : "false"),
|
|
273
|
+
(MQLInfoInteger(MQL_TRADE_ALLOWED) ? "true" : "false"));
|
|
274
|
+
}
|
|
275
|
+
else if(cmd == "note")
|
|
276
|
+
{
|
|
277
|
+
/* gövde zaten beast_note.json'dan okunur; sadece yenileme tetikle */
|
|
278
|
+
ReadNote();
|
|
279
|
+
RefreshPanel();
|
|
280
|
+
result = "{\"note\":true}";
|
|
281
|
+
}
|
|
282
|
+
else
|
|
283
|
+
{
|
|
284
|
+
ok = false;
|
|
285
|
+
err = "bilinmeyen komut: " + cmd;
|
|
286
|
+
}
|
|
287
|
+
WriteAck(id, cmd, ok, err, result);
|
|
288
|
+
FileDelete(CmdFile);
|
|
289
|
+
}
|
|
290
|
+
//+------------------------------------------------------------------+
|