nothumanallowed 12.2.0 → 12.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 +1 -1
- package/src/commands/ui.mjs +29 -10
- package/src/constants.mjs +1 -1
- package/src/services/tool-executor.mjs +10 -1
- package/src/services/web-ui.mjs +59 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.4.0",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools. Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/ui.mjs
CHANGED
|
@@ -1785,6 +1785,7 @@ export async function cmdUI(args) {
|
|
|
1785
1785
|
// Parse and execute tools
|
|
1786
1786
|
const { textParts, actions } = parseActions(fullResponse);
|
|
1787
1787
|
const toolResults = [];
|
|
1788
|
+
let inlineEmbeds = ''; // Accumulated inline cards/browser frames for the response
|
|
1788
1789
|
|
|
1789
1790
|
// Auto-detect search + screenshot intent from user message
|
|
1790
1791
|
const wantsScreenshot = /screenshot|screen\s*shot|schermo|cattura|foto|immagine/i.test(msg);
|
|
@@ -1904,19 +1905,20 @@ export async function cmdUI(args) {
|
|
|
1904
1905
|
toolResults.push({ action, result: resultStr });
|
|
1905
1906
|
sendSSE('tool', { action, status: 'done', result: typeof resultStr === 'string' ? resultStr.slice(0, 500) : '' });
|
|
1906
1907
|
|
|
1907
|
-
// After web_search: open first result in browser +
|
|
1908
|
+
// After web_search: open first result in browser + embed inline card + inline browser
|
|
1908
1909
|
if (action === 'web_search' && resultStr.length > 50) {
|
|
1909
1910
|
try {
|
|
1910
1911
|
// Extract first URL from results
|
|
1911
1912
|
const urlMatch = resultStr.match(/https?:\/\/[^\s)<]+/);
|
|
1913
|
+
let inlineBrowserMarker = '';
|
|
1914
|
+
|
|
1912
1915
|
if (urlMatch) {
|
|
1913
1916
|
const be = await import('../services/browser-engine.mjs');
|
|
1914
1917
|
sendSSE('tool', { action: 'browser_open', status: 'executing' });
|
|
1915
1918
|
await be.browserOpen(urlMatch[0]);
|
|
1916
1919
|
sendSSE('tool', { action: 'browser_open', status: 'done', result: 'Opened ' + urlMatch[0] });
|
|
1917
|
-
// Capture frame for monitor
|
|
1918
1920
|
await new Promise(r => setTimeout(r, 1500));
|
|
1919
|
-
const frame = await be.browserScreenshot({ fullPage: false, format: 'jpeg', quality:
|
|
1921
|
+
const frame = await be.browserScreenshot({ fullPage: false, format: 'jpeg', quality: 50 });
|
|
1920
1922
|
if (!frame.error) {
|
|
1921
1923
|
const info = await be.browserInfo();
|
|
1922
1924
|
const thumbDir = path.join(NHA_DIR, 'screenshots');
|
|
@@ -1925,19 +1927,33 @@ export async function cmdUI(args) {
|
|
|
1925
1927
|
fs.writeFileSync(path.join(thumbDir, thumbFile), Buffer.from(frame.base64, 'base64'));
|
|
1926
1928
|
if (!res._browserThumbs) res._browserThumbs = [];
|
|
1927
1929
|
res._browserThumbs.push({ file: thumbFile, url: (info.url || '').slice(0, 80) });
|
|
1930
|
+
// Also send for floating panel (backwards compat)
|
|
1928
1931
|
sendSSE('browser_frame', { file: thumbFile, format: 'jpeg', url: (info.url || '').slice(0, 80) });
|
|
1932
|
+
// Inline browser marker — will be embedded in the response
|
|
1933
|
+
inlineBrowserMarker = `[INLINE_BROWSER]${thumbFile}|${(info.url || urlMatch[0]).slice(0, 80)}[/INLINE_BROWSER]`;
|
|
1929
1934
|
}
|
|
1930
1935
|
}
|
|
1931
1936
|
|
|
1932
|
-
//
|
|
1937
|
+
// Build inline card with structured search results
|
|
1933
1938
|
const lines = resultStr.split('\n').filter(l => l.trim());
|
|
1934
|
-
|
|
1939
|
+
let cardHtml = '<div style="font-family:system-ui;font-size:13px">';
|
|
1940
|
+
lines.slice(0, 8).forEach(l => {
|
|
1935
1941
|
const um = l.match(/https?:\/\/[^\s)]+/);
|
|
1936
|
-
const title = l.replace(/^\d+\.\s*/, '').replace(/https?:\/\/\S+/g, '').trim();
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1942
|
+
const title = l.replace(/^\d+\.\s*/, '').replace(/https?:\/\/\S+/g, '').replace(/<[^>]+>/g, '').trim();
|
|
1943
|
+
if (title) {
|
|
1944
|
+
cardHtml += '<div style="padding:8px 10px;border-bottom:1px solid #1e1e1e">';
|
|
1945
|
+
if (um) cardHtml += '<a href="' + um[0] + '" target="_blank" style="color:#00e5ff;text-decoration:none;font-size:13px">';
|
|
1946
|
+
cardHtml += title.replace(/&/g, '&').replace(/</g, '<');
|
|
1947
|
+
if (um) cardHtml += '<div style="font-size:10px;color:#555;margin-top:2px">' + um[0].replace(/&/g, '&').replace(/</g, '<') + '</div></a>';
|
|
1948
|
+
cardHtml += '</div>';
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
cardHtml += '</div>';
|
|
1952
|
+
|
|
1953
|
+
// Accumulate inline embeds — will be appended to final response AFTER LLM
|
|
1954
|
+
if (inlineBrowserMarker) inlineEmbeds += '\n' + inlineBrowserMarker;
|
|
1955
|
+
if (cardHtml.length > 50) inlineEmbeds += '\n[INLINE_CARD]' + cardHtml + '[/INLINE_CARD]';
|
|
1956
|
+
} catch { /* non-critical */ }
|
|
1941
1957
|
}
|
|
1942
1958
|
|
|
1943
1959
|
// Send live browser frame after browser/search/fetch actions — save as thumbnail file for persistence
|
|
@@ -2058,6 +2074,9 @@ export async function cmdUI(args) {
|
|
|
2058
2074
|
// Extract memory
|
|
2059
2075
|
try { extractMemory('chat', msg, finalResponse); } catch {}
|
|
2060
2076
|
|
|
2077
|
+
// Append inline embeds (search cards, browser frames) to the response
|
|
2078
|
+
if (inlineEmbeds) finalResponse += '\n' + inlineEmbeds;
|
|
2079
|
+
|
|
2061
2080
|
const ssFiles = res._screenshotFiles || [];
|
|
2062
2081
|
const browserThumbs = res._browserThumbs || [];
|
|
2063
2082
|
sendSSE('done', { content: finalResponse, screenshotFiles: ssFiles, browserThumbs });
|
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 = '12.
|
|
8
|
+
export const VERSION = '12.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
|
|
|
@@ -364,7 +364,16 @@ TOOLS:
|
|
|
364
364
|
|
|
365
365
|
64. canvas_render(html: string, title?: string)
|
|
366
366
|
Render HTML in the web UI canvas panel. Show charts, tables, diagrams, reports.
|
|
367
|
-
|
|
367
|
+
ALWAYS use Chart.js from CDN for charts and graphs — never build charts with raw HTML/CSS.
|
|
368
|
+
Template for charts:
|
|
369
|
+
<html><head><script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script></head>
|
|
370
|
+
<body style="background:#0a0a0a;padding:20px;margin:0">
|
|
371
|
+
<canvas id="c" style="max-height:400px"></canvas>
|
|
372
|
+
<script>new Chart(document.getElementById('c'),{type:'bar',data:{labels:['A','B'],datasets:[{label:'Data',data:[10,20],backgroundColor:['#00ff41','#00e5ff']}]},options:{plugins:{legend:{labels:{color:'#ccc'}}},scales:{x:{ticks:{color:'#888'}},y:{ticks:{color:'#888'}}}}});</script>
|
|
373
|
+
</body></html>
|
|
374
|
+
Supported chart types: bar, line, pie, doughnut, radar, polarArea, scatter, bubble.
|
|
375
|
+
Use dark theme: background #0a0a0a, text #ccc, grid #333, accent colors #00ff41 #00e5ff #ffaa00 #ff4444 #a78bfa.
|
|
376
|
+
For tables: use proper HTML tables with dark styling. For Mermaid diagrams: load from CDN.
|
|
368
377
|
|
|
369
378
|
65. canvas_clear()
|
|
370
379
|
Clear the canvas panel.
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -112,6 +112,15 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
112
112
|
.tool-indicator--web{border-color:var(--cyan);color:var(--cyan)}
|
|
113
113
|
.tool-indicator--email{border-color:var(--green3);color:var(--green)}
|
|
114
114
|
.screenshot-preview{max-width:100%;border-radius:var(--r);margin:8px 0;border:1px solid var(--border)}
|
|
115
|
+
.inline-card{margin:12px 0;padding:0;border-radius:10px;border:1px solid var(--border);overflow:hidden;background:var(--bg2)}
|
|
116
|
+
.inline-card iframe{width:100%;height:280px;border:none;border-radius:0 0 10px 10px}
|
|
117
|
+
.inline-card a{color:var(--cyan);text-decoration:none}
|
|
118
|
+
.inline-card a:hover{text-decoration:underline}
|
|
119
|
+
.inline-browser{margin:12px 0;border-radius:10px;border:1px solid var(--green3);overflow:hidden;background:#000}
|
|
120
|
+
.inline-browser-bar{display:flex;align-items:center;gap:6px;padding:6px 10px;background:var(--bg);border-bottom:1px solid var(--border)}
|
|
121
|
+
.inline-browser-dot{width:8px;height:8px;border-radius:50%;background:var(--border2)}
|
|
122
|
+
.inline-browser-url{font-family:var(--mono);font-size:10px;color:var(--dim);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
123
|
+
.inline-browser img{width:100%;display:block}
|
|
115
124
|
.browser-viewer{position:fixed;top:12px;left:12px;width:440px;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}
|
|
116
125
|
.browser-viewer--open{display:block}
|
|
117
126
|
.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}
|
|
@@ -466,6 +475,10 @@ function renderMessages(){
|
|
|
466
475
|
var cm=raw.match(/\\[CANVAS_RENDER\\]([\\s\\S]*?)\\[\\/CANVAS_RENDER\\]/);
|
|
467
476
|
if(cm){try{var cd=JSON.parse(cm[1]);showCanvas(cd.html,cd.title);}catch(e){} raw=raw.replace(/\\[CANVAS_RENDER\\][\\s\\S]*?\\[\\/CANVAS_RENDER\\]/,'').trim();}
|
|
468
477
|
if(raw.indexOf('[CANVAS_CLEAR]')!==-1){closeCanvas();raw=raw.replace(/\\[CANVAS_CLEAR\\][\\s\\S]*?\\[\\/CANVAS_CLEAR\\]/,'').trim();}
|
|
478
|
+
// Inline cards — rendered as embedded HTML inside the message
|
|
479
|
+
raw=raw.replace(/\\[INLINE_CARD\\]([\\s\\S]*?)\\[\\/INLINE_CARD\\]/g,function(_,html){return '<div class="inline-card">'+html+'</div>';});
|
|
480
|
+
// Inline browser frame — rendered as embedded image inside the message
|
|
481
|
+
raw=raw.replace(/\\[INLINE_BROWSER\\]([^|]+)\\|([^\\]]+)\\[\\/INLINE_BROWSER\\]/g,function(_,file,url){return '<div class="inline-browser"><div class="inline-browser-bar"><span class="inline-browser-dot"></span><span class="inline-browser-dot"></span><span class="inline-browser-dot"></span><span class="inline-browser-url">'+esc(url)+'</span></div><img src="/api/screenshots/'+esc(file)+'" alt="'+esc(url)+'"></div>';});
|
|
469
482
|
// Handle screenshot file markers
|
|
470
483
|
var sm=raw.match(/\\[SCREENSHOT_FILE\\](.*?)\\[\\/SCREENSHOT_FILE\\]/);
|
|
471
484
|
if(sm){var fn=sm[1].split('/').pop();raw=raw.replace(/\\[SCREENSHOT_FILE\\].*?\\[\\/SCREENSHOT_FILE\\]/,'');raw='\\n'+raw;}
|
|
@@ -713,6 +726,51 @@ function reopenCanvas(){
|
|
|
713
726
|
renderCanvasPanel();
|
|
714
727
|
}
|
|
715
728
|
function closeCanvas(){var p=document.getElementById('canvasPanel');if(p)p.classList.remove('open');}
|
|
729
|
+
function canvasCopyText(){
|
|
730
|
+
var d=getConvCanvasData();var item=d.canvases[canvasIdx];
|
|
731
|
+
if(!item){alert('No canvas content');return;}
|
|
732
|
+
// Extract text from the HTML
|
|
733
|
+
var tmp=document.createElement('div');tmp.innerHTML=item.html.replace(/<script[\s\S]*?<\/script>/gi,'');
|
|
734
|
+
var text=tmp.textContent||tmp.innerText||'';
|
|
735
|
+
navigator.clipboard.writeText(text).then(function(){alert('Text copied!')}).catch(function(){alert('Copy failed')});
|
|
736
|
+
}
|
|
737
|
+
function canvasCopyHTML(){
|
|
738
|
+
var d=getConvCanvasData();var item=d.canvases[canvasIdx];
|
|
739
|
+
if(!item){alert('No canvas content');return;}
|
|
740
|
+
navigator.clipboard.writeText(item.html).then(function(){alert('HTML source copied!')}).catch(function(){alert('Copy failed')});
|
|
741
|
+
}
|
|
742
|
+
function canvasCopyImage(){
|
|
743
|
+
var f=document.getElementById('canvasFrame');
|
|
744
|
+
if(!f){alert('No canvas frame');return;}
|
|
745
|
+
try{
|
|
746
|
+
// Use html2canvas approach — render iframe to canvas then copy
|
|
747
|
+
var iframe=f;
|
|
748
|
+
// For sandboxed iframes, we capture via srcdoc render
|
|
749
|
+
var w=iframe.offsetWidth||800;var h=iframe.offsetHeight||400;
|
|
750
|
+
var canvas=document.createElement('canvas');canvas.width=w*2;canvas.height=h*2;
|
|
751
|
+
var ctx=canvas.getContext('2d');ctx.scale(2,2);
|
|
752
|
+
// Draw white background
|
|
753
|
+
ctx.fillStyle='#0a0a0a';ctx.fillRect(0,0,w,h);
|
|
754
|
+
// Render the srcdoc HTML to an image via SVG foreignObject
|
|
755
|
+
var d=getConvCanvasData();var item=d.canvases[canvasIdx];
|
|
756
|
+
if(!item){alert('No canvas');return;}
|
|
757
|
+
var svg='<svg xmlns="http://www.w3.org/2000/svg" width="'+w+'" height="'+h+'"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="background:#0a0a0a">'+item.html.replace(/<script[\s\S]*?<\/script>/gi,'')+'</div></foreignObject></svg>';
|
|
758
|
+
var img=new Image();
|
|
759
|
+
img.onload=function(){
|
|
760
|
+
ctx.drawImage(img,0,0,w,h);
|
|
761
|
+
canvas.toBlob(function(blob){
|
|
762
|
+
if(blob){
|
|
763
|
+
navigator.clipboard.write([new ClipboardItem({'image/png':blob})]).then(function(){alert('Image copied!')}).catch(function(){
|
|
764
|
+
// Fallback: download
|
|
765
|
+
var a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='canvas.png';a.click();
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
},'image/png');
|
|
769
|
+
};
|
|
770
|
+
img.onerror=function(){alert('Image render failed — try Copy HTML instead');};
|
|
771
|
+
img.src='data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svg);
|
|
772
|
+
}catch(e){alert('Image copy failed: '+e.message);}
|
|
773
|
+
}
|
|
716
774
|
function toggleCanvasSize(){
|
|
717
775
|
var p=document.getElementById('canvasPanel');if(!p)return;
|
|
718
776
|
if(p.style.width==='80vw'){p.style.width='';p.style.height='';p.style.top='';p.style.right='';}
|
|
@@ -2691,7 +2749,7 @@ init();
|
|
|
2691
2749
|
</div>
|
|
2692
2750
|
</div>
|
|
2693
2751
|
|
|
2694
|
-
<div id="canvasPanel"><div class="cvs-header"><div style="display:flex;align-items:center;gap:8px"><button id="canvasTabC" onclick="canvasShowCanvas()" style="background:none;border:none;border-bottom:2px solid var(--green);color:var(--green);cursor:pointer;font-family:var(--mono);font-size:11px;padding:2px 6px">Canvas</button><button id="canvasTabB" onclick="canvasShowBrowser()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-family:var(--mono);font-size:11px;padding:2px 6px">Browser</button><span id="canvasTitle" style="font-family:var(--mono);color:var(--green);font-size:11px;margin-left:8px">Canvas</span></div><div style="display:flex;align-items:center;gap:4px"><span id="canvasNav" style="display:none;gap:4px"><button onclick="canvasPrev()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px" title="Previous">◀</button><button onclick="canvasNext()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px" title="Next">▶</button></span><button onclick="toggleCanvasSize()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px" title="Resize">⤢</button><button onclick="closeCanvas()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px" title="Close">×</button></div></div><iframe id="canvasFrame" sandbox="allow-scripts" srcdoc=""></iframe></div>
|
|
2752
|
+
<div id="canvasPanel"><div class="cvs-header"><div style="display:flex;align-items:center;gap:8px"><button id="canvasTabC" onclick="canvasShowCanvas()" style="background:none;border:none;border-bottom:2px solid var(--green);color:var(--green);cursor:pointer;font-family:var(--mono);font-size:11px;padding:2px 6px">Canvas</button><button id="canvasTabB" onclick="canvasShowBrowser()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-family:var(--mono);font-size:11px;padding:2px 6px">Browser</button><span id="canvasTitle" style="font-family:var(--mono);color:var(--green);font-size:11px;margin-left:8px">Canvas</span></div><div style="display:flex;align-items:center;gap:4px"><span id="canvasNav" style="display:none;gap:4px"><button onclick="canvasPrev()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px" title="Previous">◀</button><button onclick="canvasNext()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px" title="Next">▶</button></span><button onclick="canvasCopyText()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:11px;font-family:var(--mono)" title="Copy text content">Copy</button><button onclick="canvasCopyHTML()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:11px;font-family:var(--mono)" title="Copy HTML source">HTML</button><button onclick="canvasCopyImage()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:11px;font-family:var(--mono)" title="Copy as image">IMG</button><button onclick="toggleCanvasSize()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px" title="Resize">⤢</button><button onclick="closeCanvas()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px" title="Close">×</button></div></div><iframe id="canvasFrame" sandbox="allow-scripts" srcdoc=""></iframe></div>
|
|
2695
2753
|
<script>${JS}</script>
|
|
2696
2754
|
</body>
|
|
2697
2755
|
</html>`;
|