nothumanallowed 9.3.18 → 9.4.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.3.18",
3
+ "version": "9.4.0",
4
4
  "description": "NotHumanAllowed — 38 AI agents + 58 tools + browser automation + web search. Streaming chat, headless Chrome CDP, multi-conversation, export. Gmail, Calendar, Drive, GitHub, Notion, Slack. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1263,6 +1263,20 @@ export async function cmdUI(args) {
1263
1263
  toolResults.push({ action, result: resultStr });
1264
1264
  sendSSE('tool', { action, status: 'done', result: typeof resultStr === 'string' ? resultStr.slice(0, 500) : '' });
1265
1265
 
1266
+ // Send live browser frame after browser actions (low-quality thumbnail for viewer)
1267
+ if (action.startsWith('browser_') && action !== 'browser_close') {
1268
+ try {
1269
+ const be = await import('../services/browser-engine.mjs');
1270
+ if (be.isBrowserRunning()) {
1271
+ const frame = await be.browserScreenshot({ fullPage: false, format: 'jpeg', quality: 30 });
1272
+ if (!frame.error) {
1273
+ const info = await be.browserInfo();
1274
+ sendSSE('browser_frame', { base64: frame.base64, format: 'jpeg', url: (info.url || '').slice(0, 80) });
1275
+ }
1276
+ }
1277
+ } catch { /* frame capture failed, non-critical */ }
1278
+ }
1279
+
1266
1280
  // If the tool produced a screenshot (web_search with screenshot=true), send it via SSE and save to disk
