beast-agent 2.63.9 → 2.64.1
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/browseragent.js +477 -0
- package/src/agent/computeragent.js +366 -0
- package/src/agent/engine.js +121 -6
- package/src/agent/typesafe.js +85 -35
- package/src/agent/watext.js +2 -0
- package/src/main.js +296 -22
- package/src/renderer/i18n.js +4 -0
- package/src/renderer/renderer.js +20 -4
package/src/main.js
CHANGED
|
@@ -324,6 +324,7 @@ try {
|
|
|
324
324
|
typesafeMod.setConfig(() => ({
|
|
325
325
|
apiKey: settings.typesafe && settings.typesafe.apiKey,
|
|
326
326
|
model: settings.typesafe && settings.typesafe.model,
|
|
327
|
+
enabled: !(settings.typesafe && settings.typesafe.enabled === false),
|
|
327
328
|
}));
|
|
328
329
|
} catch {}
|
|
329
330
|
let wa = null;
|
|
@@ -515,9 +516,10 @@ function parseSettingsText(text) {
|
|
|
515
516
|
/* TypeSafe (System One/Jev) — anahtar Ayarlar → TypeSafe sekmesinden girilir;
|
|
516
517
|
boşken typesafe_decision aracı anlaşılır hata döner */
|
|
517
518
|
if (!parsed.typesafe || typeof parsed.typesafe !== 'object') {
|
|
518
|
-
parsed.typesafe = { apiKey: '', model: 'jev-latest' };
|
|
519
|
+
parsed.typesafe = { apiKey: '', model: 'jev-latest', enabled: true };
|
|
519
520
|
}
|
|
520
521
|
if (typeof parsed.typesafe.model !== 'string' || !parsed.typesafe.model.trim()) parsed.typesafe.model = 'jev-latest';
|
|
522
|
+
if (typeof parsed.typesafe.enabled !== 'boolean') parsed.typesafe.enabled = true;
|
|
521
523
|
return parsed;
|
|
522
524
|
}
|
|
523
525
|
|
|
@@ -2375,7 +2377,7 @@ function botToolSet(cfg) {
|
|
|
2375
2377
|
]);
|
|
2376
2378
|
if (s.web_search) { set.add('web_search'); set.add('http_fetch'); set.add('webfetch'); set.add('deep_search'); }
|
|
2377
2379
|
if (s.browser) {
|
|
2378
|
-
for (const t of ['browser_open', 'browser_read', 'browser_screenshot', 'browser_snapshot', 'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select', 'browser_wait', 'ocr_read', 'computer_look']) set.add(t);
|
|
2380
|
+
for (const t of ['browser_open', 'browser_read', 'browser_screenshot', 'browser_snapshot', 'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select', 'browser_wait', 'browser_agent', 'ocr_read', 'computer_look']) set.add(t);
|
|
2379
2381
|
}
|
|
2380
2382
|
if (s.email) { set.add('email_list'); set.add('email_read'); set.add('email_send'); }
|
|
2381
2383
|
if (s.run_command) {
|
|
@@ -3395,6 +3397,7 @@ function reloadBackend() {
|
|
|
3395
3397
|
computer: {
|
|
3396
3398
|
look: captureScreenDataUrl,
|
|
3397
3399
|
act: (op, args) => computerActScaled(op, args),
|
|
3400
|
+
observe: () => screenObserve(),
|
|
3398
3401
|
},
|
|
3399
3402
|
ocr: (o) => ocrRead(o),
|
|
3400
3403
|
email: { list: emailList, read: emailRead, send: emailSend },
|
|
@@ -3404,6 +3407,7 @@ function reloadBackend() {
|
|
|
3404
3407
|
readText: (s) => browserRead(s),
|
|
3405
3408
|
screenshot: (s) => browserScreenshot(s),
|
|
3406
3409
|
snapshot: (s) => browserSnapshot(s),
|
|
3410
|
+
observe: (s) => browserObserve(s),
|
|
3407
3411
|
act: (k, a, s) => browserAct(k, a, s),
|
|
3408
3412
|
wait: (a, s) => browserWait(a, s),
|
|
3409
3413
|
},
|
|
@@ -4762,7 +4766,7 @@ async function researchRead(rawUrl, signal) {
|
|
|
4762
4766
|
/* Dahili OCR: görsel desteklemeyen modeller için tesseract.js ile metin okuma.
|
|
4763
4767
|
Dil verisi ilk kullanımda %APPDATA%\beast\tessdata'ya iner, sonra offline çalışır. */
|
|
4764
4768
|
const _ocrWorkers = new Map(); // lang -> worker (her çağrıda yeniden init olmasın)
|
|
4765
|
-
async function ocrRead({ image, lang = 'tur+eng' } = {}) {
|
|
4769
|
+
async function ocrRead({ image, lang = 'tur+eng', boxes = false } = {}) {
|
|
4766
4770
|
try {
|
|
4767
4771
|
if (!image) return { ok: false, error: 'görüntü yok' };
|
|
4768
4772
|
const t = require('tesseract.js');
|
|
@@ -4789,17 +4793,79 @@ async function ocrRead({ image, lang = 'tur+eng' } = {}) {
|
|
|
4789
4793
|
if (typeof input === 'string' && input.startsWith('data:')) {
|
|
4790
4794
|
input = Buffer.from(input.split(',')[1] || '', 'base64');
|
|
4791
4795
|
}
|
|
4792
|
-
const { data } = await worker.recognize(input);
|
|
4796
|
+
const { data } = boxes ? await worker.recognize(input, {}, { blocks: true, text: true }) : await worker.recognize(input);
|
|
4793
4797
|
const text = String((data && data.text) || '')
|
|
4794
4798
|
.replace(/[ \t]+\n/g, '\n')
|
|
4795
4799
|
.replace(/\n{3,}/g, '\n\n')
|
|
4796
4800
|
.trim();
|
|
4801
|
+
if (boxes) {
|
|
4802
|
+
/* T3SFast bilgisayar döngüsü için satır kutuları: OCR koordinatları
|
|
4803
|
+
1280px görüntü uzayındadır; computer_act bu uzayı bekler. */
|
|
4804
|
+
const lines = [];
|
|
4805
|
+
for (const block of (data && data.blocks) || []) {
|
|
4806
|
+
for (const para of block.paragraphs || []) {
|
|
4807
|
+
for (const ln of para.lines || []) {
|
|
4808
|
+
const bb = (ln && ln.bbox) || {};
|
|
4809
|
+
const label = String((ln && ln.text) || '').replace(/\s+/g, ' ').trim();
|
|
4810
|
+
if (!label || bb.x1 == null) continue;
|
|
4811
|
+
const conf = Number(ln.confidence);
|
|
4812
|
+
if (Number.isFinite(conf) && conf < 45) continue;
|
|
4813
|
+
lines.push({
|
|
4814
|
+
text: label.slice(0, 120),
|
|
4815
|
+
x: Math.round((bb.x0 + bb.x1) / 2),
|
|
4816
|
+
y: Math.round((bb.y0 + bb.y1) / 2),
|
|
4817
|
+
x0: bb.x0,
|
|
4818
|
+
y0: bb.y0,
|
|
4819
|
+
x1: bb.x1,
|
|
4820
|
+
y1: bb.y1,
|
|
4821
|
+
confidence: Math.round(Number.isFinite(conf) ? conf : 0),
|
|
4822
|
+
});
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
}
|
|
4826
|
+
return { ok: !!text, chars: text.length, text: text.slice(0, 8000), lines: lines.slice(0, 120), lang: langKey };
|
|
4827
|
+
}
|
|
4797
4828
|
return { ok: !!text, chars: text.length, text: text.slice(0, 8000), lang: langKey };
|
|
4798
4829
|
} catch (e) {
|
|
4799
4830
|
return { ok: false, error: String((e && e.message) || e) };
|
|
4800
4831
|
}
|
|
4801
4832
|
}
|
|
4802
4833
|
|
|
4834
|
+
/* T3SFast bilgisayar gözlemi: ekranı yakala (1280px, lastScreenCapture
|
|
4835
|
+
güncellenir) → OCR satırları + metin. Dönen kutu merkezleri computer_act'in
|
|
4836
|
+
beklediği görüntü koordinatlarıdır.
|
|
4837
|
+
OCR ÖNBELLEĞİ: ekran karesi öncekiyle aynıysa (ince parmak izi) tesseract
|
|
4838
|
+
hiç çalıştırılmaz — Jev döngüsünün en pahalı adımı atlanır. */
|
|
4839
|
+
const OCR_OBSERVE_CACHE_MS = 15000;
|
|
4840
|
+
let lastScreenObserve = { key: '', at: 0, result: null };
|
|
4841
|
+
|
|
4842
|
+
function screenObserveKey(cells) {
|
|
4843
|
+
return Array.isArray(cells) ? cells.map((n) => Math.round(n)).join(',') : '';
|
|
4844
|
+
}
|
|
4845
|
+
|
|
4846
|
+
async function screenObserve() {
|
|
4847
|
+
const frame = await captureScreenFrame();
|
|
4848
|
+
if (!frame) return { ok: false, error: 'ekran görüntüsü alınamadı' };
|
|
4849
|
+
const cap = lastScreenCapture || {};
|
|
4850
|
+
const key = screenObserveKey(imageSignature(frame.img));
|
|
4851
|
+
const now = Date.now();
|
|
4852
|
+
if (key && lastScreenObserve.key === key && lastScreenObserve.result && now - lastScreenObserve.at < OCR_OBSERVE_CACHE_MS) {
|
|
4853
|
+
return { ...lastScreenObserve.result, cached: true, sig: key };
|
|
4854
|
+
}
|
|
4855
|
+
const ocr = await ocrRead({ image: frame.data, boxes: true });
|
|
4856
|
+
if (!ocr.ok) return { ok: false, error: ocr.error || 'OCR başarısız' };
|
|
4857
|
+
const result = {
|
|
4858
|
+
ok: true,
|
|
4859
|
+
w: cap.imgW || 1280,
|
|
4860
|
+
h: cap.imgH || 720,
|
|
4861
|
+
text: ocr.text,
|
|
4862
|
+
lines: ocr.lines || [],
|
|
4863
|
+
sig: key,
|
|
4864
|
+
};
|
|
4865
|
+
lastScreenObserve = { key, at: now, result };
|
|
4866
|
+
return { ...result };
|
|
4867
|
+
}
|
|
4868
|
+
|
|
4803
4869
|
/* Dahili tarayıcıyla arama (web_search'ün İLK adımı): gerçek Chromium +
|
|
4804
4870
|
gerçek cookie'ler olduğu için Google bot koruması uygulamaz.
|
|
4805
4871
|
aep=1 → AI Modu/AI Overview açık: Google cevabi hazırlanmış şekilde döner,
|
|
@@ -5125,9 +5191,107 @@ const BROWSER_SNAPSHOT_JS = `(function(){
|
|
|
5125
5191
|
return JSON.stringify({count:i,title:document.title,url:location.href,snapshot:lines.join('\\n'),boxes:boxes,vw:window.innerWidth,vh:window.innerHeight});
|
|
5126
5192
|
})()`;
|
|
5127
5193
|
|
|
5194
|
+
/* JEV ULTRAFAST GÖZLEMİ: yapılı element tablosu (kind: click/fill/select),
|
|
5195
|
+
rol/ad/değer/seçenek bilgileriyle. Refler window.__beMap'e yazılır — mevcut
|
|
5196
|
+
browser_click/browser_type/browser_select ref çözümüyle BİREBİR uyumludur.
|
|
5197
|
+
Yalnız görünür + viewport içi + enabled elemanlar; scroll/wait sentetik. */
|
|
5198
|
+
const BROWSER_OBSERVE_JS = `(function(){
|
|
5199
|
+
if(!document.body) return JSON.stringify({url:location.href,title:document.title,w:innerWidth,h:innerHeight,text:'',scroll:{y:0,height:0},actions:[]});
|
|
5200
|
+
window.__beIds=window.__beIds||new WeakMap();
|
|
5201
|
+
window.__beNext=window.__beNext||0;
|
|
5202
|
+
window.__beMap={};
|
|
5203
|
+
const ids=window.__beIds;
|
|
5204
|
+
const identity=(e)=>{ if(!ids.has(e)) ids.set(e,++window.__beNext); const id=ids.get(e); window.__beMap[id]=e; return id; };
|
|
5205
|
+
const safe=(e)=>!['password','file','hidden'].includes(String(e.type||'').toLowerCase());
|
|
5206
|
+
const visible=(e)=>{ try{ return !e.closest('[aria-hidden="true"],[inert]') && e.checkVisibility({checkOpacity:true,checkVisibilityCSS:true}); }catch(err){ return false; } };
|
|
5207
|
+
const name=(e,seen=new Set())=>{
|
|
5208
|
+
if(!e||seen.has(e)) return '';
|
|
5209
|
+
seen.add(e);
|
|
5210
|
+
const referenced=(e.getAttribute('aria-labelledby')||'').split(/\\s+/).map((id)=>name(document.getElementById(id),seen)).filter(Boolean).join(' ');
|
|
5211
|
+
return referenced || e.getAttribute('aria-label') ||
|
|
5212
|
+
[...(e.labels||[])].map((l)=>name(l,seen)).filter(Boolean).join(' ') ||
|
|
5213
|
+
(['button','submit','reset'].includes(String(e.type||'').toLowerCase()) ? e.value : '') || e.getAttribute('alt') ||
|
|
5214
|
+
(e.tagName==='INPUT' ? '' : [...e.childNodes].map((n)=>n.nodeType===3 ? n.textContent :
|
|
5215
|
+
n.nodeType===1 && n.getAttribute('aria-hidden')!=='true' ? name(n,seen) : '').join(' ').trim()) ||
|
|
5216
|
+
e.getAttribute('title') || e.getAttribute('placeholder') || '';
|
|
5217
|
+
};
|
|
5218
|
+
const roles=['button','link','checkbox','radio','switch','tab','menuitem','menuitemradio',
|
|
5219
|
+
'option','gridcell','combobox','textbox','searchbox','spinbutton'];
|
|
5220
|
+
const selector='a[href],button,input,textarea,select,summary,[contenteditable="true"],'+
|
|
5221
|
+
roles.map((role)=>'[role="'+role+'"]').join(',');
|
|
5222
|
+
const role=(e)=>{
|
|
5223
|
+
const explicit=e.getAttribute('role');
|
|
5224
|
+
if(roles.includes(explicit)) return explicit;
|
|
5225
|
+
if(e.tagName==='BUTTON' || e.tagName==='SUMMARY') return 'button';
|
|
5226
|
+
if(e.tagName==='A') return 'link';
|
|
5227
|
+
if(e.tagName==='SELECT') return 'combobox';
|
|
5228
|
+
if(e.tagName==='TEXTAREA' || e.isContentEditable) return 'textbox';
|
|
5229
|
+
if(e.tagName==='INPUT'){
|
|
5230
|
+
const t=String(e.type||'').toLowerCase();
|
|
5231
|
+
if(t==='checkbox'||t==='radio') return t;
|
|
5232
|
+
if(['button','submit','reset','image'].includes(t)) return 'button';
|
|
5233
|
+
if(t==='search') return 'searchbox';
|
|
5234
|
+
if(t==='number') return 'spinbutton';
|
|
5235
|
+
if(['text','email','url','tel','date','time','month','week','datetime-local'].includes(t)) return 'textbox';
|
|
5236
|
+
}
|
|
5237
|
+
return null;
|
|
5238
|
+
};
|
|
5239
|
+
const actions=[];
|
|
5240
|
+
for(const e of document.querySelectorAll(selector)){
|
|
5241
|
+
if(actions.length>=300) break;
|
|
5242
|
+
if(!safe(e) || !visible(e) || e.matches(':disabled') || e.closest('[aria-disabled="true"]')) continue;
|
|
5243
|
+
const r=e.getBoundingClientRect(), x=r.x+r.width/2, y=r.y+r.height/2, rname=role(e);
|
|
5244
|
+
if(!rname || r.width<=0 || r.height<=0 || x<0 || y<0 || x>=innerWidth || y>=innerHeight) continue;
|
|
5245
|
+
if(rname==='gridcell' && e.querySelector('button,[role="button"]')) continue;
|
|
5246
|
+
const base={ref:identity(e),role:rname,label:name(e)||rname};
|
|
5247
|
+
const et=String(e.type||'').toLowerCase();
|
|
5248
|
+
if(et) base.input_type=et;
|
|
5249
|
+
for(const key of ['checked','selected','expanded']){
|
|
5250
|
+
const value=e.getAttribute('aria-'+key);
|
|
5251
|
+
if(value!==null) base[key]=value;
|
|
5252
|
+
}
|
|
5253
|
+
if(et==='checkbox'||et==='radio') base.checked=String(e.checked);
|
|
5254
|
+
if(e.tagName==='SELECT'){
|
|
5255
|
+
const current=[...e.selectedOptions].map((o)=>o.label).join(', ');
|
|
5256
|
+
for(const o of e.options){
|
|
5257
|
+
if(o.selected||o.disabled||o.closest('optgroup[disabled]')) continue;
|
|
5258
|
+
actions.push({...base,kind:'select',value:o.value,current_value:current,label:base.label+' → '+o.label});
|
|
5259
|
+
}
|
|
5260
|
+
} else {
|
|
5261
|
+
const editable=!e.readOnly && e.getAttribute('aria-readonly')!=='true' &&
|
|
5262
|
+
(['textbox','searchbox','spinbutton'].includes(rname) ||
|
|
5263
|
+
(rname==='combobox' && ['INPUT','TEXTAREA'].includes(e.tagName)));
|
|
5264
|
+
const value='value' in e ? String(e.value) :
|
|
5265
|
+
e.isContentEditable || rname==='combobox' ? String(e.innerText||'').trim() : '';
|
|
5266
|
+
actions.push({...base,kind:editable?'fill':'click',value});
|
|
5267
|
+
if(editable) actions.push({...base,kind:'click',value,label:'Open '+base.label});
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
const words=[], walker=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT);
|
|
5271
|
+
const range=document.createRange(); let node, length=0;
|
|
5272
|
+
while((node=walker.nextNode()) && length<6000){
|
|
5273
|
+
const value=String(node.textContent||'').trim(), parent=node.parentElement;
|
|
5274
|
+
if(!value || !parent || parent.closest('script,style,noscript,template') || !visible(parent)) continue;
|
|
5275
|
+
range.selectNodeContents(node); const tr=range.getBoundingClientRect();
|
|
5276
|
+
if(tr.width>0 && tr.height>0 && tr.bottom>0 && tr.top<innerHeight && tr.right>0 && tr.left<innerWidth){
|
|
5277
|
+
words.push(value); length+=value.length;
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
const text=words.join('\\n').slice(0,6000), height=document.documentElement.scrollHeight;
|
|
5281
|
+
const omitted=Math.max(0,actions.length-250);
|
|
5282
|
+
actions.splice(250);
|
|
5283
|
+
if(scrollY+innerHeight<height-2) actions.push({id:'scroll_down',kind:'scroll',label:'Scroll down',delta:560});
|
|
5284
|
+
if(scrollY>0) actions.push({id:'scroll_up',kind:'scroll',label:'Scroll up',delta:-560});
|
|
5285
|
+
actions.push({id:'wait',kind:'wait',label:'Wait for the page to update'});
|
|
5286
|
+
return JSON.stringify({url:location.href,title:document.title,w:innerWidth,h:innerHeight,text:text,
|
|
5287
|
+
scroll:{y:Math.round(scrollY),height:height},actions:actions,omitted_actions:omitted,
|
|
5288
|
+
rev:location.href+'|'+document.title+'|'+actions.length+'|'+text.length+'|'+Math.round(scrollY)+'|'+height});
|
|
5289
|
+
})()`;
|
|
5290
|
+
|
|
5128
5291
|
function browserActionJs(kind, args) {
|
|
5129
5292
|
const sel = JSON.stringify(String(args.selector || ''));
|
|
5130
5293
|
const ref = JSON.stringify(args.ref === undefined ? null : Number(args.ref));
|
|
5294
|
+
const expect = JSON.stringify(args.expect || null);
|
|
5131
5295
|
|
|
5132
5296
|
const resolveTarget = `
|
|
5133
5297
|
function __target(){
|
|
@@ -5141,11 +5305,19 @@ function browserActionJs(kind, args) {
|
|
|
5141
5305
|
|
|
5142
5306
|
if (kind === 'click') {
|
|
5143
5307
|
return `(function(){${BROWSER_JS_HELPERS}${resolveTarget};return new Promise((res)=>{try{
|
|
5308
|
+
const ex=${expect};
|
|
5309
|
+
if(ex&&ex.url&&location.href!==ex.url) return res(JSON.stringify({clicked:false,stale:true,reason:'sayfa degisti — karar bayat'}));
|
|
5144
5310
|
const t=__target();
|
|
5145
5311
|
if(!t) return res(JSON.stringify({clicked:false,reason:'eleman bulunamadi (ref eski olabilir - browser_snapshot al)'}));
|
|
5146
5312
|
const how=t.how,el=t.el;
|
|
5147
5313
|
el.scrollIntoView({block:'center'});
|
|
5148
5314
|
setTimeout(()=>{try{
|
|
5315
|
+
const r=el.getBoundingClientRect(), cx=r.x+r.width/2, cy=r.y+r.height/2;
|
|
5316
|
+
const top=document.elementFromPoint(cx,cy);
|
|
5317
|
+
if(top&&top!==el&&!el.contains(top)&&!top.contains(el)&&!(top.labels&&[...top.labels].includes(el))){
|
|
5318
|
+
let cover='';try{cover=__label(top);}catch(e){}
|
|
5319
|
+
return res(JSON.stringify({clicked:false,covered:true,reason:'hedef katman altinda: <'+top.tagName.toLowerCase()+'> "'+cover+'"'}));
|
|
5320
|
+
}
|
|
5149
5321
|
el.click();
|
|
5150
5322
|
res(JSON.stringify({clicked:true,target:how+' <'+el.tagName.toLowerCase()+'> "'+__label(el)+'"'}));
|
|
5151
5323
|
}catch(e){res(JSON.stringify({clicked:false,reason:String(e)}));}},80);
|
|
@@ -5175,6 +5347,8 @@ function browserActionJs(kind, args) {
|
|
|
5175
5347
|
}
|
|
5176
5348
|
/*__DT_HELPERS_END__*/
|
|
5177
5349
|
return new Promise((res)=>{try{
|
|
5350
|
+
const ex=${expect};
|
|
5351
|
+
if(ex&&ex.url&&location.href!==ex.url) return res(JSON.stringify({typed:false,stale:true,reason:'sayfa degisti — karar bayat'}));
|
|
5178
5352
|
const t=__target();
|
|
5179
5353
|
if(!t) return res(JSON.stringify({typed:false,reason:'eleman bulunamadi (ref eski olabilir - browser_snapshot al)'}));
|
|
5180
5354
|
const how=t.how,el=t.el;
|
|
@@ -5246,6 +5420,8 @@ function browserActionJs(kind, args) {
|
|
|
5246
5420
|
if (kind === 'select') {
|
|
5247
5421
|
const value = JSON.stringify(String(args.value ?? ''));
|
|
5248
5422
|
return `(function(){${BROWSER_JS_HELPERS}${resolveTarget};return new Promise((res)=>{try{
|
|
5423
|
+
const ex=${expect};
|
|
5424
|
+
if(ex&&ex.url&&location.href!==ex.url) return res(JSON.stringify({selected:false,stale:true,reason:'sayfa degisti — karar bayat'}));
|
|
5249
5425
|
const t=__target();
|
|
5250
5426
|
if(!t) return res(JSON.stringify({selected:false,reason:'eleman bulunamadi'}));
|
|
5251
5427
|
const el=t.el;
|
|
@@ -5261,6 +5437,33 @@ function browserActionJs(kind, args) {
|
|
|
5261
5437
|
return `JSON.stringify({ok:false,error:'bilinmeyen eylem'})`;
|
|
5262
5438
|
}
|
|
5263
5439
|
|
|
5440
|
+
/* GÜVENİLİR GİRDİ (trusted) için hedef koordinatı: renderer'a gönderilen
|
|
5441
|
+
gerçek fare/tuş olayları React/canvas/otocomplete widget'larında programatik
|
|
5442
|
+
click'ten güvenilirdir. Aynı zamanda katman (occlusion) kontrolü yapar. */
|
|
5443
|
+
function browserPointJs(args) {
|
|
5444
|
+
const sel = JSON.stringify(String(args.selector || ''));
|
|
5445
|
+
const ref = JSON.stringify(args.ref === undefined ? null : Number(args.ref));
|
|
5446
|
+
return `(function(){${BROWSER_JS_HELPERS}
|
|
5447
|
+
try{
|
|
5448
|
+
let el=null,how='';
|
|
5449
|
+
if(${ref}!==null){el=__resolveRef(${ref});how='ref['+${ref}+']';}
|
|
5450
|
+
else{el=__resolve(${sel});how='selector';}
|
|
5451
|
+
if(!el) return JSON.stringify({ok:false,reason:'eleman bulunamadi (ref eski olabilir)'});
|
|
5452
|
+
el.scrollIntoView({block:'center'});
|
|
5453
|
+
const r=el.getBoundingClientRect(), x=r.x+r.width/2, y=r.y+r.height/2;
|
|
5454
|
+
if(r.width<=0||r.height<=0||x<0||y<0||x>=innerWidth||y>=innerHeight) return JSON.stringify({ok:false,reason:'ekran disi'});
|
|
5455
|
+
const top=document.elementFromPoint(x,y);
|
|
5456
|
+
if(top&&top!==el&&!el.contains(top)&&!top.contains(el)&&!(top.labels&&[...top.labels].includes(el))){
|
|
5457
|
+
let cover='';try{cover=__label(top);}catch(e){}
|
|
5458
|
+
return JSON.stringify({ok:false,covered:true,reason:'hedef katman altinda: <'+top.tagName.toLowerCase()+'> "'+cover+'"'});
|
|
5459
|
+
}
|
|
5460
|
+
const editable=!!(el.isContentEditable||el.tagName==='INPUT'||el.tagName==='TEXTAREA');
|
|
5461
|
+
return JSON.stringify({ok:true,how:how,x:Math.round(x),y:Math.round(y),editable:editable,
|
|
5462
|
+
input_type:String((el.getAttribute&&el.getAttribute('type'))||'').toLowerCase()});
|
|
5463
|
+
}catch(e){return JSON.stringify({ok:false,reason:String(e)});}
|
|
5464
|
+
})()`;
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5264
5467
|
/* eylem günlüğü — her yanıtın sonuna son hamleler eklenir */
|
|
5265
5468
|
function blog(kind, detail) {
|
|
5266
5469
|
if (!Array.isArray(browser.history)) browser.history = [];
|
|
@@ -5296,31 +5499,83 @@ async function browserSnapshot(signal) {
|
|
|
5296
5499
|
}
|
|
5297
5500
|
}
|
|
5298
5501
|
|
|
5502
|
+
/* Jev döngüsü için yapılı gözlem: elementler (kind/rol/değer/seçenek) + sayfa
|
|
5503
|
+
metni + scroll bilgisi. window.__beMap tazelenir → dönen ref'ler mevcut
|
|
5504
|
+
browser_click/browser_type/browser_select tarafından aynen kullanılabilir. */
|
|
5505
|
+
async function browserObserve(signal) {
|
|
5506
|
+
if (!browser.view || !browser.open) return { ok: false, error: 'tarayıcı açık değil' };
|
|
5507
|
+
const wc = browser.view.webContents;
|
|
5508
|
+
const dlgAt = browser.lastDialog ? browser.lastDialog.at : 0;
|
|
5509
|
+
try {
|
|
5510
|
+
const raw = await wc.executeJavaScript(BROWSER_OBSERVE_JS, true);
|
|
5511
|
+
const obj = JSON.parse(raw);
|
|
5512
|
+
return { ok: true, ...obj, ...browserDialogNote(dlgAt) };
|
|
5513
|
+
} catch (e) {
|
|
5514
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
5515
|
+
}
|
|
5516
|
+
}
|
|
5517
|
+
|
|
5299
5518
|
async function browserAct(kind, args, signal) {
|
|
5300
5519
|
if (!browser.view || !browser.open) return { ok: false, error: 'tarayıcı açık değil' };
|
|
5301
5520
|
const wc = browser.view.webContents;
|
|
5521
|
+
const a = args || {};
|
|
5302
5522
|
const dlgAt = browser.lastDialog ? browser.lastDialog.at : 0;
|
|
5523
|
+
/* JEV HIZLI YOLU: TypeSafe ajanı her adımda zaten taze observe yapar —
|
|
5524
|
+
ağır imza + taze snapshot + uzun bekleme gereksizdir. */
|
|
5525
|
+
const fast = !!a.fast;
|
|
5526
|
+
const trusted = !!a.trusted && ['click', 'type'].includes(kind) && browser.attached;
|
|
5303
5527
|
/* DOĞRULAMA: eylem öncesi görsel parmak izi (yalnız anlamlı eylemlerde) */
|
|
5304
|
-
const verify = ['click', 'type', 'select', 'press'].includes(kind) && !
|
|
5528
|
+
const verify = ['click', 'type', 'select', 'press'].includes(kind) && !fast && !(a.verify === false);
|
|
5305
5529
|
const sigBefore = verify ? await pageSignature(wc) : null;
|
|
5306
5530
|
try {
|
|
5307
|
-
const raw = await wc.executeJavaScript(browserActionJs(kind, args || {}), true);
|
|
5308
5531
|
let obj = {};
|
|
5309
|
-
|
|
5532
|
+
if (trusted) {
|
|
5533
|
+
const pointRaw = await wc.executeJavaScript(browserPointJs(a), true);
|
|
5534
|
+
let point = {};
|
|
5535
|
+
try { point = JSON.parse(pointRaw); } catch { point = { ok: false, reason: 'koordinat alinamadi' }; }
|
|
5536
|
+
if (!point.ok) {
|
|
5537
|
+
obj = kind === 'click' ? { clicked: false, ...point } : { typed: false, ...point };
|
|
5538
|
+
} else if (kind === 'type' && /^(date|time|month|week|datetime-local)$/.test(String(point.input_type || ''))) {
|
|
5539
|
+
/* native tarih/saat alanı: gerçek klavye yerine programatik değer + event
|
|
5540
|
+
(tarayıcı segment segment yazmayı reddeder) */
|
|
5541
|
+
const raw = await wc.executeJavaScript(browserActionJs(kind, a), true);
|
|
5542
|
+
try { obj = JSON.parse(raw); } catch { obj = { result: String(raw).slice(0, 300) }; }
|
|
5543
|
+
} else {
|
|
5544
|
+
wc.sendInputEvent({ type: 'mouseMove', x: point.x, y: point.y });
|
|
5545
|
+
wc.sendInputEvent({ type: 'mouseDown', x: point.x, y: point.y, button: 'left', clickCount: 1 });
|
|
5546
|
+
wc.sendInputEvent({ type: 'mouseUp', x: point.x, y: point.y, button: 'left', clickCount: 1 });
|
|
5547
|
+
if (kind === 'click') {
|
|
5548
|
+
obj = { clicked: true, trusted: true, target: point.how };
|
|
5549
|
+
} else {
|
|
5550
|
+
/* gerçek klavye: önce mevcut değeri seç, sonra yaz — otocomplete tetiklenir */
|
|
5551
|
+
await new Promise((r) => setTimeout(r, 40));
|
|
5552
|
+
try { wc.selectAll(); } catch {}
|
|
5553
|
+
wc.insertText(String(a.text == null ? '' : a.text));
|
|
5554
|
+
if (a.submit) {
|
|
5555
|
+
wc.sendInputEvent({ type: 'keyDown', keyCode: 'Enter' });
|
|
5556
|
+
wc.sendInputEvent({ type: 'keyUp', keyCode: 'Enter' });
|
|
5557
|
+
}
|
|
5558
|
+
obj = { typed: true, trusted: true, target: point.how, value: String(a.text == null ? '' : a.text) };
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
} else {
|
|
5562
|
+
const raw = await wc.executeJavaScript(browserActionJs(kind, a), true);
|
|
5563
|
+
try { obj = JSON.parse(raw); } catch { obj = { result: String(raw).slice(0, 300) }; }
|
|
5564
|
+
}
|
|
5310
5565
|
|
|
5311
5566
|
// tıklama/form gönderimi sonrası kısa gezinme bekleme
|
|
5312
5567
|
let navigated = false;
|
|
5313
|
-
if (kind === 'click' || (kind === 'type' &&
|
|
5568
|
+
if (kind === 'click' || (kind === 'type' && a.submit) || kind === 'press') {
|
|
5314
5569
|
await new Promise((resolve) => {
|
|
5315
5570
|
let done = false;
|
|
5316
5571
|
const onNav = () => { navigated = true; setTimeout(fin, 400); };
|
|
5317
5572
|
const fin = () => { if (!done) { done = true; clearTimeout(t); wc.removeListener('did-navigate', onNav); resolve(); } };
|
|
5318
|
-
const t = setTimeout(fin, 1800);
|
|
5573
|
+
const t = setTimeout(fin, fast ? 500 : 1800);
|
|
5319
5574
|
wc.on('did-navigate', onNav);
|
|
5320
5575
|
});
|
|
5321
5576
|
}
|
|
5322
5577
|
/* SPA: sayfa değiştiyse DOM oturmadan snapshot alınmaz */
|
|
5323
|
-
if (navigated) await browserSettle(wc, signal, 2500);
|
|
5578
|
+
if (navigated) await browserSettle(wc, signal, fast ? 500 : 2500);
|
|
5324
5579
|
|
|
5325
5580
|
/* DOĞRULAMA: eylem sonrası değişim oranı — "tıkladım ama bir şey olmadı" tuzağını yakalar */
|
|
5326
5581
|
let changed = null;
|
|
@@ -5344,8 +5599,9 @@ async function browserAct(kind, args, signal) {
|
|
|
5344
5599
|
: (obj.reason || 'tamam')
|
|
5345
5600
|
);
|
|
5346
5601
|
|
|
5347
|
-
/* HIZ: eylem cevabına taze snapshot göm — model ayrı browser_snapshot çağırmaz (tur sayısı yarıya iner)
|
|
5348
|
-
|
|
5602
|
+
/* HIZ: eylem cevabına taze snapshot göm — model ayrı browser_snapshot çağırmaz (tur sayısı yarıya iner).
|
|
5603
|
+
Jev hızlı yolunda snapshot gömülmez; döngü zaten observe eder. */
|
|
5604
|
+
const freshSnap = fast ? null : await browserSnapshotNow(wc);
|
|
5349
5605
|
|
|
5350
5606
|
return {
|
|
5351
5607
|
ok: true,
|
|
@@ -5354,6 +5610,7 @@ async function browserAct(kind, args, signal) {
|
|
|
5354
5610
|
url: wc.getURL(),
|
|
5355
5611
|
title: wc.getTitle(),
|
|
5356
5612
|
navigated,
|
|
5613
|
+
...(fast ? { fast: true } : {}),
|
|
5357
5614
|
...(changed != null ? { changed, changeRatio } : {}),
|
|
5358
5615
|
...(changed === false && ['click', 'type', 'select'].includes(kind)
|
|
5359
5616
|
? {
|
|
@@ -5365,11 +5622,13 @@ async function browserAct(kind, args, signal) {
|
|
|
5365
5622
|
: {}),
|
|
5366
5623
|
...browserDialogNote(dlgAt),
|
|
5367
5624
|
...(freshSnap ? { snapshot: freshSnap.snapshot, refCount: freshSnap.count } : {}),
|
|
5368
|
-
...(
|
|
5369
|
-
? {
|
|
5370
|
-
:
|
|
5371
|
-
? { note: 'yanıtta güncel snapshot
|
|
5372
|
-
:
|
|
5625
|
+
...(fast
|
|
5626
|
+
? {}
|
|
5627
|
+
: navigated
|
|
5628
|
+
? { note: 'sayfa degisti — yanıtta güncel snapshot var; refler eskiyse yeni browser_snapshot al' }
|
|
5629
|
+
: freshSnap
|
|
5630
|
+
? { note: 'yanıtta güncel snapshot (' + freshSnap.count + ' ref) — sonraki hamlede bunları kullan, ayrıca snapshot alma' }
|
|
5631
|
+
: {}),
|
|
5373
5632
|
recent: recentLog(),
|
|
5374
5633
|
};
|
|
5375
5634
|
} catch (e) {
|
|
@@ -7153,21 +7412,28 @@ ipcMain.handle('supermemory:get', () => {
|
|
|
7153
7412
|
settings'ten CANLI okur (setConfig kancası) — kaydetmek yeterli. */
|
|
7154
7413
|
ipcMain.handle('typesafe:get', () => {
|
|
7155
7414
|
const t = settings.typesafe || {};
|
|
7156
|
-
return { apiKey: t.apiKey ? SECRET_MASK : '', model: t.model || 'jev-latest', set: !!t.apiKey };
|
|
7415
|
+
return { apiKey: t.apiKey ? SECRET_MASK : '', model: t.model || 'jev-latest', enabled: t.enabled !== false, set: !!t.apiKey };
|
|
7157
7416
|
});
|
|
7158
7417
|
ipcMain.handle('typesafe:set', (_e, cfg) => {
|
|
7159
7418
|
try {
|
|
7160
7419
|
const incomingKey = String((cfg && cfg.apiKey) || '').trim();
|
|
7161
7420
|
const prevKey = settings.typesafe && settings.typesafe.apiKey ? String(settings.typesafe.apiKey) : '';
|
|
7421
|
+
const prevEnabled = !(settings.typesafe && settings.typesafe.enabled === false);
|
|
7162
7422
|
settings.typesafe = {
|
|
7163
7423
|
/* maskeli anahtar geri gelirse mevcut korunur */
|
|
7164
7424
|
apiKey: incomingKey === SECRET_MASK ? prevKey : incomingKey,
|
|
7165
7425
|
model: String((cfg && cfg.model) || 'jev-latest').trim() || 'jev-latest',
|
|
7426
|
+
enabled: cfg && typeof cfg.enabled === 'boolean' ? cfg.enabled : prevEnabled,
|
|
7166
7427
|
};
|
|
7167
7428
|
saveSettings();
|
|
7168
7429
|
return {
|
|
7169
7430
|
ok: true,
|
|
7170
|
-
typesafe: {
|
|
7431
|
+
typesafe: {
|
|
7432
|
+
apiKey: settings.typesafe.apiKey ? SECRET_MASK : '',
|
|
7433
|
+
model: settings.typesafe.model,
|
|
7434
|
+
enabled: settings.typesafe.enabled !== false,
|
|
7435
|
+
set: !!settings.typesafe.apiKey,
|
|
7436
|
+
},
|
|
7171
7437
|
};
|
|
7172
7438
|
} catch (e) {
|
|
7173
7439
|
return { ok: false, error: String((e && e.message) || e) };
|
|
@@ -16691,7 +16957,7 @@ async function annotateScreenShot(img) {
|
|
|
16691
16957
|
/* Ana ekrandan JPEG dataURL yakalar (computer_use ve screen:capture ortak).
|
|
16692
16958
|
opts.annotate: ızgara + imleç işareti (yalnız ajan computer_look kullanır;
|
|
16693
16959
|
OCR/komut yolları düz görüntü ister). */
|
|
16694
|
-
async function
|
|
16960
|
+
async function captureScreenFrame(opts) {
|
|
16695
16961
|
try {
|
|
16696
16962
|
const sources = await desktopCapturer.getSources({
|
|
16697
16963
|
types: ['screen'],
|
|
@@ -16722,12 +16988,17 @@ async function captureScreenDataUrl(opts) {
|
|
|
16722
16988
|
}
|
|
16723
16989
|
if (!jpeg || !jpeg.length) jpeg = img.toJPEG(72);
|
|
16724
16990
|
if (!jpeg || !jpeg.length) return null;
|
|
16725
|
-
return 'data:image/jpeg;base64,' + jpeg.toString('base64');
|
|
16991
|
+
return { data: 'data:image/jpeg;base64,' + jpeg.toString('base64'), img };
|
|
16726
16992
|
} catch {
|
|
16727
16993
|
return null;
|
|
16728
16994
|
}
|
|
16729
16995
|
}
|
|
16730
16996
|
|
|
16997
|
+
async function captureScreenDataUrl(opts) {
|
|
16998
|
+
const frame = await captureScreenFrame(opts);
|
|
16999
|
+
return frame ? frame.data : null;
|
|
17000
|
+
}
|
|
17001
|
+
|
|
16731
17002
|
/* ---- görsel parmak izi: aksiyon "işe yaradı mı" (verify) ---- */
|
|
16732
17003
|
function imageSignature(img) {
|
|
16733
17004
|
try {
|
|
@@ -16784,7 +17055,10 @@ async function computerActScaled(op, args) {
|
|
|
16784
17055
|
}
|
|
16785
17056
|
const opName = String(op || '').toLowerCase();
|
|
16786
17057
|
const scaled = cap && cap.imgW > 0 ? computeruse.scaleArgs(a, cap) : a;
|
|
16787
|
-
|
|
17058
|
+
/* JEV HIZLI YOLU: T3SFast döngüsü ekranı zaten observe ile izler — ağır
|
|
17059
|
+
imza + 350 ms bekleme gereksiz. */
|
|
17060
|
+
const fast = !!a.fast;
|
|
17061
|
+
const verify = CU_VERIFY_OPS.has(opName) && !fast && a.verify !== false;
|
|
16788
17062
|
const before = verify ? await screenSignature() : null;
|
|
16789
17063
|
let r;
|
|
16790
17064
|
try {
|
package/src/renderer/i18n.js
CHANGED
|
@@ -758,8 +758,10 @@
|
|
|
758
758
|
ts_sub: 'Tipli karar motoru: metin üretmeden olasılıklı yanıt (noul/choice/score). Anahtar girilince ajanlar typesafe_decision aracını kullanır; Beast Finance rol→skill eşleştirmesinde "typesafe-ai" seçilirse roller bu kararları kullanır.',
|
|
759
759
|
ts_key_label: 'TypeSafe API anahtarı (typesafe.ai)',
|
|
760
760
|
ts_model_label: 'Model (varsayılan: jev-latest)',
|
|
761
|
+
ts_enabled_label: 'TypeSafe açık (browser_agent/T3SFast + typesafe_decision kullanılabilir)',
|
|
761
762
|
ts_status_set: 'TypeSafe aktif — kayıtlı anahtar: *** · model: ',
|
|
762
763
|
ts_status_unset: 'TypeSafe pasif — anahtar girmeden typesafe_decision aracı çalışmaz.',
|
|
764
|
+
ts_status_off: 'TypeSafe anahtarı kayıtlı ama AYAR KAPALI — Jev tabanlı araçlar (browser_agent) çalışmaz.',
|
|
763
765
|
ts_save: 'Kaydet',
|
|
764
766
|
ts_test: 'Bağlantıyı Test Et',
|
|
765
767
|
ts_testing: 'Test ediliyor…',
|
|
@@ -1513,8 +1515,10 @@
|
|
|
1513
1515
|
ts_sub: 'Typed decision engine: probabilistic answers without text generation (noul/choice/score). Once a key is set, agents can use the typesafe_decision tool; when "typesafe-ai" is picked in Beast Finance role→skill matching, those roles use it.',
|
|
1514
1516
|
ts_key_label: 'TypeSafe API key (typesafe.ai)',
|
|
1515
1517
|
ts_model_label: 'Model (default: jev-latest)',
|
|
1518
|
+
ts_enabled_label: 'TypeSafe ON (enables browser_agent/T3SFast + typesafe_decision)',
|
|
1516
1519
|
ts_status_set: 'TypeSafe active — saved key: *** · model: ',
|
|
1517
1520
|
ts_status_unset: 'TypeSafe inactive — typesafe_decision does nothing without a key.',
|
|
1521
|
+
ts_status_off: 'TypeSafe key saved but the SWITCH IS OFF — Jev-powered tools (browser_agent) will not run.',
|
|
1518
1522
|
ts_save: 'Save',
|
|
1519
1523
|
ts_test: 'Test Connection',
|
|
1520
1524
|
ts_testing: 'Testing…',
|
package/src/renderer/renderer.js
CHANGED
|
@@ -1688,7 +1688,7 @@ async function renderWebSearchPane() {
|
|
|
1688
1688
|
async function renderTypesafePane() {
|
|
1689
1689
|
const pane = $('#tab-typesafe');
|
|
1690
1690
|
if (!pane) return;
|
|
1691
|
-
const ts = await beast.typesafeGet().catch(() => ({ set: false, apiKey: '', model: 'jev-latest' }));
|
|
1691
|
+
const ts = await beast.typesafeGet().catch(() => ({ set: false, apiKey: '', model: 'jev-latest', enabled: true }));
|
|
1692
1692
|
pane.innerHTML =
|
|
1693
1693
|
'<h2>' + _t('ts_h2') + '</h2>' +
|
|
1694
1694
|
'<div class="sub">' + _t('ts_sub') + '</div>' +
|
|
@@ -1697,6 +1697,8 @@ async function renderTypesafePane() {
|
|
|
1697
1697
|
'<input id="tsKeyInp" class="inp" type="password" placeholder="ts_..." autocomplete="new-password" spellcheck="false" />' +
|
|
1698
1698
|
'<label class="mem-label" style="margin-top:10px">' + _t('ts_model_label') + '</label>' +
|
|
1699
1699
|
'<input id="tsModelInp" class="inp" type="text" placeholder="jev-latest" spellcheck="false" />' +
|
|
1700
|
+
'<label class="mem-label" style="margin-top:10px;display:flex;align-items:center;gap:8px;cursor:pointer">' +
|
|
1701
|
+
'<input id="tsEnabled" type="checkbox" /> ' + _t('ts_enabled_label') + '</label>' +
|
|
1700
1702
|
'<div class="form-grid" style="grid-template-columns:auto auto auto;gap:8px;margin-top:10px">' +
|
|
1701
1703
|
'<button id="tsSave" class="btn">' + _t('ts_save') + '</button>' +
|
|
1702
1704
|
'<button id="tsTest" class="btn ghost">' + _t('ts_test') + '</button>' +
|
|
@@ -1704,9 +1706,13 @@ async function renderTypesafePane() {
|
|
|
1704
1706
|
'<div class="sub" style="margin-top:10px">' + _t('ts_hint') + '</div>';
|
|
1705
1707
|
const tsSt = $('#tsStatus');
|
|
1706
1708
|
const tsModel = $('#tsModelInp');
|
|
1709
|
+
const tsEnabled = $('#tsEnabled');
|
|
1707
1710
|
tsModel.value = ts.model || 'jev-latest';
|
|
1711
|
+
tsEnabled.checked = ts.enabled !== false;
|
|
1708
1712
|
const setTsSt = (r) => {
|
|
1709
|
-
|
|
1713
|
+
if (!r.set) tsSt.textContent = _t('ts_status_unset');
|
|
1714
|
+
else if (r.enabled === false) tsSt.textContent = _t('ts_status_off');
|
|
1715
|
+
else tsSt.textContent = _t('ts_status_set') + r.model;
|
|
1710
1716
|
};
|
|
1711
1717
|
setTsSt(ts);
|
|
1712
1718
|
const tsTest = $('#tsTest');
|
|
@@ -1724,7 +1730,7 @@ async function renderTypesafePane() {
|
|
|
1724
1730
|
$('#tsSave').addEventListener('click', async () => {
|
|
1725
1731
|
const v = $('#tsKeyInp').value.trim();
|
|
1726
1732
|
if (!v) { toast(_t('ts_empty_toast')); return; }
|
|
1727
|
-
const rr = await beast.typesafeSet({ apiKey: v, model: tsModel.value.trim() }).catch(() => null);
|
|
1733
|
+
const rr = await beast.typesafeSet({ apiKey: v, model: tsModel.value.trim(), enabled: tsEnabled.checked }).catch(() => null);
|
|
1728
1734
|
$('#tsKeyInp').value = '';
|
|
1729
1735
|
if (rr && rr.ok) {
|
|
1730
1736
|
setTsSt(rr.typesafe || {});
|
|
@@ -1733,8 +1739,18 @@ async function renderTypesafePane() {
|
|
|
1733
1739
|
toast(_t('ts_fail_toast'));
|
|
1734
1740
|
}
|
|
1735
1741
|
});
|
|
1742
|
+
/* Anahtar zaten kayıtlıyken aç/kapa: maskeli anahtar geri gönderilir, anahtar korunur */
|
|
1743
|
+
tsEnabled.addEventListener('change', async () => {
|
|
1744
|
+
const rr = await beast.typesafeSet({ apiKey: ts.apiKey || '', model: tsModel.value.trim(), enabled: tsEnabled.checked }).catch(() => null);
|
|
1745
|
+
if (rr && rr.ok) {
|
|
1746
|
+
setTsSt(rr.typesafe || {});
|
|
1747
|
+
toast(_t('ts_saved_toast'));
|
|
1748
|
+
} else {
|
|
1749
|
+
toast(_t('ts_fail_toast'));
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1736
1752
|
$('#tsClear').addEventListener('click', async () => {
|
|
1737
|
-
await beast.typesafeSet({ apiKey: '', model: tsModel.value.trim() }).catch(() => {});
|
|
1753
|
+
await beast.typesafeSet({ apiKey: '', model: tsModel.value.trim(), enabled: tsEnabled.checked }).catch(() => {});
|
|
1738
1754
|
$('#tsKeyInp').value = '';
|
|
1739
1755
|
setTsSt({ set: false, model: tsModel.value.trim() || 'jev-latest' });
|
|
1740
1756
|
toast(_t('ts_cleared_toast'));
|