beast-agent 2.3.1 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.3.1",
4
+ "version": "2.3.2",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
package/src/main.js CHANGED
@@ -5778,6 +5778,53 @@ ipcMain.handle('stt:transcribe', async (_e, b64, lang, priority) => {
5778
5778
  });
5779
5779
 
5780
5780
  ipcMain.handle('stt:lang:get', () => settings.sttLang || 'tr');
5781
+
5782
+ /* STT DURUMU: ayarlar ekranı için — model indirildi mi/in-progress mi,
5783
+ hangi motor, ne kadar disk */
5784
+ ipcMain.handle('stt:status', () => {
5785
+ const provider = sttProvider();
5786
+ const model = sttModelName();
5787
+ const out = { provider, engineLabel: sttEngineLabel(), model };
5788
+ if (provider !== 'local') { out.state = 'cloud'; return out; }
5789
+ const dir = path.join(APP_DIR, 'models', ...model.split('/'));
5790
+ let files = [];
5791
+ try {
5792
+ files = fs.readdirSync(dir, { recursive: true, withFileTypes: true })
5793
+ .filter((d) => d.isFile())
5794
+ .map((d) => path.join(d.parentPath || d.path, d.name));
5795
+ } catch {}
5796
+ let bytes = 0;
5797
+ let tmp = 0;
5798
+ let onnx = 0;
5799
+ let hasConfig = false;
5800
+ for (const p of files) {
5801
+ try {
5802
+ bytes += fs.statSync(p).size;
5803
+ if (/\.tmp/i.test(p)) tmp++;
5804
+ else if (/\.onnx$/i.test(p)) onnx++;
5805
+ else if (/config\.json$/i.test(p)) hasConfig = true;
5806
+ } catch {}
5807
+ }
5808
+ let state;
5809
+ if (sttPipeline) state = 'ready';
5810
+ else if (sttLoading) state = 'loading';
5811
+ else if (hasConfig && onnx >= 2 && tmp === 0) state = 'downloaded';
5812
+ else if (files.length) state = 'partial';
5813
+ else state = 'missing';
5814
+ out.state = state;
5815
+ out.mb = Math.round(bytes / (1024 * 1024));
5816
+ out.onnx = onnx;
5817
+ out.tmp = tmp;
5818
+ return out;
5819
+ });
5820
+
5821
+ /* istenirse modeli şimdi indir/yükle (bloklamaz — arka planda) */
5822
+ ipcMain.handle('stt:prefetch', () => {
5823
+ ensureStt()
5824
+ .then(() => waLog('STT prefetch: hazır'))
5825
+ .catch((e) => waLog('STT prefetch hata: ' + String((e && e.message) || e)));
5826
+ return { ok: true, loading: true };
5827
+ });
5781
5828
  ipcMain.handle('stt:lang:set', (_e, lang) => {
5782
5829
  const v = ['auto', 'tr', 'en'].includes(String(lang)) ? String(lang) : 'tr';
5783
5830
  settings.sttLang = v;
package/src/preload.js CHANGED
@@ -53,6 +53,8 @@ contextBridge.exposeInMainWorld('beast', {
53
53
  approvalRespond: (id, ok, always) => ipcRenderer.invoke('approval:respond', { id, ok, always }),
54
54
  sttTranscribe: (b64, lang, priority) => ipcRenderer.invoke('stt:transcribe', b64, lang, priority),
55
55
  sttLangGet: () => ipcRenderer.invoke('stt:lang:get'),
56
+ sttStatus: () => ipcRenderer.invoke('stt:status'),
57
+ sttPrefetchNow: () => ipcRenderer.invoke('stt:prefetch'),
56
58
  sttLangSet: (lang) => ipcRenderer.invoke('stt:lang:set', lang),
57
59
  updateStatus: () => ipcRenderer.invoke('update:status'),
58
60
  updateCheck: () => ipcRenderer.invoke('update:check'),
@@ -214,7 +214,8 @@
214
214
  tts_sub: 'Açık olursa WhatsApp cevapları metnin yanı sıra sesli not olarak da gönderilir.',
215
215
  tts_active: 'Aktif',
216
216
  tts_save: 'TTS Kaydet',
217
- tts_test: 'Test sesi çal',
217
+ tts_test: 'Test sesi çal',
218
+ stt_download_now: 'Şimdi indir / hazırla',
218
219
  tts_note: 'Edge TTS: ücretsiz yerel motor — anahtar gerekmez (Ahmet/Emel vb.). OpenAI motoru: uyumlu speech API (tts-1, tts-1-hd; sesler alloy, echo, fable, onyx, nova, shimmer).',
219
220
  tts_engine: 'Motor',
220
221
  tts_edge_voice: 'Edge sesi',
@@ -789,7 +790,8 @@
789
790
  tts_sub: 'When on, WhatsApp replies are also sent as voice notes alongside text.',
790
791
  tts_active: 'Active',
791
792
  tts_save: 'Save TTS',
792
- tts_test: 'Play test sound',
793
+ tts_test: 'Play test sound',
794
+ stt_download_now: 'Download / prepare now',
793
795
  tts_note: 'Edge TTS: free local engine — no key needed (Ahmet/Emel etc.). OpenAI engine: compatible speech API (tts-1, tts-1-hd; voices alloy, echo, fable, onyx, nova, shimmer).',
794
796
  tts_engine: 'Engine',
795
797
  tts_edge_voice: 'Edge voice',
@@ -2097,9 +2097,23 @@ function ttsFlushTail() {
2097
2097
  if (rest) ttsEnqueueSentence(rest);
2098
2098
  else ttsKick();
2099
2099
  }
2100
-
2101
- async function renderTtsPane() { const pane = $('#tab-tts');
2100
+ async function renderTtsPane() {
2101
+ const pane = $('#tab-tts');
2102
2102
  if (!pane) return;
2103
+ /* STT DURUMU: model indirildi mi / iniyor mu / hangi motor */
2104
+ let stt = null;
2105
+ try { stt = await beast.sttStatus(); } catch {}
2106
+ const stateText = {
2107
+ ready: 'HAZIR — model yüklü',
2108
+ loading: 'İNİYOR / yükleniyor…',
2109
+ downloaded: 'indirildi — ilk kullanımda yüklenir',
2110
+ partial: 'kısmen indi — devam edecek',
2111
+ missing: 'henüz indirilmedi',
2112
+ cloud: 'bulut motoru — indirme gerekmez',
2113
+ };
2114
+ const sttLine = stt
2115
+ ? stt.engineLabel + ' · ' + (stateText[stt.state] || stt.state) + (stt.mb ? ' · ' + stt.mb + ' MB' : '')
2116
+ : (window.beast ? 'bilinmiyor' : '');
2103
2117
  const edgeOpts = [
2104
2118
  ['tr-TR-AhmetNeural', 'Ahmet — Türkçe (erkek)'],
2105
2119
  ['tr-TR-EmelNeural', 'Emel — Türkçe (kadın)'],
@@ -2112,6 +2126,10 @@ async function renderTtsPane() { const pane = $('#tab-tts');
2112
2126
  .join('');
2113
2127
  pane.innerHTML =
2114
2128
  '<h2>' + _t('tts_h2') + '</h2><div class="sub">' + _t('tts_sub') + '</div>' +
2129
+ '<div class="sub" style="margin:10px 0 4px;font-weight:700;color:var(--accent)">STT (Ses → Yazı)</div>' +
2130
+ `<div class="sub" id="sttStatusTxt" style="margin-bottom:8px">${sttLine}</div>` +
2131
+ `<button id="sttDlBtn" class="btn ghost" style="margin-bottom:14px">${_t('stt_download_now')}</button>` +
2132
+ '<div class="sub" style="margin:10px 0 4px;font-weight:700;color:var(--accent)">TTS (Yazı → Ses)</div>' +
2115
2133
  `<div class="form-grid" style="grid-template-columns:auto 1fr 1fr;align-items:center;margin-top:10px">
2116
2134
  <label class="lock-row"><input type="checkbox" id="ttsOn" /><span>${_t('tts_active')}</span></label>
2117
2135
  <label style="grid-column:1">${_t('tts_engine')}</label>
@@ -2153,6 +2171,24 @@ async function renderTtsPane() { const pane = $('#tab-tts');
2153
2171
  } catch {}
2154
2172
  syncEngineUi();
2155
2173
  $('#ttsEngine').addEventListener('change', syncEngineUi);
2174
+
2175
+ /* STT durum satırı: yenile + şimdi indir (yüklenirken 3 sn'de bir güncellenir) */
2176
+ const refreshSttStatus = async () => {
2177
+ try {
2178
+ stt = await beast.sttStatus();
2179
+ const el = $('#sttStatusTxt');
2180
+ if (el && stt) el.textContent = stt.engineLabel + ' · ' + (stateText[stt.state] || stt.state) + (stt.mb ? ' · ' + stt.mb + ' MB' : '');
2181
+ return stt;
2182
+ } catch { return null; }
2183
+ };
2184
+ $('#sttDlBtn').addEventListener('click', async () => {
2185
+ await beast.sttPrefetchNow().catch(() => {});
2186
+ toast('STT modeli indiriliyor/yükleniyor…');
2187
+ const poll = setInterval(async () => {
2188
+ const s = await refreshSttStatus();
2189
+ if (s && (s.state === 'ready' || s.state === 'cloud')) { clearInterval(poll); toast('STT hazır'); }
2190
+ }, 3000);
2191
+ });
2156
2192
  $('#ttsSave').addEventListener('click', async () => {
2157
2193
  await beast.waSetTts({
2158
2194
  enabled: $('#ttsOn').checked,