1267
1281
  if (resultStr.includes('[Screenshot of results captured')) {
1268
1282
  try {
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.3.18';
8
+ export const VERSION = '9.4.0';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -1014,13 +1014,29 @@ export async function browserExtract(options = {}) {
1014
1014
  export async function browserEval(code, options = {}) {
1015
1015
  const browser = await getBrowser();
1016
1016
 
1017
- const result = await browser.ws.send('Runtime.evaluate', {
1018
- expression: code,
1017
+ // Auto-wrap object literals: {key: val} → ({key: val}) to avoid block/label ambiguity
1018
+ let expression = code.trim();
1019
+ if (expression.startsWith('{') && !expression.startsWith('{(')) {
1020
+ expression = `(${expression})`;
1021
+ }
1022
+
1023
+ let result = await browser.ws.send('Runtime.evaluate', {
1024
+ expression,
1019
1025
  returnByValue: true,
1020
1026
  awaitPromise: options.awaitPromise !== false,
1021
1027
  generatePreview: true,
1022
1028
  }, COMMAND_TIMEOUT_MS);
1023
1029
 
1030
+ // If SyntaxError, retry with IIFE wrapper
1031
+ if (result.exceptionDetails?.exception?.description?.includes('SyntaxError')) {
1032
+ result = await browser.ws.send('Runtime.evaluate', {
1033
+ expression: `(() => { return (${code.trim()}); })()`,
1034
+ returnByValue: true,
1035
+ awaitPromise: options.awaitPromise !== false,
1036
+ generatePreview: true,
1037
+ }, COMMAND_TIMEOUT_MS);
1038
+ }
1039
+
1024
1040
  if (result.exceptionDetails) {
1025
1041
  const errMsg = result.exceptionDetails.exception?.description
1026
1042
  || result.exceptionDetails.text
@@ -101,6 +101,18 @@ input:focus,textarea:focus{border-color:var(--green3)}
101
101
  .tool-indicator--web{border-color:var(--cyan);color:var(--cyan)}
102
102
  .tool-indicator--email{border-color:var(--green3);color:var(--green)}
103
103
  .screenshot-preview{max-width:100%;border-radius:var(--r);margin:8px 0;border:1px solid var(--border)}
104
+ .browser-viewer{position:fixed;bottom:16px;right:16px;width:380px;background:var(--bg2);border:2px solid #9c27b0;border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,0.6);z-index:300;overflow:hidden;display:none;transition:all .3s ease}
105
+ .browser-viewer--open{display:block}
106
+ .browser-viewer__header{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#1a1a2e;border-bottom:1px solid #9c27b0;font-size:10px;color:#ce93d8}
107
+ .browser-viewer__dot{width:6px;height:6px;border-radius:50%;background:#9c27b0;animation:bvpulse 1.5s infinite}
108
+ @keyframes bvpulse{0%,100%{opacity:1}50%{opacity:.3}}
109
+ .browser-viewer__title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
110
+ .browser-viewer__close{background:none;border:none;color:#666;cursor:pointer;font-size:14px;padding:0 4px}
111
+ .browser-viewer__close:hover{color:#fff}
112
+ .browser-viewer__frame{width:100%;aspect-ratio:16/9;background:#000;display:flex;align-items:center;justify-content:center}
113
+ .browser-viewer__frame img{width:100%;height:100%;object-fit:contain}
114
+ .browser-viewer__status{padding:4px 10px;font-size:9px;color:var(--dim);border-top:1px solid var(--border)}
115
+ @media(max-width:600px){.browser-viewer{width:280px;bottom:70px}}
104
116
  .chat__bar{display:flex;gap:8px;padding:10px 0 12px 0;border-top:1px solid var(--border);flex-shrink:0}
105
117
  .chat__input{flex:1;resize:none;min-height:40px;max-height:100px;padding:10px 14px}
106
118
  .chat__send{background:var(--green3);color:var(--bg);padding:10px 16px;border-radius:var(--r);font-weight:700;font-size:12px}
@@ -226,6 +238,24 @@ function stopChat(){
226
238
  renderMessages();
227
239
  }
228
240
 
241
+ // ---- BROWSER VIEWER (live preview of headless Chrome) ----
242
+ function showBrowserViewer(title,status){
243
+ var v=document.getElementById('browserViewer');if(!v)return;
244
+ v.classList.add('browser-viewer--open');
245
+ var t=document.getElementById('bvTitle');if(t)t.textContent=title||'Browser';
246
+ var s=document.getElementById('bvStatus');if(s)s.textContent=status||'Loading...';
247
+ }
248
+ function updateBrowserFrame(base64,format){
249
+ var f=document.getElementById('bvFrame');if(!f)return;
250
+ f.innerHTML='<img src="data:image/'+(format||'jpeg')+';base64,'+base64+'" alt="Browser view">';
251
+ }
252
+ function updateBrowserStatus(status){
253
+ var s=document.getElementById('bvStatus');if(s)s.textContent=status;
254
+ }
255
+ function closeBrowserViewer(){
256
+ var v=document.getElementById('browserViewer');if(v)v.classList.remove('browser-viewer--open');
257
+ }
258
+
229
259
  function loadConvList(){return apiGet('/api/conversations').then(function(r){convList=(r&&r.conversations)||[];renderConvSidebar();})}
230
260
  function loadConv(id){return apiGet('/api/conversations/'+id).then(function(r){if(r&&r.conversation){activeConvId=r.conversation.id;chatHistory=r.conversation.messages||[];renderMessages();renderConvSidebar();}})}
231
261
  function createNewConv(){return apiPost('/api/conversations',{}).then(function(r){if(r&&r.conversation){activeConvId=r.conversation.id;chatHistory=[];renderMessages();loadConvList();}})}
@@ -533,6 +563,12 @@ function sendChat(){
533
563
  var label=toolLabels[data.action]||data.action;
534
564
  var indicator=data.status==='executing'?'\\u23f3 '+label+'...':'\\u2705 '+label;
535
565
  if(data.status==='error')indicator='\\u274c '+label+' failed';
566
+ // Show browser viewer for browser actions
567
+ var isBrowserAction=data.action&&data.action.startsWith('browser_');
568
+ if(isBrowserAction&&data.status==='executing'){showBrowserViewer(label,'Executing...');}
569
+ if(isBrowserAction&&data.status==='done'){updateBrowserStatus('\\u2705 '+label);}
570
+ if(isBrowserAction&&data.status==='error'){updateBrowserStatus('\\u274c '+label);}
571
+ if(data.action==='browser_close'&&data.status==='done'){setTimeout(closeBrowserViewer,2000);}
536
572
  // Strip JSON action blocks from streamed content (they are internal tool calls, not for the user)
537
573
  if(data.status==='executing'){chatHistory[streamIdx].content=chatHistory[streamIdx].content.replace(new RegExp('\\x60\\x60\\x60json[\\\\s\\\\S]*?\\x60\\x60\\x60','g'),'').trim()+'\\n';}
538
574
  chatHistory[streamIdx].content+=indicator+'\\n';
@@ -540,11 +576,18 @@ function sendChat(){
540
576
  }
541
577
  if(currentEvent==='screenshot'&&data.base64){
542
578
  if(!chatHistory[streamIdx]._screenshots)chatHistory[streamIdx]._screenshots=[];
543
- // Use file URL if available (persists across sessions), fall back to base64
544
579
  var ssUrl=data.filename?'/api/screenshots/'+data.filename:'data:image/'+(data.format||'jpeg')+';base64,'+data.base64;
545
580
  chatHistory[streamIdx]._screenshots.push(ssUrl);
546
581
  chatHistory[streamIdx].content+='\\n![Screenshot]('+ssUrl+')\\n';
547
582
  renderMessages();
583
+ // Update browser viewer with the screenshot
584
+ updateBrowserFrame(data.base64,data.format||'jpeg');
585
+ updateBrowserStatus('Screenshot captured');
586
+ }
587
+ if(currentEvent==='browser_frame'&&data.base64){
588
+ // Live frame update (thumbnail from browser during operations)
589
+ updateBrowserFrame(data.base64,data.format||'jpeg');
590
+ if(data.url)updateBrowserStatus(data.url);
548
591
  }
549
592
  if(currentEvent==='tool_synthesis'){chatHistory[streamIdx].content='';renderMessages();}
550
593
  if(currentEvent==='done'){endStreaming();if(data.content)chatHistory[streamIdx].content=data.content;var ss=chatHistory[streamIdx]._screenshots;if(ss&&ss.length>0){for(var si=0;si<ss.length;si++){chatHistory[streamIdx].content+='\\n![Screenshot]('+ss[si]+')\\n';}}renderMessages();loadConvList();}
@@ -1837,6 +1880,18 @@ init();
1837
1880
  <button onclick="openSidebar()" style="position:fixed;top:8px;left:8px;z-index:100;background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);color:var(--green);font-size:20px;padding:4px 10px;cursor:pointer;line-height:1" id="mobileBurger">&#9776;</button>
1838
1881
 
1839
1882
  <div class="content" id="content"></div>
1883
+
1884
+ <div class="browser-viewer" id="browserViewer">
1885
+ <div class="browser-viewer__header">
1886
+ <span class="browser-viewer__dot"></span>
1887
+ <span class="browser-viewer__title" id="bvTitle">Browser</span>
1888
+ <button class="browser-viewer__close" onclick="closeBrowserViewer()">&times;</button>
1889
+ </div>
1890
+ <div class="browser-viewer__frame" id="bvFrame">
1891
+ <span style="color:var(--dim);font-size:11px">Waiting...</span>
1892
+ </div>
1893
+ <div class="browser-viewer__status" id="bvStatus">Idle</div>
1894
+ </div>
1840
1895
  </div>
1841
1896
 
1842
1897
  <div class="modal-overlay" id="agentModal">