beast-agent 2.44.13 → 2.45.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 +54 -12
- 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 +527 -31
- package/src/renderer/renderer.js +59 -29
package/src/agent/tools.js
CHANGED
|
@@ -72,6 +72,19 @@ function boundToolOutput(text) {
|
|
|
72
72
|
başına sıraya girer (opencode ile aynı: oturum başına sıralı yürütme). */
|
|
73
73
|
const SHELL_QUEUE_CAP = 6; /* en fazla bu kadar ayrı workspace oturumu */
|
|
74
74
|
const SHELL_IDLE_MS = 10 * 60 * 1000; /* 10 dk boşta kalan oturum kapatılır */
|
|
75
|
+
/* TAKILMA SAVUNMASI:
|
|
76
|
+
- PROMPT_STALL_MS: komut bu süre HİÇ çıktı üretmediyse ve çıktı kuyruğu
|
|
77
|
+
etkileşimli prompt'a benziyorsa (y/n, "Ok to proceed?", parola...) komut
|
|
78
|
+
beklemeden kesilir — 120 sn timeout'u beklemek "takılma" gibi görünür.
|
|
79
|
+
- LONG_RUNNING_MS: dev server / uzun süreç kalıbı taşıyan komut bu süre
|
|
80
|
+
içinde bitmezse kesilir; ajan turu kilitlenmez. */
|
|
81
|
+
const PROMPT_STALL_MS = 3500;
|
|
82
|
+
const PROMPT_WATCH_MS = 1200;
|
|
83
|
+
const LONG_RUNNING_MS = 10000;
|
|
84
|
+
const PROMPT_STALL_RE =
|
|
85
|
+
/(?:\(y\/n\)|\[y\/n\]|\[Y\/n\]|\[y\/N\]|\(yes\/no\)|ok to proceed\?|do you want to (?:proceed|continue|allow)|are you sure|press any key|press enter|enter (?:the )?(?:password|passphrase|username|otp|verification code)|password:\s*$|passphrase:\s*$|username for [^\n]*:\s*$|(?:^|\n)\s*>>?\s*$)/i;
|
|
86
|
+
const LONG_RUNNING_RE =
|
|
87
|
+
/\b(?:(?:npm|yarn|pnpm|bun)\s+(?:run\s+)?(?:dev|start|serve)\b|(?:npm|yarn|pnpm|bun)\s+run\s+\S*(?:dev|watch|serve)\S*|\bvite\b(?!\s+build)|next\s+dev|nuxt\s+dev|ng\s+serve|nodemon|live-server|http-server|(?:python|py|python3)\s+-m\s+http\.server|uvicorn|gunicorn|flask\s+run|streamlit\s+run|manage\.py\s+runserver|hugo\s+server|jekyll\s+serve|(?:cargo|dotnet)\s+watch|docker[ -]compose\s+up\b(?!.*\s-d\b))/i;
|
|
75
88
|
const _shSessions = new Map(); // cwd → session
|
|
76
89
|
|
|
77
90
|
/* boşta reaper: unref'li zamanlayıcı — event loop'u tek başına tutmaz */
|
|
@@ -86,6 +99,142 @@ const _shellReaper = setInterval(() => {
|
|
|
86
99
|
}, 60000);
|
|
87
100
|
if (_shellReaper.unref) _shellReaper.unref();
|
|
88
101
|
|
|
102
|
+
/* prompt watchdog: çıktı akmıyorsa ve son satırlar girdi/onay prompt'una
|
|
103
|
+
benziyorsa komutu (ve yalnız onu) kes; sıradaki komutlar taze oturumda sürer */
|
|
104
|
+
const _shellPromptWatch = setInterval(() => {
|
|
105
|
+
const now = Date.now();
|
|
106
|
+
for (const sess of _shSessions.values()) {
|
|
107
|
+
if (!sess.busy || sess.dead || !sess.queue.length) continue;
|
|
108
|
+
const w = sess.queue[0];
|
|
109
|
+
if (!w || !w.dispatched) continue;
|
|
110
|
+
if (now - (sess.lastDataAt || sess.dispatchAt || 0) < PROMPT_STALL_MS) continue;
|
|
111
|
+
const tail = sess.buf.slice(-300);
|
|
112
|
+
if (!tail.trim() || !PROMPT_STALL_RE.test(tail)) continue;
|
|
113
|
+
_shellAbortCurrent(sess, 'komut girdi/onay bekliyor görünüyor (etkileşimli prompt)');
|
|
114
|
+
}
|
|
115
|
+
}, PROMPT_WATCH_MS);
|
|
116
|
+
if (_shellPromptWatch.unref) _shellPromptWatch.unref();
|
|
117
|
+
|
|
118
|
+
/* süren komutu kes, kuyruktakileri TAZE oturumda sürdür: tur kilitlenmez,
|
|
119
|
+
paralel çağrılar kaybolmaz. Diske/oturuma yazılan değişkenler sıfırlanır. */
|
|
120
|
+
function _shellAbortCurrent(sess, reason) {
|
|
121
|
+
const w = sess.queue.shift();
|
|
122
|
+
if (!w) return;
|
|
123
|
+
clearTimeout(w.timer);
|
|
124
|
+
clearTimeout(w.longTimer);
|
|
125
|
+
if (w.onAbort && w.signal) {
|
|
126
|
+
try { w.signal.removeEventListener('abort', w.onAbort); } catch {}
|
|
127
|
+
}
|
|
128
|
+
const out = sess.buf.slice(-1500);
|
|
129
|
+
sess.buf = '';
|
|
130
|
+
sess.err = '';
|
|
131
|
+
const pending = sess.queue.splice(0);
|
|
132
|
+
_shellDispose(sess);
|
|
133
|
+
if (_shSessions.get(sess.cwd) === sess) _shSessions.delete(sess.cwd);
|
|
134
|
+
try {
|
|
135
|
+
w.resolve({
|
|
136
|
+
ok: false,
|
|
137
|
+
code: null,
|
|
138
|
+
output:
|
|
139
|
+
(out.trim() ? out.trim() + '\n' : '') +
|
|
140
|
+
'[beast] ' + reason +
|
|
141
|
+
' — komut durduruldu. Etkileşimsiz yeniden dene (ör. -y / -m / --yes / --accept-*) ya da kalıcı girdi gerektiren programı ajan terminalinde çalıştırma.',
|
|
142
|
+
});
|
|
143
|
+
} catch {}
|
|
144
|
+
if (!pending.length) return;
|
|
145
|
+
let fresh = null;
|
|
146
|
+
try {
|
|
147
|
+
fresh = _spawnShell(sess.cwd);
|
|
148
|
+
_shSessions.set(sess.cwd, fresh);
|
|
149
|
+
} catch {}
|
|
150
|
+
if (!fresh) {
|
|
151
|
+
for (const p of pending) {
|
|
152
|
+
clearTimeout(p.timer);
|
|
153
|
+
clearTimeout(p.longTimer);
|
|
154
|
+
p.resolve({ ok: false, code: null, output: '[beast] kabuk oturumu yenilendi — komut çalıştırılamadı' });
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
fresh.busy = true;
|
|
159
|
+
fresh.queue = pending;
|
|
160
|
+
_shellDispatch(fresh);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* etkileşimli/REPL komutlar kalıcı kabuğun stdin işaret protokolünü bozar
|
|
164
|
+
(Read-Host işaret satırını yer) ya da 120 sn boyunca asılı kalır. Bilinen
|
|
165
|
+
kalıplar daha spawn edilmeden net hata + düzeltme önerisiyle geri çevrilir.
|
|
166
|
+
null dönerse komut güvenli sayılır. */
|
|
167
|
+
function interactiveCommandReason(command) {
|
|
168
|
+
const raw = String(command || '').trim();
|
|
169
|
+
if (!raw) return null;
|
|
170
|
+
const chain = raw.split(/[;&|]+/)[0].trim().replace(/^&\s*/, '');
|
|
171
|
+
/* ilk token: tırnaklı yol da olabilir (ör. "C:\Program Files\Git\python.exe") */
|
|
172
|
+
const qm = /^(["'])(.+?)\1/.exec(chain);
|
|
173
|
+
const firstTok = qm ? qm[2] : (chain.split(/\s+/)[0] || '');
|
|
174
|
+
const rest = (qm ? chain.slice(qm[0].length) : chain.slice(firstTok.length)).trim();
|
|
175
|
+
const base = path.basename(firstTok.replace(/\.(exe|cmd|bat)$/i, '')).toLowerCase();
|
|
176
|
+
const bare = !rest;
|
|
177
|
+
/* çıplak REPL / iç içe kabuk: ajan oraya ASLA yazamaz */
|
|
178
|
+
if (bare && /^(python[23]?|py|node|deno|bun|cmd|powershell|pwsh|bash|sh|wsl|mysql|psql|sqlite3|mongosh|redis-cli|irb|ruby|php|lua|r)$/.test(base)) {
|
|
179
|
+
return (
|
|
180
|
+
'`' + firstTok + '` çıplak bir REPL/kabuktur — etkileşimli girdi bekler ve ajan turunu kilitler. ' +
|
|
181
|
+
'Betiği dosyaya yazıp çalıştır (ör. `python script.py`, `node script.js`) ya da tek satır komut kullan (ör. `python -c "..."`).'
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
/* editörler: pencere açar ve kapanana kadar bekler */
|
|
185
|
+
if (/^(vi|vim|nvim|nano|emacs|notepad|code)$/.test(base)) {
|
|
186
|
+
return (
|
|
187
|
+
'`' + base + '` etkileşimli editör — ajan terminalinde asılı kalır. Dosyayı write_file/edit_file araçlarıyla düzenle.'
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
/* pager: dosya argümanı yoksa stdin bekler */
|
|
191
|
+
if (/^(less|more)$/.test(base) && !/[<|]|\S+\.\S{1,6}\b/.test(rest)) {
|
|
192
|
+
return (
|
|
193
|
+
'`' + base + '` girdi dosyası olmadan stdin bekler. Dosyayı doğrudan belirt (ör. `more dosya.txt`) ya da read_file aracını kullan.'
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
/* stdin filtreleri: dosya/pipe yoksa pipe'tan okumaya çalışır ve asılır */
|
|
197
|
+
if (/^(findstr|sort|clip|tee)$/.test(base) && !/[<|]|\S+\.\S{1,6}\b/.test(rest)) {
|
|
198
|
+
return (
|
|
199
|
+
'`' + base + '` girdi belirtilmeden stdin bekler. Girdiyi dosya/pipe ile ver ya da grep/read_file araçlarını kullan.'
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
/* PowerShell onay/girdi cmdlet'leri */
|
|
203
|
+
if (/\b(Read-Host|Get-Credential|Out-GridView|Wait-Event|Wait-Input)\b/i.test(raw)) {
|
|
204
|
+
return (
|
|
205
|
+
'`Read-Host`/`Get-Credential` benzeri etkileşimli cmdlet ajan terminalinde çalışmaz — girdiyi parametre olarak ver.'
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
if (/(?:^|[;&|]\s*|\/c\s+)pause(?:\s|$)/i.test(raw)) {
|
|
209
|
+
return '`pause` girdi bekler — ajan terminalinde çalışmaz; beklemeyi kaldır.';
|
|
210
|
+
}
|
|
211
|
+
/* git: editör açan / interaktif alt komutlar */
|
|
212
|
+
if (/\bgit\s+commit\b/i.test(raw) && !/(\s-[a-zA-Z]*m\b|\s--message\b|\s-F\b|\s--file\b|--no-edit\b|--fixup\b|--squash\b)/i.test(raw)) {
|
|
213
|
+
return '`git commit` mesaj bayrağı olmadan editör açar — `git commit -m "mesaj"` kullan (gerekiyorsa `-F dosya`).';
|
|
214
|
+
}
|
|
215
|
+
if (/\bgit\s+merge\b/i.test(raw) && !/(\s-[a-zA-Z]*m\b|\s--message\b|--no-edit\b|--abort\b|--continue\b|--quit\b)/i.test(raw)) {
|
|
216
|
+
return '`git merge` düzenleyici açabilir — `git merge --no-edit` ya da `-m "mesaj"` kullan.';
|
|
217
|
+
}
|
|
218
|
+
if (/\bgit\s+(?:rebase\s+(?:-i|--interactive)|add\s+(?:-i|-p|--interactive|--patch))\b/i.test(raw)) {
|
|
219
|
+
return '`git rebase -i` / `git add -p` interaktiftir — ajan terminalinde çalışmaz; otomatik/`-m` formunu kullan.';
|
|
220
|
+
}
|
|
221
|
+
/* kurulum sihirbazları: soru sorar */
|
|
222
|
+
if (/\bnpm\s+init\b/i.test(raw) && !/(\s-y\b|\s--yes\b)/i.test(raw)) {
|
|
223
|
+
return '`npm init` sihirbazı soru sorar — `npm init -y` kullan ya da package.json\'u write_file ile yaz.';
|
|
224
|
+
}
|
|
225
|
+
if (/\b(?:choco|chocolatey)\s+install\b/i.test(raw) && !/(\s-y\b|\s--yes\b)/i.test(raw)) {
|
|
226
|
+
return '`choco install` onay sorar — `-y` ekle.';
|
|
227
|
+
}
|
|
228
|
+
/* çıplak `npx` paket yoksa "Ok to proceed? (y)" ile asılır: --yes öner */
|
|
229
|
+
if (bare && base === 'npx') {
|
|
230
|
+
return '`npx` paket kurulum onayı isteyebilir — `npx --yes <paket>` kullan.';
|
|
231
|
+
}
|
|
232
|
+
if (base === 'ssh' && !/BatchMode/i.test(raw)) {
|
|
233
|
+
return '`ssh` parola sorabilir ve asılır — `ssh -o BatchMode=yes` kullan ya da anahtar tabanlı oturum kur.';
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
89
238
|
function _shellDispose(sess) {
|
|
90
239
|
try {
|
|
91
240
|
if (sess.proc && sess.proc.pid) {
|
|
@@ -104,7 +253,7 @@ function disposeShellSessions() {
|
|
|
104
253
|
function _spawnShell(cwd) {
|
|
105
254
|
const proc = spawn(
|
|
106
255
|
'powershell.exe',
|
|
107
|
-
['-NoProfile', '-NoExit', '-Command', '-'],
|
|
256
|
+
['-NoProfile', '-NonInteractive', '-NoExit', '-ExecutionPolicy', 'Bypass', '-Command', '-'],
|
|
108
257
|
{ cwd: cwd || process.cwd(), windowsHide: true, env: envWithPathPrefix() }
|
|
109
258
|
);
|
|
110
259
|
const sess = {
|
|
@@ -114,9 +263,23 @@ function _spawnShell(cwd) {
|
|
|
114
263
|
err: '',
|
|
115
264
|
seq: 0,
|
|
116
265
|
busy: false,
|
|
117
|
-
queue: [], // {command, resolve, timer, signal, onAbort}
|
|
266
|
+
queue: [], // {command, resolve, timer, longTimer, signal, onAbort}
|
|
118
267
|
dead: false,
|
|
268
|
+
lastDataAt: 0,
|
|
269
|
+
dispatchAt: 0,
|
|
119
270
|
};
|
|
271
|
+
/* TAKILMA SAVUNMASI: prompter/prefs kapatılır — Read-Host, -Confirm gibi
|
|
272
|
+
girdi bekleyen cmdlet'ler asılmak yerine ANINDA hata verir.
|
|
273
|
+
Çıkış UTF-8: Türkçe çıktı (Write-Output, dosya adları) bozulmaz.
|
|
274
|
+
Giriş kodlaması ÖNEMSİZ: komutlar base64 (ASCII) ile gönderilir. */
|
|
275
|
+
try {
|
|
276
|
+
proc.stdin.write(
|
|
277
|
+
"$ConfirmPreference='None'\n" +
|
|
278
|
+
"$ProgressPreference='SilentlyContinue'\n" +
|
|
279
|
+
"[Console]::OutputEncoding=[System.Text.Encoding]::UTF8\n" +
|
|
280
|
+
"$OutputEncoding=[System.Text.Encoding]::UTF8\n"
|
|
281
|
+
);
|
|
282
|
+
} catch {}
|
|
120
283
|
/* Node 22: child + stdio stream'leri event loop'a bağlanmaz — test/CLI'da
|
|
121
284
|
bekleyen oturum sürecin çıkmasını ENGELLEMEZ; uygulama kapanışında
|
|
122
285
|
disposeShellSessions() yine de temiz kapatır */
|
|
@@ -130,10 +293,12 @@ function _spawnShell(cwd) {
|
|
|
130
293
|
proc.stdout.setEncoding('utf8');
|
|
131
294
|
proc.stderr.setEncoding('utf8');
|
|
132
295
|
proc.stdout.on('data', (d) => {
|
|
296
|
+
sess.lastDataAt = Date.now();
|
|
133
297
|
sess.buf += d;
|
|
134
298
|
_shellPump(sess);
|
|
135
299
|
});
|
|
136
300
|
proc.stderr.on('data', (d) => {
|
|
301
|
+
sess.lastDataAt = Date.now();
|
|
137
302
|
sess.err += d;
|
|
138
303
|
if (sess.err.length > MAX_CMD_OUTPUT * 4) sess.err = sess.err.slice(-MAX_CMD_OUTPUT * 2);
|
|
139
304
|
});
|
|
@@ -143,6 +308,7 @@ function _spawnShell(cwd) {
|
|
|
143
308
|
while (sess.queue.length) {
|
|
144
309
|
const w = sess.queue.shift();
|
|
145
310
|
clearTimeout(w.timer);
|
|
311
|
+
clearTimeout(w.longTimer);
|
|
146
312
|
w.resolve({ ok: false, code: null, output: sess.buf.slice(-2000) + '\n[beast] shell oturumu kapandı' });
|
|
147
313
|
}
|
|
148
314
|
sess.busy = false;
|
|
@@ -168,6 +334,10 @@ function _shellPump(sess) {
|
|
|
168
334
|
sess.queue.shift();
|
|
169
335
|
sess.busy = sess.queue.length > 0;
|
|
170
336
|
clearTimeout(w.timer);
|
|
337
|
+
clearTimeout(w.longTimer);
|
|
338
|
+
if (w.onAbort && w.signal) {
|
|
339
|
+
try { w.signal.removeEventListener('abort', w.onAbort); } catch {}
|
|
340
|
+
}
|
|
171
341
|
const codeM = /_(\-?\d+)\s*$/.exec(markerLine);
|
|
172
342
|
const exitCode = codeM ? Number(codeM[1]) : null;
|
|
173
343
|
if (out.length > MAX_CMD_OUTPUT * 4) out = out.slice(-MAX_CMD_OUTPUT * 2);
|
|
@@ -193,23 +363,58 @@ function _shellDispatch(sess) {
|
|
|
193
363
|
w.n = ++sess.seq;
|
|
194
364
|
const cmd = w.command;
|
|
195
365
|
const marker = `${SHELL_MARKER_PREFIX}${w.n}_$LASTEXITCODE`;
|
|
196
|
-
/*
|
|
197
|
-
|
|
198
|
-
|
|
366
|
+
/* KOMUT base64 ile taşınır: PowerShell'ın stdin okuyucusu oturum açılışında
|
|
367
|
+
sabit (eski) codepage ile kurulur; UTF-8 komut metni Türkçe karakterlerde
|
|
368
|
+
bozulurdu. base64 ASCII'dir → kod sayfası ne olursa olsun metin birebir
|
|
369
|
+
çözülür. Tüm satır TEK satır olarak yazılır: PowerShell tüm ifadeyi
|
|
370
|
+
okuyup öyle çalıştırır — stdin okuyan bir program protokolü yiyemez. */
|
|
371
|
+
const b64 = Buffer.from(String(cmd), 'utf8').toString('base64');
|
|
372
|
+
sess.dispatchAt = Date.now();
|
|
373
|
+
sess.lastDataAt = sess.dispatchAt;
|
|
374
|
+
try {
|
|
375
|
+
sess.proc.stdin.write(
|
|
376
|
+
`$__beastB64='${b64}'; $global:LASTEXITCODE=0; ` +
|
|
377
|
+
`Invoke-Expression ([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($__beastB64))); ` +
|
|
378
|
+
`Write-Output "${marker}"\n`
|
|
379
|
+
);
|
|
380
|
+
} catch {
|
|
381
|
+
sess.dead = true;
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
199
384
|
const finishTimeout = () => {
|
|
200
385
|
_shellDispose(sess);
|
|
201
386
|
if (_shSessions.get(sess.cwd) === sess) _shSessions.delete(sess.cwd);
|
|
202
387
|
sess.busy = false;
|
|
388
|
+
const secs = Math.round((w.timeoutMs || 0) / 1000);
|
|
203
389
|
while (sess.queue.length) {
|
|
204
390
|
const x = sess.queue.shift();
|
|
205
391
|
clearTimeout(x.timer);
|
|
206
|
-
x.
|
|
392
|
+
clearTimeout(x.longTimer);
|
|
393
|
+
x.resolve({
|
|
394
|
+
ok: false,
|
|
395
|
+
code: null,
|
|
396
|
+
output:
|
|
397
|
+
'[beast] shell komutu zaman aşımı (' + secs + ' sn) — komut girdi bekliyor olabilir ' +
|
|
398
|
+
'ya da çok uzun sürdü. Etkileşimsiz bayraklarla dene (-y / -m / --yes); uzun süreçleri ' +
|
|
399
|
+
'arka planda başlat (panel_run ya da Start-Process + çıktıyı dosyaya yönlendir). Oturum tazelendi.',
|
|
400
|
+
});
|
|
207
401
|
}
|
|
208
402
|
};
|
|
209
403
|
w.timer = setTimeout(finishTimeout, w.timeoutMs);
|
|
404
|
+
/* UZUN SÜRELİ SÜREÇ: dev server/serving kalıbı turu kilitlemesin — makul
|
|
405
|
+
süre içinde bitmezse erken kes, ajana nasıl başlatacağını söyle. */
|
|
406
|
+
if (LONG_RUNNING_RE.test(cmd)) {
|
|
407
|
+
w.longTimer = setTimeout(() => {
|
|
408
|
+
_shellAbortCurrent(
|
|
409
|
+
sess,
|
|
410
|
+
'uzun süreli süreç (dev server/serving) ' + Math.round(LONG_RUNNING_MS / 1000) + ' sn içinde bitmedi — turu kilitlememek için durduruldu'
|
|
411
|
+
);
|
|
412
|
+
}, LONG_RUNNING_MS);
|
|
413
|
+
}
|
|
210
414
|
if (w.signal) {
|
|
211
415
|
if (w.signal.aborted) return finishTimeout();
|
|
212
|
-
w.
|
|
416
|
+
w.onAbort = finishTimeout;
|
|
417
|
+
w.signal.addEventListener('abort', w.onAbort, { once: true });
|
|
213
418
|
}
|
|
214
419
|
}
|
|
215
420
|
|
|
@@ -269,6 +474,8 @@ function runBashCommand(command, cwd, timeoutMs = 90000, signal) {
|
|
|
269
474
|
child = spawn(gitbash(), ['-lc', command], {
|
|
270
475
|
cwd: cwd || process.cwd(),
|
|
271
476
|
windowsHide: true,
|
|
477
|
+
/* stdin KAPALI: etkileşimli programlar girdi bekleyip asılmaz, hemen çıkar */
|
|
478
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
272
479
|
env: envWithPathPrefix(),
|
|
273
480
|
});
|
|
274
481
|
} catch {
|
|
@@ -277,7 +484,9 @@ function runBashCommand(command, cwd, timeoutMs = 90000, signal) {
|
|
|
277
484
|
}
|
|
278
485
|
const timer = setTimeout(() => {
|
|
279
486
|
try { spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true }); } catch {}
|
|
280
|
-
err +=
|
|
487
|
+
err +=
|
|
488
|
+
'\n[beast] komut zaman aşımı (' + Math.round(timeoutMs / 1000) + ' sn) — girdi bekliyor olabilir ' +
|
|
489
|
+
'ya da çok uzun sürdü; etkileşimsiz bayraklarla dene, uzun süreçleri arka planda başlat.';
|
|
281
490
|
finish(null, true);
|
|
282
491
|
}, timeoutMs);
|
|
283
492
|
if (signal) {
|
|
@@ -329,16 +538,23 @@ function runCommand(command, cwd, timeoutMs = 90000, signal) {
|
|
|
329
538
|
const child = spawn(
|
|
330
539
|
'powershell.exe',
|
|
331
540
|
['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', command],
|
|
332
|
-
{
|
|
541
|
+
{
|
|
542
|
+
cwd: cwd || process.cwd(),
|
|
543
|
+
windowsHide: true,
|
|
544
|
+
/* stdin KAPALI: etkileşimli programlar girdi bekleyip asılmaz, hemen çıkar */
|
|
545
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
546
|
+
env: envWithPathPrefix(),
|
|
547
|
+
}
|
|
333
548
|
);
|
|
334
549
|
|
|
335
550
|
const timer = setTimeout(() => {
|
|
336
551
|
try {
|
|
337
552
|
spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true });
|
|
338
553
|
} catch {}
|
|
554
|
+
err +=
|
|
555
|
+
'\n[beast] komut zaman aşımı (' + Math.round(timeoutMs / 1000) + ' sn) — girdi bekliyor olabilir ' +
|
|
556
|
+
'ya da çok uzun sürdü; etkileşimsiz bayraklarla dene, uzun süreçleri arka planda başlat.';
|
|
339
557
|
finish(null, true);
|
|
340
|
-
// append notice
|
|
341
|
-
err += '\n[beast] command timed out';
|
|
342
558
|
}, timeoutMs);
|
|
343
559
|
|
|
344
560
|
if (signal) {
|
|
@@ -1587,7 +1803,9 @@ const definitions = [
|
|
|
1587
1803
|
function: {
|
|
1588
1804
|
name: 'run_command',
|
|
1589
1805
|
description:
|
|
1590
|
-
'Executes a given PowerShell command on the user\'s Windows machine in the workspace directory and returns combined stdout/stderr. Use this tool for terminal operations like builds, git, npm installs, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead: read_file, write_file, edit_file, grep, glob
|
|
1806
|
+
'Executes a given PowerShell command on the user\'s Windows machine in the workspace directory and returns combined stdout/stderr. Use this tool for terminal operations like builds, git, npm installs, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead: read_file, write_file, edit_file, grep, glob.\n' +
|
|
1807
|
+
'NON-INTERACTIVE: Komutlar girdi alamaz — REPL/editör/pager başlatma (`node`, `python`, `vi`, `notepad`), girdi bekleyen komut çalıştırma (`git commit` mesajsız, `npm init -y`siz, `ssh` BatchMode olmadan) — bunlar otomatik reddedilir/kilitlenmeden kesilir. Etkileşimsiz bayrak kullan (-y, -m, --yes, --accept-*), betikleri dosyaya yazıp çalıştır.\n' +
|
|
1808
|
+
'UZUN SÜRELİ SÜREÇ: dev server/serving komutlarını (npm run dev/start, vite, uvicorn, flask run...) bu araçla BAŞLATMA — 10 sn içinde kesilir; panel_run (ÇALIŞTIR paneli) ya da arka plan (Start-Process + çıktıyı log dosyasına yönlendir) kullan.',
|
|
1591
1809
|
parameters: {
|
|
1592
1810
|
type: 'object',
|
|
1593
1811
|
properties: {
|
|
@@ -1884,11 +2102,18 @@ async function exec(name, args, ctx) {
|
|
|
1884
2102
|
return JSON.stringify({ ok: false, error: `Invalid timeout value: ${tRaw}. Timeout must be a positive number.` });
|
|
1885
2103
|
}
|
|
1886
2104
|
const t = Number.isFinite(tRaw) && tRaw > 0 ? tRaw : 120000;
|
|
2105
|
+
const cmdStr = String(args.command || '');
|
|
2106
|
+
/* TAKILMA SAVUNMASI: etkileşimli komutlar stdin beklerken turu kilitler
|
|
2107
|
+
(Read-Host işaret satırını yer, REPL/editör asılı kalır) — başlamadan reddet */
|
|
2108
|
+
const interact = interactiveCommandReason(cmdStr);
|
|
2109
|
+
if (interact) {
|
|
2110
|
+
return JSON.stringify({ ok: false, code: null, output: '[beast] ' + interact });
|
|
2111
|
+
}
|
|
1887
2112
|
const shell = String(args.shell || 'powershell').toLowerCase();
|
|
1888
2113
|
const r =
|
|
1889
2114
|
shell === 'bash' || shell === 'sh'
|
|
1890
|
-
? await runBashCommand(
|
|
1891
|
-
: await runShellCommand(
|
|
2115
|
+
? await runBashCommand(cmdStr, cwd, t, ctx.signal)
|
|
2116
|
+
: await runShellCommand(cmdStr, cwd, t, ctx.signal);
|
|
1892
2117
|
const b = boundToolOutput((r && r.output) || '');
|
|
1893
2118
|
return JSON.stringify({
|
|
1894
2119
|
ok: !!(r && r.ok),
|
|
@@ -2360,6 +2585,11 @@ function banBrowser(minutes = 10) {
|
|
|
2360
2585
|
_browserBanUntil = Date.now() + minutes * 60 * 1000;
|
|
2361
2586
|
}
|
|
2362
2587
|
|
|
2588
|
+
/* test/teşhis: ban bitimine kalan süre */
|
|
2589
|
+
function browserBanRemainingMs() {
|
|
2590
|
+
return Math.max(0, _browserBanUntil - Date.now());
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2363
2593
|
/* Zinciri sırayla koştur: her motor boş/hata dönerse sıradakine geç.
|
|
2364
2594
|
browser motoru yalnız engine'den gelir (hook); araç-bağımsız çağrılarda atlanır. */
|
|
2365
2595
|
async function searchChainWeb(query, maxResults, { signal, browser } = {}) {
|
|
@@ -2384,7 +2614,10 @@ async function searchChainWeb(query, maxResults, { signal, browser } = {}) {
|
|
|
2384
2614
|
if (typeof browser !== 'function' || banned) continue;
|
|
2385
2615
|
out = await browser();
|
|
2386
2616
|
if (!out || !out.ok || !(out.results || []).length) {
|
|
2387
|
-
|
|
2617
|
+
/* GERÇEK engel (CAPTCHA / olağandışı trafik) → 10 dk; geçici hata
|
|
2618
|
+
(ağ, zaman aşımı, boş yanıt) → yalnızca 1 dk bekleme — eskiden
|
|
2619
|
+
her geçici hatada tarayıcı 10 dk boyunca devre dışı kalıyordu. */
|
|
2620
|
+
banBrowser(out && out.blocked ? 10 : 1);
|
|
2388
2621
|
out = null;
|
|
2389
2622
|
}
|
|
2390
2623
|
} else if (row.id === 'stealth') {
|
|
@@ -2404,7 +2637,7 @@ async function searchChainWeb(query, maxResults, { signal, browser } = {}) {
|
|
|
2404
2637
|
if (out && out.ok && !(out.results || []).length) out = null; /* boş → sıradaki motor */
|
|
2405
2638
|
}
|
|
2406
2639
|
if (out && out.ok && banned && (out.results || []).length) {
|
|
2407
|
-
out.note = 'dahili tarayıcı
|
|
2640
|
+
out.note = 'dahili tarayıcı kısa süre askıda (CAPTCHA/trafik ya da geçici hata) — alternatif motor kullanıldı';
|
|
2408
2641
|
}
|
|
2409
2642
|
return out || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' };
|
|
2410
2643
|
}
|
|
@@ -2474,6 +2707,7 @@ module.exports = {
|
|
|
2474
2707
|
runCommand,
|
|
2475
2708
|
runShellCommand,
|
|
2476
2709
|
runBashCommand,
|
|
2710
|
+
interactiveCommandReason,
|
|
2477
2711
|
gitbash,
|
|
2478
2712
|
disposeShellSessions,
|
|
2479
2713
|
boundToolOutput,
|
|
@@ -2498,6 +2732,7 @@ module.exports = {
|
|
|
2498
2732
|
searchChainWeb,
|
|
2499
2733
|
browserBanned,
|
|
2500
2734
|
banBrowser,
|
|
2735
|
+
browserBanRemainingMs,
|
|
2501
2736
|
setTinyfishKey,
|
|
2502
2737
|
tinyfishSearch,
|
|
2503
2738
|
stealthSearch,
|
package/src/agent/watext.js
CHANGED
|
@@ -49,6 +49,8 @@ const TOOL_LABELS = {
|
|
|
49
49
|
browser_screenshot: () => 'Ekran görüntüsü',
|
|
50
50
|
browser_snapshot: () => 'Sayfa yapısı',
|
|
51
51
|
browser_read_text: () => 'Sayfa metni',
|
|
52
|
+
browser_wait: (a) =>
|
|
53
|
+
`Bekliyor${a.text ? ': "' + String(a.text).slice(0, 40) + '"' : a.selector ? ': ' + String(a.selector).slice(0, 40) : a.ms ? ' (' + a.ms + ' ms)' : ''}`,
|
|
52
54
|
email_list: () => 'E-posta listesi',
|
|
53
55
|
email_read: (a) => `E-posta oku: ${a.uid ?? ''}`,
|
|
54
56
|
email_send: () => 'E-posta gönder',
|