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.
|
|
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.
|
|
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
|
-
|
|
773
|
-
|
|
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
|
|
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
|
-
//
|
|
811
|
-
await new Promise(r => setTimeout(r,
|
|
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
|
-
|
|
274
|
-
|
|
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
|
-
|
|
296
|
-
|
|
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,
|