nothumanallowed 9.3.1 → 9.3.3

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.1",
3
+ "version": "9.3.3",
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.1';
8
+ export const VERSION = '9.3.3';
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
  /**
@@ -290,10 +290,12 @@ TOOLS:
290
290
  fullPage=true captures the entire scrollable page. saveTo saves to a file path (e.g. "~/screenshot.png").
291
291
  Returns base64-encoded image data. Use after browser_open to see what's on screen.
292
292
 
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.
293
+ 51. browser_click(text?: string, selector?: string, x?: number, y?: number)
294
+ Click an element on the page by visible text, CSS selector, or x/y coordinates.
295
+ PREFERRED: use text="Rifiuta tutto" or text="Submit" to click buttons/links by their visible label (case-insensitive partial match).
296
+ CSS selector: selector="#submit-btn", selector="a.nav-link"
297
+ Coordinates: x=500, y=300 for precise clicking.
298
+ Always try text first — it works for buttons, links, and any clickable element regardless of CSS structure.
297
299
 
298
300
  52. browser_type(text: string, selector?: string, clear?: boolean, delay?: number)
299
301
  Type text into an input field. If selector is provided, clicks the element first to focus it.
@@ -1129,6 +1131,7 @@ export async function executeTool(action, params, config) {
1129
1131
  if (!be.isBrowserRunning()) return 'No browser open. Use browser_open first.';
1130
1132
 
1131
1133
  const result = await be.browserClick({
1134
+ text: params.text,
1132
1135
  selector: params.selector,
1133
1136
  x: params.x,
1134
1137
  y: params.y,
@@ -84,8 +84,9 @@ input:focus,textarea:focus{border-color:var(--green3)}
84
84
  .section-title{font-size:12px;color:var(--cyan);text-transform:uppercase;letter-spacing:1px;margin-bottom:10px}
85
85
 
86
86
  /* ---- CHAT ---- */
87
- .chat{display:flex;flex-direction:column;height:calc(100vh - 52px);height:calc(100dvh - 52px)}
88
- @media(min-width:901px){.chat{height:calc(100vh - 52px)}}
87
+ .content--chat{overflow:hidden!important;padding:0!important;display:flex;flex-direction:column}
88
+ .chat{display:flex;flex-direction:column;flex:1;min-height:0;padding:16px;padding-bottom:0}
89
+ @media(min-width:901px){.content--chat{padding:0!important}}
89
90
  .chat__messages{flex:1;overflow-y:auto;padding-bottom:12px;-webkit-overflow-scrolling:touch}
90
91
  .chat__empty{text-align:center;padding:60px 16px;color:var(--dim)}
91
92
  .chat__empty-title{font-size:28px;color:var(--green);margin-bottom:12px}
@@ -100,7 +101,7 @@ input:focus,textarea:focus{border-color:var(--green3)}
100
101
  .tool-indicator--web{border-color:var(--cyan);color:var(--cyan)}
101
102
  .tool-indicator--email{border-color:var(--green3);color:var(--green)}
102
103
  .screenshot-preview{max-width:100%;border-radius:var(--r);margin:8px 0;border:1px solid var(--border)}
103
- .chat__bar{display:flex;gap:8px;padding:10px 0 0 0;border-top:1px solid var(--border);flex-shrink:0}
104
+ .chat__bar{display:flex;gap:8px;padding:10px 0 12px 0;border-top:1px solid var(--border);flex-shrink:0}
104
105
  .chat__input{flex:1;resize:none;min-height:40px;max-height:100px;padding:10px 14px}
105
106
  .chat__send{background:var(--green3);color:var(--bg);padding:10px 16px;border-radius:var(--r);font-weight:700;font-size:12px}
106
107
  .chat__send:disabled{opacity:.4}
@@ -230,6 +231,9 @@ function switchView(v) {
230
231
  var titles = {dashboard:'Dashboard',chat:'Chat',plan:'Daily Plan',tasks:'Tasks',emails:'Emails',calendar:'Calendar',drive:'Drive',contacts:'Contacts',notes:'Notes',onedrive:'OneDrive',mstodo:'Microsoft To Do',agents:'Agents',settings:'Settings'};
231
232
  var spt=document.getElementById('sidebarPageTitle');
232
233
  if(spt)spt.textContent=titles[v]||v;
234
+ // Toggle content--chat class for proper chat layout (no overflow, flex column)
235
+ var ct=document.getElementById('content');
236
+ if(ct){if(v==='chat'){ct.classList.add('content--chat')}else{ct.classList.remove('content--chat')}}
233
237
  closeSidebar();
234
238
  render();
235
239
  }
@@ -505,6 +509,8 @@ function sendChat(){
505
509
  var label=toolLabels[data.action]||data.action;
506
510
  var indicator=data.status==='executing'?'\\u23f3 '+label+'...':'\\u2705 '+label;
507
511
  if(data.status==='error')indicator='\\u274c '+label+' failed';
512
+ // Strip JSON action blocks from streamed content (they are internal tool calls, not for the user)
513
+ if(data.status==='executing'){chatHistory[streamIdx].content=chatHistory[streamIdx].content.replace(new RegExp('\\x60\\x60\\x60json[\\\\s\\\\S]*?\\x60\\x60\\x60','g'),'').trim()+'\\n';}
508
514
  chatHistory[streamIdx].content+=indicator+'\\n';
509
515
  renderMessages();
510
516
  }