beast-agent 2.6.0 → 2.6.2
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/apps.js +587 -0
- package/src/agent/engine.js +39 -4
- package/src/agent/logger.js +138 -5
- package/src/agent/seed-apps/hesap-makinesi/app.json +11 -0
- package/src/agent/seed-apps/hesap-makinesi/main.js +41 -0
- package/src/agent/seed-apps/hesap-makinesi/ui/index.html +151 -0
- package/src/agent/watchers.js +25 -4
- package/src/agent/watext.js +1 -0
- package/src/main.js +29 -6
- package/src/preload.js +1 -0
- package/src/renderer/app-preload.js +20 -0
- package/src/renderer/i18n.js +10 -2
- package/src/renderer/index.html +3 -3
- package/src/renderer/renderer.js +51 -14
- package/src/renderer/style.css +16 -10
package/src/agent/logger.js
CHANGED
|
@@ -10,9 +10,11 @@ const fs = require('fs');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const os = require('os');
|
|
12
12
|
|
|
13
|
-
const ROOT = process.env.
|
|
14
|
-
?
|
|
15
|
-
:
|
|
13
|
+
const ROOT = process.env.BEAST_DATA
|
|
14
|
+
? process.env.BEAST_DATA
|
|
15
|
+
: process.env.APPDATA
|
|
16
|
+
? path.join(process.env.APPDATA, 'beast')
|
|
17
|
+
: path.join(os.homedir(), 'AppData', 'Roaming', 'beast');
|
|
16
18
|
const LOG_DIR = path.join(ROOT, 'logs');
|
|
17
19
|
const KEEP_DAYS = 14;
|
|
18
20
|
const RING_MAX = 1000;
|
|
@@ -74,7 +76,6 @@ function dir() {
|
|
|
74
76
|
function recent() {
|
|
75
77
|
return ring.slice(-200);
|
|
76
78
|
}
|
|
77
|
-
|
|
78
79
|
function clear() {
|
|
79
80
|
try { ring.length = 0; } catch {}
|
|
80
81
|
/* tail() bugün + dünün dosyasını okur; ikisini de boşalt ki
|
|
@@ -87,4 +88,136 @@ function clear() {
|
|
|
87
88
|
return true;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
/* ---------- LOG ZEKASI: desen analizi + zaman penceresi sayacı ---------- */
|
|
92
|
+
|
|
93
|
+
/* Satırı parçala: [ts] [LEVEL] [tag] msg — biçime uymayan satır null */
|
|
94
|
+
function parseLine(line) {
|
|
95
|
+
const m = /^\[([^\]]+)\] \[([A-Za-z]+)\] \[([^\]]+)\] (.*)$/.exec(String(line || ''));
|
|
96
|
+
if (!m) return null;
|
|
97
|
+
const ts = Date.parse(m[1]);
|
|
98
|
+
return { ts: Number.isFinite(ts) ? ts : null, level: m[2].toLowerCase(), tag: m[3], msg: m[4] };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Değişken kısımları maskele → aynı hatanın farklı örnekleri tek desene düşer */
|
|
102
|
+
function normalizeMsg(msg) {
|
|
103
|
+
return String(msg || '')
|
|
104
|
+
.replace(/https?:\/\/\S+/g, '<url>')
|
|
105
|
+
.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '<guid>')
|
|
106
|
+
.replace(/\b0x[0-9a-f]+\b/gi, '<hex>')
|
|
107
|
+
.replace(/(["'`]).*?\1/g, '<q>')
|
|
108
|
+
.replace(/\d+/g, 'N')
|
|
109
|
+
.replace(/\s+/g, ' ')
|
|
110
|
+
.trim()
|
|
111
|
+
.slice(0, 160);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Tail'i tara → seviye sayıları, en çok tekrarlanan desenler, son kayıtlar.
|
|
115
|
+
opts: { last, level: 'error'|'warn'|'info', query: regex } */
|
|
116
|
+
function analyze(opts = {}) {
|
|
117
|
+
const last = Math.min(Math.max(Number(opts.last) || 1500, 1), 5000);
|
|
118
|
+
const level = String(opts.level || '').toLowerCase();
|
|
119
|
+
let query = null;
|
|
120
|
+
if (opts.query !== undefined && opts.query !== null && String(opts.query).trim() !== '') {
|
|
121
|
+
try {
|
|
122
|
+
query = new RegExp(String(opts.query), 'i');
|
|
123
|
+
} catch {
|
|
124
|
+
return { ok: false, error: 'geçersiz regex (query)' };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const counts = {};
|
|
128
|
+
const groups = new Map();
|
|
129
|
+
const recent = [];
|
|
130
|
+
let scanned = 0;
|
|
131
|
+
for (const raw of tail(last)) {
|
|
132
|
+
const p = parseLine(raw);
|
|
133
|
+
if (!p) continue;
|
|
134
|
+
if ((level === 'error' || level === 'warn' || level === 'info') && p.level !== level) continue;
|
|
135
|
+
if (query && !(query.test(p.msg) || query.test(p.tag))) continue;
|
|
136
|
+
scanned++;
|
|
137
|
+
counts[p.level] = (counts[p.level] || 0) + 1;
|
|
138
|
+
const pattern = normalizeMsg(p.msg);
|
|
139
|
+
const key = p.tag + ' :: ' + pattern;
|
|
140
|
+
const g = groups.get(key);
|
|
141
|
+
if (g) {
|
|
142
|
+
g.count++;
|
|
143
|
+
g.levels[p.level] = (g.levels[p.level] || 0) + 1;
|
|
144
|
+
if (p.ts && (!g.firstTs || p.ts < g.firstTs)) g.firstTs = p.ts;
|
|
145
|
+
if (p.ts && (!g.lastTs || p.ts > g.lastTs)) g.lastTs = p.ts;
|
|
146
|
+
} else {
|
|
147
|
+
groups.set(key, {
|
|
148
|
+
pattern,
|
|
149
|
+
tag: p.tag,
|
|
150
|
+
count: 1,
|
|
151
|
+
levels: { [p.level]: 1 },
|
|
152
|
+
firstTs: p.ts,
|
|
153
|
+
lastTs: p.ts,
|
|
154
|
+
sample: p.msg.slice(0, 300),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (recent.length < 12) recent.push(p);
|
|
158
|
+
}
|
|
159
|
+
const top = [...groups.values()]
|
|
160
|
+
.sort((a, b) => b.count - a.count)
|
|
161
|
+
.slice(0, 15)
|
|
162
|
+
.map((g) => ({
|
|
163
|
+
pattern: g.pattern,
|
|
164
|
+
tag: g.tag,
|
|
165
|
+
count: g.count,
|
|
166
|
+
levels: g.levels,
|
|
167
|
+
first: g.firstTs ? new Date(g.firstTs).toISOString() : null,
|
|
168
|
+
last: g.lastTs ? new Date(g.lastTs).toISOString() : null,
|
|
169
|
+
sample: g.sample,
|
|
170
|
+
}));
|
|
171
|
+
return {
|
|
172
|
+
ok: true,
|
|
173
|
+
scanned,
|
|
174
|
+
counts,
|
|
175
|
+
top,
|
|
176
|
+
recent: recent.map((p) => ({
|
|
177
|
+
ts: p.ts ? new Date(p.ts).toISOString() : null,
|
|
178
|
+
level: p.level,
|
|
179
|
+
tag: p.tag,
|
|
180
|
+
msg: p.msg.slice(0, 300),
|
|
181
|
+
})),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Son windowMin dakikadaki eşleşen kayıt sayısı (izleyici + hızlı kontrol için).
|
|
186
|
+
opts: { windowMin, level: 'error'|'warn'|'info', re: regex } */
|
|
187
|
+
function countSince(opts = {}) {
|
|
188
|
+
const windowMin = Math.min(Math.max(Number(opts.windowMin) || 10, 1), 720);
|
|
189
|
+
const level = String(opts.level || 'error').toLowerCase();
|
|
190
|
+
let re = null;
|
|
191
|
+
if (opts.re !== undefined && opts.re !== null && String(opts.re).trim() !== '') {
|
|
192
|
+
try {
|
|
193
|
+
re = new RegExp(String(opts.re), 'i');
|
|
194
|
+
} catch {
|
|
195
|
+
re = null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const since = Date.now() - windowMin * 60000;
|
|
199
|
+
let n = 0;
|
|
200
|
+
for (const raw of tail(4000)) {
|
|
201
|
+
const p = parseLine(raw);
|
|
202
|
+
if (!p || p.ts === null || p.ts < since) continue;
|
|
203
|
+
if ((level === 'error' || level === 'warn' || level === 'info') && p.level !== level) continue;
|
|
204
|
+
if (re && !(re.test(p.msg) || re.test(p.tag))) continue;
|
|
205
|
+
n++;
|
|
206
|
+
}
|
|
207
|
+
return n;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = {
|
|
211
|
+
info,
|
|
212
|
+
warn,
|
|
213
|
+
error,
|
|
214
|
+
tail,
|
|
215
|
+
dir,
|
|
216
|
+
recent,
|
|
217
|
+
clear,
|
|
218
|
+
analyze,
|
|
219
|
+
countSince,
|
|
220
|
+
parseLine,
|
|
221
|
+
normalizeMsg,
|
|
222
|
+
LOG_DIR,
|
|
223
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "hesap-makinesi",
|
|
3
|
+
"name": "Hesap Makinesi",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Güvenli matematik hesaplayıcı — hem ajan aracı (hesapla) hem tıklanabilir arayüz. Geçmiş app storage'da saklanır.",
|
|
6
|
+
"author": "beast",
|
|
7
|
+
"icon": "🧮",
|
|
8
|
+
"permissions": ["tools", "storage", "notify"],
|
|
9
|
+
"ui": "ui/index.html",
|
|
10
|
+
"builtin": true
|
|
11
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Hesap Makinesi — Beast App örneği.
|
|
4
|
+
Ajan için güvenli hesaplama aracı kaydeder; UI'daki geçmiş app storage'da yaşar. */
|
|
5
|
+
|
|
6
|
+
module.exports = (beast) => {
|
|
7
|
+
const SAFE_RE = /^[\s+\-*/().,%0-9e]*$/i;
|
|
8
|
+
|
|
9
|
+
function calc(expr) {
|
|
10
|
+
const cleaned = String(expr || '').replace(/,/g, '.').trim();
|
|
11
|
+
if (!cleaned) return { ok: false, error: 'ifade boş' };
|
|
12
|
+
if (!SAFE_RE.test(cleaned.replace(/e[+-]?\d+/gi, 'N'))) {
|
|
13
|
+
return { ok: false, error: 'yalnızca sayılar ve + - * / ( ) % , e işlemlerine izin var' };
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const val = Function('"use strict"; return (' + cleaned + ')')();
|
|
17
|
+
if (typeof val !== 'number' || !isFinite(val)) return { ok: false, error: 'sonuç sayı değil' };
|
|
18
|
+
return { ok: true, expression: cleaned, result: val };
|
|
19
|
+
} catch (e) {
|
|
20
|
+
return { ok: false, error: 'geçersiz ifade: ' + String((e && e.message) || e).slice(0, 120) };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
beast.tools.register('hesapla', {
|
|
25
|
+
description: 'Güvenli matematik hesaplayıcı. Örnek: "(1200*1.2)/3". Sadece sayılar ve + - * / ( ) % e desteklenir.',
|
|
26
|
+
parameters: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
expression: { type: 'string', description: 'Hesaplanacak matematiksel ifade' },
|
|
30
|
+
},
|
|
31
|
+
required: ['expression'],
|
|
32
|
+
},
|
|
33
|
+
handler: (args) => calc(args.expression),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/* Geçmişi yükle (UI ile aynı storage'ı paylaşır) */
|
|
37
|
+
const history = beast.storage.get('history', []);
|
|
38
|
+
beast.log(`hazır — geçmişte ${history.length} hesap var`);
|
|
39
|
+
|
|
40
|
+
beast.notify('Hesap Makinesi hazır — araç: app__hesap-makinesi__hesapla');
|
|
41
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="tr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Hesap Makinesi</title>
|
|
6
|
+
<style>
|
|
7
|
+
:root { color-scheme: dark; }
|
|
8
|
+
* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; }
|
|
9
|
+
body {
|
|
10
|
+
font-family: 'Segoe UI', system-ui, sans-serif;
|
|
11
|
+
background: #14151a; color: #e8e8ec;
|
|
12
|
+
display: flex; flex-direction: column; align-items: center;
|
|
13
|
+
padding: 18px 14px; gap: 14px; height: 100vh;
|
|
14
|
+
}
|
|
15
|
+
h1 { font-size: 15px; font-weight: 600; color: #9aa0b0; letter-spacing: .4px; }
|
|
16
|
+
#screen {
|
|
17
|
+
width: 100%; max-width: 320px; background: #0d0e12; border: 1px solid #26282f;
|
|
18
|
+
border-radius: 12px; padding: 14px; text-align: right; min-height: 74px;
|
|
19
|
+
display: flex; flex-direction: column; justify-content: center;
|
|
20
|
+
}
|
|
21
|
+
#expr { font-size: 14px; color: #8b90a0; min-height: 18px; word-break: break-all; }
|
|
22
|
+
#result { font-size: 28px; font-weight: 600; margin-top: 4px; word-break: break-all; }
|
|
23
|
+
#pad {
|
|
24
|
+
display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; width: 100%; max-width: 320px;
|
|
25
|
+
}
|
|
26
|
+
button {
|
|
27
|
+
height: 46px; border: 1px solid #26282f; border-radius: 10px; font-size: 17px;
|
|
28
|
+
background: #1c1e25; color: #e8e8ec; cursor: pointer; transition: background .12s;
|
|
29
|
+
}
|
|
30
|
+
button:hover { background: #262933; }
|
|
31
|
+
button:active { background: #2f3340; }
|
|
32
|
+
.op { color: #ffb454; }
|
|
33
|
+
.eq { background: #4f7cff; border-color: #4f7cff; color: #fff; grid-column: span 2; }
|
|
34
|
+
.clr { color: #ff6b6b; }
|
|
35
|
+
#hist { width: 100%; max-width: 320px; overflow-y: auto; flex: 1; display: flex; flex-direction: column; gap: 6px; }
|
|
36
|
+
.h-row {
|
|
37
|
+
background: #17181e; border: 1px solid #23252d; border-radius: 8px;
|
|
38
|
+
padding: 8px 10px; font-size: 13px; display: flex; justify-content: space-between; gap: 8px; cursor: pointer;
|
|
39
|
+
}
|
|
40
|
+
.h-row:hover { background: #1e2028; }
|
|
41
|
+
.h-e { color: #8b90a0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
42
|
+
.h-r { color: #9ecbff; font-weight: 600; }
|
|
43
|
+
#histHead { width: 100%; max-width: 320px; display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: #7d8292; }
|
|
44
|
+
#histClear { background: none; border: none; color: #ff6b6b; font-size: 12px; cursor: pointer; height: auto; }
|
|
45
|
+
</style>
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<h1>🧮 HESAP MAKİNESİ</h1>
|
|
49
|
+
<div id="screen">
|
|
50
|
+
<div id="expr"> </div>
|
|
51
|
+
<div id="result">0</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div id="pad"></div>
|
|
54
|
+
<div id="histHead"><span>GEÇMİŞ</span><button id="histClear">temizle</button></div>
|
|
55
|
+
<div id="hist"></div>
|
|
56
|
+
<script>
|
|
57
|
+
(function () {
|
|
58
|
+
const exprEl = document.getElementById('expr');
|
|
59
|
+
const resEl = document.getElementById('result');
|
|
60
|
+
const pad = document.getElementById('pad');
|
|
61
|
+
const hist = document.getElementById('hist');
|
|
62
|
+
let expr = '';
|
|
63
|
+
let history = [];
|
|
64
|
+
|
|
65
|
+
const store = window.beastApp;
|
|
66
|
+
function fmt(n) {
|
|
67
|
+
if (typeof n !== 'number' || !isFinite(n)) return '—';
|
|
68
|
+
return Math.abs(n) > 1e15 ? n.toExponential(6) : String(+n.toFixed(10));
|
|
69
|
+
}
|
|
70
|
+
function evalLocal(e) {
|
|
71
|
+
const cleaned = String(e || '').replace(/,/g, '.').trim();
|
|
72
|
+
if (!cleaned || !/^[\s+\-*/().,%0-9e]*$/i.test(cleaned.replace(/e[+-]?\d+/gi, 'N'))) return null;
|
|
73
|
+
try {
|
|
74
|
+
const v = Function('"use strict"; return (' + cleaned + ')')();
|
|
75
|
+
return typeof v === 'number' && isFinite(v) ? v : null;
|
|
76
|
+
} catch { return null; }
|
|
77
|
+
}
|
|
78
|
+
function render() {
|
|
79
|
+
exprEl.textContent = expr || '\u00A0';
|
|
80
|
+
const v = evalLocal(expr);
|
|
81
|
+
resEl.textContent = expr ? (v === null ? '…' : fmt(v)) : '0';
|
|
82
|
+
}
|
|
83
|
+
function save() {
|
|
84
|
+
try { store && store.storageSet('history', history.slice(0, 30)); } catch {}
|
|
85
|
+
}
|
|
86
|
+
function pushHistory(e, r) {
|
|
87
|
+
history.unshift({ e, r });
|
|
88
|
+
history = history.slice(0, 30);
|
|
89
|
+
save();
|
|
90
|
+
renderHist();
|
|
91
|
+
}
|
|
92
|
+
function renderHist() {
|
|
93
|
+
hist.innerHTML = '';
|
|
94
|
+
for (const h of history) {
|
|
95
|
+
const row = document.createElement('div');
|
|
96
|
+
row.className = 'h-row';
|
|
97
|
+
row.innerHTML = '<span class="h-e"></span><span class="h-r"></span>';
|
|
98
|
+
row.querySelector('.h-e').textContent = h.e;
|
|
99
|
+
row.querySelector('.h-r').textContent = fmt(h.r);
|
|
100
|
+
row.addEventListener('click', () => { expr = h.e; render(); });
|
|
101
|
+
hist.appendChild(row);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function equals() {
|
|
105
|
+
if (!expr) return;
|
|
106
|
+
const v = evalLocal(expr);
|
|
107
|
+
if (v === null) { resEl.textContent = 'HATA'; return; }
|
|
108
|
+
pushHistory(expr, v);
|
|
109
|
+
if (store) store.notify('Hesap: ' + expr + ' = ' + fmt(v));
|
|
110
|
+
resEl.textContent = fmt(v);
|
|
111
|
+
expr = String(v);
|
|
112
|
+
render();
|
|
113
|
+
}
|
|
114
|
+
const keys = [
|
|
115
|
+
['C', 'clr'], ['(', 'op'], [')', 'op'], ['⌫', 'clr'],
|
|
116
|
+
['7'], ['8'], ['9'], ['/', 'op'],
|
|
117
|
+
['4'], ['5'], ['6'], ['*', 'op'],
|
|
118
|
+
['1'], ['2'], ['3'], ['-', 'op'],
|
|
119
|
+
['0'], ['.', '%'], ['=', 'eq'],
|
|
120
|
+
];
|
|
121
|
+
for (const [label, cls] of keys) {
|
|
122
|
+
const b = document.createElement('button');
|
|
123
|
+
b.textContent = label;
|
|
124
|
+
if (cls) b.className = cls;
|
|
125
|
+
b.addEventListener('click', () => {
|
|
126
|
+
if (label === 'C') { expr = ''; }
|
|
127
|
+
else if (label === '⌫') { expr = expr.slice(0, -1); }
|
|
128
|
+
else if (label === '=') { equals(); return; }
|
|
129
|
+
else { expr += label; }
|
|
130
|
+
render();
|
|
131
|
+
});
|
|
132
|
+
pad.appendChild(b);
|
|
133
|
+
}
|
|
134
|
+
document.getElementById('histClear').addEventListener('click', () => { history = []; save(); renderHist(); });
|
|
135
|
+
document.addEventListener('keydown', (e) => {
|
|
136
|
+
if (/^[0-9+\-*/().%]$/.test(e.key)) { expr += e.key; render(); }
|
|
137
|
+
else if (e.key === 'Enter') { e.preventDefault(); equals(); }
|
|
138
|
+
else if (e.key === 'Backspace') { expr = expr.slice(0, -1); render(); }
|
|
139
|
+
else if (e.key === 'Escape') { expr = ''; render(); }
|
|
140
|
+
});
|
|
141
|
+
(async () => {
|
|
142
|
+
try {
|
|
143
|
+
if (store) history = (await store.storageGet('history', [])) || [];
|
|
144
|
+
} catch {}
|
|
145
|
+
renderHist();
|
|
146
|
+
render();
|
|
147
|
+
})();
|
|
148
|
+
})();
|
|
149
|
+
</script>
|
|
150
|
+
</body>
|
|
151
|
+
</html>
|
package/src/agent/watchers.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
/* Beast izleyiciler (watchers): arka planda periyodik kontrol.
|
|
4
4
|
kind=web → URL periyodik çekilir, değer çıkarılır (json path / regex), koşul karşılaştırılır
|
|
5
5
|
kind=battery → yerel pil yüzdesi izlenir
|
|
6
|
+
kind=logs → Beast log dosyası taranır; son windowMin dakikadaki error/warn sayısı
|
|
7
|
+
değer olur ("10 dk içinde 3+ hata olursa bağır")
|
|
6
8
|
Koşul sağlanınca ilgili oturuma mesaj düşer (WA köprüsüne de otomatik akar).
|
|
7
9
|
Depo: %APPDATA%\beast\watchers.json */
|
|
8
10
|
|
|
@@ -10,6 +12,7 @@ const fs = require('fs');
|
|
|
10
12
|
const path = require('path');
|
|
11
13
|
const { execFile } = require('child_process');
|
|
12
14
|
const { beastRoot } = require('./memory');
|
|
15
|
+
const logger = require('./logger');
|
|
13
16
|
|
|
14
17
|
function file() {
|
|
15
18
|
return path.join(beastRoot(), 'watchers.json');
|
|
@@ -43,18 +46,28 @@ const OPS = ['lt', 'lte', 'gt', 'gte', 'eq', 'neq', 'changed'];
|
|
|
43
46
|
function normalize(input) {
|
|
44
47
|
const i = input || {};
|
|
45
48
|
const name = String(i.name || '').trim().slice(0, 80);
|
|
46
|
-
const kind = String(i.kind || '').trim().toLowerCase();
|
|
47
49
|
if (!name) return { error: 'isim gerekli' };
|
|
48
|
-
|
|
50
|
+
const kind = String(i.kind || '').trim().toLowerCase();
|
|
51
|
+
if (kind !== 'web' && kind !== 'battery' && kind !== 'logs') {
|
|
52
|
+
return { error: "kind 'web', 'battery' ya da 'logs' olmalı" };
|
|
53
|
+
}
|
|
49
54
|
let url = '';
|
|
50
55
|
if (kind === 'web') {
|
|
51
56
|
url = String(i.url || '').trim();
|
|
52
57
|
if (!/^https?:\/\//i.test(url)) return { error: 'web izleyicisi için geçerli http(s) url gerekli' };
|
|
53
58
|
if (url.length > 2000) return { error: 'url çok uzun' };
|
|
54
59
|
}
|
|
55
|
-
const
|
|
56
|
-
const
|
|
60
|
+
const opRaw = String(i.op || '').toLowerCase();
|
|
61
|
+
const op = OPS.includes(opRaw) ? opRaw : kind === 'logs' ? 'gt' : 'lte';
|
|
62
|
+
let value = i.value === undefined || i.value === null || i.value === '' ? null : i.value;
|
|
63
|
+
if (kind === 'logs' && value === null) value = 0; // varsayılan: 0'dan çoksa (yani 1+ kayıt)
|
|
57
64
|
if (op !== 'changed' && value === null) return { error: 'bu op için value (eşik) gerekli' };
|
|
65
|
+
/* logs türü: seviye + kayan pencere (dakika) */
|
|
66
|
+
const level = ['error', 'warn', 'info'].includes(String(i.level || '').toLowerCase())
|
|
67
|
+
? String(i.level).toLowerCase()
|
|
68
|
+
: 'error';
|
|
69
|
+
const rawWin = Number(i.windowMin ?? i.window_min);
|
|
70
|
+
const windowMin = Math.min(Math.max(Number.isFinite(rawWin) ? Math.round(rawWin) : 10, 1), 720);
|
|
58
71
|
let re = '';
|
|
59
72
|
if (i.re !== undefined && i.re !== null && String(i.re).trim() !== '') {
|
|
60
73
|
try {
|
|
@@ -89,6 +102,8 @@ function normalize(input) {
|
|
|
89
102
|
re,
|
|
90
103
|
op,
|
|
91
104
|
value,
|
|
105
|
+
level: kind === 'logs' ? level : undefined,
|
|
106
|
+
windowMin: kind === 'logs' ? windowMin : undefined,
|
|
92
107
|
everyMin: Math.max(1, Math.round(everySec / 60)), // geriye dönük uyum
|
|
93
108
|
everySec,
|
|
94
109
|
cooldownMin,
|
|
@@ -242,10 +257,16 @@ function batteryLevel() {
|
|
|
242
257
|
});
|
|
243
258
|
}
|
|
244
259
|
|
|
260
|
+
/* LOG ZEKASI: son windowMin dakikada eşleşen log kaydı sayısı (değer = sayı) */
|
|
261
|
+
function logCount(w) {
|
|
262
|
+
return logger.countSince({ windowMin: w.windowMin, level: w.level, re: w.re });
|
|
263
|
+
}
|
|
264
|
+
|
|
245
265
|
async function defaultCheck(w, deps = {}) {
|
|
246
266
|
/* tek noktadan taklit (testler / özel köprüler) */
|
|
247
267
|
if (typeof deps.check === 'function') return deps.check({ ...w });
|
|
248
268
|
if (w.kind === 'battery') return deps.battery ? deps.battery() : batteryLevel();
|
|
269
|
+
if (w.kind === 'logs') return deps.logs ? deps.logs({ ...w }) : logCount(w);
|
|
249
270
|
return deps.web ? deps.web({ ...w }) : webValue(w, deps.fetch);
|
|
250
271
|
}
|
|
251
272
|
|
package/src/agent/watext.js
CHANGED
|
@@ -61,6 +61,7 @@ const TOOL_LABELS = {
|
|
|
61
61
|
watcher_add: (a) => `İzleyici kur: ${a.name || ''}`,
|
|
62
62
|
watcher_list: () => 'İzleyiciler',
|
|
63
63
|
watcher_remove: (a) => `İzleyici sil: ${a.id || ''}`,
|
|
64
|
+
log_analyze: (a) => `Log analizi${a.level ? ' [' + a.level + ']' : ''}`,
|
|
64
65
|
event_subscribe: (a) => `Olay aboneliği: ${a.type || ''}`,
|
|
65
66
|
event_list: () => 'Olay abonelikleri',
|
|
66
67
|
event_unsubscribe: (a) => `Abonelik sil: ${a.id || ''}`,
|
package/src/main.js
CHANGED
|
@@ -3476,6 +3476,11 @@ function browserW() {
|
|
|
3476
3476
|
try { return win.getContentSize()[0]; } catch { return 0; }
|
|
3477
3477
|
}
|
|
3478
3478
|
|
|
3479
|
+
/* Tarayıcı gizleme ÖZELLİĞİ: Ayarlar → Web Arama'dan açılır, VARSAYILAN KAPALI.
|
|
3480
|
+
Kapalıyken tarayıcı her zaman görünür; açıkken göz ikonu gizle/göster yapar. */
|
|
3481
|
+
function browserHideEnabled() { return settings.browserHide === true; }
|
|
3482
|
+
function browserHeadlessPref() { return browserHideEnabled() && settings.browserHeadless === true; }
|
|
3483
|
+
|
|
3479
3484
|
function browserShownWidth(w) {
|
|
3480
3485
|
const avail = Math.max(320, w - 320);
|
|
3481
3486
|
/* MOBİL ÖNİZLEME: siluet + çerçeve payı — siluet tam otursun (phone-mode'dan önce) */
|
|
@@ -3754,12 +3759,13 @@ function setBrowserOpen(v, forceVisible) {
|
|
|
3754
3759
|
}
|
|
3755
3760
|
|
|
3756
3761
|
// AÇMA — görünürlük: forceVisible true/false ise onu uygula;
|
|
3757
|
-
// belirtilmemişse kullanıcı tercihi
|
|
3762
|
+
// belirtilmemişse kullanıcı tercihi belirler. Tarayıcı gizleme ÖZELLİĞİ
|
|
3763
|
+
// (settings.browserHide) kapalıysa tercih ne olursa olsun tarayıcı GÖRÜNÜR açılır.
|
|
3758
3764
|
// PARALEL AJANLAR (bg oturum) her zaman forceVisible=false ile çağırır →
|
|
3759
3765
|
// tarayıcı gizli modda çalışır, kullanıcı ekranı ve ajan konsolu rahatsız edilmez.
|
|
3760
3766
|
browser.open = true;
|
|
3761
3767
|
browser.visible =
|
|
3762
|
-
forceVisible === true ? true : forceVisible === false ? false :
|
|
3768
|
+
forceVisible === true ? true : forceVisible === false ? false : !browserHeadlessPref();
|
|
3763
3769
|
ensureBrowser();
|
|
3764
3770
|
if (!browser.started) {
|
|
3765
3771
|
browser.started = true;
|
|
@@ -4660,7 +4666,7 @@ function resyncBrowserUi() {
|
|
|
4660
4666
|
layoutBrowser(); resyncBrowserUi();
|
|
4661
4667
|
setTimeout(() => { try { layoutBrowser(); resyncBrowserUi(); } catch {} }, 80);
|
|
4662
4668
|
setTimeout(() => { try { layoutBrowser(); resyncBrowserUi(); } catch {} }, 300);
|
|
4663
|
-
});
|
|
4669
|
+
});
|
|
4664
4670
|
win.on('show', layoutBrowser);
|
|
4665
4671
|
// X'e basınca gizle — tepside yaşamaya devam, WhatsApp bağlantısı sürer
|
|
4666
4672
|
win.on('close', (e) => {
|
|
@@ -6699,9 +6705,11 @@ ipcMain.handle('browser:toggle', () => {
|
|
|
6699
6705
|
setBrowserOpen(!browser.open, true);
|
|
6700
6706
|
return { open: browser.open, visible: browser.visible };
|
|
6701
6707
|
});
|
|
6702
|
-
/* göz ikonu: ajan tarayıcısını görünür/gizli yap
|
|
6703
|
-
|
|
6708
|
+
/* göz ikonu: ajan tarayıcısını görünür/gizli yap — yalnızca gizleme özelliği
|
|
6709
|
+
(Ayarlar → Web Arama) açıkken etkilidir; özellik kapalıysa hep görünür */
|
|
6710
|
+
ipcMain.handle('browser:shown:get', () => ({ shown: !browserHeadlessPref(), enabled: browserHideEnabled() }));
|
|
6704
6711
|
ipcMain.handle('browser:shown:set', (_e, v) => {
|
|
6712
|
+
if (!browserHideEnabled()) return { shown: true, enabled: false };
|
|
6705
6713
|
settings.browserHeadless = !v;
|
|
6706
6714
|
saveSettings();
|
|
6707
6715
|
if (browser.open) {
|
|
@@ -6709,7 +6717,22 @@ ipcMain.handle('browser:shown:set', (_e, v) => {
|
|
|
6709
6717
|
layoutBrowser();
|
|
6710
6718
|
browserEmit({ open: true, width: browserShownWidth(browserW()) });
|
|
6711
6719
|
}
|
|
6712
|
-
return { shown: !!v };
|
|
6720
|
+
return { shown: !!v, enabled: true };
|
|
6721
|
+
});
|
|
6722
|
+
/* gizleme özelliği anahtarı: kapalıyken göz ikonu yok + tarayıcı zorla görünür */
|
|
6723
|
+
ipcMain.handle('browser:hide:set', (_e, v) => {
|
|
6724
|
+
settings.browserHide = !!v;
|
|
6725
|
+
if (settings.browserHide) {
|
|
6726
|
+
/* özellik yeni açıldı → görünür başla, kullanıcı gözle istediğinde gizler */
|
|
6727
|
+
settings.browserHeadless = false;
|
|
6728
|
+
}
|
|
6729
|
+
saveSettings();
|
|
6730
|
+
if (!browserHideEnabled() && browser.open) {
|
|
6731
|
+
browser.visible = true;
|
|
6732
|
+
layoutBrowser();
|
|
6733
|
+
browserEmit({ open: true, width: browserShownWidth(browserW()) });
|
|
6734
|
+
}
|
|
6735
|
+
return { enabled: browserHideEnabled(), shown: !browserHeadlessPref() };
|
|
6713
6736
|
});
|
|
6714
6737
|
ipcMain.handle('browser:navigate', (_e, url) => browserNavigate(url));
|
|
6715
6738
|
ipcMain.handle('browser:ctrl', (_e, action) => {
|
package/src/preload.js
CHANGED
|
@@ -73,6 +73,7 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
73
73
|
toggleBrowser: () => ipcRenderer.invoke('browser:toggle'),
|
|
74
74
|
browserShownGet: () => ipcRenderer.invoke('browser:shown:get'),
|
|
75
75
|
browserShownSet: (v) => ipcRenderer.invoke('browser:shown:set', v),
|
|
76
|
+
browserHideSet: (v) => ipcRenderer.invoke('browser:hide:set', v),
|
|
76
77
|
terminalToggle: () => ipcRenderer.invoke('terminal:toggle'),
|
|
77
78
|
terminalRun: (cmd, shell) => ipcRenderer.invoke('terminal:run', { cmd, shell }),
|
|
78
79
|
terminalStop: () => ipcRenderer.invoke('terminal:stop'),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Beast App webview köprüsü: app UI'ları (file://) contextBridge üzerinden
|
|
4
|
+
window.beastApp API'sini görür. App kimliği webview src'sindeki ?appid=
|
|
5
|
+
parametresinden okunur; main tarafı id'yi yeniden doğrular. */
|
|
6
|
+
|
|
7
|
+
const { contextBridge, ipcRenderer } = require('electron');
|
|
8
|
+
|
|
9
|
+
let appId = '';
|
|
10
|
+
try {
|
|
11
|
+
appId = String(new URLSearchParams(window.location.search).get('appid') || '');
|
|
12
|
+
} catch {}
|
|
13
|
+
|
|
14
|
+
contextBridge.exposeInMainWorld('beastApp', {
|
|
15
|
+
id: appId,
|
|
16
|
+
storageGet: (key, dflt) => ipcRenderer.invoke('appui:storage:get', { appId, key, dflt }),
|
|
17
|
+
storageSet: (key, value) => ipcRenderer.invoke('appui:storage:set', { appId, key, value }),
|
|
18
|
+
notify: (text) => ipcRenderer.invoke('appui:notify', { appId, text }),
|
|
19
|
+
info: () => ipcRenderer.invoke('appui:info', appId),
|
|
20
|
+
});
|
package/src/renderer/i18n.js
CHANGED
|
@@ -311,7 +311,6 @@
|
|
|
311
311
|
ev_price_ph: 'Fiyat sembolü (örn PAXGUSDT)',
|
|
312
312
|
ev_save: 'Kaydet',
|
|
313
313
|
ev_price_sub: 'boş sembol = fiyat feed kapalı (Binance miniTicker)',
|
|
314
|
-
ev_token_copy: 'token kopyala',
|
|
315
314
|
ev_subs_h3: 'Aktif Abonelikler',
|
|
316
315
|
ev_no_sub: 'Abonelik yok — agent\u2019a "mail gelince haber ver" ya da "fiyat X altına inerse bağır" de, kendisi kurar.',
|
|
317
316
|
ev_on_toast: 'Olay merkezi açık',
|
|
@@ -354,6 +353,11 @@
|
|
|
354
353
|
so_engine_searxng: 'SearXNG (yerel — beast searxng)',
|
|
355
354
|
so_engine_python: 'Python Çoklu-Motor (ddgs/DDG/Bing/Mojeek)',
|
|
356
355
|
so_saved_toast: 'Arama sırası kaydedildi',
|
|
356
|
+
bh_h2: 'Tarayıcı Gizleme',
|
|
357
|
+
bh_sub: 'Ajan aramalarını görünür panel yerine gizli pencerede çalıştırır. Açıkken üst çubukta göz ikonu çıkar — göz açıkken aramalar panelde izlenir, kapalıyken gizli çalışır.',
|
|
358
|
+
bh_label: 'Tarayıcı gizleme özelliği',
|
|
359
|
+
bh_on_toast: 'Tarayıcı gizleme açık — göz ikonu üst çubukta',
|
|
360
|
+
bh_off_toast: 'Tarayıcı gizleme kapandı — tarayıcı her zaman görünür',
|
|
357
361
|
ws_save: 'Kaydet',
|
|
358
362
|
ws_clear: 'Anahtarı Sil',
|
|
359
363
|
ws_note: 'Anahtar maskeli tutulur; yenisini yazıp kaydettiğinde eskisinin yerine geçer. Boş kaydet: mevcut anahtar korunur.',
|
|
@@ -936,7 +940,6 @@
|
|
|
936
940
|
ev_price_ph: 'Price symbol (e.g. PAXGUSDT)',
|
|
937
941
|
ev_save: 'Save',
|
|
938
942
|
ev_price_sub: 'empty symbol = price feed off (Binance miniTicker)',
|
|
939
|
-
ev_token_copy: 'copy token',
|
|
940
943
|
ev_subs_h3: 'Active Subscriptions',
|
|
941
944
|
ev_no_sub: 'No subscriptions — tell the agent "notify me on mail" or "shout if price drops below X" and it sets one up.',
|
|
942
945
|
ev_on_toast: 'Event center on',
|
|
@@ -979,6 +982,11 @@
|
|
|
979
982
|
so_engine_searxng: 'SearXNG (local — beast searxng)',
|
|
980
983
|
so_engine_python: 'Python Multi-Engine (ddgs/DDG/Bing/Mojeek)',
|
|
981
984
|
so_saved_toast: 'Search order saved',
|
|
985
|
+
bh_h2: 'Browser Hiding',
|
|
986
|
+
bh_sub: 'Run agent searches in a hidden window instead of the visible panel. When on, an eye icon appears in the top bar — eye open searches are watched in the panel, eye closed runs hidden.',
|
|
987
|
+
bh_label: 'Browser hiding feature',
|
|
988
|
+
bh_on_toast: 'Browser hiding on — eye icon in the top bar',
|
|
989
|
+
bh_off_toast: 'Browser hiding off — browser always visible',
|
|
982
990
|
ws_save: 'Save',
|
|
983
991
|
ws_clear: 'Delete Key',
|
|
984
992
|
ws_note: 'The key is kept masked; saving a new one replaces the old. Saving empty keeps the current key.',
|
package/src/renderer/index.html
CHANGED
|
@@ -50,7 +50,6 @@
|
|
|
50
50
|
<button id="storeBtn" title="Skills Store" data-i18n-title="tipStore"><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="M4 7h16l-1.3 12.1a2 2 0 0 1-2 1.9H7.3a2 2 0 0 1-2-1.9L4 7z"/><path d="M8 10V6a4 4 0 0 1 8 0v4"/></svg></button>
|
|
51
51
|
<button id="gitBtn" title="GitHub Trending" data-i18n-title="tipGithub"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></button>
|
|
52
52
|
<button id="ideBtn" title="IDE Modu" data-i18n-title="tipIde"><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="M8 6l-5 6 5 6"/><path d="M16 6l5 6-5 6"/><path d="M13.5 4l-3 16"/></svg></button>
|
|
53
|
-
<button id="studioBtn" 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>
|
|
54
53
|
<span id="netDot" class="net-on" title="İnternet: bağlı" data-i18n-title="tipNet"><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="M2 8.82a15 15 0 0 1 20 0"/><path d="M5 12.85a10 10 0 0 1 14 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><line x1="12" y1="20" x2="12.01" y2="20"/><line class="net-slash" x1="4" y1="4" x2="20" y2="20"/></svg></span>
|
|
55
54
|
</div>
|
|
56
55
|
</aside>
|
|
@@ -59,7 +58,7 @@
|
|
|
59
58
|
<header id="topbar">
|
|
60
59
|
<span id="botChip" title="Aktif bot">🦁 Beast</span>
|
|
61
60
|
<div id="modelDD" class="dd">
|
|
62
|
-
<button id="modelBtn" class="dd-btn" title="Model seç" data-i18n-title="tipModel"><span id="modelBtnLabel">Model</span
|
|
61
|
+
<button id="modelBtn" class="dd-btn" title="Model seç" data-i18n-title="tipModel"><span id="modelBtnLabel">Model</span></button>
|
|
63
62
|
<div id="modelMenu" class="dd-menu" hidden>
|
|
64
63
|
<input id="modelFilter" class="dd-filter" placeholder="Model ara…" autocomplete="off" />
|
|
65
64
|
<div id="modelList" class="dd-list"></div>
|
|
@@ -82,12 +81,13 @@
|
|
|
82
81
|
<div id="cfgList" class="dd-list"></div>
|
|
83
82
|
</div>
|
|
84
83
|
</div>
|
|
84
|
+
<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>
|
|
85
85
|
<button id="railBtn" class="dd-btn dd-icon-btn" title="Paralel Ajan Konsolu" data-i18n-title="tipRail">▤</button>
|
|
86
86
|
<button id="watchBtn" class="dd-btn dd-icon-btn" title="İzleyiciler (Watchers)" data-i18n-title="tipWatch">◎</button>
|
|
87
87
|
<button id="cronBtn" class="dd-btn dd-icon-btn" title="Cron Görevler" data-i18n-title="tipCron">⏱︎</button>
|
|
88
88
|
<button id="termCBtn" class="dd-btn dd-icon-btn" title="CMD terminali" data-i18n-title="tipTermC">❯︎</button>
|
|
89
89
|
<button id="browserBtn" class="dd-btn dd-icon-btn" title="Dahili tarayıcı" data-i18n-title="tipBrowser">⧉</button>
|
|
90
|
-
<button id="eyeBtn" class="dd-btn dd-icon-btn" title="Ajan tarayıcısı görünür/gizli — göz açıkken aramalar panelde izlenir, kapalıyken gizli çalışır" data-i18n-title="tipEye"><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="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/><line class="eye-slash" x1="4" y1="4" x2="20" y2="20"/></svg></button>
|
|
90
|
+
<button id="eyeBtn" class="dd-btn dd-icon-btn" hidden title="Ajan tarayıcısı görünür/gizli — göz açıkken aramalar panelde izlenir, kapalıyken gizli çalışır" data-i18n-title="tipEye"><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="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/><line class="eye-slash" x1="4" y1="4" x2="20" y2="20"/></svg></button>
|
|
91
91
|
<div class="drag-spacer"></div>
|
|
92
92
|
<div class="win-controls" id="winControls">
|
|
93
93
|
<button id="winMin" title="Simge durumunda küçült" data-i18n-title="tip_win_min">─︎</button>
|