nothumanallowed 9.3.2 → 9.3.4

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.2",
3
+ "version": "9.3.4",
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": {
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.2';
8
+ export const VERSION = '9.3.4';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -532,12 +532,17 @@ async function launchBrowser() {
532
532
 
533
533
  const port = await findFreePort();
534
534
 
535
+ // Realistic user agent to avoid bot detection on major sites
536
+ const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
537
+
535
538
  const chromeArgs = [
536
539
  `--remote-debugging-port=${port}`,
537
540
  '--headless=new',
538
541
  `--user-data-dir=${userDataDir}`,
542
+ `--user-agent=${UA}`,
539
543
  '--no-first-run',
540
544
  '--no-default-browser-check',
545
+ '--disable-blink-features=AutomationControlled',
541
546
  '--disable-background-networking',
542
547
  '--disable-background-timer-throttling',
543
548
  '--disable-backgrounding-occluded-windows',
@@ -603,6 +608,16 @@ async function launchBrowser() {
603
608
  await ws.send('Network.enable');
604
609
  await ws.send('DOM.enable');
605
610
 
611
+ // Anti-detection: remove navigator.webdriver flag and other headless indicators
612
+ await ws.send('Page.addScriptToEvaluateOnNewDocument', {
613
+ source: `
614
+ Object.defineProperty(navigator, 'webdriver', { get: () => false });
615
+ Object.defineProperty(navigator, 'languages', { get: () => ['it-IT', 'it', 'en-US', 'en'] });
616
+ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
617
+ window.chrome = { runtime: {} };
618
+ `,
619
+ });
620
+
606
621
  _browser = { process: proc, ws, wsUrl, userDataDir, port };
607
622
 
608
623
  // Cleanup on process exit
@@ -756,10 +771,11 @@ export async function browserScreenshot(options = {}) {
756
771
  }
757
772
 
758
773
  /**
759
- * Click an element by CSS selector or coordinates.
774
+ * Click an element by CSS selector, visible text, or coordinates.
760
775
  *
761
776
  * @param {object} target
762
777
  * @param {string} [target.selector] - CSS selector to click
778
+ * @param {string} [target.text] - Visible text of a button/link/element to click (case-insensitive partial match)
763
779
  * @param {number} [target.x] - X coordinate
764
780
  * @param {number} [target.y] - Y coordinate
765
781
  * @returns {Promise<{ clicked: boolean, selector?: string }>}
@@ -768,9 +784,44 @@ export async function browserClick(target) {
768
784
  const browser = await getBrowser();
769
785
 
770
786
  let x, y;
787
+ let label = '';
788
+
789
+ if (target.text) {
790
+ // Find element by visible text — searches buttons, links, and all clickable elements
791
+ const searchText = target.text;
792
+ const result = await browser.ws.send('Runtime.evaluate', {
793
+ expression: `(() => {
794
+ const searchText = ${JSON.stringify(searchText)}.toLowerCase();
795
+ // Search in priority order: buttons, links, then any visible element
796
+ const candidates = [
797
+ ...document.querySelectorAll('button, [role="button"], input[type="submit"], input[type="button"]'),
798
+ ...document.querySelectorAll('a'),
799
+ ...document.querySelectorAll('[onclick], [tabindex]'),
800
+ ];
801
+ for (const el of candidates) {
802
+ const text = (el.textContent || el.value || el.getAttribute('aria-label') || '').trim();
803
+ if (text.toLowerCase().includes(searchText) && el.offsetHeight > 0 && el.offsetWidth > 0) {
804
+ const rect = el.getBoundingClientRect();
805
+ if (rect.width > 0 && rect.height > 0) {
806
+ return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2, tag: el.tagName, text: text.slice(0, 60) };
807
+ }
808
+ }
809
+ }
810
+ return null;
811
+ })()`,
812
+ returnByValue: true,
813
+ });
771
814
 
772
- if (target.selector) {
773
- // Find element and get its center point
815
+ const info = result.result?.value;
816
+ if (!info) {
817
+ return { error: true, message: `No clickable element found with text "${searchText}". Try browser_extract to see visible buttons.` };
818
+ }
819
+
820
+ x = Math.round(info.x);
821
+ y = Math.round(info.y);
822
+ label = `"${info.text}" (${info.tag})`;
823
+ } else if (target.selector) {
824
+ // Find element by CSS selector and get its center point
774
825
  const result = await browser.ws.send('Runtime.evaluate', {
775
826
  expression: `(() => {
776
827
  const el = document.querySelector(${JSON.stringify(target.selector)});
@@ -789,11 +840,13 @@ export async function browserClick(target) {
789
840
 
790
841
  x = Math.round(info.x);
791
842
  y = Math.round(info.y);
843
+ label = target.selector;
792
844
  } else if (target.x !== undefined && target.y !== undefined) {
793
845
  x = target.x;
794
846
  y = target.y;
847
+ label = `(${x}, ${y})`;
795
848
  } else {
796
- return { error: true, message: 'Provide a CSS selector or x/y coordinates' };
849
+ return { error: true, message: 'Provide text, CSS selector, or x/y coordinates' };
797
850
  }
798
851
 
799
852
  // Dispatch mouse events: move → down → up (simulates real click)
@@ -807,10 +860,25 @@ export async function browserClick(target) {
807
860
  type: 'mouseReleased', x, y, button: 'left', clickCount: 1,
808
861
  });
809
862
 
810
- // Brief wait for any navigation or JS handlers
811
- await new Promise(r => setTimeout(r, 300));
863
+ // Wait for any navigation or JS handlers triggered by the click
864
+ await new Promise(r => setTimeout(r, 500));
865
+
866
+ // Check if the click triggered a navigation — wait for it
867
+ try {
868
+ const navCheck = await browser.ws.send('Runtime.evaluate', {
869
+ expression: 'document.readyState',
870
+ returnByValue: true,
871
+ });
872
+ if (navCheck.result?.value === 'loading') {
873
+ // Page is navigating — wait for load
874
+ try {
875
+ await browser.ws.waitForEvent('Page.loadEventFired', 10000);
876
+ await new Promise(r => setTimeout(r, 500));
877
+ } catch { /* timeout ok */ }
878
+ }
879
+ } catch { /* evaluation failed during navigation, that's fine */ }
812
880
 
