@minded-ai/mindedjs 2.0.7 → 2.0.8-beta-2
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/dist/browserTask/README.md +419 -0
- package/dist/browserTask/browserAgent.py +632 -0
- package/dist/browserTask/captcha_isolated.png +0 -0
- package/dist/browserTask/executeBrowserTask.d.ts +12 -3
- package/dist/browserTask/executeBrowserTask.d.ts.map +1 -1
- package/dist/browserTask/executeBrowserTask.js +35 -3
- package/dist/browserTask/executeBrowserTask.js.map +1 -1
- package/dist/browserTask/executeBrowserTask.py +42 -0
- package/dist/browserTask/executeBrowserTask.ts +79 -0
- package/dist/browserTask/localBrowserTask.d.ts +21 -0
- package/dist/browserTask/localBrowserTask.d.ts.map +1 -0
- package/dist/browserTask/localBrowserTask.js +229 -0
- package/dist/browserTask/localBrowserTask.js.map +1 -0
- package/dist/browserTask/requirements.txt +8 -0
- package/dist/browserTask/setup.sh +144 -0
- package/dist/cli/index.js +0 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/internalTools/retell.d.ts +12 -0
- package/dist/internalTools/retell.d.ts.map +1 -0
- package/dist/internalTools/retell.js +54 -0
- package/dist/internalTools/retell.js.map +1 -0
- package/dist/internalTools/sendPlaceholderMessage.d.ts +14 -0
- package/dist/internalTools/sendPlaceholderMessage.d.ts.map +1 -0
- package/dist/internalTools/sendPlaceholderMessage.js +61 -0
- package/dist/internalTools/sendPlaceholderMessage.js.map +1 -0
- package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -1
- package/dist/nodes/addBrowserTaskNode.js +6 -1
- package/dist/nodes/addBrowserTaskNode.js.map +1 -1
- package/dist/nodes/addBrowserTaskRunNode.d.ts.map +1 -1
- package/dist/nodes/addBrowserTaskRunNode.js +1 -1
- package/dist/nodes/addBrowserTaskRunNode.js.map +1 -1
- package/dist/nodes/addRpaNode.d.ts +18 -0
- package/dist/nodes/addRpaNode.d.ts.map +1 -0
- package/dist/nodes/addRpaNode.js +251 -0
- package/dist/nodes/addRpaNode.js.map +1 -0
- package/dist/nodes/nodeFactory.d.ts.map +1 -1
- package/dist/nodes/nodeFactory.js +4 -0
- package/dist/nodes/nodeFactory.js.map +1 -1
- package/dist/types/Flows.types.d.ts +47 -2
- package/dist/types/Flows.types.d.ts.map +1 -1
- package/dist/types/Flows.types.js +13 -1
- package/dist/types/Flows.types.js.map +1 -1
- package/dist/utils/extractStateMemoryResponse.d.ts +5 -0
- package/dist/utils/extractStateMemoryResponse.d.ts.map +1 -0
- package/dist/utils/extractStateMemoryResponse.js +91 -0
- package/dist/utils/extractStateMemoryResponse.js.map +1 -0
- package/package.json +5 -2
- package/src/browserTask/executeBrowserTask.py +42 -0
- package/src/browserTask/executeBrowserTask.ts +36 -2
- package/src/browserTask/localBrowserTask.ts +250 -0
- package/src/index.ts +3 -0
- package/src/nodes/addBrowserTaskNode.ts +7 -2
- package/src/nodes/addBrowserTaskRunNode.ts +1 -0
- package/src/nodes/addRpaNode.ts +289 -0
- package/src/nodes/nodeFactory.ts +4 -0
- package/src/types/Flows.types.ts +49 -1
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.invokeBrowserTask = exports.destroyBrowserSession = exports.createBrowserSession = void 0;
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
4
8
|
const logger_1 = require("../utils/logger");
|
|
5
9
|
const mindedConnection_1 = require("../platform/mindedConnection");
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
6
11
|
const mindedConnectionTypes_1 = require("../platform/mindedConnectionTypes");
|
|
12
|
+
const node_util_1 = require("node:util");
|
|
13
|
+
const localBrowserTask_1 = require("./localBrowserTask");
|
|
14
|
+
const exec = (0, node_util_1.promisify)(node_child_process_1.exec);
|
|
7
15
|
// Socket-based browser task functions
|
|
8
|
-
const createBrowserSession = async (proxy, onPrem) => {
|
|
16
|
+
const createBrowserSession = async ({ sessionId, proxy, onPrem, localRun }) => {
|
|
9
17
|
logger_1.logger.debug({ msg: 'Creating browser session via socket', proxy });
|
|
18
|
+
if (localRun) {
|
|
19
|
+
const { cdpUrl } = await (0, localBrowserTask_1.getOrStartLocalCDP)({ headless: false });
|
|
20
|
+
return {
|
|
21
|
+
sessionId,
|
|
22
|
+
cdpUrl
|
|
23
|
+
};
|
|
24
|
+
}
|
|
10
25
|
const response = await mindedConnection_1.mindedConnection.awaitEmit(mindedConnectionTypes_1.mindedConnectionSocketMessageType.CREATE_BROWSER_SESSION, {
|
|
11
26
|
type: mindedConnectionTypes_1.mindedConnectionSocketMessageType.CREATE_BROWSER_SESSION,
|
|
12
27
|
proxy,
|
|
@@ -24,8 +39,14 @@ const createBrowserSession = async (proxy, onPrem) => {
|
|
|
24
39
|
return response;
|
|
25
40
|
};
|
|
26
41
|
exports.createBrowserSession = createBrowserSession;
|
|
27
|
-
const destroyBrowserSession = async (sessionId, onPrem) => {
|
|
42
|
+
const destroyBrowserSession = async ({ sessionId, onPrem, localRun }) => {
|
|
28
43
|
logger_1.logger.debug({ msg: 'Destroying browser session via socket', sessionId, onPrem });
|
|
44
|
+
if (localRun) {
|
|
45
|
+
await (0, localBrowserTask_1.kill)();
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
29
50
|
const response = await mindedConnection_1.mindedConnection.awaitEmit(mindedConnectionTypes_1.mindedConnectionSocketMessageType.DESTROY_BROWSER_SESSION, {
|
|
30
51
|
type: mindedConnectionTypes_1.mindedConnectionSocketMessageType.DESTROY_BROWSER_SESSION,
|
|
31
52
|
sessionId,
|
|
@@ -39,7 +60,7 @@ const destroyBrowserSession = async (sessionId, onPrem) => {
|
|
|
39
60
|
return response;
|
|
40
61
|
};
|
|
41
62
|
exports.destroyBrowserSession = destroyBrowserSession;
|
|
42
|
-
const invokeBrowserTask = async (sessionId, cdpUrl, task, keepAlive, hooks, onPrem, toolSchemas, outputSchema) => {
|
|
63
|
+
const invokeBrowserTask = async (sessionId, cdpUrl, task, keepAlive, hooks, onPrem, localRun, toolSchemas, outputSchema) => {
|
|
43
64
|
var _a, _b;
|
|
44
65
|
logger_1.logger.debug({
|
|
45
66
|
msg: 'Invoking browser task via socket',
|
|
@@ -51,6 +72,17 @@ const invokeBrowserTask = async (sessionId, cdpUrl, task, keepAlive, hooks, onPr
|
|
|
51
72
|
outputSchemaFields: (outputSchema === null || outputSchema === void 0 ? void 0 : outputSchema.length) || 0,
|
|
52
73
|
});
|
|
53
74
|
try {
|
|
75
|
+
if (localRun) {
|
|
76
|
+
const pythonScriptPath = path_1.default.resolve(__dirname, 'executeBrowserTask.py');
|
|
77
|
+
const command = `uv run ${pythonScriptPath} "${sessionId}" "${cdpUrl}" "${task.replace(/"/g, '\\"')}"`;
|
|
78
|
+
const { stdout, stderr } = await exec(command);
|
|
79
|
+
logger_1.logger.info({ message: 'Operator finished', stdout, stderr });
|
|
80
|
+
return {
|
|
81
|
+
result: stdout,
|
|
82
|
+
steps: [],
|
|
83
|
+
recordings: [],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
54
86
|
const response = await mindedConnection_1.mindedConnection.awaitEmit(mindedConnectionTypes_1.mindedConnectionSocketMessageType.INVOKE_BROWSER_TASK, {
|
|
55
87
|
type: mindedConnectionTypes_1.mindedConnectionSocketMessageType.INVOKE_BROWSER_TASK,
|
|
56
88
|
cdpUrl,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executeBrowserTask.js","sourceRoot":"","sources":["../../src/browserTask/executeBrowserTask.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"executeBrowserTask.js","sourceRoot":"","sources":["../../src/browserTask/executeBrowserTask.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from browser_use import Agent, BrowserSession, BrowserProfile
|
|
3
|
+
from browser_use.llm import ChatOpenAI
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
|
|
8
|
+
load_dotenv()
|
|
9
|
+
|
|
10
|
+
async def main(session_id: str, cdp_url: str, task: str):
|
|
11
|
+
llm = ChatOpenAI(
|
|
12
|
+
model="gpt-4.1",
|
|
13
|
+
api_key=os.getenv("OPENAI_API_KEY"),
|
|
14
|
+
)
|
|
15
|
+
folder_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'downloads')
|
|
16
|
+
|
|
17
|
+
# Create folder if it doesn't exist
|
|
18
|
+
os.makedirs(folder_path, exist_ok=True)
|
|
19
|
+
|
|
20
|
+
available_files = [os.path.join(folder_path, file) for file in os.listdir(folder_path)]
|
|
21
|
+
|
|
22
|
+
# Set downloads_path to the local tools/files directory
|
|
23
|
+
browser_session = BrowserSession(
|
|
24
|
+
browser_profile=BrowserProfile(downloads_path=folder_path),
|
|
25
|
+
cdp_url=cdp_url
|
|
26
|
+
)
|
|
27
|
+
agent = Agent(
|
|
28
|
+
task=task,
|
|
29
|
+
llm=llm,
|
|
30
|
+
available_file_paths=available_files,
|
|
31
|
+
browser=browser_session,
|
|
32
|
+
)
|
|
33
|
+
history = await agent.run()
|
|
34
|
+
print(history.final_result())
|
|
35
|
+
|
|
36
|
+
if __name__ == '__main__':
|
|
37
|
+
if len(sys.argv) < 3:
|
|
38
|
+
raise SystemExit("Usage: uv run uploadG2n.py <session_id> <cdp_url> <task>")
|
|
39
|
+
session_id = sys.argv[1]
|
|
40
|
+
cdp_url = sys.argv[2]
|
|
41
|
+
task = sys.argv[3]
|
|
42
|
+
asyncio.run(main(session_id, cdp_url, task))
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { logger } from '../utils/logger';
|
|
4
|
+
|
|
5
|
+
export const executeBrowserTask = async (prompt: string): Promise<string> => {
|
|
6
|
+
const pythonScriptPath = join(__dirname, 'browserAgent.py');
|
|
7
|
+
const venvPythonPath = join(__dirname, '.venv', 'bin', 'python');
|
|
8
|
+
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
// Determine which Python to use - prefer venv if it exists
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const pythonCommand = fs.existsSync(venvPythonPath) ? venvPythonPath : 'python3';
|
|
13
|
+
|
|
14
|
+
// Use Python to run our browser agent script with CAPTCHA bypass
|
|
15
|
+
const child = spawn(pythonCommand, [pythonScriptPath, '-p', prompt, '--output-format', 'json'], {
|
|
16
|
+
env: {
|
|
17
|
+
...process.env,
|
|
18
|
+
// Ensure Python can find the script and dependencies
|
|
19
|
+
PYTHONPATH: __dirname,
|
|
20
|
+
// Set virtual environment if it exists
|
|
21
|
+
VIRTUAL_ENV: fs.existsSync(join(__dirname, '.venv')) ? join(__dirname, '.venv') : undefined,
|
|
22
|
+
},
|
|
23
|
+
cwd: __dirname,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
let output = '';
|
|
27
|
+
let errorOutput = '';
|
|
28
|
+
|
|
29
|
+
// Stream stdout to console and collect it
|
|
30
|
+
child.stdout.on('data', (data) => {
|
|
31
|
+
const chunk = data.toString();
|
|
32
|
+
logger.trace({ message: 'Browser task output', output: chunk });
|
|
33
|
+
output += chunk;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Stream stderr to console and collect errors
|
|
37
|
+
child.stderr.on('data', (data) => {
|
|
38
|
+
const chunk = data.toString();
|
|
39
|
+
logger.error({ message: 'Browser task error', error: chunk });
|
|
40
|
+
errorOutput += chunk;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
child.on('error', (error) => {
|
|
44
|
+
logger.error({ message: 'Failed to start browser task process', error: error.message });
|
|
45
|
+
reject(new Error(`Failed to start browser task: ${error.message}`));
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
child.on('close', (code) => {
|
|
49
|
+
if (code !== 0) {
|
|
50
|
+
logger.error({
|
|
51
|
+
message: 'Browser task process exited with error',
|
|
52
|
+
code,
|
|
53
|
+
stderr: errorOutput,
|
|
54
|
+
});
|
|
55
|
+
reject(new Error(`Browser task failed with code ${code}: ${errorOutput}`));
|
|
56
|
+
} else {
|
|
57
|
+
try {
|
|
58
|
+
// Parse JSON output from Python script
|
|
59
|
+
const result = JSON.parse(output.trim());
|
|
60
|
+
if (result.success) {
|
|
61
|
+
logger.info({ message: 'Browser task completed successfully' });
|
|
62
|
+
resolve(result.result || 'Task completed successfully');
|
|
63
|
+
} else {
|
|
64
|
+
logger.error({ message: 'Browser task failed', error: result.error });
|
|
65
|
+
reject(new Error(result.error || 'Unknown error occurred'));
|
|
66
|
+
}
|
|
67
|
+
} catch (parseError) {
|
|
68
|
+
// Fallback to plain text output if JSON parsing fails
|
|
69
|
+
logger.warn({
|
|
70
|
+
message: 'Could not parse JSON output, using plain text',
|
|
71
|
+
output: output.trim(),
|
|
72
|
+
});
|
|
73
|
+
resolve(output.trim() || 'Task completed');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type StartChromiumOptions = {
|
|
2
|
+
/** Headless by default. Set to false to see a window. */
|
|
3
|
+
headless?: boolean;
|
|
4
|
+
/** Extra CLI flags to pass to Chromium. */
|
|
5
|
+
extraArgs?: string[];
|
|
6
|
+
/** Provide your own user-data-dir. If omitted, a temp dir is created and later removable via `kill()`. */
|
|
7
|
+
userDataDir?: string;
|
|
8
|
+
/** Extra env vars. */
|
|
9
|
+
env?: NodeJS.ProcessEnv;
|
|
10
|
+
/** How long to wait for the DevTools port to come up (ms). Default: 10000 */
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Launch Chromium with --remote-debugging-port=0 and return the CDP ws URL.
|
|
15
|
+
* Works with Chrome/Chromium/Chrome for Testing.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getOrStartLocalCDP(opts: StartChromiumOptions): Promise<{
|
|
18
|
+
cdpUrl: string;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function kill(): Promise<void>;
|
|
21
|
+
//# sourceMappingURL=localBrowserTask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localBrowserTask.d.ts","sourceRoot":"","sources":["../../src/browserTask/localBrowserTask.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,oBAAoB,GAAG;IACjC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,0GAA0G;IAC1G,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAsJ7B;AA4DD,wBAAsB,IAAI,kBAMzB"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.getOrStartLocalCDP = getOrStartLocalCDP;
|
|
27
|
+
exports.kill = kill;
|
|
28
|
+
// getOrStartLocalCDP.ts
|
|
29
|
+
const child_process_1 = require("child_process");
|
|
30
|
+
const fs = __importStar(require("fs/promises"));
|
|
31
|
+
const fscb = __importStar(require("fs"));
|
|
32
|
+
const path = __importStar(require("path"));
|
|
33
|
+
const http = __importStar(require("http"));
|
|
34
|
+
const playwright_1 = require("playwright");
|
|
35
|
+
const logger_1 = require("../utils/logger");
|
|
36
|
+
let localBrowserTask = null;
|
|
37
|
+
/**
|
|
38
|
+
* Launch Chromium with --remote-debugging-port=0 and return the CDP ws URL.
|
|
39
|
+
* Works with Chrome/Chromium/Chrome for Testing.
|
|
40
|
+
*/
|
|
41
|
+
async function getOrStartLocalCDP(opts) {
|
|
42
|
+
var _a;
|
|
43
|
+
if (localBrowserTask) {
|
|
44
|
+
return {
|
|
45
|
+
cdpUrl: localBrowserTask.cdpUrl,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const { env, timeoutMs = 10000, } = opts;
|
|
49
|
+
const executablePath = playwright_1.chromium.executablePath();
|
|
50
|
+
const profileDir = path.join(__dirname, "profile");
|
|
51
|
+
await fs.mkdir(profileDir, { recursive: true });
|
|
52
|
+
const userDataDir = (_a = opts.userDataDir) !== null && _a !== void 0 ? _a : profileDir;
|
|
53
|
+
const args = [
|
|
54
|
+
"--new-window",
|
|
55
|
+
`--user-data-dir=${userDataDir}`,
|
|
56
|
+
// "--headless",
|
|
57
|
+
"--disable-component-extensions-with-background-pages",
|
|
58
|
+
"--disable-background-networking",
|
|
59
|
+
"--disable-back-forward-cache",
|
|
60
|
+
"--disable-popup-blocking",
|
|
61
|
+
"--simulate-outdated-no-au=\"Tue, 31 Dec 2099 23:59:59 GMT\"",
|
|
62
|
+
"--disable-renderer-backgrounding",
|
|
63
|
+
"--no-service-autorun",
|
|
64
|
+
"--disable-background-timer-throttling",
|
|
65
|
+
"--allow-legacy-extension-manifests",
|
|
66
|
+
"--allow-pre-commit-input",
|
|
67
|
+
"--log-level=2",
|
|
68
|
+
"--unsafely-disable-devtools-self-xss-warnings",
|
|
69
|
+
"--metrics-recording-only",
|
|
70
|
+
"--disable-window-activation",
|
|
71
|
+
"--disable-dev-shm-usage",
|
|
72
|
+
"--disable-backgrounding-occluded-windows",
|
|
73
|
+
"--disable-search-engine-choice-screen",
|
|
74
|
+
"--disable-print-preview",
|
|
75
|
+
"--disable-external-intent-requests",
|
|
76
|
+
"--disable-desktop-notifications",
|
|
77
|
+
"--disable-component-update",
|
|
78
|
+
"--generate-pdf-document-outline",
|
|
79
|
+
"--disable-focus-on-load",
|
|
80
|
+
"--disable-speech-api",
|
|
81
|
+
"--silent-debugger-extension-api",
|
|
82
|
+
"--suppress-message-center-popups",
|
|
83
|
+
"--disable-default-apps",
|
|
84
|
+
"--enable-network-information-downlink-max",
|
|
85
|
+
"--use-mock-keychain",
|
|
86
|
+
"--disable-ipc-flooding-protection",
|
|
87
|
+
"--safebrowsing-disable-auto-update",
|
|
88
|
+
"--install-autogenerated-theme=0,0,0",
|
|
89
|
+
"--disable-blink-features=AutomationControlled",
|
|
90
|
+
"--no-first-run",
|
|
91
|
+
"--extensions-on-chrome-urls",
|
|
92
|
+
"--ash-no-nudges",
|
|
93
|
+
"--no-pings",
|
|
94
|
+
"--test-type=gpu",
|
|
95
|
+
"--disable-client-side-phishing-detection",
|
|
96
|
+
"--password-store=basic",
|
|
97
|
+
"--enable-features=NetworkService,NetworkServiceInProcess",
|
|
98
|
+
"--disable-extensions-http-throttling",
|
|
99
|
+
"--disable-speech-synthesis-api",
|
|
100
|
+
"--no-default-browser-check",
|
|
101
|
+
"--disable-hang-monitor",
|
|
102
|
+
"--hide-crash-restore-bubble",
|
|
103
|
+
"--disable-domain-reliability",
|
|
104
|
+
"--export-tagged-pdf",
|
|
105
|
+
"--disable-sync",
|
|
106
|
+
"--disable-features=AcceptCHFrame,AutoExpandDetailsElement,AvoidUnnecessaryBeforeUnloadCheckSync,CertificateTransparencyComponentUpdater,DestroyProfileOnBrowserClose,DialMediaRouteProvider,ExtensionManifestV2Disabled,GlobalMediaControls,HttpsUpgrades,ImprovedCookieControls,LazyFrameLoading,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,Translate,AutomationControlled,BackForwardCache,OptimizationHints,ProcessPerSiteUpToMainFrameThreshold,InterestFeedContentSuggestions,CalculateNativeWinOcclusion,HeavyAdPrivacyMitigations,PrivacySandboxSettings4,AutofillServerCommunication,CrashReporting,OverscrollHistoryNavigation,InfiniteSessionRestore,ExtensionDisableUnsupportedDeveloper",
|
|
107
|
+
"--disable-prompt-on-repost",
|
|
108
|
+
"--disable-infobars",
|
|
109
|
+
"--disable-datasaver-prompt",
|
|
110
|
+
"--noerrdialogs",
|
|
111
|
+
"--disable-breakpad",
|
|
112
|
+
"--disable-field-trial-config",
|
|
113
|
+
"--window-size=1920,1080",
|
|
114
|
+
"--window-position=0,0",
|
|
115
|
+
"--enable-extensions",
|
|
116
|
+
"--disable-extensions-file-access-check",
|
|
117
|
+
"--enable-extension-activity-logging",
|
|
118
|
+
`--remote-debugging-port=0`,
|
|
119
|
+
"--remote-debugging-address=0.0.0.0",
|
|
120
|
+
"about:blank",
|
|
121
|
+
];
|
|
122
|
+
const proc = (0, child_process_1.spawn)(executablePath, args, {
|
|
123
|
+
stdio: ["ignore", "ignore", "pipe"], // stderr is useful for debugging
|
|
124
|
+
env: { ...process.env, ...env },
|
|
125
|
+
});
|
|
126
|
+
// If Chromium dies early, surface the error.
|
|
127
|
+
const earlyExit = new Promise((_, reject) => {
|
|
128
|
+
proc.once("exit", (code, signal) => {
|
|
129
|
+
reject(new Error(`Chromium exited prematurely (code=${code}, signal=${signal}).`));
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
const devtoolsPortFile = path.join(userDataDir, "DevToolsActivePort");
|
|
133
|
+
// Wait for DevToolsActivePort file to appear, then read it for port + browserId.
|
|
134
|
+
const whenReady = (async () => {
|
|
135
|
+
const startedAt = Date.now();
|
|
136
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
137
|
+
try {
|
|
138
|
+
// Access succeeds once the file is written.
|
|
139
|
+
await fs.access(devtoolsPortFile, fscb.constants.F_OK);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
catch (_a) {
|
|
143
|
+
await delay(50);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// If still not there, bail.
|
|
147
|
+
await fs.access(devtoolsPortFile, fscb.constants.F_OK).catch(() => {
|
|
148
|
+
throw new Error(`Timed out after ${timeoutMs}ms waiting for DevToolsActivePort at ${devtoolsPortFile}`);
|
|
149
|
+
});
|
|
150
|
+
const raw = await fs.readFile(devtoolsPortFile, "utf8");
|
|
151
|
+
const [portLine] = raw.trim().split(/\r?\n/);
|
|
152
|
+
const port = Number(portLine);
|
|
153
|
+
if (!Number.isFinite(port)) {
|
|
154
|
+
throw new Error(`Invalid DevTools port read from file: "${portLine}"`);
|
|
155
|
+
}
|
|
156
|
+
// Optional: ping /json/version to ensure the endpoint is responsive.
|
|
157
|
+
await waitForHttpOk(`http://127.0.0.1:${port}/json/version`, timeoutMs);
|
|
158
|
+
const cdpUrl = await fetchWsFromVersionEndpoint(port, timeoutMs);
|
|
159
|
+
localBrowserTask = {
|
|
160
|
+
cdpUrl,
|
|
161
|
+
proc,
|
|
162
|
+
};
|
|
163
|
+
return { cdpUrl };
|
|
164
|
+
})();
|
|
165
|
+
const { cdpUrl } = await Promise.race([whenReady, earlyExit]);
|
|
166
|
+
logger_1.logger.info({ message: 'Local browser task started', cdpUrl });
|
|
167
|
+
return { cdpUrl };
|
|
168
|
+
}
|
|
169
|
+
// ---- helpers ----
|
|
170
|
+
function delay(ms) {
|
|
171
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
172
|
+
}
|
|
173
|
+
async function waitForHttpOk(url, timeoutMs) {
|
|
174
|
+
const startedAt = Date.now();
|
|
175
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
176
|
+
const ok = await httpGetOk(url).catch(() => false);
|
|
177
|
+
if (ok)
|
|
178
|
+
return;
|
|
179
|
+
await delay(50);
|
|
180
|
+
}
|
|
181
|
+
throw new Error(`Timed out after ${timeoutMs}ms waiting for ${url}`);
|
|
182
|
+
}
|
|
183
|
+
function httpGetOk(urlStr) {
|
|
184
|
+
return new Promise((resolve, reject) => {
|
|
185
|
+
const req = http.get(urlStr, (res) => {
|
|
186
|
+
// Drain data to allow socket reuse.
|
|
187
|
+
res.resume();
|
|
188
|
+
resolve(res.statusCode === 200);
|
|
189
|
+
});
|
|
190
|
+
req.on("error", reject);
|
|
191
|
+
req.setTimeout(3000, () => {
|
|
192
|
+
req.destroy(new Error("HTTP timeout"));
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
async function fetchWsFromVersionEndpoint(port, timeoutMs) {
|
|
197
|
+
const urlStr = `http://127.0.0.1:${port}/json/version`;
|
|
198
|
+
const body = await httpGetBody(urlStr, timeoutMs);
|
|
199
|
+
const parsed = JSON.parse(body);
|
|
200
|
+
if (!parsed.webSocketDebuggerUrl) {
|
|
201
|
+
throw new Error(`No webSocketDebuggerUrl in ${urlStr} response`);
|
|
202
|
+
}
|
|
203
|
+
return parsed.webSocketDebuggerUrl;
|
|
204
|
+
}
|
|
205
|
+
function httpGetBody(urlStr, timeoutMs) {
|
|
206
|
+
return new Promise((resolve, reject) => {
|
|
207
|
+
const req = http.get(urlStr, (res) => {
|
|
208
|
+
let data = "";
|
|
209
|
+
res.setEncoding("utf8");
|
|
210
|
+
res.on("data", (chunk) => (data += chunk));
|
|
211
|
+
res.on("end", () => {
|
|
212
|
+
if (res.statusCode === 200)
|
|
213
|
+
resolve(data);
|
|
214
|
+
else
|
|
215
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
req.on("error", reject);
|
|
219
|
+
req.setTimeout(timeoutMs, () => req.destroy(new Error("HTTP timeout")));
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async function kill() {
|
|
223
|
+
if (localBrowserTask) {
|
|
224
|
+
localBrowserTask.proc.kill("SIGTERM");
|
|
225
|
+
await delay(300);
|
|
226
|
+
localBrowserTask.proc.kill("SIGKILL");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=localBrowserTask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localBrowserTask.js","sourceRoot":"","sources":["../../src/browserTask/localBrowserTask.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Browser Agent Setup Script
|
|
4
|
+
# This script sets up the Python environment for the browser agent with CAPTCHA bypass
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "🚀 Setting up Browser Agent with CAPTCHA Bypass..."
|
|
9
|
+
|
|
10
|
+
# Get the directory where this script is located
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
cd "$SCRIPT_DIR"
|
|
13
|
+
|
|
14
|
+
# Check if Python 3 is installed
|
|
15
|
+
if ! command -v python3 &> /dev/null; then
|
|
16
|
+
echo "❌ Python 3 is not installed. Please install Python 3.11 or higher."
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Check Python version - browser-use requires Python 3.11+
|
|
21
|
+
PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
|
|
22
|
+
PYTHON_MAJOR=$(echo $PYTHON_VERSION | cut -d. -f1)
|
|
23
|
+
PYTHON_MINOR=$(echo $PYTHON_VERSION | cut -d. -f2)
|
|
24
|
+
|
|
25
|
+
if [ "$PYTHON_MAJOR" -lt 3 ] || { [ "$PYTHON_MAJOR" -eq 3 ] && [ "$PYTHON_MINOR" -lt 11 ]; }; then
|
|
26
|
+
echo "❌ Python 3.11 or higher is required for browser-use. Found Python $PYTHON_VERSION"
|
|
27
|
+
echo "💡 Please install Python 3.11+ or use a version manager like pyenv:"
|
|
28
|
+
echo " # Install pyenv (if not installed)"
|
|
29
|
+
echo " curl https://pyenv.run | bash"
|
|
30
|
+
echo " # Install and use Python 3.12"
|
|
31
|
+
echo " pyenv install 3.12.0"
|
|
32
|
+
echo " pyenv local 3.12.0"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
echo "✅ Python $PYTHON_VERSION detected"
|
|
37
|
+
|
|
38
|
+
# Check if uv is installed
|
|
39
|
+
if ! command -v uv &> /dev/null; then
|
|
40
|
+
echo "⚠️ uv is not installed. Installing uv..."
|
|
41
|
+
# Install uv
|
|
42
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
43
|
+
|
|
44
|
+
# Source the shell to get uv in PATH
|
|
45
|
+
export PATH="$HOME/.cargo/bin:$PATH"
|
|
46
|
+
|
|
47
|
+
# Verify installation
|
|
48
|
+
if ! command -v uv &> /dev/null; then
|
|
49
|
+
echo "❌ Failed to install uv. Please install manually:"
|
|
50
|
+
echo " curl -LsSf https://astral.sh/uv/install.sh | sh"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
echo "✅ uv detected"
|
|
56
|
+
|
|
57
|
+
echo "🐍 Setting up Python environment with uv..."
|
|
58
|
+
|
|
59
|
+
# Create virtual environment with uv (if not exists)
|
|
60
|
+
if [ ! -d ".venv" ]; then
|
|
61
|
+
echo "🐍 Creating virtual environment..."
|
|
62
|
+
|
|
63
|
+
# Try Python 3.12 first, then fall back to 3.11
|
|
64
|
+
if uv venv --python 3.12 2>/dev/null; then
|
|
65
|
+
echo "✅ Created virtual environment with Python 3.12"
|
|
66
|
+
elif uv venv --python 3.11 2>/dev/null; then
|
|
67
|
+
echo "✅ Created virtual environment with Python 3.11"
|
|
68
|
+
else
|
|
69
|
+
echo "❌ Failed to create virtual environment with Python 3.11+. Please check your Python installation."
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
else
|
|
73
|
+
echo "✅ Virtual environment already exists"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
echo "📦 Installing Python dependencies with uv..."
|
|
77
|
+
|
|
78
|
+
# Install Python dependencies using uv
|
|
79
|
+
uv pip install -r requirements.txt
|
|
80
|
+
|
|
81
|
+
# Install playwright browsers
|
|
82
|
+
echo "🌐 Installing Playwright browsers..."
|
|
83
|
+
uv run playwright install
|
|
84
|
+
|
|
85
|
+
# Check if tesseract is installed (required for OCR)
|
|
86
|
+
if ! command -v tesseract &> /dev/null; then
|
|
87
|
+
echo "⚠️ Tesseract OCR is not installed. Installing..."
|
|
88
|
+
|
|
89
|
+
# Detect OS and install tesseract
|
|
90
|
+
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
91
|
+
# Linux
|
|
92
|
+
if command -v apt-get &> /dev/null; then
|
|
93
|
+
sudo apt-get update
|
|
94
|
+
sudo apt-get install -y tesseract-ocr tesseract-ocr-eng
|
|
95
|
+
elif command -v yum &> /dev/null; then
|
|
96
|
+
sudo yum install -y tesseract tesseract-langpack-eng
|
|
97
|
+
elif command -v dnf &> /dev/null; then
|
|
98
|
+
sudo dnf install -y tesseract tesseract-langpack-eng
|
|
99
|
+
else
|
|
100
|
+
echo "❌ Could not install tesseract automatically. Please install it manually."
|
|
101
|
+
echo " For Ubuntu/Debian: sudo apt-get install tesseract-ocr"
|
|
102
|
+
echo " For CentOS/RHEL: sudo yum install tesseract"
|
|
103
|
+
fi
|
|
104
|
+
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
|
105
|
+
# macOS
|
|
106
|
+
if command -v brew &> /dev/null; then
|
|
107
|
+
brew install tesseract
|
|
108
|
+
else
|
|
109
|
+
echo "❌ Homebrew not found. Please install tesseract manually:"
|
|
110
|
+
echo " brew install tesseract"
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
113
|
+
else
|
|
114
|
+
echo "❌ Unsupported OS. Please install tesseract manually."
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# Verify tesseract installation
|
|
120
|
+
if command -v tesseract &> /dev/null; then
|
|
121
|
+
TESSERACT_VERSION=$(tesseract --version | head -n1)
|
|
122
|
+
echo "✅ $TESSERACT_VERSION detected"
|
|
123
|
+
else
|
|
124
|
+
echo "❌ Tesseract installation failed or not found in PATH"
|
|
125
|
+
exit 1
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Make the Python script executable
|
|
129
|
+
chmod +x browserAgent.py
|
|
130
|
+
|
|
131
|
+
echo "🎉 Setup completed successfully!"
|
|
132
|
+
echo ""
|
|
133
|
+
echo "📋 Next steps:"
|
|
134
|
+
echo "1. Activate the virtual environment:"
|
|
135
|
+
echo " source .venv/bin/activate"
|
|
136
|
+
echo ""
|
|
137
|
+
echo "2. Set your OpenAI API key in environment variables:"
|
|
138
|
+
echo " export OPENAI_API_KEY='your-api-key-here'"
|
|
139
|
+
echo ""
|
|
140
|
+
echo "3. Test the browser agent:"
|
|
141
|
+
echo " uv run python browserAgent.py -p 'Navigate to google.com and search for browser automation'"
|
|
142
|
+
echo " # Or with activated venv: python browserAgent.py -p 'Navigate to google.com'"
|
|
143
|
+
echo ""
|
|
144
|
+
echo "✨ The browser agent is now ready with CAPTCHA bypass capabilities!"
|
package/dist/cli/index.js
CHANGED
|
File without changes
|
package/dist/index.d.ts
CHANGED
|
@@ -13,8 +13,8 @@ export { createParallelWrapper } from './platform/models/parallelWrapper';
|
|
|
13
13
|
export type { MindedChatOpenAIFields, BaseParallelChatFields } from './platform/models/mindedChatOpenAI';
|
|
14
14
|
export type { BaseParallelChatFields as ParallelWrapperFields } from './platform/models/parallelWrapper';
|
|
15
15
|
export type { PIIGatewayInstance, HttpRequestConfig, HttpResponse } from './platform/piiGateway';
|
|
16
|
-
export type { Flow, Node, Edge, TriggerNode, AppTriggerNode, PromptNode, PromptConditionEdge, LogicalConditionEdge, StepForwardEdge, ManualTriggerNode, JunctionNode, ToolNode, AppToolNode, VoiceTriggerNode, WebhookTriggerNode, InterfaceTriggerNode, } from './types/Flows.types';
|
|
17
|
-
export { NodeType, TriggerType, EdgeType, AppNodeMetadata, AppNodeMetadataType, NodeMetadata, KnownTriggerNames, } from './types/Flows.types';
|
|
16
|
+
export type { Flow, Node, Edge, TriggerNode, AppTriggerNode, PromptNode, PromptConditionEdge, LogicalConditionEdge, StepForwardEdge, ManualTriggerNode, JunctionNode, ToolNode, AppToolNode, VoiceTriggerNode, WebhookTriggerNode, InterfaceTriggerNode, RpaNode, RpaStep, } from './types/Flows.types';
|
|
17
|
+
export { NodeType, TriggerType, EdgeType, AppNodeMetadata, AppNodeMetadataType, NodeMetadata, KnownTriggerNames, RpaActionType, } from './types/Flows.types';
|
|
18
18
|
export type { Tool, ToolExecuteInput } from './types/Tools.types';
|
|
19
19
|
export { DocumentProcessor, extractFromDocument } from './internalTools/documentExtraction/documentExtraction';
|
|
20
20
|
export type { DocumentProcessorConfig, DocumentExtractionOptions, DocumentProcessingResult, } from './internalTools/documentExtraction/types';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,MAAM,MAAM,UAAU,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,kCAAkC,CAAC;AAErF,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,KAAK,EACL,MAAM,EACN,MAAM,EACN,sBAAsB,EACtB,UAAU,EACV,WAAW,EACX,OAAO,EACP,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,iCAAiC,GAClC,CAAC;AAGF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,YAAY,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACzG,YAAY,EAAE,sBAAsB,IAAI,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAGzG,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEjG,YAAY,EACV,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,WAAW,EACX,cAAc,EACd,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|