Skip to content

Commit fc823f7

Browse files
authored
Merge pull request #54 from browser-use/feature/stealth
Enabled better stealth abilities
2 parents 624adac + a1616d9 commit fc823f7

File tree

6 files changed

+1945
-1389
lines changed

6 files changed

+1945
-1389
lines changed

workflows/cli.py

Lines changed: 84 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
from pathlib import Path
88

99
import typer
10-
from browser_use.browser.browser import Browser
10+
from browser_use import Browser
1111

1212
# Assuming OPENAI_API_KEY is set in the environment
1313
from langchain_openai import ChatOpenAI
14+
from patchright.async_api import async_playwright as patchright_async_playwright
1415

1516
from workflow_use.builder.service import BuilderService
1617
from workflow_use.controller.service import WorkflowController
@@ -318,90 +319,96 @@ def run_workflow_command(
318319
"""
319320
Loads and executes a workflow, prompting the user for required inputs.
320321
"""
321-
typer.echo(
322-
typer.style(f'Loading workflow from: {typer.style(str(workflow_path.resolve()), fg=typer.colors.MAGENTA)}', bold=True)
323-
)
324-
typer.echo() # Add space
325322

326-
try:
327-
# Instantiate Browser and WorkflowController for the Workflow instance
328-
# Pass llm_instance for potential agent fallbacks or agentic steps
329-
browser_instance = Browser() # Add any necessary config if required
330-
controller_instance = WorkflowController() # Add any necessary config if required
331-
workflow_obj = Workflow.load_from_file(
332-
str(workflow_path),
333-
llm=llm_instance,
334-
browser=browser_instance,
335-
controller=controller_instance,
336-
page_extraction_llm=page_extraction_llm,
323+
async def _run_workflow():
324+
typer.echo(
325+
typer.style(f'Loading workflow from: {typer.style(str(workflow_path.resolve()), fg=typer.colors.MAGENTA)}', bold=True)
337326
)
338-
except Exception as e:
339-
typer.secho(f'Error loading workflow: {e}', fg=typer.colors.RED)
340-
raise typer.Exit(code=1)
341-
342-
typer.secho('Workflow loaded successfully.', fg=typer.colors.GREEN, bold=True)
343-
344-
inputs = {}
345-
input_definitions = workflow_obj.inputs_def # Access inputs_def from the Workflow instance
346-
347-
if input_definitions: # Check if the list is not empty
348-
typer.echo() # Add space
349-
typer.echo(typer.style('Provide values for the following workflow inputs:', bold=True))
350327
typer.echo() # Add space
351328

352-
for input_def in input_definitions:
353-
var_name_styled = typer.style(input_def.name, fg=typer.colors.CYAN, bold=True)
354-
prompt_question = typer.style(f'Enter value for {var_name_styled}', bold=True)
355-
356-
var_type = input_def.type.lower() # type is a direct attribute
357-
is_required = input_def.required
358-
359-
type_info_str = f'type: {var_type}'
360-
if is_required:
361-
status_str = typer.style('required', fg=typer.colors.RED)
362-
else:
363-
status_str = typer.style('optional', fg=typer.colors.YELLOW)
364-
365-
full_prompt_text = f'{prompt_question} ({status_str}, {type_info_str})'
366-
367-
input_val = None
368-
if var_type == 'bool':
369-
input_val = typer.confirm(full_prompt_text)
370-
elif var_type == 'number':
371-
input_val = typer.prompt(full_prompt_text, type=float)
372-
elif var_type == 'string': # Default to string for other unknown types as well
373-
input_val = typer.prompt(full_prompt_text, type=str)
374-
else: # Should ideally not happen if schema is validated, but good to have a fallback
375-
typer.secho(
376-
f"Warning: Unknown type '{var_type}' for variable '{input_def.name}'. Treating as string.",
377-
fg=typer.colors.YELLOW,
378-
)
379-
input_val = typer.prompt(full_prompt_text, type=str)
380-
381-
inputs[input_def.name] = input_val
382-
typer.echo() # Add space after each prompt
383-
else:
384-
typer.echo('No input schema found in the workflow, or no properties defined. Proceeding without inputs.')
329+
try:
330+
# Instantiate Browser and WorkflowController for the Workflow instance
331+
# Pass llm_instance for potential agent fallbacks or agentic steps
332+
playwright = await patchright_async_playwright().start()
333+
334+
browser = Browser(playwright=playwright)
335+
controller_instance = WorkflowController() # Add any necessary config if required
336+
workflow_obj = Workflow.load_from_file(
337+
str(workflow_path),
338+
browser=browser,
339+
llm=llm_instance,
340+
controller=controller_instance,
341+
page_extraction_llm=page_extraction_llm,
342+
)
343+
except Exception as e:
344+
typer.secho(f'Error loading workflow: {e}', fg=typer.colors.RED)
345+
raise typer.Exit(code=1)
385346

386-
typer.echo() # Add space
387-
typer.echo(typer.style('Running workflow...', bold=True))
347+
typer.secho('Workflow loaded successfully.', fg=typer.colors.GREEN, bold=True)
348+
349+
inputs = {}
350+
input_definitions = workflow_obj.inputs_def # Access inputs_def from the Workflow instance
351+
352+
if input_definitions: # Check if the list is not empty
353+
typer.echo() # Add space
354+
typer.echo(typer.style('Provide values for the following workflow inputs:', bold=True))
355+
typer.echo() # Add space
356+
357+
for input_def in input_definitions:
358+
var_name_styled = typer.style(input_def.name, fg=typer.colors.CYAN, bold=True)
359+
prompt_question = typer.style(f'Enter value for {var_name_styled}', bold=True)
360+
361+
var_type = input_def.type.lower() # type is a direct attribute
362+
is_required = input_def.required
363+
364+
type_info_str = f'type: {var_type}'
365+
if is_required:
366+
status_str = typer.style('required', fg=typer.colors.RED)
367+
else:
368+
status_str = typer.style('optional', fg=typer.colors.YELLOW)
369+
370+
full_prompt_text = f'{prompt_question} ({status_str}, {type_info_str})'
371+
372+
input_val = None
373+
if var_type == 'bool':
374+
input_val = typer.confirm(full_prompt_text)
375+
elif var_type == 'number':
376+
input_val = typer.prompt(full_prompt_text, type=float)
377+
elif var_type == 'string': # Default to string for other unknown types as well
378+
input_val = typer.prompt(full_prompt_text, type=str)
379+
else: # Should ideally not happen if schema is validated, but good to have a fallback
380+
typer.secho(
381+
f"Warning: Unknown type '{var_type}' for variable '{input_def.name}'. Treating as string.",
382+
fg=typer.colors.YELLOW,
383+
)
384+
input_val = typer.prompt(full_prompt_text, type=str)
385+
386+
inputs[input_def.name] = input_val
387+
typer.echo() # Add space after each prompt
388+
else:
389+
typer.echo('No input schema found in the workflow, or no properties defined. Proceeding without inputs.')
388390

389-
try:
390-
# Call run on the Workflow instance
391-
# close_browser_at_end=True is the default for Workflow.run, but explicit for clarity
392-
result = asyncio.run(workflow_obj.run(inputs=inputs, close_browser_at_end=True))
391+
typer.echo() # Add space
392+
typer.echo(typer.style('Running workflow...', bold=True))
393393

394-
typer.secho('\nWorkflow execution completed!', fg=typer.colors.GREEN, bold=True)
395-
typer.echo(typer.style('Result:', bold=True))
396-
# Output the number of steps executed, similar to previous behavior
397-
typer.echo(f'{typer.style(str(len(result.step_results)), bold=True)} steps executed.')
398-
# For more detailed results, one might want to iterate through the 'result' list
399-
# and print each item, or serialize the whole list to JSON.
400-
# For now, sticking to the step count as per original output.
394+
try:
395+
# Call run on the Workflow instance
396+
# close_browser_at_end=True is the default for Workflow.run, but explicit for clarity
397+
result = await workflow_obj.run(inputs=inputs, close_browser_at_end=True)
398+
399+
typer.secho('\nWorkflow execution completed!', fg=typer.colors.GREEN, bold=True)
400+
typer.echo(typer.style('Result:', bold=True))
401+
# Output the number of steps executed, similar to previous behavior
402+
typer.echo(f'{typer.style(str(len(result.step_results)), bold=True)} steps executed.')
403+
# For more detailed results, one might want to iterate through the 'result' list
404+
# and print each item, or serialize the whole list to JSON.
405+
# For now, sticking to the step count as per original output.
406+
407+
except Exception as e:
408+
typer.secho(f'Error running workflow: {e}', fg=typer.colors.RED)
409+
raise typer.Exit(code=1)
401410

402-
except Exception as e:
403-
typer.secho(f'Error running workflow: {e}', fg=typer.colors.RED)
404-
raise typer.Exit(code=1)
411+
return asyncio.run(_run_workflow())
405412

406413

407414
@app.command(name='mcp-server', help='Starts the MCP server which expose all the created workflows as tools.')

workflows/pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ classifiers = [
1313

1414
dependencies = [
1515
"aiofiles>=24.1.0",
16-
"browser-use>=0.1.46",
16+
"browser-use>=0.2.4",
1717
"fastapi>=0.115.12",
1818
"fastmcp>=2.3.4",
1919
"typer>=0.15.3",
@@ -56,3 +56,6 @@ build-backend = "hatchling.build"
5656
include = [
5757
"workflow_use/**/*.py"
5858
]
59+
60+
[tool.hatch.metadata]
61+
allow-direct-references = true

0 commit comments

Comments
 (0)