nothumanallowed 12.1.3 → 12.3.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": "12.1.3",
3
+ "version": "12.3.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": {
@@ -1904,8 +1904,62 @@ export async function cmdUI(args) {
1904
1904
  toolResults.push({ action, result: resultStr });
1905
1905
  sendSSE('tool', { action, status: 'done', result: typeof resultStr === 'string' ? resultStr.slice(0, 500) : '' });
1906
1906
 
1907
+ // After web_search: open first result in browser + embed inline card + inline browser
1908
+ if (action === 'web_search' && resultStr.length > 50) {
1909
+ try {
1910
+ // Extract first URL from results
1911
+ const urlMatch = resultStr.match(/https?:\/\/[^\s)<]+/);
1912
+ let inlineBrowserMarker = '';
1913
+
1914
+ if (urlMatch) {
1915
+ const be = await import('../services/browser-engine.mjs');
1916
+ sendSSE('tool', { action: 'browser_open', status: 'executing' });
1917
+ await be.browserOpen(urlMatch[0]);
1918
+ sendSSE('tool', { action: 'browser_open', status: 'done', result: 'Opened ' + urlMatch[0] });
1919
+ await new Promise(r => setTimeout(r, 1500));
1920
+ const frame = await be.browserScreenshot({ fullPage: false, format: 'jpeg', quality: 50 });
1921
+ if (!frame.error) {
1922
+ const info = await be.browserInfo();
1923
+ const thumbDir = path.join(NHA_DIR, 'screenshots');
1924
+ fs.mkdirSync(thumbDir, { recursive: true });
1925
+ const thumbFile = `thumb-${Date.now()}.jpg`;
1926
+ fs.writeFileSync(path.join(thumbDir, thumbFile), Buffer.from(frame.base64, 'base64'));
1927
+ if (!res._browserThumbs) res._browserThumbs = [];
1928
+ res._browserThumbs.push({ file: thumbFile, url: (info.url || '').slice(0, 80) });
1929
+ // Also send for floating panel (backwards compat)
1930
+ sendSSE('browser_frame', { file: thumbFile, format: 'jpeg', url: (info.url || '').slice(0, 80) });
1931
+ // Inline browser marker — will be embedded in the response
1932
+ inlineBrowserMarker = `[INLINE_BROWSER]${thumbFile}|${(info.url || urlMatch[0]).slice(0, 80)}[/INLINE_BROWSER]`;
1933
+ }
1934
+ }
1935
+
1936
+ // Build inline card with structured search results
1937
+ const lines = resultStr.split('\n').filter(l => l.trim());
1938
+ let cardHtml = '<div style="font-family:system-ui;font-size:13px">';
1939
+ lines.slice(0, 8).forEach(l => {
1940
+ const um = l.match(/https?:\/\/[^\s)]+/);
1941
+ const title = l.replace(/^\d+\.\s*/, '').replace(/https?:\/\/\S+/g, '').replace(/<[^>]+>/g, '').trim();
1942
+ if (title) {
1943
+ cardHtml += '<div style="padding:8px 10px;border-bottom:1px solid #1e1e1e">';
1944
+ if (um) cardHtml += '<a href="' + um[0] + '" target="_blank" style="color:#00e5ff;text-decoration:none;font-size:13px">';
1945
+ cardHtml += title.replace(/&/g, '&amp;').replace(/</g, '&lt;');
1946
+ if (um) cardHtml += '<div style="font-size:10px;color:#555;margin-top:2px">' + um[0].replace(/&/g, '&amp;').replace(/</g, '&lt;') + '</div></a>';
1947
+ cardHtml += '</div>';
1948
+ }
1949
+ });
1950
+ cardHtml += '</div>';
1951
+
1952
+ // Append inline markers to the tool result so they end up in the response
1953
+ if (inlineBrowserMarker || cardHtml.length > 50) {
1954
+ const inlineMarkers = (inlineBrowserMarker ? inlineBrowserMarker + '\n' : '') + '[INLINE_CARD]' + cardHtml + '[/INLINE_CARD]';
1955
+ // Append to the last tool result so LLM includes it in response
1956
+ toolResults[toolResults.length - 1].result += '\n' + inlineMarkers;
1957
+ }
1958
+ } catch { /* non-critical */ }
1959
+ }
1960
+
1907
1961
  // Send live browser frame after browser/search/fetch actions — save as thumbnail file for persistence
1908
- if ((action.startsWith('browser_') && action !== 'browser_close') || action === 'web_search' || action === 'fetch_url') {
1962
+ if ((action.startsWith('browser_') && action !== 'browser_close') || action === 'fetch_url') {
1909
1963
  try {
1910
1964
  const be = await import('../services/browser-engine.mjs');
1911
1965
  if (be.isBrowserRunning()) {
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.1.3';
8
+ export const VERSION = '12.3.0';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -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='![Screenshot](/api/screenshots/'+fn+')\\n'+raw;}