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.
@@ -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.APPDATA
14
- ? path.join(process.env.APPDATA, 'beast')
15
- : path.join(os.homedir(), 'AppData', 'Roaming', 'beast');
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
- module.exports = { info, warn, error, tail, dir, recent, clear, LOG_DIR };
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">&nbsp;</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>
@@ -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
- if (kind !== 'web' && kind !== 'battery') return { error: "kind 'web' ya da 'battery' olmalı" };
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 op = OPS.includes(String(i.op || '').toLowerCase()) ? String(i.op).toLowerCase() : 'lte';
56
- const value = i.value === undefined || i.value === null || i.value === '' ? null : i.value;
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
 
@@ -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 (settings.browserHeadless) belirler.
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 : settings.browserHeadless !== true;
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
- ipcMain.handle('browser:shown:get', () => ({ shown: settings.browserHeadless === false }));
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
+ });
@@ -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.',
@@ -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><span class="dd-caret">Ⓘ</span></button>
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">&#x25A4;</button>
86
86
  <button id="watchBtn" class="dd-btn dd-icon-btn" title="İzleyiciler (Watchers)" data-i18n-title="tipWatch">&#x25CE;</button>
87
87
  <button id="cronBtn" class="dd-btn dd-icon-btn" title="Cron Görevler" data-i18n-title="tipCron">&#x23F1;&#xFE0E;</button>
88
88
  <button id="termCBtn" class="dd-btn dd-icon-btn" title="CMD terminali" data-i18n-title="tipTermC">&#x276F;&#xFE0E;</button>
89
89
  <button id="browserBtn" class="dd-btn dd-icon-btn" title="Dahili tarayıcı" data-i18n-title="tipBrowser">&#x29C9;</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">&#x2500;&#xFE0E;</button>