beast-agent 2.44.14 → 2.46.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/computeruse.js +217 -69
- package/src/agent/engine.js +70 -18
- package/src/agent/opencode/toolmap.js +3 -1
- package/src/agent/tools.js +251 -16
- package/src/agent/watext.js +2 -0
- package/src/main.js +694 -27
- package/src/preload.js +5 -0
- package/src/renderer/index.html +18 -0
- package/src/renderer/renderer.js +307 -0
- package/src/renderer/style.css +125 -1
package/package.json
CHANGED
package/src/agent/computeruse.js
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
mouse/klavye PowerShell üzerinden user32 API ile sürülür.
|
|
6
6
|
|
|
7
7
|
Güvenlik: yalnızca engine araç çağrılarıyla çalışır; koordinat sınırlı,
|
|
8
|
-
hız limiting'li (aksiyon arası min gecikme) ve yazma metni kırpılır.
|
|
8
|
+
hız limiting'li (aksiyon arası min gecikme) ve yazma metni kırpılır.
|
|
9
|
+
|
|
10
|
+
KOORDİNAT SÖZLEŞMESİ: computer_look görüntüsü 1280px genişliğe ölçeklenir;
|
|
11
|
+
1280 tabanlı koordinatların GERÇEK ekrana çevrilmesi main.js'teki
|
|
12
|
+
computerActScaled sarmalayıcısında yapılır. Bu modül gerçek koordinat alır. */
|
|
9
13
|
|
|
10
14
|
const { execFile } = require('child_process');
|
|
11
15
|
|
|
@@ -29,14 +33,141 @@ function ps(script) {
|
|
|
29
33
|
});
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
/** Sanal masaüstü çözünürlüğü */
|
|
33
|
-
|
|
36
|
+
/** Sanal masaüstü çözünürlüğü (30 sn önbellek: her aksiyonda PS açma maliyeti olmasın) */
|
|
37
|
+
let sizeCache = { at: 0, w: 0, h: 0 };
|
|
38
|
+
async function screenSize(force) {
|
|
39
|
+
if (!force && sizeCache.w && Date.now() - sizeCache.at < 30000) {
|
|
40
|
+
return { w: sizeCache.w, h: sizeCache.h };
|
|
41
|
+
}
|
|
34
42
|
const out = await ps(
|
|
35
43
|
'Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width.ToString() + "x" + [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height'
|
|
36
44
|
);
|
|
37
45
|
const m = out.match(/(\d+)x(\d+)/);
|
|
38
46
|
if (!m) throw new Error('çözünürlük alınamadı');
|
|
39
|
-
|
|
47
|
+
sizeCache = { at: Date.now(), w: Number(m[1]), h: Number(m[2]) };
|
|
48
|
+
return { w: sizeCache.w, h: sizeCache.h };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* koordinat kelepçesi: sanal masaüstünde negatif değerler geçerli (sol/üst monitör) */
|
|
52
|
+
function clampCoord(v) {
|
|
53
|
+
const n = Math.round(Number(v));
|
|
54
|
+
if (!Number.isFinite(n)) return null;
|
|
55
|
+
return Math.max(-32000, Math.min(32000, n));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* computer_look görüntüsü 1280px genişliğe ölçeklenir; modelden gelen 1280
|
|
59
|
+
tabanlı koordinatları GERÇEK ekran koordinatına çevirir (çok monitörde
|
|
60
|
+
ekran ofseti dahil). Saf fonksiyon — test edilebilir. cap:
|
|
61
|
+
{ x, y, w, h, imgW, imgH } (main.js captureScreenDataUrl doldurur). */
|
|
62
|
+
function scaleArgs(a, cap) {
|
|
63
|
+
const out = { ...(a || {}) };
|
|
64
|
+
if (!cap || !cap.imgW || !cap.imgH) return out;
|
|
65
|
+
const kx = cap.w / cap.imgW;
|
|
66
|
+
const ky = cap.h / cap.imgH;
|
|
67
|
+
for (const key of ['x', 'x2']) {
|
|
68
|
+
if (out[key] != null && out[key] !== '') out[key] = Math.round(cap.x + Number(out[key]) * kx);
|
|
69
|
+
}
|
|
70
|
+
for (const key of ['y', 'y2']) {
|
|
71
|
+
if (out[key] != null && out[key] !== '') out[key] = Math.round(cap.y + Number(out[key]) * ky);
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const MOUSE_CLASS = `
|
|
77
|
+
Add-Type @"
|
|
78
|
+
using System;
|
|
79
|
+
using System.Runtime.InteropServices;
|
|
80
|
+
public class BMouse {
|
|
81
|
+
[DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y);
|
|
82
|
+
[DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, int dx, int dy, int dwData, UIntPtr dwExtraInfo);
|
|
83
|
+
}
|
|
84
|
+
"@`;
|
|
85
|
+
|
|
86
|
+
const LMB_DOWN = 0x0002;
|
|
87
|
+
const LMB_UP = 0x0004;
|
|
88
|
+
const RMB_DOWN = 0x0008;
|
|
89
|
+
const RMB_UP = 0x0010;
|
|
90
|
+
const WHEEL = 0x0800;
|
|
91
|
+
|
|
92
|
+
/* Tek PS sürecinde: imleci taşı + (gerekiyorsa) tık(lar) — eski sürüm her
|
|
93
|
+
adım için ayrı süreç açıyordu (tık başına 2-3 spawn → ~1 sn gecikme). */
|
|
94
|
+
function mouseScript({ x, y, down, up, dbl, settleMs = 35 }) {
|
|
95
|
+
const ev = (flags) => `[BMouse]::mouse_event(${flags}, 0, 0, 0, [UIntPtr]::Zero)`;
|
|
96
|
+
let body = '';
|
|
97
|
+
if (x != null && y != null) {
|
|
98
|
+
body += `[BMouse]::SetCursorPos(${x}, ${y}) | Out-Null\nStart-Sleep -Milliseconds ${settleMs}\n`;
|
|
99
|
+
}
|
|
100
|
+
if (down && up) {
|
|
101
|
+
body += `${ev(down)}\nStart-Sleep -Milliseconds 45\n${ev(up)}\n`;
|
|
102
|
+
if (dbl) body += `Start-Sleep -Milliseconds 70\n${ev(down)}\nStart-Sleep -Milliseconds 45\n${ev(up)}\n`;
|
|
103
|
+
}
|
|
104
|
+
return `${MOUSE_CLASS}\n${body}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* tek çağrıda tekerlek: eski sürüm |dy|*3 ayrı süreç açıyordu (dy=10 → 30 spawn) */
|
|
108
|
+
function wheelScript(x, y, dy) {
|
|
109
|
+
const delta = Math.round(dy * 720); // eski davranış: tik başına ±240 × 3 × dy
|
|
110
|
+
return (
|
|
111
|
+
`${MOUSE_CLASS}\n` +
|
|
112
|
+
`[BMouse]::SetCursorPos(${x}, ${y}) | Out-Null\n` +
|
|
113
|
+
`[BMouse]::mouse_event(${WHEEL}, 0, 0, ${delta}, [UIntPtr]::Zero)\n`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* sürükle-bırak: basılı tut → ara noktalardan geç → bırak */
|
|
118
|
+
function dragScript(x1, y1, x2, y2) {
|
|
119
|
+
const steps = 12;
|
|
120
|
+
return (
|
|
121
|
+
`${MOUSE_CLASS}\n` +
|
|
122
|
+
`[BMouse]::SetCursorPos(${x1}, ${y1}) | Out-Null\n` +
|
|
123
|
+
`Start-Sleep -Milliseconds 60\n` +
|
|
124
|
+
`[BMouse]::mouse_event(${LMB_DOWN}, 0, 0, 0, [UIntPtr]::Zero)\n` +
|
|
125
|
+
`for ($i = 1; $i -le ${steps}; $i++) {\n` +
|
|
126
|
+
` $nx = [int](${x1} + (${x2} - ${x1}) * $i / ${steps})\n` +
|
|
127
|
+
` $ny = [int](${y1} + (${y2} - ${y1}) * $i / ${steps})\n` +
|
|
128
|
+
` [BMouse]::SetCursorPos($nx, $ny) | Out-Null\n` +
|
|
129
|
+
` Start-Sleep -Milliseconds 18\n` +
|
|
130
|
+
`}\n` +
|
|
131
|
+
`Start-Sleep -Milliseconds 40\n` +
|
|
132
|
+
`[BMouse]::mouse_event(${LMB_UP}, 0, 0, 0, [UIntPtr]::Zero)\n`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* pencere başlığına göre öne getir (menü/pencere odaklama için) */
|
|
137
|
+
function focusScript(titleB64) {
|
|
138
|
+
return `
|
|
139
|
+
Add-Type @"
|
|
140
|
+
using System;
|
|
141
|
+
using System.Text;
|
|
142
|
+
using System.Runtime.InteropServices;
|
|
143
|
+
public class BWin {
|
|
144
|
+
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
145
|
+
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc cb, IntPtr lParam);
|
|
146
|
+
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
|
|
147
|
+
[DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
|
|
148
|
+
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
149
|
+
[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
150
|
+
public static IntPtr Found = IntPtr.Zero;
|
|
151
|
+
public static string Needle = "";
|
|
152
|
+
public static bool Cb(IntPtr h, IntPtr l) {
|
|
153
|
+
if (!IsWindowVisible(h)) return true;
|
|
154
|
+
var sb = new StringBuilder(512);
|
|
155
|
+
GetWindowText(h, sb, 512);
|
|
156
|
+
var t = sb.ToString();
|
|
157
|
+
if (t.Length > 0 && t.IndexOf(Needle, StringComparison.OrdinalIgnoreCase) >= 0) { Found = h; return false; }
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
"@
|
|
162
|
+
$n = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("${titleB64}"))
|
|
163
|
+
[BWin]::Needle = $n
|
|
164
|
+
[BWin]::EnumWindows([BWin+EnumWindowsProc][BWin]::Cb, [IntPtr]::Zero) | Out-Null
|
|
165
|
+
if ([BWin]::Found -ne [IntPtr]::Zero) {
|
|
166
|
+
[BWin]::ShowWindow([BWin]::Found, 9) | Out-Null
|
|
167
|
+
$r = [BWin]::SetForegroundWindow([BWin]::Found)
|
|
168
|
+
if ($r) { 'ok' } else { 'noforeground' }
|
|
169
|
+
} else { 'notfound' }
|
|
170
|
+
`;
|
|
40
171
|
}
|
|
41
172
|
|
|
42
173
|
/**
|
|
@@ -45,36 +176,54 @@ async function screenSize() {
|
|
|
45
176
|
* dblclick x y → çift tık
|
|
46
177
|
* rightclick x y → sağ tık
|
|
47
178
|
* move x y → imleci taşı
|
|
48
|
-
*
|
|
179
|
+
* hover x y → taşı + menü açılsın diye bekle
|
|
180
|
+
* drag x y x2 y2 → sürükle-bırak
|
|
181
|
+
* focus title → başlığı eşleşen pencereyi öne getir
|
|
182
|
+
* type text → klavyeyle yaz (pano köprüsü; pano geri yüklenir)
|
|
49
183
|
* key combo → "enter", "ctrl+s", "alt+tab", "win" ...
|
|
50
|
-
* scroll
|
|
184
|
+
* scroll x y dy → tekerlek (tek çağrı)
|
|
51
185
|
*/
|
|
52
186
|
async function act(op, args = {}) {
|
|
53
187
|
await pace();
|
|
54
|
-
const x =
|
|
55
|
-
const y =
|
|
188
|
+
const x = clampCoord(args.x == null ? 0 : args.x);
|
|
189
|
+
const y = clampCoord(args.y == null ? 0 : args.y);
|
|
56
190
|
switch (op) {
|
|
57
191
|
case 'move':
|
|
58
|
-
await
|
|
192
|
+
await ps(mouseScript({ x, y }));
|
|
59
193
|
return { ok: true };
|
|
194
|
+
case 'hover':
|
|
195
|
+
await ps(mouseScript({ x, y }));
|
|
196
|
+
await sleep(280);
|
|
197
|
+
return { ok: true, at: { x, y } };
|
|
60
198
|
case 'click':
|
|
61
|
-
await
|
|
62
|
-
await clickSimple('left');
|
|
199
|
+
await ps(mouseScript({ x, y, down: LMB_DOWN, up: LMB_UP }));
|
|
63
200
|
return { ok: true, at: { x, y } };
|
|
64
201
|
case 'dblclick':
|
|
65
|
-
await
|
|
66
|
-
await clickSimple('left');
|
|
67
|
-
await sleep(70);
|
|
68
|
-
await clickSimple('left');
|
|
202
|
+
await ps(mouseScript({ x, y, down: LMB_DOWN, up: LMB_UP, dbl: true }));
|
|
69
203
|
return { ok: true, at: { x, y } };
|
|
70
204
|
case 'rightclick':
|
|
71
|
-
await
|
|
72
|
-
await clickSimple('right');
|
|
205
|
+
await ps(mouseScript({ x, y, down: RMB_DOWN, up: RMB_UP }));
|
|
73
206
|
return { ok: true, at: { x, y } };
|
|
207
|
+
case 'drag': {
|
|
208
|
+
const x2 = clampCoord(args.x2);
|
|
209
|
+
const y2 = clampCoord(args.y2);
|
|
210
|
+
if (x == null || y == null || x2 == null || y2 == null) {
|
|
211
|
+
return { ok: false, error: 'drag için x,y,x2,y2 gerekli' };
|
|
212
|
+
}
|
|
213
|
+
await ps(dragScript(x, y, x2, y2));
|
|
214
|
+
return { ok: true, from: { x, y }, to: { x2, y2 } };
|
|
215
|
+
}
|
|
216
|
+
case 'focus': {
|
|
217
|
+
const title = String(args.title || args.text || '').trim().slice(0, 120);
|
|
218
|
+
if (!title) return { ok: false, error: 'focus için title gerekli' };
|
|
219
|
+
const out = await ps(focusScript(Buffer.from(title, 'utf8').toString('base64')));
|
|
220
|
+
if (out.includes('ok')) return { ok: true, focused: true, title };
|
|
221
|
+
if (out.includes('noforeground')) return { ok: true, focused: false, title, note: 'pencere bulundu ama öne getirilemedi' };
|
|
222
|
+
return { ok: false, error: `pencere bulunamadı: ${title}` };
|
|
223
|
+
}
|
|
74
224
|
case 'type': {
|
|
75
225
|
const text = String(args.text ?? '').slice(0, 2000);
|
|
76
226
|
if (!text) return { ok: false, error: 'boş metin' };
|
|
77
|
-
/* Add-Type SendInput tabanlı Unicode typer */
|
|
78
227
|
await ps(buildTyperScript(text));
|
|
79
228
|
return { ok: true, chars: text.length };
|
|
80
229
|
}
|
|
@@ -87,11 +236,7 @@ async function act(op, args = {}) {
|
|
|
87
236
|
case 'scroll': {
|
|
88
237
|
const dy = Math.max(-10, Math.min(10, Math.round(Number(args.dy) || 0)));
|
|
89
238
|
if (!dy) return { ok: false, error: 'dy gerekli (-10..10)' };
|
|
90
|
-
await
|
|
91
|
-
const clicks = Math.abs(dy) * 3;
|
|
92
|
-
for (let i = 0; i < clicks; i++) {
|
|
93
|
-
await wheel(dy > 0 ? 1 : -1); // pozitif dy = aşağı
|
|
94
|
-
}
|
|
239
|
+
await ps(wheelScript(x || 640, y || 360, dy));
|
|
95
240
|
return { ok: true, dy };
|
|
96
241
|
}
|
|
97
242
|
default:
|
|
@@ -105,38 +250,20 @@ function sleep(ms) {
|
|
|
105
250
|
|
|
106
251
|
/* ---------- PowerShell snippet üreticileri ---------- */
|
|
107
252
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
using System;
|
|
111
|
-
using System.Runtime.InteropServices;
|
|
112
|
-
public class BM {
|
|
113
|
-
[DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y);
|
|
114
|
-
}
|
|
115
|
-
"@
|
|
116
|
-
[BM]::SetCursorPos(%%X%%, %%Y%%) | Out-Null
|
|
117
|
-
`;
|
|
118
|
-
|
|
119
|
-
const WHEEL_TPL = `
|
|
120
|
-
Add-Type @"
|
|
121
|
-
using System;
|
|
122
|
-
using System.Runtime.InteropServices;
|
|
123
|
-
public class BW {
|
|
124
|
-
[DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
|
|
125
|
-
}
|
|
126
|
-
"@
|
|
127
|
-
[BW]::mouse_event(0x0800, 0, 0, %%DELTA%%, [UIntPtr]::Zero)
|
|
128
|
-
`;
|
|
129
|
-
|
|
130
|
-
/* Unicode güvenli yazım: SendInput yerine pano + Ctrl+V fallback'u en sağlamı.
|
|
131
|
-
Panoyu geri yüklemek yerine tek seferlik kullanıp bırakıyoruz (hız öncelik). */
|
|
253
|
+
/* Unicode güvenli yazım: pano köprüsü. Panoyu ÖNCE kaydet, yapıştırdıktan
|
|
254
|
+
SONRA geri koy — kullanıcının panosu kaybolmaz. */
|
|
132
255
|
function buildTyperScript(text) {
|
|
133
256
|
const b64 = Buffer.from(text, 'utf16le').toString('base64');
|
|
134
257
|
return `
|
|
135
258
|
$t = [System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String("${b64}"))
|
|
136
259
|
Add-Type -AssemblyName System.Windows.Forms
|
|
260
|
+
$prev = $null
|
|
261
|
+
try { $prev = [System.Windows.Forms.Clipboard]::GetText() } catch {}
|
|
137
262
|
[System.Windows.Forms.Clipboard]::SetText($t)
|
|
138
263
|
Start-Sleep -Milliseconds 80
|
|
139
264
|
[System.Windows.Forms.SendKeys]::SendWait("^v")
|
|
265
|
+
Start-Sleep -Milliseconds 150
|
|
266
|
+
if ($prev -ne $null -and $prev -ne "") { try { [System.Windows.Forms.Clipboard]::SetText($prev) } catch {} }
|
|
140
267
|
`;
|
|
141
268
|
}
|
|
142
269
|
|
|
@@ -145,18 +272,19 @@ function buildKeyScript(combo) {
|
|
|
145
272
|
const map = {
|
|
146
273
|
enter: '{ENTER}', tab: '{TAB}', esc: '{ESC}', escape: '{ESC}',
|
|
147
274
|
backspace: '{BACKSPACE}', delete: '{DELETE}', del: '{DELETE}',
|
|
275
|
+
insert: '{INSERT}', ins: '{INSERT}', capslock: '{CAPSLOCK}',
|
|
148
276
|
home: '{HOME}', end: '{END}', pgup: '{PGUP}', pgdn: '{PGDN}',
|
|
149
277
|
up: '{UP}', down: '{DOWN}', left: '{LEFT}', right: '{RIGHT}',
|
|
150
|
-
space: ' ',
|
|
278
|
+
space: ' ',
|
|
151
279
|
};
|
|
152
280
|
let out = '';
|
|
153
281
|
const parts = String(combo).toLowerCase().split('+').map((p) => p.trim());
|
|
154
282
|
let mods = '';
|
|
155
283
|
for (const p of parts) {
|
|
156
|
-
if (p === 'ctrl') mods += '^';
|
|
284
|
+
if (p === 'ctrl' || p === 'control') mods += '^';
|
|
157
285
|
else if (p === 'alt') mods += '%';
|
|
158
286
|
else if (p === 'shift') mods += '+';
|
|
159
|
-
else if (p === 'win')
|
|
287
|
+
else if (p === 'win') mods += '^{ESC}';
|
|
160
288
|
else if (/^f\d{1,2}$/.test(p)) out += '{' + p.toUpperCase() + '}';
|
|
161
289
|
else if (map[p]) out += map[p];
|
|
162
290
|
else out += p.slice(0, 1).toUpperCase(); // tek harf/tuş
|
|
@@ -171,30 +299,50 @@ Add-Type -AssemblyName System.Windows.Forms
|
|
|
171
299
|
}
|
|
172
300
|
|
|
173
301
|
async function mouseMove(x, y) {
|
|
174
|
-
await ps(
|
|
302
|
+
await ps(mouseScript({ x: clampCoord(x), y: clampCoord(y) }));
|
|
175
303
|
}
|
|
176
304
|
|
|
177
|
-
/* down+up tek script'te
|
|
305
|
+
/* down+up tek script'te */
|
|
178
306
|
async function clickSimple(button) {
|
|
179
|
-
|
|
180
|
-
const up = button === 'right' ? 0x0010 : 0x0004;
|
|
181
|
-
await ps(`
|
|
182
|
-
Add-Type @"
|
|
183
|
-
using System;
|
|
184
|
-
using System.Runtime.InteropServices;
|
|
185
|
-
public class BC2 {
|
|
186
|
-
[DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
|
|
307
|
+
await ps(mouseScript({ down: button === 'right' ? RMB_DOWN : LMB_DOWN, up: button === 'right' ? RMB_UP : LMB_UP }));
|
|
187
308
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
309
|
+
|
|
310
|
+
/* ---------- GÖRSEL DOĞRULAMA (verify) ----------
|
|
311
|
+
"Aksiyon işe yaradı mı?" sorusunu ucuz bir görsel parmak iziyle yanıtlar:
|
|
312
|
+
kareyi 8x6 hücreye bölüp her hücrenin ortalama parlaklığını çıkarır.
|
|
313
|
+
İki imza arasındaki fark > eşik olan hücre oranı = değişim oranı.
|
|
314
|
+
Animasyon/anti-alias gürültüsüne toleranslıdır (eşik 8/255). */
|
|
315
|
+
function bitmapSignature(bmp, w, h) {
|
|
316
|
+
try {
|
|
317
|
+
if (!bmp || !w || !h) return null;
|
|
318
|
+
const cells = [];
|
|
319
|
+
const cw = Math.max(1, Math.floor(w / 8));
|
|
320
|
+
const ch = Math.max(1, Math.floor(h / 6));
|
|
321
|
+
for (let cy = 0; cy < 6; cy++) {
|
|
322
|
+
for (let cx = 0; cx < 8; cx++) {
|
|
323
|
+
let sum = 0;
|
|
324
|
+
let n = 0;
|
|
325
|
+
for (let y = cy * ch; y < Math.min(h, (cy + 1) * ch); y += 2) {
|
|
326
|
+
for (let x = cx * cw; x < Math.min(w, (cx + 1) * cw); x += 2) {
|
|
327
|
+
const o = (y * w + x) * 4;
|
|
328
|
+
sum += 0.114 * bmp[o] + 0.587 * bmp[o + 1] + 0.299 * bmp[o + 2];
|
|
329
|
+
n++;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
cells.push(n ? sum / n : 0);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return cells;
|
|
336
|
+
} catch {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
193
339
|
}
|
|
194
340
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
341
|
+
function signatureDiff(a, b, thresh = 8) {
|
|
342
|
+
if (!a || !b || a.length !== b.length || !a.length) return null;
|
|
343
|
+
let changed = 0;
|
|
344
|
+
for (let i = 0; i < a.length; i++) if (Math.abs(a[i] - b[i]) > thresh) changed++;
|
|
345
|
+
return changed / a.length;
|
|
198
346
|
}
|
|
199
347
|
|
|
200
|
-
module.exports = { act, screenSize, clickSimple, mouseMove };
|
|
348
|
+
module.exports = { act, screenSize, scaleArgs, bitmapSignature, signatureDiff, clickSimple, mouseMove };
|
package/src/agent/engine.js
CHANGED
|
@@ -155,11 +155,12 @@ const PERM_TOOL_SETS = {
|
|
|
155
155
|
'web_search', 'http_fetch', 'webfetch', 'deep_search',
|
|
156
156
|
'browser_open', 'browser_read', 'browser_snapshot', 'browser_screenshot',
|
|
157
157
|
'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select',
|
|
158
|
+
'browser_wait',
|
|
158
159
|
'ocr_read', 'tool_request',
|
|
159
160
|
]),
|
|
160
161
|
read: new Set([
|
|
161
162
|
'web_search', 'http_fetch', 'webfetch', 'deep_search',
|
|
162
|
-
'browser_open', 'browser_read', 'browser_snapshot',
|
|
163
|
+
'browser_open', 'browser_read', 'browser_snapshot', 'browser_wait',
|
|
163
164
|
'list_dir', 'read_file', 'grep', 'glob',
|
|
164
165
|
'git_diff_review', 'repo_map', 'repo_symbols', 'xlsx_read',
|
|
165
166
|
'tool_request',
|
|
@@ -191,6 +192,7 @@ const CEO_EXEC_TOOLS = new Set([
|
|
|
191
192
|
'web_search', 'http_fetch', 'webfetch', 'deep_search',
|
|
192
193
|
'browser_open', 'browser_read', 'browser_screenshot', 'browser_snapshot',
|
|
193
194
|
'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select',
|
|
195
|
+
'browser_wait',
|
|
194
196
|
'computer_look', 'computer_act',
|
|
195
197
|
'ocr_read',
|
|
196
198
|
'channel_send', // dış kişilere mesaj: CEO devreder, kendisi atmaz
|
|
@@ -1426,10 +1428,10 @@ class Engine {
|
|
|
1426
1428
|
return out.slice(0, 100);
|
|
1427
1429
|
}
|
|
1428
1430
|
|
|
1429
|
-
/*
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1431
|
+
/* Panel oturumları: belirli bir bgTitle taşıyan gizli oturumlar (Beast Code,
|
|
1432
|
+
Beast MCP…) — ana sohbet listesinden (listSessions) TAMAMEN AYRIDIR. */
|
|
1433
|
+
_listTitledSessions(title, limit = 100) {
|
|
1434
|
+
const wanted = String(title || '');
|
|
1433
1435
|
let files = [];
|
|
1434
1436
|
try {
|
|
1435
1437
|
files = fs.readdirSync(this.sessionsDir).filter((f) => f.endsWith('.jsonl'));
|
|
@@ -1439,7 +1441,7 @@ class Engine {
|
|
|
1439
1441
|
const id = f.replace(/\.jsonl$/, '');
|
|
1440
1442
|
let s;
|
|
1441
1443
|
try { s = this._load(id); } catch { continue; }
|
|
1442
|
-
if (s.bgTitle !==
|
|
1444
|
+
if (s.bgTitle !== wanted) continue;
|
|
1443
1445
|
let title = '';
|
|
1444
1446
|
for (const m of s.messages) {
|
|
1445
1447
|
if (m.role !== 'user') continue;
|
|
@@ -1451,7 +1453,7 @@ class Engine {
|
|
|
1451
1453
|
}
|
|
1452
1454
|
out.push({
|
|
1453
1455
|
id,
|
|
1454
|
-
title: title || '
|
|
1456
|
+
title: title || wanted + ' oturumu',
|
|
1455
1457
|
updatedAt: s.updatedAt,
|
|
1456
1458
|
createdAt: s.createdAt,
|
|
1457
1459
|
count: s.messages.length,
|
|
@@ -1462,6 +1464,16 @@ class Engine {
|
|
|
1462
1464
|
return out.slice(0, Math.max(1, Math.min(200, Number(limit) || 100)));
|
|
1463
1465
|
}
|
|
1464
1466
|
|
|
1467
|
+
/* Beast Code panel oturumları: soldaki dosya panelinin geçmiş listesi gösterir */
|
|
1468
|
+
listBcSessions(limit = 100) {
|
|
1469
|
+
return this._listTitledSessions('Beast Code', limit);
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/* Beast MCP sohbet oturumları: MCP modundaki sağ sohbet paneli gösterir */
|
|
1473
|
+
listMcpSessions(limit = 100) {
|
|
1474
|
+
return this._listTitledSessions('Beast MCP', limit);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1465
1477
|
createSession() {
|
|
1466
1478
|
const id = uid();
|
|
1467
1479
|
const code = this._newCode();
|
|
@@ -2125,6 +2137,7 @@ class Engine {
|
|
|
2125
2137
|
[
|
|
2126
2138
|
'Basit soruları araç kullanmadan doğrudan cevapla — hız önceliklidir.',
|
|
2127
2139
|
'Kod/dosya işlerinde: içerik araması grep (regex), dosya adı araması glob, VAR OLAN dosyayı değiştirme edit_file (write_file yalnız yeni dosya/tam yeniden yazım). read_file satır numaralı döner; büyük dosyada devamını offset parametresiyle oku, ASLA baştan okuma; bir dosyayı aynı oturumda BİR KEZ okumak yeter — okunan içerik oturum sonuna kadar bağlamda kalır, dosyayı tekrar okuma. Dosya işlemlerinde özel araçları kullan (edit_file/write_file/read_file/grep/glob); run_command terminal işlerindir (build, git, kurulum, paket) — dosya düzenlemeyi komutla değil edit_file ile yap. edit_file/write_file sonucu additions/deletions döner ve değişiklik diske ANINDA uygulanır — doğrulamak için dosyayı TEKRAR OKUMA; önceki okuduğun içerik + kendi değişikliklerin üzerinden devam et.',
|
|
2140
|
+
'TERMİNAL DİSİPLİNİ: run_command komutları ETKİLEŞİMSİZDİR — REPL/editör/pager (`node`, `python`, `vi`, `notepad`) ve girdi bekleyen komutlar (mesajsız `git commit`, -y\'siz `npm init`, BatchMode\'suz `ssh`) otomatik reddedilir ya da kilitlenmeden kesilir. Etkileşimsiz bayrakları kullan (-y, -m, --yes, --accept-*), uzun betikleri dosyaya yazıp çalıştır. Uzun süreli sunucuları (`npm run dev/start`, `vite`, `uvicorn`, `flask run`...) run_command ile BAŞLATMA — 10 sn içinde kesilir; Sandbox\'ta panel_run, diğer yerlerde arka plan (`Start-Process ... -RedirectStandardOutput log.txt`) kullan, çıktıyı sonra dosyadan oku.',
|
|
2128
2141
|
'Kullanıcı bir tarihte/saatte hatırlatılmasını isterse set_reminder kullan; when değerini ORTAMdaki bugüne göre hesapla (yerel saat). "Her sabah/gün/hafta" gibi tekrarlı isteklerde repeat alanını da ver (daily/weekly/monthly/weekdays veya cron).',
|
|
2129
2142
|
'İLK MESAJ (channel_send): kullanıcı "X kişiye yaz / haber ver / duyur" derse channel_send ile SEN ilk mesajı at — karşıdan mesaj gelmesini BEKLEME. Yalnız allow listteki kişiler hedeflenebilir: isim (kısmi), telefon numarası, Telegram/Discord ID, @kullanıcı adı, "sahip" ya da "all". Kişi listede yoksa gönderme, kullanıcıya söyle. Telegram/Discord\'da karşı taraf bota daha önce hiç yazmadıysa gönderim başarısız olabilir; sonucu dürüstçe bildir.',
|
|
2130
2143
|
'Kullanıcı kalıcı bir arka plan takibi isterse (fiyat eşiği, pil seviyesi, sayfa değişikliği) watcher_add ile izleyici kur; kurduktan sonra watcher_list ile doğrula ve kullanıcıya koşulu + kontrol sıklığını kısaca bildir.',
|
|
@@ -3711,6 +3724,7 @@ class Engine {
|
|
|
3711
3724
|
`Ortam: Windows + PowerShell; çalışma klasörü: ${this.workspace}\n` +
|
|
3712
3725
|
`GENEL AJAN DİSİPLİNİ: görevi A'dan Z'ye yürüt — bağlam topla, uygula, DOĞRULA; yarım bırakma; doğrulama sonucunu rapora yaz.\n` +
|
|
3713
3726
|
`DOSYA KURALI: dosya işlemlerinde özel araçları kullan — VAR OLAN dosyayı edit_file ile düzenle, yeniyi write_file ile yaz, okuma/arama read_file/grep/glob; run_command terminal işlerindir (build, git, kurulum). Bir dosyayı BİR KEZ oku — içerik bağlamda kalır, tekrar okuma.\n` +
|
|
3727
|
+
`TERMİNAL DİSİPLİNİ: komutlar ETKİLEŞİMSİZ koşar — REPL/editör/pager ve girdi bekleyen komutlar (mesajsız git commit, -y'siz npm init) reddedilir/kesilir; -y, -m, --yes, --accept-* bayraklarını kullan. Uzun süreli sunucuları run_command ile BAŞLATMA (10 sn'de kesilir) — arka planda başlat (panel_run ya da Start-Process + çıktıyı log dosyasına yönlendir).\n` +
|
|
3714
3728
|
(proj ? `PROJE TALİMATLARI (workspace AGENTS/CLAUDE/CONTEXT — daima uy):\n${proj}\n` : '') +
|
|
3715
3729
|
`HIZ KURALLARI:\n` +
|
|
3716
3730
|
`- TOOL İSTEĞİ (CEPHANE): ihtiyacın olan araç yoksa/bozuksa tool_request ile TOOL botuna yazdır (asenkron: istek anında döner, Tool botu arka planda yazar; araç doğrulanınca tool__<ad> olarak anında çağrılır, rapor sohbete düşer); küçük aracı skill("tool-yazma") ile kendin de yaz ve run_command ile doğrula.\n` +
|
|
@@ -5139,7 +5153,8 @@ const skills = require('./skills');
|
|
|
5139
5153
|
if (!this.browser || typeof this.browser.screenshot !== 'function') {
|
|
5140
5154
|
return { ok: false, error: 'dahili tarayıcı kullanılamıyor' };
|
|
5141
5155
|
}
|
|
5142
|
-
|
|
5156
|
+
/* OCR düz görüntü ister — ref etiketleri metni kirletmesin */
|
|
5157
|
+
const shot = await this.browser.screenshot(signal, { sessionId, annotate: false });
|
|
5143
5158
|
if (!shot || !shot.ok) return { ok: false, error: (shot && shot.error) || 'görüntü alınamadı' };
|
|
5144
5159
|
image = shot.__injectImage || shot.image || null;
|
|
5145
5160
|
} else if (src === 'screen') {
|
|
@@ -5147,7 +5162,8 @@ const skills = require('./skills');
|
|
|
5147
5162
|
if (!this.computer || typeof this.computer.look !== 'function') {
|
|
5148
5163
|
return { ok: false, error: 'ekran erişimi yok' };
|
|
5149
5164
|
}
|
|
5150
|
-
|
|
5165
|
+
/* OCR düz görüntü ister — ızgara/imleç etiketleri OCR'a girmesin */
|
|
5166
|
+
const shot = await this.computer.look({ annotate: false });
|
|
5151
5167
|
image = typeof shot === 'string' ? shot : (shot && (shot.image || shot.dataUrl)) || null;
|
|
5152
5168
|
} else {
|
|
5153
5169
|
const abs = path.isAbsolute(src) ? src : path.join(this.workspace, src);
|
|
@@ -5737,12 +5753,14 @@ const skills = require('./skills');
|
|
|
5737
5753
|
return JSON.stringify({ ok: false, error: 'ekran erişimi yok' });
|
|
5738
5754
|
}
|
|
5739
5755
|
emitSafe(this, sessionId, { type: 'status', status: 'ekrana bakıyor' });
|
|
5740
|
-
const shot = await this.computer.look();
|
|
5756
|
+
const shot = await this.computer.look({ annotate: true });
|
|
5741
5757
|
if (!shot) return JSON.stringify({ ok: false, error: 'ekran görüntüsü alınamadı' });
|
|
5742
5758
|
/* görsel sonraki tura vision mesajı olarak enjekte edilir */
|
|
5743
5759
|
return JSON.stringify({
|
|
5744
5760
|
ok: true,
|
|
5745
|
-
note:
|
|
5761
|
+
note:
|
|
5762
|
+
'ekran görüntüsü alındı — görsel aşağıda; 128px ızgara + kırmızı imleç işareti çizili (etiketli koordinatlar görüntü tabanlı). ' +
|
|
5763
|
+
'computer_act koordinatları 1280px görüntü tabanlı alır ve gerçek ekrana otomatik çevirir',
|
|
5746
5764
|
__injectImage: shot,
|
|
5747
5765
|
});
|
|
5748
5766
|
}
|
|
@@ -5817,6 +5835,13 @@ const skills = require('./skills');
|
|
|
5817
5835
|
const r = await this.browser.act(name.slice(8), args, signal, { sessionId });
|
|
5818
5836
|
return JSON.stringify(r);
|
|
5819
5837
|
}
|
|
5838
|
+
if (name === 'browser_wait') {
|
|
5839
|
+
if (!this.browser || typeof this.browser.wait !== 'function') {
|
|
5840
|
+
return JSON.stringify({ ok: false, error: 'dahili tarayıcı kullanılamıyor' });
|
|
5841
|
+
}
|
|
5842
|
+
const r = await this.browser.wait(args || {}, signal, { sessionId });
|
|
5843
|
+
return JSON.stringify(r);
|
|
5844
|
+
}
|
|
5820
5845
|
/* Beast Apps: app__<id>__<tool> → apps host'a dispatch */
|
|
5821
5846
|
if (String(name).startsWith('app__')) {
|
|
5822
5847
|
return JSON.stringify(await apps.call(name, args || {}, signal));
|
|
@@ -6403,7 +6428,7 @@ const TOOLS = [
|
|
|
6403
6428
|
function: {
|
|
6404
6429
|
name: 'computer_look',
|
|
6405
6430
|
description:
|
|
6406
|
-
'Take a screenshot of the user\u2019s screen and receive it as an image. Use before computer_act to see the GUI; coordinates are on a
|
|
6431
|
+
'Take a screenshot of the user\u2019s screen and receive it as an image. Use before computer_act to see the GUI; coordinates are on a 1280px-wide image basis — computer_act converts them to real screen coordinates automatically (multi-monitor offsets included).',
|
|
6407
6432
|
parameters: { type: 'object', properties: {} },
|
|
6408
6433
|
},
|
|
6409
6434
|
},
|
|
@@ -6412,19 +6437,23 @@ const TOOLS = [
|
|
|
6412
6437
|
function: {
|
|
6413
6438
|
name: 'computer_act',
|
|
6414
6439
|
description:
|
|
6415
|
-
'Control mouse/keyboard on the user\u2019s Windows desktop (GUI automation). Ops: click{x,y}, dblclick{x,y}, rightclick{x,y}, move{x,y}, type{text}, key{combo e.g. "ctrl+s","enter","alt+tab"}, scroll{x,y,dy}. Look first with computer_look, act step by step, look again after acting.',
|
|
6440
|
+
'Control mouse/keyboard on the user\u2019s Windows desktop (GUI automation). Ops: click{x,y}, dblclick{x,y}, rightclick{x,y}, move{x,y}, hover{x,y} (opens hover menus), drag{x,y,x2,y2}, focus{title} (bring a window whose title contains the text to front), type{text}, key{combo e.g. "ctrl+s","enter","alt+tab"}, scroll{x,y,dy}. Coordinates are 1280px-wide image based (as seen in computer_look) — converted automatically. After click/type/key/drag the response reports changed/changeRatio (visual verification); if changed:false a warning means the action probably had no effect — do NOT assume success, re-check with computer_look / focus the right window. Look first with computer_look, act step by step, look again after acting.',
|
|
6416
6441
|
parameters: {
|
|
6417
6442
|
type: 'object',
|
|
6418
6443
|
properties: {
|
|
6419
6444
|
op: {
|
|
6420
6445
|
type: 'string',
|
|
6421
|
-
enum: ['click', 'dblclick', 'rightclick', 'move', 'type', 'key', 'scroll'],
|
|
6446
|
+
enum: ['click', 'dblclick', 'rightclick', 'move', 'hover', 'drag', 'focus', 'type', 'key', 'scroll'],
|
|
6422
6447
|
},
|
|
6423
6448
|
x: { type: 'number' },
|
|
6424
6449
|
y: { type: 'number' },
|
|
6450
|
+
x2: { type: 'number', description: 'for op=drag: end x' },
|
|
6451
|
+
y2: { type: 'number', description: 'for op=drag: end y' },
|
|
6452
|
+
title: { type: 'string', description: 'for op=focus: window title substring (case-insensitive)' },
|
|
6425
6453
|
text: { type: 'string', description: 'for op=type (max 2000 chars)' },
|
|
6426
6454
|
combo: { type: 'string', description: 'for op=key: "enter", "ctrl+s", "alt+tab", "win"' },
|
|
6427
6455
|
dy: { type: 'number', description: 'for op=scroll: positive = down (-10..10)' },
|
|
6456
|
+
verify: { type: 'boolean', description: 'set false to skip the visual change check' },
|
|
6428
6457
|
},
|
|
6429
6458
|
required: ['op'],
|
|
6430
6459
|
},
|
|
@@ -6537,12 +6566,13 @@ const TOOLS = [
|
|
|
6537
6566
|
function: {
|
|
6538
6567
|
name: 'browser_click',
|
|
6539
6568
|
description:
|
|
6540
|
-
'Click an element in the built-in browser. Prefer ref from the latest snapshot (e.g. {"ref":3}); CSS selector or text=X also accepted. The response includes a FRESH snapshot with new refs — continue with those directly instead of calling browser_snapshot again.',
|
|
6569
|
+
'Click an element in the built-in browser. Prefer ref from the latest snapshot (e.g. {"ref":3}); CSS selector or text=X also accepted. The response includes a FRESH snapshot with new refs — continue with those directly instead of calling browser_snapshot again. It also reports changed/changeRatio: if changed is false the click probably had no effect (stale ref/hidden element) — take a fresh snapshot and retry, do NOT assume success.',
|
|
6541
6570
|
parameters: {
|
|
6542
6571
|
type: 'object',
|
|
6543
6572
|
properties: {
|
|
6544
6573
|
ref: { type: 'number', description: 'element number from browser_snapshot' },
|
|
6545
6574
|
selector: { type: 'string', description: 'CSS selector or text=X alternative' },
|
|
6575
|
+
verify: { type: 'boolean', description: 'set false to skip the visual change check' },
|
|
6546
6576
|
},
|
|
6547
6577
|
},
|
|
6548
6578
|
},
|
|
@@ -6552,7 +6582,7 @@ const TOOLS = [
|
|
|
6552
6582
|
function: {
|
|
6553
6583
|
name: 'browser_type',
|
|
6554
6584
|
description:
|
|
6555
|
-
'Type text into an input/textarea/contenteditable in the built-in browser (prefer ref). Set submit=true to press Enter afterwards. Date/time fields (input type=date/time/month/datetime-local) are set PROGRAMMATICALLY — just send the date as text in any common format ("2026-03-15", "15.03.2026", "15 Mart 2026"); do NOT click the calendar popup. The response includes a FRESH snapshot with new refs.',
|
|
6585
|
+
'Type text into an input/textarea/contenteditable in the built-in browser (prefer ref). Set submit=true to press Enter afterwards. Date/time fields (input type=date/time/month/datetime-local) are set PROGRAMMATICALLY — just send the date as text in any common format ("2026-03-15", "15.03.2026", "15 Mart 2026"); do NOT click the calendar popup. The response includes a FRESH snapshot with new refs and changed/changeRatio (visual verification).',
|
|
6556
6586
|
parameters: {
|
|
6557
6587
|
type: 'object',
|
|
6558
6588
|
properties: {
|
|
@@ -6560,6 +6590,7 @@ const TOOLS = [
|
|
|
6560
6590
|
selector: { type: 'string' },
|
|
6561
6591
|
text: { type: 'string' },
|
|
6562
6592
|
submit: { type: 'boolean' },
|
|
6593
|
+
verify: { type: 'boolean', description: 'set false to skip the visual change check' },
|
|
6563
6594
|
},
|
|
6564
6595
|
required: ['text'],
|
|
6565
6596
|
},
|
|
@@ -6570,12 +6601,13 @@ const TOOLS = [
|
|
|
6570
6601
|
function: {
|
|
6571
6602
|
name: 'browser_press',
|
|
6572
6603
|
description:
|
|
6573
|
-
'Press a key (Enter, Tab, Escape, ArrowDown…) on the focused element in the built-in browser; optionally focus a ref first. The response includes a FRESH snapshot with new refs.',
|
|
6604
|
+
'Press a key (Enter, Tab, Escape, ArrowDown…) on the focused element in the built-in browser; optionally focus a ref first. The response includes a FRESH snapshot with new refs and changed/changeRatio (visual verification).',
|
|
6574
6605
|
parameters: {
|
|
6575
6606
|
type: 'object',
|
|
6576
6607
|
properties: {
|
|
6577
6608
|
key: { type: 'string' },
|
|
6578
6609
|
ref: { type: 'number', description: 'optional element to focus first' },
|
|
6610
|
+
verify: { type: 'boolean', description: 'set false to skip the visual change check' },
|
|
6579
6611
|
},
|
|
6580
6612
|
required: ['key'],
|
|
6581
6613
|
},
|
|
@@ -6602,18 +6634,38 @@ const TOOLS = [
|
|
|
6602
6634
|
function: {
|
|
6603
6635
|
name: 'browser_select',
|
|
6604
6636
|
description:
|
|
6605
|
-
'Pick an <option> of a dropdown (<select>) in the built-in browser by value/text (prefer ref). The response includes a FRESH snapshot with new refs. For CUSTOM (JS) dropdowns that are not <select>, click the trigger, then click the [role=option] ref from the snapshot (popups are listed first).',
|
|
6637
|
+
'Pick an <option> of a dropdown (<select>) in the built-in browser by value/text (prefer ref). The response includes a FRESH snapshot with new refs and changed/changeRatio (visual verification). For CUSTOM (JS) dropdowns that are not <select>, click the trigger, then click the [role=option] ref from the snapshot (popups are listed first).',
|
|
6606
6638
|
parameters: {
|
|
6607
6639
|
type: 'object',
|
|
6608
6640
|
properties: {
|
|
6609
6641
|
ref: { type: 'number' },
|
|
6610
6642
|
selector: { type: 'string' },
|
|
6611
6643
|
value: { type: 'string' },
|
|
6644
|
+
verify: { type: 'boolean', description: 'set false to skip the visual change check' },
|
|
6612
6645
|
},
|
|
6613
6646
|
required: ['value'],
|
|
6614
6647
|
},
|
|
6615
6648
|
},
|
|
6616
6649
|
},
|
|
6650
|
+
{
|
|
6651
|
+
type: 'function',
|
|
6652
|
+
function: {
|
|
6653
|
+
name: 'browser_wait',
|
|
6654
|
+
description:
|
|
6655
|
+
'Wait in the built-in browser until a condition is met (or timeout): selector appears (or disappears with gone:true), visible text appears, or a ref becomes valid; ms only = fixed sleep. Use this on SPAs / slow pages AFTER an action instead of blind retries — then act on the fresh snapshot. Timeout default 10000ms (max 30000).',
|
|
6656
|
+
parameters: {
|
|
6657
|
+
type: 'object',
|
|
6658
|
+
properties: {
|
|
6659
|
+
selector: { type: 'string', description: 'CSS selector to wait for' },
|
|
6660
|
+
text: { type: 'string', description: 'visible page text/substring to wait for' },
|
|
6661
|
+
ref: { type: 'number', description: 'ref from snapshot that should become valid' },
|
|
6662
|
+
gone: { type: 'boolean', description: 'wait until selector/text/ref DISAPPEARS instead' },
|
|
6663
|
+
ms: { type: 'number', description: 'fixed wait in ms (max 10000); used alone or as extra delay' },
|
|
6664
|
+
timeout_ms: { type: 'number', description: 'condition timeout, default 10000 (max 30000)' },
|
|
6665
|
+
},
|
|
6666
|
+
},
|
|
6667
|
+
},
|
|
6668
|
+
},
|
|
6617
6669
|
];
|
|
6618
6670
|
|
|
6619
6671
|
/* ---------- GÖREV (TODO) ID + GERİ ALMA SİSTEMİ ----------
|
|
@@ -60,7 +60,9 @@ After ensuring proper quoting, execute the command.
|
|
|
60
60
|
- If the commands depend on each other and must run sequentially, avoid '&&' in this shell because Windows PowerShell (5.1) does not support it. Use PowerShell conditionals such as \`cmd1; if ($?) { cmd2 }\` when later commands must depend on earlier success.
|
|
61
61
|
- DO NOT use newlines to separate commands (newlines are ok in quoted strings)
|
|
62
62
|
- The shell is PERSISTENT: working directory and environment variables are preserved between calls.
|
|
63
|
-
- Commands run in Windows PowerShell (5.1)
|
|
63
|
+
- Commands run in Windows PowerShell (5.1).
|
|
64
|
+
- Commands are NON-INTERACTIVE: no REPLs/editors/pagers (\`node\`, \`python\`, \`vi\`, \`notepad\`), no input-waiting commands (\`git commit\` without -m, \`npm init\` without -y, \`ssh\` without BatchMode). They are rejected or aborted to avoid stalling. Use non-interactive flags (-y, -m, --yes) and write scripts to files instead.
|
|
65
|
+
- NEVER start long-running servers (npm run dev/start, vite, uvicorn, flask run...) with this tool — they are aborted after 10s. Use panel_run (run panel) or a background process (Start-Process with output redirected to a log file).`;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
/* opencode tool şemaları — Schema.Struct karşılığı JSON Schema'lar */
|