nothumanallowed 9.8.8 → 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.8",
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.8';
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
 
@@ -252,16 +252,24 @@ function stopChat(){
252
252
 
253
253
  // ---- BROWSER VIEWER (live preview of headless Chrome) ----
254
254
  function showBrowserViewer(title,status){
255
- var v=document.getElementById('browserViewer');if(!v)return;
256
- v.classList.add('browser-viewer--open');
255
+ // Update old monitor viewer
256
+ var v=document.getElementById('browserViewer');if(v)v.classList.add('browser-viewer--open');
257
257
  var t=document.getElementById('bvTitle');if(t)t.textContent=title||'Browser';
258
258
  var s=document.getElementById('bvStatus');if(s)s.textContent=status||'Loading...';
259
+ // Also auto-open canvas panel in browser tab
260
+ var p=document.getElementById('canvasPanel');
261
+ if(p&&!p.classList.contains('open')){canvasMode='browser';renderCanvasPanel();}
259
262
  }
260
- function updateBrowserFrame(base64,format,url){
261
- var f=document.getElementById('bvFrame');if(!f)return;
262
- f.innerHTML='<img src="data:image/'+(format||'jpeg')+';base64,'+base64+'" alt="Browser view">';
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;
266
+ // Update old monitor viewer
267
+ var f=document.getElementById('bvFrame');if(f)f.innerHTML='<img src="'+imgSrc+'" alt="Browser view">';
263
268
  // Save to per-conversation browser history for canvas Browser tab
264
- addBrowserPage(base64,url);
269
+ addBrowserPage(data.file||null,data.base64||null,data.url);
270
+ // Update canvas browser tab live if open
271
+ var p=document.getElementById('canvasPanel');
272
+ if(p&&p.classList.contains('open')&&canvasMode==='browser'){renderCanvasPanel();}
265
273
  }
266
274
  function updateBrowserStatus(status){
267
275
  var s=document.getElementById('bvStatus');if(s)s.textContent=status;
@@ -409,7 +417,7 @@ function renderChat(el){
409
417
  '</div>'+
410
418
  '<div class="chat"><div class="chat__messages" id="chatMessages"></div>'+
411
419
  '<div id="chatAttachInfo" style="display:none;padding:4px 12px;font-size:11px;color:var(--cyan);background:var(--bg2);border-top:1px solid var(--border)"><span id="chatAttachName"></span> <button onclick="clearChatAttach()" style="background:none;border:none;color:#f44;cursor:pointer;font-size:14px;font-weight:700">&times;</button></div>'+
412
- '<div class="chat__bar"><button class="chat__mic" id="chatMic" onclick="toggleVoiceInput()" title="Voice input">&#127908;</button><button onclick="document.getElementById(\\x27chatFileInput\\x27).click()" style="background:none;border:none;cursor:pointer;font-size:16px;padding:4px" title="Attach file">&#128206;</button><button onclick="document.getElementById(\\x27chatImageInput\\x27).click()" style="background:none;border:none;cursor:pointer;font-size:16px;padding:4px" title="Attach image">&#128247;</button><input type="file" id="chatFileInput" style="display:none" onchange="handleChatFile(this)"><input type="file" id="chatImageInput" accept="image/*" style="display:none" onchange="handleChatImage(this)"><textarea class="chat__input" id="chatInput" placeholder="Ask anything... (or attach file/image first)" rows="1"></textarea><button class="chat__send" id="chatSend">Send</button><button class="chat__stop" id="chatStop" onclick="stopChat()">Stop</button><button onclick="reopenCanvas()" style="background:none;border:1px solid var(--border2);border-radius:6px;cursor:pointer;font-size:11px;padding:4px 8px;color:var(--dim);font-family:var(--mono);display:flex;align-items:center;gap:4px" title="Open Canvas / Browser panel"><span style="font-size:14px">&#x25A3;</span>Canvas</button></div>'+
420
+ '<div class="chat__bar"><button class="chat__mic" id="chatMic" onclick="toggleVoiceInput()" title="Voice input">&#127908;</button><button onclick="document.getElementById(\\x27chatFileInput\\x27).click()" style="background:none;border:none;cursor:pointer;font-size:16px;padding:4px" title="Attach file">&#128206;</button><button onclick="document.getElementById(\\x27chatImageInput\\x27).click()" style="background:none;border:none;cursor:pointer;font-size:16px;padding:4px" title="Attach image">&#128247;</button><input type="file" id="chatFileInput" style="display:none" onchange="handleChatFile(this)"><input type="file" id="chatImageInput" accept="image/*" style="display:none" onchange="handleChatImage(this)"><textarea class="chat__input" id="chatInput" placeholder="Ask anything... (or attach file/image first)" rows="1"></textarea><button class="chat__send" id="chatSend">Send</button><button class="chat__stop" id="chatStop" onclick="stopChat()">Stop</button><button onclick="reopenCanvas()" style="background:none;border:1px solid var(--border2);border-radius:6px;cursor:pointer;font-size:11px;padding:4px 8px;color:var(--dim);font-family:var(--mono);display:flex;align-items:center;gap:4px" title="Open Canvas / Browser panel"><span style="font-size:13px">&#x25A3;</span>Panel</button></div>'+
413
421
  '</div>'+
414
422
  '</div>'+
415
423
  '</div>';
@@ -545,29 +553,33 @@ function showCanvas(html,title){
545
553
  saveCanvasData();
546
554
  }
547
555
 
548
- function addBrowserPage(base64,url){
556
+ function addBrowserPage(file,base64,url){
549
557
  var d=getConvCanvasData();
550
558
  var cleanUrl=(url||'Browser').replace(/^https?:\\/\\//, '').slice(0,60);
551
559
  // Only add if URL is different from the last entry (avoid frame duplicates)
552
560
  if(d.browsers.length>0&&d.browsers[d.browsers.length-1].url===cleanUrl){
553
- // Update the thumbnail with the latest frame
554
- 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;
555
564
  return;
556
565
  }
557
- 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()});
558
567
  browserIdx=d.browsers.length-1;
559
- // Auto-update canvas panel if browser tab is open
560
- var p=document.getElementById('canvasPanel');
561
- if(p&&p.classList.contains('open')&&canvasMode==='browser'){renderCanvasPanel();}
568
+ // Persist file refs to localStorage (not base64)
569
+ saveCanvasData();
562
570
  }
563
571
 
564
572
  function saveCanvasData(){
565
- // Save only canvas HTML (not browser base64 — too large) per conversation
566
573
  try{
567
574
  var save={};
568
575
  for(var id in allCanvasData){
569
- if(allCanvasData[id].canvases.length>0){
570
- 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
+ };
571
583
  }
572
584
  }
573
585
  localStorage.setItem('nha_canvas_data',JSON.stringify(save));
@@ -582,6 +594,7 @@ function loadCanvasData(){
582
594
  for(var id in parsed){
583
595
  if(!allCanvasData[id])allCanvasData[id]={canvases:[],browsers:[]};
584
596
  allCanvasData[id].canvases=parsed[id].canvases||[];
597
+ allCanvasData[id].browsers=parsed[id].browsers||[];
585
598
  }
586
599
  }
587
600
  }catch(e){}
@@ -620,11 +633,12 @@ function renderCanvasPanel(){
620
633
  if(d.browsers.length===0){
621
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();
622
635
  } else {
623
- 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">';
624
638
  for(var bi=0;bi<d.browsers.length;bi++){
625
639
  var b=d.browsers[bi];
626
- var sel=bi===browserIdx?' selected':'';
627
- 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>';
628
642
  }
629
643
  gallery+='</div></body></html>';
630
644
  doc.open();doc.write(gallery);doc.close();
@@ -779,12 +793,12 @@ function sendChat(){
779
793
  }
780
794
  if(currentEvent==='screenshot'&&data.base64){
781
795
  showBrowserViewer('Screenshot','Captured');
782
- updateBrowserFrame(data.base64,data.format||'jpeg','Screenshot');
796
+ updateBrowserFrame({base64:data.base64,format:data.format||'jpeg',url:'Screenshot'});
783
797
  updateBrowserStatus('Screenshot captured');
784
798
  }
785
- if(currentEvent==='browser_frame'&&data.base64){
799
+ if(currentEvent==='browser_frame'&&(data.base64||data.file)){
786
800
  showBrowserViewer(data.url||'Browser','Live');
787
- updateBrowserFrame(data.base64,data.format||'jpeg',data.url||'Browser');
801
+ updateBrowserFrame(data);
788
802
  if(data.url)updateBrowserStatus(data.url);
789
803
  }
790
804
  if(currentEvent==='canvas'&&data.markers){
@@ -794,7 +808,7 @@ function sendChat(){
794
808
  if(data.markers.indexOf('[CANVAS_CLEAR]')!==-1)closeCanvas();
795
809
  }
796
810
  if(currentEvent==='tool_synthesis'){chatHistory[streamIdx].content='';renderMessages();}
797
- 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);}
798
812
  if(currentEvent==='error'){endStreaming();chatHistory[streamIdx].content='Error: '+(data.message||'Unknown');renderMessages();}
799
813
  }catch(e){}
800
814
  }