beast-agent 2.10.0 → 2.12.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.
@@ -84,6 +84,29 @@ const els = {
84
84
  bcMic: $('#bcMic'),
85
85
  bcChips: $('#bcChips'),
86
86
  studioBtn: $('#studioBtn'),
87
+ sbBtn: $('#sbBtn'),
88
+ sandboxRow: $('#sandboxRow'),
89
+ sbRepo: $('#sbRepo'),
90
+ sbReveal: $('#sbReveal'),
91
+ sbRemove: $('#sbRemove'),
92
+ sbNew: $('#sbNew'),
93
+ sbCloneInput: $('#sbCloneInput'),
94
+ sbCloneBtn: $('#sbCloneBtn'),
95
+ sbGhBtn: $('#sbGhBtn'),
96
+ sbOut: $('#sbOut'),
97
+ sbStatus: $('#sbStatus'),
98
+ sbInput: $('#sbInput'),
99
+ sbStop: $('#sbStop'),
100
+ sbRunCwd: $('#sbRunCwd'),
101
+ sbRunInstall: $('#sbRunInstall'),
102
+ sbRunStart: $('#sbRunStart'),
103
+ sbRunDev: $('#sbRunDev'),
104
+ sbRunStop: $('#sbRunStop'),
105
+ sbRunCmd: $('#sbRunCmd'),
106
+ sbRunGo: $('#sbRunGo'),
107
+ sbRunOut: $('#sbRunOut'),
108
+ sbRunOpen: $('#sbRunOpen'),
109
+ sbRunUrl: $('#sbRunUrl'),
87
110
  stPanel: $('#stPanel'),
88
111
  stTitle: $('#stTitle'),
89
112
  stCwd: $('#stCwd'),
@@ -4984,6 +5007,19 @@ function onEvent(ev) {
4984
5007
  }
4985
5008
  /* OFFLINE MESAJ KUYRUĞU: bağlantı + kuyruk olayları (sessionId filtresinden önce) */
4986
5009
  if (ev.type === 'net' || ev.type === 'netQueue') { onNetEvent(ev); return; }
5010
+ /* Sandbox klonlama progress'i (sessionId'siz global olay) */
5011
+ if (ev.type === 'sb-clone') { sbCloneProgress(ev.pct, ev.text, !!ev.done); return; }
5012
+ /* Sandbox ÇALIŞTIRICI süreç akışı: sessionId'siz global olaylar */
5013
+ if (ev.type === 'sb-run' || ev.type === 'sb-run-url' || ev.type === 'sb-run-end') {
5014
+ sbRunIngest(ev);
5015
+ return;
5016
+ }
5017
+ /* Beast Sandbox oturumu: olaylar SADECE Sandbox paneline akar — ana sohbeti
5018
+ ve Code/Studio panellerini kirletmez (dünyalar ayrı) */
5019
+ if (sbSessionId && ev.sessionId === sbSessionId) {
5020
+ sbIngest(ev);
5021
+ return;
5022
+ }
4987
5023
  /* Beast Studio oturumu: olaylar SADECE Studio paneline akar — ana sohbeti
4988
5024
  ve Beast Code panelini kirletmez (dünyalar ayrı) */
4989
5025
  if (stSessionId && ev.sessionId === stSessionId) {
@@ -6896,6 +6932,7 @@ function ghCard(repo, isFav) {
6896
6932
  <div class="gh-actions">
6897
6933
  <button class="gh-fav ${isFav ? 'on' : ''}" data-act="fav">${isFav ? '★ Favori' : '☆ Favorile'}</button>
6898
6934
  <button data-act="chat"><svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:4px"><path d="M22 2 11 13"/><path d="M22 2 15 22l-4-9-9-4z"/></svg>Chate at</button>
6935
+ <button data-act="dl" title="Sandbox'a indir (git clone)">⬇ İndir</button>
6899
6936
  <button data-act="copy">⧉ Kopyala</button>
6900
6937
  </div>
6901
6938
  </div>`;
@@ -6911,6 +6948,12 @@ function wireGhCards(container, list, isFavPane) {
6911
6948
  /* mesaj chate gitti — cevap chatten geleceği için modal kapanır */
6912
6949
  closeGit();
6913
6950
  });
6951
+ /* Sandbox'a indir: klonlama Sandbox panelinde görünür, panel açılır */
6952
+ card.querySelector('[data-act="dl"]').addEventListener('click', async () => {
6953
+ closeGit();
6954
+ setSandboxMode(true);
6955
+ sbClone(repo.full_name);
6956
+ });
6914
6957
  card.querySelector('[data-act="copy"]').addEventListener('click', async () => {
6915
6958
  try { await navigator.clipboard.writeText(repo.html_url); toast('Link kopyalandı'); } catch {}
6916
6959
  });
@@ -7068,6 +7111,7 @@ function ideModeOn() {
7068
7111
 
7069
7112
  async function setIdeMode(on) {
7070
7113
  if (on && studioModeOn()) await setStudioMode(false); /* Studio açıkken IDE'ye geçiş — Studio kapanır */
7114
+ if (on && sbModeOn()) setSandboxMode(false); /* Sandbox açıkken IDE'ye geçiş — Sandbox kapanır */
7071
7115
  document.body.classList.toggle('ide-mode', !!on);
7072
7116
  if (els.ideBtn) els.ideBtn.classList.toggle('on', !!on);
7073
7117
  /* soldaki marka: chat modunda BEAST Agent, IDE modunda BEAST Code, Studio modunda BEAST Studio */
@@ -7137,6 +7181,7 @@ function studioModeOn() {
7137
7181
 
7138
7182
  async function setStudioMode(on) {
7139
7183
  if (on && ideModeOn()) await setIdeMode(false); /* IDE açıkken Studio'ya geçiş — IDE kapanır */
7184
+ if (on && sbModeOn()) setSandboxMode(false); /* Sandbox açıkken Studio'ya geçiş — Sandbox kapanır */
7140
7185
  document.body.classList.toggle('studio-mode', !!on);
7141
7186
  if (els.studioBtn) els.studioBtn.classList.toggle('on', !!on);
7142
7187
  /* ALT TERMINAL: yalnız Beast Code'a özgü — Studio'ya geçişte kapanır */
@@ -7359,7 +7404,8 @@ if (els.ideSplit) {
7359
7404
  }
7360
7405
 
7361
7406
  async function loadIdeTree() {
7362
- ideState.cache.clear();
7407
+ /* hangi moddaysak o ağacın önbelleği tazelenir (Sandbox → repo ağacı) */
7408
+ (sbModeOn() ? sbTreeState : studioModeOn() ? studioState : ideState).cache.clear();
7363
7409
  await renderFileTree();
7364
7410
  }
7365
7411
 
@@ -7375,17 +7421,26 @@ async function renderFileTree() {
7375
7421
  if (!tree) return;
7376
7422
  tree.innerHTML = '';
7377
7423
  const wsLabel = $('#filePanelPath');
7378
- /* Studio modunda ağaç studioRoot'tan, IDE modunda ideRoot'tan okunur —
7379
- aynı panel, aynı çizim; yalnız kaynak ve davranış moduna göre değişir */
7424
+ /* Studio modunda ağaç studioRoot'tan, Sandbox modunda seçili reponun
7425
+ klasöründen, IDE modunda ideRoot'tan okunur — aynı panel, aynı çizim */
7426
+ const sbx = sbModeOn();
7380
7427
  const studio = studioModeOn();
7381
- const st = studio ? studioState : ideState;
7382
- const treeApi = studio ? beast.studioTree : beast.ideTree;
7428
+ const st = sbx ? sbTreeState : studio ? studioState : ideState;
7429
+ const treeApi = sbx
7430
+ ? (rel) => beast.sandboxTree({ folder: sbFolder, rel })
7431
+ : studio
7432
+ ? beast.studioTree
7433
+ : beast.ideTree;
7383
7434
  const build = async (rel, depth, container) => {
7384
7435
  let entries = st.cache.get(rel);
7385
7436
  if (!entries) {
7386
7437
  const r = await treeApi(rel).catch(() => null);
7387
7438
  if (!r || !r.ok) {
7388
- if (!rel) container.innerHTML = `<div class="file-empty">${escapeHtml((r && r.error) || 'okunamadı')}</div>`;
7439
+ if (!rel) {
7440
+ container.innerHTML = sbx
7441
+ ? '<div class="file-empty">Repo seçili değil — yukarıdan indir ya da seç</div>'
7442
+ : `<div class="file-empty">${escapeHtml((r && r.error) || 'okunamadı')}</div>`;
7443
+ }
7389
7444
  return;
7390
7445
  }
7391
7446
  entries = r.entries;
@@ -7393,7 +7448,9 @@ async function renderFileTree() {
7393
7448
  if (r.workspace) {
7394
7449
  st.path = r.workspace;
7395
7450
  if (wsLabel) { wsLabel.textContent = r.workspace; wsLabel.title = r.workspace; }
7396
- if (studio) {
7451
+ if (sbx) {
7452
+ if (els.sbRunCwd) { els.sbRunCwd.textContent = r.workspace; els.sbRunCwd.title = r.workspace; }
7453
+ } else if (studio) {
7397
7454
  if (els.stCwd) { els.stCwd.textContent = r.workspace; els.stCwd.title = r.workspace; }
7398
7455
  stOnFolderChanged(r.workspace);
7399
7456
  } else {
@@ -7418,6 +7475,13 @@ async function renderFileTree() {
7418
7475
  if (st.open.has(child)) st.open.delete(child);
7419
7476
  else st.open.add(child);
7420
7477
  renderFileTree();
7478
+ } else if (sbx) {
7479
+ /* Sandbox: dosyayı chat'e bağlam olarak bırak — ajan incelesin */
7480
+ if (els.sbInput) {
7481
+ els.sbInput.value = 'Şu dosyayı incele: ' + child;
7482
+ els.sbInput.focus();
7483
+ sbInputResize();
7484
+ }
7421
7485
  } else if (studio && MEDIA_FILE_RE.test(e.name)) {
7422
7486
  stVideoPlay(child); /* video/ses → sağdaki video bölümünde oynat */
7423
7487
  } else if (studio) {
@@ -7449,7 +7513,8 @@ function ideRefreshTree() {
7449
7513
  ideTreeTimer = null;
7450
7514
  try {
7451
7515
  /* hangi moddaysak o ağacın önbelleği tazelenir */
7452
- if (studioModeOn()) studioState.cache.clear();
7516
+ if (sbModeOn()) sbTreeState.cache.clear();
7517
+ else if (studioModeOn()) studioState.cache.clear();
7453
7518
  else ideState.cache.clear();
7454
7519
  renderFileTree().catch(() => {});
7455
7520
  } catch {}
@@ -8028,8 +8093,9 @@ function fileCtxShow(e, rel, isDir) {
8028
8093
  const menu = els.fileCtxMenu;
8029
8094
  if (!menu || !rel) return;
8030
8095
  e.preventDefault();
8096
+ const sbx = sbModeOn();
8031
8097
  const studio = studioModeOn();
8032
- const st = studio ? studioState : ideState;
8098
+ const st = sbx ? sbTreeState : studio ? studioState : ideState;
8033
8099
  const isMedia = MEDIA_FILE_RE.test(rel.split('/').pop() || '');
8034
8100
  const items = [];
8035
8101
  if (isDir) {
@@ -8041,6 +8107,22 @@ function fileCtxShow(e, rel, isDir) {
8041
8107
  renderFileTree();
8042
8108
  },
8043
8109
  });
8110
+ } else if (sbx) {
8111
+ /* Sandbox: dosya düzenleme Code moduna aittir — chate bağlam bırak/aç */
8112
+ items.push({
8113
+ label: 'Chate ekle (şu dosyaya bak)',
8114
+ fn: () => {
8115
+ if (els.sbInput) {
8116
+ els.sbInput.value = 'Şu dosyayı incele: ' + rel;
8117
+ els.sbInput.focus();
8118
+ sbInputResize();
8119
+ }
8120
+ },
8121
+ });
8122
+ items.push({
8123
+ label: 'Dış uygulamada aç',
8124
+ fn: () => beast.openPath((sbTreeState.path || '') + '\\' + rel.replace(/\//g, '\\')).catch(() => {}),
8125
+ });
8044
8126
  } else if (studio && isMedia) {
8045
8127
  items.push({ label: 'Oynat', fn: () => stVideoPlay(rel) });
8046
8128
  } else if (studio) {
@@ -9247,3 +9329,463 @@ if (els.stNew) els.stNew.addEventListener('click', async () => {
9247
9329
  });
9248
9330
  /* Uygulama her açılışta agent (chat) modunda başlar — IDE modu yalnız
9249
9331
  kullanıcı butona basınca girilir (oturumluk). */
9332
+
9333
+ /* ================= BEAST SANDBOX =================
9334
+ GitHub reposu indir (git clone) + repo klasöründe ajanla çalış.
9335
+ Sağ dock — ana chat solda yerinde kalır; Code/Studio'dan tamamen ayrı. */
9336
+
9337
+ let sbSessionId = '';
9338
+ let sbFolder = '';
9339
+ let sbBusy = false;
9340
+ let sbItems = [];
9341
+ let sbStreamEl = null;
9342
+ let sbStreamRaw = '';
9343
+ let sbCloning = false;
9344
+ /* sol dosya konsolu (repo ağacı) + sağ çalıştırıcı durumu */
9345
+ const sbTreeState = { cache: new Map(), open: new Set(['']), path: '' };
9346
+ let sbRunUrl = '';
9347
+ let sbRunRunning = false;
9348
+
9349
+ function sbModeOn() {
9350
+ return document.body.classList.contains('sandbox-mode');
9351
+ }
9352
+
9353
+ function sbLine(cls, text) {
9354
+ if (!els.sbOut) return null;
9355
+ const div = document.createElement('div');
9356
+ div.className = cls || 't-out';
9357
+ div.textContent = String(text || '');
9358
+ els.sbOut.appendChild(div);
9359
+ els.sbOut.scrollTop = els.sbOut.scrollHeight;
9360
+ return div;
9361
+ }
9362
+
9363
+ function sbStreamDelta(delta) {
9364
+ if (!els.sbOut) return;
9365
+ if (!sbStreamEl) {
9366
+ sbStreamEl = sbLine('t-out', '');
9367
+ }
9368
+ sbStreamRaw += String(delta || '');
9369
+ sbStreamEl.textContent = sbStreamRaw;
9370
+ els.sbOut.scrollTop = els.sbOut.scrollHeight;
9371
+ }
9372
+
9373
+ function sbFlushStream() {
9374
+ sbStreamEl = null;
9375
+ sbStreamRaw = '';
9376
+ }
9377
+
9378
+ function sbMsgText(content) {
9379
+ const t = Array.isArray(content)
9380
+ ? content.filter((p) => p && p.type === 'text').map((p) => String(p.text || '')).join('\n')
9381
+ : String(content || '');
9382
+ return t.replace(/\s+/g, ' ').trim();
9383
+ }
9384
+
9385
+ function sbArgsSummary(args) {
9386
+ try {
9387
+ const j = typeof args === 'string' ? JSON.parse(args) : args || {};
9388
+ return String(j.command || j.path || j.url || j.query || j.pattern || '').replace(/\s+/g, ' ').slice(0, 90);
9389
+ } catch {
9390
+ return '';
9391
+ }
9392
+ }
9393
+
9394
+ function sbSetBusy(b) {
9395
+ sbBusy = !!b;
9396
+ if (els.sbStop) els.sbStop.hidden = !sbBusy;
9397
+ }
9398
+
9399
+ function sbStatusShow(text) {
9400
+ if (els.sbStatus) {
9401
+ els.sbStatus.hidden = false;
9402
+ els.sbStatus.textContent = text;
9403
+ }
9404
+ }
9405
+ function sbStatusHide() {
9406
+ if (els.sbStatus) els.sbStatus.hidden = true;
9407
+ }
9408
+
9409
+ /* Sandbox oturumunun engine olayları → panel (bcIngest'in minimal karşılığı) */
9410
+ function sbIngest(ev) {
9411
+ if (!sbSessionId || ev.sessionId !== sbSessionId) return;
9412
+ switch (ev.type) {
9413
+ case 'token':
9414
+ sbStreamDelta(ev.delta);
9415
+ break;
9416
+ case 'message': {
9417
+ const m = ev.message;
9418
+ if (!m) break;
9419
+ /* KULLANICI ECHO BASILMAZ — gönderirken zaten 'sen>' satırı düştü
9420
+ (Beast Code paneliyle aynı disiplin; yoksa mesaj ÇİFT görünür) */
9421
+ if (m.role === 'assistant') {
9422
+ sbFlushStream();
9423
+ const hasTools = Array.isArray(m.tool_calls) && m.tool_calls.length > 0;
9424
+ const txt = typeof m.content === 'string' ? m.content.trim() : '';
9425
+ if (txt && !hasTools) sbLine('t-out', txt);
9426
+ }
9427
+ break;
9428
+ }
9429
+ case 'tool-start':
9430
+ sbSetBusy(true);
9431
+ sbLine('t-dim', '\u2692 ' + (ev.name || '?') + (sbArgsSummary(ev.args) ? ' \u00B7 ' + sbArgsSummary(ev.args) : ''));
9432
+ break;
9433
+ case 'tool-end': {
9434
+ const ok = ev.ok !== false;
9435
+ sbLine(ok ? 't-dim' : 't-err', (ok ? ' \u2713 ' : ' \u2717 ') + String(ev.result || '').replace(/\s+/g, ' ').slice(0, 160));
9436
+ /* ajan dosya yazarsa/editlerse soldaki repo ağacı canlı tazelenir */
9437
+ if (ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'run_command') ideRefreshTree();
9438
+ break;
9439
+ }
9440
+ case 'status':
9441
+ if (ev.status === 'idle') {
9442
+ sbSetBusy(false);
9443
+ sbStatusHide();
9444
+ } else {
9445
+ sbSetBusy(true);
9446
+ sbStatusShow(ev.status === 'thinking' ? 'düşünüyor…' : String(ev.status || ''));
9447
+ }
9448
+ break;
9449
+ case 'done':
9450
+ sbFlushStream();
9451
+ sbSetBusy(false);
9452
+ sbStatusHide();
9453
+ sbLine('t-dim', ev.aborted ? '[durduruldu' + (ev.reason ? ' — ' + ev.reason : '') + ']' : '(tamamlandı)');
9454
+ break;
9455
+ case 'error':
9456
+ sbFlushStream();
9457
+ sbSetBusy(false);
9458
+ sbStatusHide();
9459
+ sbLine('t-err', '[hata] ' + (ev.error || ''));
9460
+ break;
9461
+ }
9462
+ }
9463
+
9464
+ async function loadSandboxList() {
9465
+ const r = await beast.sandboxList().catch(() => ({ ok: false }));
9466
+ sbItems = (r && r.items) || [];
9467
+ const sel = els.sbRepo;
9468
+ if (!sel) return;
9469
+ sel.innerHTML = '';
9470
+ if (!sbItems.length) {
9471
+ const o = document.createElement('option');
9472
+ o.value = '';
9473
+ o.textContent = 'Repo yok, indir';
9474
+ sel.appendChild(o);
9475
+ }
9476
+ for (const it of sbItems) {
9477
+ const o = document.createElement('option');
9478
+ o.value = it.folder;
9479
+ o.textContent = it.name + (it.busy ? ' ●' : '');
9480
+ sel.appendChild(o);
9481
+ }
9482
+ if (sbFolder && sbItems.some((x) => x.folder === sbFolder)) sel.value = sbFolder;
9483
+ else if (sbItems.length) {
9484
+ sel.value = sbItems[0].folder;
9485
+ sbFolder = sel.value;
9486
+ } else {
9487
+ sbFolder = '';
9488
+ }
9489
+ await sbSelectCurrent();
9490
+ }
9491
+
9492
+ async function sbSelectCurrent() {
9493
+ sbFolder = els.sbRepo ? String(els.sbRepo.value || '') : '';
9494
+ if (els.sbOut) els.sbOut.innerHTML = '';
9495
+ sbSessionId = '';
9496
+ sbStreamEl = null;
9497
+ sbStreamRaw = '';
9498
+ sbSetBusy(false);
9499
+ sbStatusHide();
9500
+ /* çalıştırıcı + ağaç yeni repoya döner */
9501
+ sbRunUrl = '';
9502
+ sbRunRunning = false;
9503
+ if (els.sbRunOpen) els.sbRunOpen.disabled = true;
9504
+ if (els.sbRunUrl) els.sbRunUrl.textContent = '';
9505
+ if (els.sbRunOut) els.sbRunOut.innerHTML = '';
9506
+ sbTreeState.cache.clear();
9507
+ sbTreeState.open = new Set(['']);
9508
+ if (sbModeOn()) renderFileTree().catch(() => {});
9509
+ if (!sbFolder) {
9510
+ sbLine('t-dim', 'Repo indir: kutuya github.com/owner/repo yaz → İndir. Ya da 🔍 ile GitHub\u2019da ara.');
9511
+ return;
9512
+ }
9513
+ const r = await beast.sandboxOpen({ folder: sbFolder }).catch(() => null);
9514
+ if (r && r.ok) {
9515
+ sbSessionId = r.sessionId;
9516
+ for (const m of r.messages || []) {
9517
+ sbLine(m.role === 'assistant' ? 't-out' : 't-cmd', (m.role === 'assistant' ? '' : 'sen> ') + m.text);
9518
+ }
9519
+ sbLine('t-dim', 'Oturum yüklendi');
9520
+ } else {
9521
+ sbLine('t-err', (r && r.error) || 'oturum açılamadı');
9522
+ }
9523
+ sbSetBusy(!!(r && r.busy));
9524
+ /* sol konsol: seçilen reponun klasörü — oturum açıldıktan sonra da garanti tazele */
9525
+ sbTreeState.cache.clear();
9526
+ if (sbModeOn()) renderFileTree().catch(() => {});
9527
+ }
9528
+
9529
+ function sbInputResize() {
9530
+ const ta = els.sbInput;
9531
+ if (!ta) return;
9532
+ ta.style.height = 'auto';
9533
+ ta.style.height = Math.min(ta.scrollHeight, 140) + 'px';
9534
+ ta.classList.toggle('expand', ta.scrollHeight > 140);
9535
+ }
9536
+
9537
+ function sbRunCurrent() {
9538
+ const msg = els.sbInput.value.trim();
9539
+ if (!msg || !sbFolder || sbCloning) return;
9540
+ els.sbInput.value = '';
9541
+ sbInputResize();
9542
+ sbLine('t-cmd', 'sen> ' + msg);
9543
+ sbSetBusy(true);
9544
+ beast.sandboxSend({ folder: sbFolder, msg }).then((r) => {
9545
+ if (r && r.ok) {
9546
+ if (r.sessionId) sbSessionId = r.sessionId;
9547
+ if (r.queued) sbLine('t-dim', '⏳ kuyrukta (' + r.count + ') — iş bitince gönderilir');
9548
+ } else {
9549
+ sbSetBusy(false);
9550
+ sbLine('t-err', (r && r.error) || 'gönderilemedi');
9551
+ }
9552
+ }).catch((e) => {
9553
+ sbSetBusy(false);
9554
+ sbLine('t-err', String((e && e.message) || e));
9555
+ });
9556
+ }
9557
+
9558
+ let sbCloneLine = null;
9559
+ function sbCloneProgress(pct, text, done) {
9560
+ /* buton etiketi: %34 · İndir */
9561
+ const label = document.getElementById('sbCloneLabel');
9562
+ if (label) label.textContent = pct != null ? '%' + pct : 'İndir';
9563
+ if (!els.sbOut) return;
9564
+ if (done) {
9565
+ if (sbCloneLine && sbCloneLine.isConnected) sbCloneLine.remove();
9566
+ sbCloneLine = null;
9567
+ return;
9568
+ }
9569
+ if (!sbCloneLine || !sbCloneLine.isConnected) {
9570
+ sbCloneLine = sbLine('t-dim', '');
9571
+ }
9572
+ if (sbCloneLine) {
9573
+ sbCloneLine.textContent = (pct != null ? '⬇ %' + pct : '⬇ klonlanıyor') + (text ? ' · ' + text : '');
9574
+ els.sbOut.scrollTop = els.sbOut.scrollHeight;
9575
+ }
9576
+ }
9577
+
9578
+ async function sbClone(input) {
9579
+ if (sbCloning) return;
9580
+ const src = String(input || '').trim();
9581
+ if (!src) {
9582
+ toast('Repo adresi gir: github.com/owner/repo');
9583
+ return;
9584
+ }
9585
+ sbCloning = true;
9586
+ if (els.sbCloneBtn) els.sbCloneBtn.disabled = true;
9587
+ sbCloneLine = null;
9588
+ sbLine('t-dim', '⬇ klonlanıyor: ' + src + ' — (büyük repolar sürebilir…)');
9589
+ const r = await beast.sandboxClone(src).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
9590
+ sbCloning = false;
9591
+ if (els.sbCloneBtn) els.sbCloneBtn.disabled = false;
9592
+ sbCloneProgress(null, '', true); /* progress satırını temizle + etiketi geri koy */
9593
+ if (r && r.ok) {
9594
+ sbLine('t-dim', '✓ indirildi: ' + r.name);
9595
+ if (els.sbCloneInput) els.sbCloneInput.value = '';
9596
+ sbFolder = r.folder;
9597
+ await loadSandboxList();
9598
+ if (els.sbInput) els.sbInput.focus();
9599
+ } else {
9600
+ sbLine('t-err', '[klonlama] ' + ((r && r.error) || 'başarısız'));
9601
+ }
9602
+ }
9603
+
9604
+ function setSandboxMode(on) {
9605
+ if (on && ideModeOn()) { setIdeMode(false); }
9606
+ if (on && studioModeOn()) { setStudioMode(false); }
9607
+ document.body.classList.toggle('sandbox-mode', !!on);
9608
+ if (els.sbBtn) els.sbBtn.classList.toggle('on', !!on);
9609
+ /* marka + sol konsol başlığı: moduna göre BEAST Agent/Code/Studio/Sandbox */
9610
+ const brandSub = document.querySelector('#brand .brand-sub');
9611
+ if (brandSub) brandSub.textContent = on ? 'Sandbox' : 'Agent';
9612
+ const ftp = document.getElementById('filePanelTitle');
9613
+ if (ftp && on) ftp.textContent = 'REPO DOSYALARI';
9614
+ if (on) {
9615
+ loadSandboxList();
9616
+ sbTreeState.cache.clear();
9617
+ renderFileTree().catch(() => {});
9618
+ } else if (!ideModeOn() && !studioModeOn()) {
9619
+ /* agent moduna dönüş: ağaç eski köküne döner */
9620
+ ideState.cache.clear();
9621
+ renderFileTree().catch(() => {});
9622
+ }
9623
+ }
9624
+
9625
+ /* ---------- ÇALIŞTIRICI: butonlar KOMUT DEĞİL AJANA TALİMATTIR ----------
9626
+ Her proje farklı çalışır; komutu kullanıcı bilemez, AJAN bilir:
9627
+ paket bildirim dosyasına + README'ye bakıp doğru komutu bulur, uzun süreli
9628
+ süreci panel_run aracıyla ÇALIŞTIR panelinde (turu kilitlemeden) başlatır. */
9629
+ function sbInstruct(text) {
9630
+ if (!sbFolder) { toast('Önce repo indir ya da seç'); return; }
9631
+ sbLine('t-cmd', 'sen> ' + text);
9632
+ sbSetBusy(true);
9633
+ beast.sandboxSend({ folder: sbFolder, msg: text }).then((r) => {
9634
+ if (r && r.ok) {
9635
+ if (r.sessionId) sbSessionId = r.sessionId;
9636
+ if (r.queued) sbLine('t-dim', '⏳ kuyrukta (' + r.count + ') — iş bitince gönderilir');
9637
+ } else {
9638
+ sbSetBusy(false);
9639
+ sbLine('t-err', (r && r.error) || 'gönderilemedi');
9640
+ }
9641
+ }).catch((e) => {
9642
+ sbSetBusy(false);
9643
+ sbLine('t-err', String((e && e.message) || e));
9644
+ });
9645
+ }
9646
+
9647
+ function sbRunLine(text, cls) {
9648
+ if (!els.sbRunOut) return null;
9649
+ const div = document.createElement('div');
9650
+ div.className = cls || 't-out';
9651
+ div.textContent = String(text || '');
9652
+ els.sbRunOut.appendChild(div);
9653
+ els.sbRunOut.scrollTop = els.sbRunOut.scrollHeight;
9654
+ return div;
9655
+ }
9656
+
9657
+ /* süreç çıktısı akışı: sessionId'siz global olaylar — en üstte yönlendirilir */
9658
+ function sbRunIngest(ev) {
9659
+ if (ev.type === 'sb-run') {
9660
+ sbRunLine(ev.data);
9661
+ return;
9662
+ }
9663
+ if (ev.type === 'sb-run-url') {
9664
+ sbRunUrl = String(ev.url || '');
9665
+ sbRunLine('🌐 dev server: ' + sbRunUrl, 't-dim');
9666
+ if (els.sbRunOpen) els.sbRunOpen.disabled = !sbRunUrl;
9667
+ if (els.sbRunUrl) els.sbRunUrl.textContent = sbRunUrl;
9668
+ return;
9669
+ }
9670
+ if (ev.type === 'sb-run-end') {
9671
+ sbRunRunning = false;
9672
+ if (els.sbRunStop) els.sbRunStop.hidden = true;
9673
+ }
9674
+ }
9675
+
9676
+ function sbRunExec(cmd) {
9677
+ if (!sbFolder) { toast('Önce repo indir ya da seç'); return; }
9678
+ if (sbRunRunning) { toast('Zaten çalışıyor — önce ■ ile durdur'); return; }
9679
+ sbRunRunning = true;
9680
+ if (els.sbRunStop) els.sbRunStop.hidden = false;
9681
+ beast.sandboxRun({ folder: sbFolder, cmd }).then((r) => {
9682
+ if (!r || !r.ok) {
9683
+ sbRunRunning = false;
9684
+ if (els.sbRunStop) els.sbRunStop.hidden = true;
9685
+ sbRunLine('[hata] ' + ((r && r.error) || 'çalıştırılamadı'), 't-err');
9686
+ }
9687
+ }).catch((e) => {
9688
+ sbRunRunning = false;
9689
+ if (els.sbRunStop) els.sbRunStop.hidden = true;
9690
+ sbRunLine('[hata] ' + String((e && e.message) || e), 't-err');
9691
+ });
9692
+ }
9693
+
9694
+ /* topbar + dock olay bağlama */
9695
+ if (els.sbBtn) els.sbBtn.addEventListener('click', () => setSandboxMode(!sbModeOn()));
9696
+ if (els.sbRepo) els.sbRepo.addEventListener('change', sbSelectCurrent);
9697
+ if (els.sbCloneBtn) els.sbCloneBtn.addEventListener('click', () => sbClone(els.sbCloneInput.value));
9698
+ if (els.sbCloneInput) {
9699
+ els.sbCloneInput.addEventListener('keydown', (e) => {
9700
+ if (e.key === 'Enter') {
9701
+ e.preventDefault();
9702
+ sbClone(els.sbCloneInput.value);
9703
+ }
9704
+ });
9705
+ }
9706
+ if (els.sbGhBtn) els.sbGhBtn.addEventListener('click', () => openGit());
9707
+ if (els.sbNew) els.sbNew.addEventListener('click', async () => {
9708
+ if (sbBusy) { toast('Mesaj sürüyor — önce ■ ile durdur'); return; }
9709
+ if (!sbFolder) return;
9710
+ const r = await beast.sandboxNew({ folder: sbFolder }).catch(() => null);
9711
+ if (r && r.ok) {
9712
+ sbSessionId = '';
9713
+ await sbSelectCurrent();
9714
+ sbLine('t-sys', 'yeni oturum — taze başlar');
9715
+ }
9716
+ });
9717
+ if (els.sbRemove) els.sbRemove.addEventListener('click', async () => {
9718
+ if (!sbFolder) { toast('Önce repo seç'); return; }
9719
+ const name = (sbItems.find((x) => x.folder === sbFolder) || {}).name || 'repo';
9720
+ const ok = await uiConfirm('"' + name + '" reposunu diskten silmek istiyor musun? (klasör + sohbet)', 'Sil');
9721
+ if (!ok) return;
9722
+ const r = await beast.sandboxRemove({ folder: sbFolder }).catch(() => null);
9723
+ if (r && r.ok) {
9724
+ sbFolder = '';
9725
+ sbSessionId = '';
9726
+ await loadSandboxList();
9727
+ } else {
9728
+ toast((r && r.error) || 'silinemedi');
9729
+ }
9730
+ });
9731
+ if (els.sbReveal) els.sbReveal.addEventListener('click', () => {
9732
+ if (sbFolder) beast.sandboxReveal({ folder: sbFolder }).catch(() => {});
9733
+ });
9734
+ if (els.sbInput) {
9735
+ els.sbInput.addEventListener('input', sbInputResize);
9736
+ els.sbInput.addEventListener('keydown', (e) => {
9737
+ if (e.key === 'Enter' && !e.shiftKey) {
9738
+ e.preventDefault();
9739
+ sbRunCurrent();
9740
+ }
9741
+ });
9742
+ }
9743
+ if (els.sbStop) {
9744
+ els.sbStop.addEventListener('click', () => {
9745
+ beast.sandboxStop({ folder: sbFolder }).then((r) => {
9746
+ if (r && r.ok && r.wasBusy === false) {
9747
+ sbSetBusy(false);
9748
+ sbStatusHide();
9749
+ }
9750
+ }).catch(() => {});
9751
+ });
9752
+ }
9753
+ /* ÇALIŞTIRICI butonları — KOMUT DEĞİL AJANA TALİMAT (komutu ajan bulur):
9754
+ uzun süreli süreci panel_run ile ÇALIŞTIR paneline bırakır, tur kilitlenmez */
9755
+ if (els.sbRunInstall) els.sbRunInstall.addEventListener('click', () =>
9756
+ sbInstruct('Bu projeyi KUR — paket bildirim dosyasına (package.json, requirements.txt, Cargo.toml, go.mod, pom.xml vb.) göre doğru kurulum komutunu bul ve çalıştır (run_command); sonucu 1-2 satırla raporla.')
9757
+ );
9758
+ if (els.sbRunStart) els.sbRunStart.addEventListener('click', () =>
9759
+ sbInstruct('Bu projeyi ÇALIŞTIR — README/package.json scripts\u2019e göre doğru başlatma komutunu bul; uzun süreli sunucu/süreçse panel_run ile ÇALIŞTIR panelinde başlat (tur kilitleme), adresini/çalıştığını kullanıcıya bildir.')
9760
+ );
9761
+ if (els.sbRunDev) els.sbRunDev.addEventListener('click', () =>
9762
+ sbInstruct('Bu projeyi GELİŞTİRME modunda başlat — package.json scripts\u2019e bak (npm run dev, vite, expo start, ng serve vb. hangisi varsa) ve panel_run ile ÇALIŞTIR panelinde başlat; adresini kullanıcıya bildir.')
9763
+ );
9764
+ if (els.sbRunGo) els.sbRunGo.addEventListener('click', () => {
9765
+ const c = els.sbRunCmd.value.trim();
9766
+ if (!c) { toast('Komut yaz'); return; }
9767
+ sbRunExec(c);
9768
+ });
9769
+ if (els.sbRunCmd) {
9770
+ els.sbRunCmd.addEventListener('keydown', (e) => {
9771
+ if (e.key === 'Enter') {
9772
+ e.preventDefault();
9773
+ const c = els.sbRunCmd.value.trim();
9774
+ if (c) sbRunExec(c);
9775
+ }
9776
+ });
9777
+ }
9778
+ if (els.sbRunStop) {
9779
+ els.sbRunStop.addEventListener('click', () => {
9780
+ beast.sandboxRunStop().then((r) => {
9781
+ if (r && r.ok) sbRunLine('⏹ durduruldu', 't-dim');
9782
+ }).catch(() => {});
9783
+ });
9784
+ }
9785
+ if (els.sbRunOpen) {
9786
+ els.sbRunOpen.addEventListener('click', () => {
9787
+ /* Tarayıcıda aç da AJANA TALİMAT: ajan çalışma adresini bulup kendi
9788
+ browser_open aracıyla dahili tarayıcıda açar */
9789
+ sbInstruct('Bu projenin web arayüzü çalışıyorsa adresini tespit et (çıktılar/README/port taraması) ve browser_open aracıyla DAHİLİ TARAYICIDA aç.')
9790
+ });
9791
+ }