813
- return { error: false, clicked: true, x, y, selector: target.selector || `(${x}, ${y})` };
881
+ return { error: false, clicked: true, x, y, selector: label || target.selector || `(${x}, ${y})` };
814
882
  }
815
883
 
816
884
  /**
@@ -270,8 +270,8 @@ TOOLS:
270
270
  47. web_search(query: string, deep?: 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
- Use this when the user asks about current events, recent news, or needs up-to-date information
274
- that you don't have in your training data.
273
+ ALWAYS use this for ANY web search request ("search for X", "find X", "look up X", "cerca X").
274
+ Do NOT open Google/Bing in the browser for searches — use this tool instead. It's faster and never gets blocked.
275
275
 
276
276
  48. fetch_url(url: string)
277
277
  Fetch a web page and extract its text content. SSRF-protected (blocks private IPs, localhost).
@@ -284,16 +284,19 @@ TOOLS:
284
284
  Open a URL in a headless Chrome browser. Launches Chrome automatically on first use.
285
285
  SSRF-protected (blocks private IPs, localhost). Renders JavaScript, SPAs, and dynamic pages.
286
286
  Use this when you need to interact with a page (click, type, screenshot) or when fetch_url fails on JS-rendered content.
287
+ WARNING: Do NOT use this to search the web — use web_search instead. Google/Bing block automated browsers with CAPTCHAs.
287
288
 
288
289
  50. browser_screenshot(fullPage?: boolean, format?: "png"|"jpeg"|"webp", quality?: number, saveTo?: string)
289
290
  Capture a screenshot of the current browser page.
290
291
  fullPage=true captures the entire scrollable page. saveTo saves to a file path (e.g. "~/screenshot.png").
291
292
  Returns base64-encoded image data. Use after browser_open to see what's on screen.
292
293
 
293
- 51. browser_click(selector?: string, x?: number, y?: number)
294
- Click an element on the page by CSS selector or x/y coordinates.
295
- Examples: selector="#submit-btn", selector="a.nav-link", selector="button:nth-of-type(2)"
296
- Use coordinates for precise clicking when selector doesn't work.
294
+ 51. browser_click(text?: string, selector?: string, x?: number, y?: number)
295
+ Click an element on the page by visible text, CSS selector, or x/y coordinates.
296
+ PREFERRED: use text="Rifiuta tutto" or text="Submit" to click buttons/links by their visible label (case-insensitive partial match).
297
+ CSS selector: selector="#submit-btn", selector="a.nav-link"
298
+ Coordinates: x=500, y=300 for precise clicking.
299
+ Always try text first — it works for buttons, links, and any clickable element regardless of CSS structure.
297
300
 
298
301
  52. browser_type(text: string, selector?: string, clear?: boolean, delay?: number)
299
302
  Type text into an input field. If selector is provided, clicks the element first to focus it.
@@ -328,6 +331,7 @@ TOOLS:
328
331
  Close the browser. Frees resources. Browser auto-closes when NHA exits.
329
332
 
330
333
  RULES:
334
+ - CRITICAL: For web searches ("search for X", "find X online", "look up X"), ALWAYS use web_search — NEVER open Google/Bing/DuckDuckGo in the browser. web_search is faster, more reliable, and doesn't get blocked by CAPTCHAs. Only use browser_open for interacting with specific websites (filling forms, clicking buttons, taking screenshots of specific pages).
331
335
  - For search/read operations, execute immediately and present results conversationally.
332
336
  - For write/send/delete operations (gmail_send, gmail_reply, gmail_delete, calendar_create, calendar_move, calendar_update, contact_delete, task_done, notify_remind), DESCRIBE what you're about to do and include the JSON block so the system can ask the user for confirmation.
333
337
  - For schedule_meeting and schedule_draft_email, execute immediately — these are read operations that suggest slots.
@@ -1129,6 +1133,7 @@ export async function executeTool(action, params, config) {
1129
1133
  if (!be.isBrowserRunning()) return 'No browser open. Use browser_open first.';
1130
1134
 
1131
1135
  const result = await be.browserClick({
1136
+ text: params.text,
1132
1137
  selector: params.selector,
1133
1138
  x: params.x,
1134
1139
  y: params.y,