nothumanallowed 9.8.9 → 9.9.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "9.8.9",
3
+ "version": "9.9.0",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 53 tools. Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, GitHub, Notion, Slack, voice chat, 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1414,7 +1414,7 @@ export async function cmdUI(args) {
1414
1414
  toolResults.push({ action, result: resultStr });
1415
1415
  sendSSE('tool', { action, status: 'done', result: typeof resultStr === 'string' ? resultStr.slice(0, 500) : '' });
1416
1416
 
1417
- // Send live browser frame after browser actions (low-quality thumbnail for viewer)
1417
+ // Send live browser frame after browser actions save as thumbnail file for persistence
1418
1418
  if (action.startsWith('browser_') && action !== 'browser_close') {
1419
1419
  try {
1420
1420
  const be = await import('../services/browser-engine.mjs');
@@ -1422,7 +1422,15 @@ export async function cmdUI(args) {
1422
1422
  const frame = await be.browserScreenshot({ fullPage: false, format: 'jpeg', quality: 30 });
1423
1423
  if (!frame.error) {
1424
1424
  const info = await be.browserInfo();
1425
- sendSSE('browser_frame', { base64: frame.base64, format: 'jpeg', url: (info.url || '').slice(0, 80) });
1425
+ const pageUrl = (info.url || '').slice(0, 80);
1426
+ // Save thumbnail to disk for persistence
1427
+ const thumbDir = path.join(NHA_DIR, 'screenshots');
1428
+ fs.mkdirSync(thumbDir, { recursive: true });
1429
+ const thumbFile = `thumb-${Date.now()}.jpg`;
1430
+ fs.writeFileSync(path.join(thumbDir, thumbFile), Buffer.from(frame.base64, 'base64'));
1431
+ if (!res._browserThumbs) res._browserThumbs = [];
1432
+ res._browserThumbs.push({ file: thumbFile, url: pageUrl });
1433
+ sendSSE('browser_frame', { file: thumbFile, format: 'jpeg', url: pageUrl });
1426
1434
  }
1427
1435
  }
1428
1436
  } catch { /* frame capture failed, non-critical */ }
@@ -1442,7 +1450,7 @@ export async function cmdUI(args) {
1442
1450
  const ssBase64 = fs.readFileSync(ssPath).toString('base64');
1443
1451
  console.log(` [screenshot] sending SSE, base64 size: ${ssBase64.length}`);
1444
1452
  sendSSE('screenshot', { base64: ssBase64, format: 'jpeg', filename: ssFilename });
1445
- sendSSE('browser_frame', { base64: ssBase64, format: 'jpeg', url: 'Search results' });
1453
+ sendSSE('browser_frame', { file: ssFilename, format: 'jpeg', url: 'Search results' });
1446
1454
  if (!res._screenshotFiles) res._screenshotFiles = [];
1447
1455
  res._screenshotFiles.push(ssFilename);
1448
1456
  }
@@ -1512,7 +1520,8 @@ export async function cmdUI(args) {
1512
1520
  try { extractMemory('chat', msg, finalResponse); } catch {}
1513
1521
 
1514
1522
  const ssFiles = res._screenshotFiles || [];
1515
- sendSSE('done', { content: finalResponse, screenshotFiles: ssFiles });
1523
+ const browserThumbs = res._browserThumbs || [];
1524
+ sendSSE('done', { content: finalResponse, screenshotFiles: ssFiles, browserThumbs });
1516
1525
  } catch (e) {
1517
1526
  sendSSE('error', { message: e.message });
1518
1527
  }
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '9.8.9';
8
+ export const VERSION = '9.9.0';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -260,11 +260,13 @@ function showBrowserViewer(title,status){
260
260
  var p=document.getElementById('canvasPanel');
261
261
  if(p&&!p.classList.contains('open')){canvasMode='browser';renderCanvasPanel();}
262
262
  }
263
- function updateBrowserFrame(base64,format,url){
263
+ function updateBrowserFrame(data){
264
+ // data = {base64?, file?, format, url}
265
+ var imgSrc=data.file?API+'/api/screenshots/'+data.file:'data:image/'+(data.format||'jpeg')+';base64,'+data.base64;
264
266
  // Update old monitor viewer
265
- var f=document.getElementById('bvFrame');if(f)f.innerHTML='<img src="data:image/'+(format||'jpeg')+';base64,'+base64+'" alt="Browser view">';
267
+ var f=document.getElementById('bvFrame');if(f)f.innerHTML='<img src="'+imgSrc+'" alt="Browser view">';
266
268
  // Save to per-conversation browser history for canvas Browser tab
267
- addBrowserPage(base64,url);
269
+ addBrowserPage(data.file||null,data.base64||null,data.url);
268
270
  // Update canvas browser tab live if open
269
271
  var p=document.getElementById('canvasPanel');
270
272
  if(p&&p.classList.contains('open')&&canvasMode==='browser'){renderCanvasPanel();}
@@ -551,29 +553,33 @@ function showCanvas(html,title){
551
553
  saveCanvasData();
552
554
  }
553
555
 
554
- function addBrowserPage(base64,url){
556
+ function addBrowserPage(file,base64,url){
555
557
  var d=getConvCanvasData();
556
558
  var cleanUrl=(url||'Browser').replace(/^https?:\\/\\//, '').slice(0,60);
557
559
  // Only add if URL is different from the last entry (avoid frame duplicates)
558
560
  if(d.browsers.length>0&&d.browsers[d.browsers.length-1].url===cleanUrl){
559
- // Update the thumbnail with the latest frame
560
- d.browsers[d.browsers.length-1].base64=base64;
561
+ // Update the thumbnail file ref
562
+ if(file)d.browsers[d.browsers.length-1].file=file;
563
+ if(base64)d.browsers[d.browsers.length-1].base64=base64;
561
564
  return;
562
565
  }
563
- d.browsers.push({base64:base64,url:cleanUrl,ts:new Date().toLocaleTimeString()});
566
+ d.browsers.push({file:file,base64:base64,url:cleanUrl,ts:new Date().toLocaleTimeString()});
564
567
  browserIdx=d.browsers.length-1;
565
- // Auto-update canvas panel if browser tab is open
566
- var p=document.getElementById('canvasPanel');
567
- if(p&&p.classList.contains('open')&&canvasMode==='browser'){renderCanvasPanel();}
568
+ // Persist file refs to localStorage (not base64)
569
+ saveCanvasData();
568
570
  }
569
571
 
570
572
  function saveCanvasData(){
571
- // Save only canvas HTML (not browser base64 — too large) per conversation
572
573
  try{
573
574
  var save={};
574
575
  for(var id in allCanvasData){
575
- if(allCanvasData[id].canvases.length>0){
576
- save[id]={canvases:allCanvasData[id].canvases.slice(-20)};
576
+ var d=allCanvasData[id];
577
+ if(d.canvases.length>0||d.browsers.length>0){
578
+ save[id]={
579
+ canvases:d.canvases.slice(-20),
580
+ // Save browser entries with file refs only (no base64)
581
+ browsers:d.browsers.slice(-30).map(function(b){return {file:b.file,url:b.url,ts:b.ts};})
582
+ };
577
583
  }
578
584
  }
579
585
  localStorage.setItem('nha_canvas_data',JSON.stringify(save));
@@ -588,6 +594,7 @@ function loadCanvasData(){
588
594
  for(var id in parsed){
589
595
  if(!allCanvasData[id])allCanvasData[id]={canvases:[],browsers:[]};
590
596
  allCanvasData[id].canvases=parsed[id].canvases||[];
597
+ allCanvasData[id].browsers=parsed[id].browsers||[];
591
598
  }
592
599
  }
593
600
  }catch(e){}
@@ -626,11 +633,12 @@ function renderCanvasPanel(){
626
633
  if(d.browsers.length===0){
627
634
  doc.open();doc.write('<html><body style="margin:0;background:#111;display:flex;align-items:center;justify-content:center;height:100vh;font-family:monospace;color:#555"><div style="text-align:center"><div style="font-size:48px;margin-bottom:12px">&#x1F310;</div><div>No pages visited</div><div style="font-size:11px;margin-top:8px;color:#333">in this conversation</div></div></body></html>');doc.close();
628
635
  } else {
629
- var gallery='<html><head><style>*{margin:0;padding:0;box-sizing:border-box}body{background:#111;padding:12px;font-family:monospace}h3{color:#00ff41;font-size:12px;margin-bottom:12px}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}.card{background:#1a1a1a;border:1px solid #333;border-radius:8px;overflow:hidden;cursor:pointer;transition:border-color .2s}.card:hover{border-color:#00ff41}.card img{width:100%;height:120px;object-fit:cover;display:block}.card .info{padding:6px 8px}.card .url{color:#8ab4f8;font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .time{color:#555;font-size:9px;margin-top:2px}.selected{border-color:#00ff41;box-shadow:0 0 10px rgba(0,255,65,0.2)}</style></head><body><h3>Pages visited ('+d.browsers.length+')</h3><div class="grid">';
636
+ var apiBase=window.API||'';
637
+ var gallery='<html><head><style>*{margin:0;padding:0;box-sizing:border-box}body{background:#111;padding:12px;font-family:monospace}h3{color:#00ff41;font-size:12px;margin-bottom:12px}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}.card{background:#1a1a1a;border:1px solid #333;border-radius:8px;overflow:hidden;cursor:pointer;transition:border-color .2s}.card:hover{border-color:#00ff41}.card img{width:100%;height:120px;object-fit:cover;display:block;background:#222}.card .info{padding:6px 8px}.card .url{color:#8ab4f8;font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .time{color:#555;font-size:9px;margin-top:2px}</style></head><body><h3>Pages visited ('+d.browsers.length+')</h3><div class="grid">';
630
638
  for(var bi=0;bi<d.browsers.length;bi++){
631
639
  var b=d.browsers[bi];
632
- var sel=bi===browserIdx?' selected':'';
633
- gallery+='<div class="card'+sel+'" onclick="parent.selectBrowserPage('+bi+')"><img src="data:image/jpeg;base64,'+b.base64+'" alt="'+b.url+'"/><div class="info"><div class="url">'+b.url+'</div><div class="time">'+b.ts+'</div></div></div>';
640
+ var imgSrc=b.file?apiBase+'/api/screenshots/'+b.file:(b.base64?'data:image/jpeg;base64,'+b.base64:'');
641
+ gallery+='<div class="card" onclick="parent.selectBrowserPage('+bi+')">'+(imgSrc?'<img src="'+imgSrc+'" alt="'+b.url+'"/>':'<div style="height:120px;display:flex;align-items:center;justify-content:center;color:#555">No preview</div>')+'<div class="info"><div class="url">'+b.url+'</div><div class="time">'+(b.ts||'')+'</div></div></div>';
634
642
  }
635
643
  gallery+='</div></body></html>';
636
644
  doc.open();doc.write(gallery);doc.close();
@@ -785,12 +793,12 @@ function sendChat(){
785
793
  }
786
794
  if(currentEvent==='screenshot'&&data.base64){
787
795
  showBrowserViewer('Screenshot','Captured');
788
- updateBrowserFrame(data.base64,data.format||'jpeg','Screenshot');
796
+ updateBrowserFrame({base64:data.base64,format:data.format||'jpeg',url:'Screenshot'});
789
797
  updateBrowserStatus('Screenshot captured');
790
798
  }
791
- if(currentEvent==='browser_frame'&&data.base64){
799
+ if(currentEvent==='browser_frame'&&(data.base64||data.file)){
792
800
  showBrowserViewer(data.url||'Browser','Live');
793
- updateBrowserFrame(data.base64,data.format||'jpeg',data.url||'Browser');
801
+ updateBrowserFrame(data);
794
802
  if(data.url)updateBrowserStatus(data.url);
795
803
  }
796
804
  if(currentEvent==='canvas'&&data.markers){
@@ -800,7 +808,7 @@ function sendChat(){
800
808
  if(data.markers.indexOf('[CANVAS_CLEAR]')!==-1)closeCanvas();
801
809
  }
802
810
  if(currentEvent==='tool_synthesis'){chatHistory[streamIdx].content='';renderMessages();}
803
- if(currentEvent==='done'){endStreaming();if(data.content)chatHistory[streamIdx].content=data.content;var ssf=data.screenshotFiles||[];for(var fi=0;fi<ssf.length;fi++){chatHistory[streamIdx].content+='\\n![Screenshot](/api/screenshots/'+ssf[fi]+')\\n';}renderMessages();loadConvList();if(ssf.length>0)setTimeout(closeBrowserViewer,5000);}
811
+ if(currentEvent==='done'){endStreaming();if(data.content)chatHistory[streamIdx].content=data.content;var ssf=data.screenshotFiles||[];for(var fi=0;fi<ssf.length;fi++){chatHistory[streamIdx].content+='\\n![Screenshot](/api/screenshots/'+ssf[fi]+')\\n';}var bt=data.browserThumbs||[];if(bt.length>0){var cd=getConvCanvasData();for(var bti=0;bti<bt.length;bti++){var exists=cd.browsers.some(function(b){return b.file===bt[bti].file;});if(!exists)cd.browsers.push({file:bt[bti].file,url:bt[bti].url,ts:new Date().toLocaleTimeString()});}browserIdx=cd.browsers.length-1;saveCanvasData();}renderMessages();loadConvList();if(ssf.length>0)setTimeout(closeBrowserViewer,5000);}
804
812
  if(currentEvent==='error'){endStreaming();chatHistory[streamIdx].content='Error: '+(data.message||'Unknown');renderMessages();}
805
813
  }catch(e){}
806
814
  }