nothumanallowed 9.3.5 → 9.3.6

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.5",
3
+ "version": "9.3.6",
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": {
@@ -1189,8 +1189,22 @@ export async function cmdUI(args) {
1189
1189
  }
1190
1190
 
1191
1191
  const result = await executeTool(action, params, config);
1192
- toolResults.push({ action, result: typeof result === 'object' ? JSON.stringify(result) : String(result) });
1193
- sendSSE('tool', { action, status: 'done', result: typeof result === 'string' ? result.slice(0, 500) : '' });
1192
+ const resultStr = typeof result === 'object' ? JSON.stringify(result) : String(result);
1193
+ toolResults.push({ action, result: resultStr });
1194
+ sendSSE('tool', { action, status: 'done', result: typeof resultStr === 'string' ? resultStr.slice(0, 500) : '' });
1195
+
1196
+ // If the tool produced a screenshot (web_search with screenshot=true), send it via SSE
1197
+ if (resultStr.includes('[Screenshot of results captured')) {
1198
+ try {
1199
+ const be = await import('../services/browser-engine.mjs');
1200
+ if (be.isBrowserRunning()) {
1201
+ const ssResult = await be.browserScreenshot({ fullPage: true, format: 'png' });
1202
+ if (!ssResult.error) {
1203
+ sendSSE('screenshot', { base64: ssResult.base64, format: 'png' });
1204
+ }
1205
+ }
1206
+ } catch { /* screenshot send failed */ }
1207
+ }
1194
1208
  } catch (e) {
1195
1209
  toolResults.push({ action, result: `Error: ${e.message}` });
1196
1210
  sendSSE('tool', { action, status: 'error', error: e.message });
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.5';
8
+ export const VERSION = '9.3.6';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -267,9 +267,10 @@ TOOLS:
267
267
 
268
268
  --- WEB SEARCH & FETCH ---
269
269
 
270
- 47. web_search(query: string, deep?: boolean)
270
+ 47. web_search(query: string, deep?: boolean, screenshot?: boolean)
271
271
  Search the web using DuckDuckGo. Returns titles, URLs, and snippets.
272
272
  Set deep=true to also fetch and extract the top 3 pages' full content (slower but more detailed).
273
+ Set screenshot=true to also render the search results as a visual page and capture a screenshot (useful when the user asks to "see" results or wants a screenshot of search results).
273
274
  ALWAYS use this for ANY web search request ("search for X", "find X", "look up X", "cerca X").
274
275
  Do NOT open Google/Bing in the browser for searches — use this tool instead. It's faster and never gets blocked.
275
276
 
@@ -1288,10 +1289,36 @@ export async function executeTool(action, params, config) {
1288
1289
  if (result.error) return `Search error: ${result.message}`;
1289
1290
  if (result.results.length === 0) return `No results found for "${query}".`;
1290
1291
 
1291
- return `Web search: "${query}" — ${result.resultCount} results\n\n` +
1292
+ const textResult = `Web search: "${query}" — ${result.resultCount} results\n\n` +
1292
1293
  result.results.map((r, i) =>
1293
1294
  `${i + 1}. ${r.title}\n ${r.url}\n ${r.snippet}`
1294
1295
  ).join('\n\n');
1296
+
1297
+ // If screenshot requested, render results as HTML in browser and capture
1298
+ if (params.screenshot) {
1299
+ try {
1300
+ const be = await import('./browser-engine.mjs');
1301
+ // Ensure browser is running (open about:blank if needed)
1302
+ if (!be.isBrowserRunning()) {
1303
+ await be.browserOpen('https://example.com', { waitForLoad: true });
1304
+ }
1305
+ // Build search results HTML
1306
+ const esc = (s) => (s || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
1307
+ const htmlItems = result.results.map((r, i) =>
1308
+ `<div style="margin-bottom:24px;padding-bottom:16px;border-bottom:1px solid #3c4043"><div style="font-size:18px;color:#8ab4f8;margin-bottom:4px;font-weight:600">${i + 1}. ${esc(r.title)}</div><div style="font-size:13px;color:#bdc1c6;margin-bottom:6px">${esc(r.url)}</div><div style="font-size:14px;color:#969ba1;line-height:1.5">${esc(r.snippet)}</div></div>`
1309
+ ).join('');
1310
+ const fullHtml = `<html><head><style>body{background:#202124;color:#e8eaed;font-family:Arial,Helvetica,sans-serif;padding:24px 40px;max-width:800px;margin:0 auto}h1{font-size:22px;color:#8ab4f8;margin-bottom:24px;border-bottom:2px solid #3c4043;padding-bottom:12px}</style></head><body><h1>Search results: "${esc(query)}"</h1>${htmlItems}<div style="color:#5f6368;font-size:12px;margin-top:16px">Powered by NHA web_search — ${result.resultCount} results via DuckDuckGo</div></body></html>`;
1311
+ // Render by injecting HTML into current page via JS
1312
+ await be.browserEval(`document.open();document.write(${JSON.stringify(fullHtml)});document.close();`);
1313
+ await new Promise(r => setTimeout(r, 300));
1314
+ const ss = await be.browserScreenshot({ fullPage: true });
1315
+ if (!ss.error) {
1316
+ return textResult + `\n\n[Screenshot of results captured (${Math.round(ss.size / 1024)}KB)]`;
1317
+ }
1318
+ } catch { /* screenshot failed, return text only */ }
1319
+ }
1320
+
1321
+ return textResult;
1295
1322
  }
1296
1323
 
1297
1324
  case 'fetch_url': {