Skip to content

Commit 49e4092

Browse files
authored
Merge pull request #10 from browser-use/sandbox
add scheduler, change featured templates
2 parents dfe7fae + 56d291e commit 49e4092

File tree

10 files changed

+793
-47
lines changed

10 files changed

+793
-47
lines changed

.github/scripts/validate_templates.py

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
def validate_json_file(templates_json_path: Path) -> dict:
1818
"""Validate templates.json is valid JSON and return parsed data."""
1919
try:
20-
with open(templates_json_path, 'r', encoding='utf-8') as f:
20+
with open(templates_json_path, "r", encoding="utf-8") as f:
2121
data = json.load(f)
2222
print(f"✓ {templates_json_path} is valid JSON")
2323
return data
@@ -34,35 +34,35 @@ def validate_template_entry(name: str, config: dict, repo_root: Path) -> list[st
3434
errors = []
3535

3636
# Check required fields
37-
if 'file' not in config:
37+
if "file" not in config:
3838
errors.append(f"Template '{name}' missing required field 'file'")
39-
if 'description' not in config:
39+
if "description" not in config:
4040
errors.append(f"Template '{name}' missing required field 'description'")
4141

42-
if 'file' not in config:
42+
if "file" not in config:
4343
return errors # Can't continue without file field
4444

4545
# Check main file exists
46-
main_file = repo_root / config['file']
46+
main_file = repo_root / config["file"]
4747
if not main_file.exists():
4848
errors.append(f"Template '{name}': main file '{config['file']}' does not exist")
4949

5050
# Validate featured field if present (optional)
51-
if 'featured' in config:
52-
if not isinstance(config['featured'], bool):
51+
if "featured" in config:
52+
if not isinstance(config["featured"], bool):
5353
errors.append(f"Template '{name}': 'featured' field must be a boolean")
5454

5555
# Validate author field if present (optional)
56-
if 'author' in config:
57-
author = config['author']
56+
if "author" in config:
57+
author = config["author"]
5858
if not isinstance(author, dict):
5959
errors.append(f"Template '{name}': 'author' field must be an object")
6060
else:
6161
# All author fields are optional, but validate types if present
6262
optional_author_fields = {
63-
'name': str,
64-
'github_profile': str,
65-
'last_modified_date': str
63+
"name": str,
64+
"github_profile": str,
65+
"last_modified_date": str,
6666
}
6767
for field, expected_type in optional_author_fields.items():
6868
if field in author and not isinstance(author[field], expected_type):
@@ -71,31 +71,34 @@ def validate_template_entry(name: str, config: dict, repo_root: Path) -> list[st
7171
)
7272

7373
# Check files array if present
74-
if 'files' in config:
75-
for file_spec in config['files']:
76-
if 'source' not in file_spec:
74+
if "files" in config:
75+
for file_spec in config["files"]:
76+
if "source" not in file_spec:
7777
errors.append(f"Template '{name}': file entry missing 'source' field")
7878
continue
79-
if 'dest' not in file_spec:
79+
if "dest" not in file_spec:
8080
errors.append(f"Template '{name}': file entry missing 'dest' field")
8181
continue
8282

83-
source_path = repo_root / file_spec['source']
83+
source_path = repo_root / file_spec["source"]
8484
if not source_path.exists():
85-
errors.append(f"Template '{name}': source file '{file_spec['source']}' does not exist")
85+
errors.append(
86+
f"Template '{name}': source file '{file_spec['source']}' does not exist"
87+
)
8688

8789
# For complex templates, check required files exist
88-
is_complex = len(config['files']) > 1
90+
is_complex = len(config["files"]) > 1
8991
if is_complex:
90-
template_dir = Path(config['file']).parent
91-
required_files = ['README.md', 'pyproject.toml.template', '.env.example.template']
92+
template_dir = Path(config["file"]).parent
93+
required_files = [
94+
"README.md",
95+
"pyproject.toml.template",
96+
".env.example.template",
97+
]
9298

9399
for required in required_files:
94100
# Check if it's in the files array
95-
found = any(
96-
Path(f['source']).name == required
97-
for f in config['files']
98-
)
101+
found = any(Path(f["source"]).name == required for f in config["files"])
99102
if not found:
100103
errors.append(
101104
f"Template '{name}': complex template missing '{required}' in files array"
@@ -106,7 +109,7 @@ def validate_template_entry(name: str, config: dict, repo_root: Path) -> list[st
106109

107110
def main():
108111
repo_root = Path(__file__).parent.parent.parent
109-
templates_json = repo_root / 'templates.json'
112+
templates_json = repo_root / "templates.json"
110113

111114
print("Validating template registry...\n")
112115

@@ -126,7 +129,7 @@ def main():
126129
print(f"✗ {error}")
127130

128131
# Summary
129-
print(f"\n{'='*60}")
132+
print(f"\n{'=' * 60}")
130133
if all_errors:
131134
print(f"Validation failed with {len(all_errors)} error(s)")
132135
sys.exit(1)
@@ -135,5 +138,5 @@ def main():
135138
sys.exit(0)
136139

137140

138-
if __name__ == '__main__':
141+
if __name__ == "__main__":
139142
main()

.github/scripts/verify_template_output.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@
1212

1313
def main():
1414
repo_root = Path(__file__).parent.parent.parent
15-
templates_json = repo_root / 'templates.json'
15+
templates_json = repo_root / "templates.json"
1616

1717
# Validate templates.json is valid JSON
1818
try:
1919
with open(templates_json) as f:
2020
templates = json.load(f)
21-
print(f"✓ templates.json is valid JSON")
21+
print("✓ templates.json is valid JSON")
2222
except json.JSONDecodeError as e:
2323
print(f"✗ templates.json is invalid JSON: {e}")
2424
sys.exit(1)
2525
except FileNotFoundError:
26-
print(f"✗ templates.json not found")
26+
print("✗ templates.json not found")
2727
sys.exit(1)
2828

2929
errors = []
@@ -33,39 +33,44 @@ def main():
3333
print(f"\nValidating template: {template_name}")
3434

3535
# Check required fields
36-
if 'file' not in config:
36+
if "file" not in config:
3737
errors.append(f"✗ {template_name}: missing 'file' field")
3838
print(errors[-1])
3939
continue
4040

41-
if 'description' not in config:
41+
if "description" not in config:
4242
errors.append(f"✗ {template_name}: missing 'description' field")
4343
print(errors[-1])
4444

4545
# Check main file exists
46-
main_file = repo_root / config['file']
46+
main_file = repo_root / config["file"]
4747
if main_file.exists():
4848
print(f" ✓ Main file exists: {config['file']}")
4949

5050
# Try to compile Python files
51-
if config['file'].endswith('.py'):
51+
if config["file"].endswith(".py"):
5252
try:
5353
import py_compile
54+
5455
py_compile.compile(main_file, doraise=True)
5556
print(f" ✓ Python file compiles: {config['file']}")
5657
except py_compile.PyCompileError as e:
57-
errors.append(f"✗ {template_name}: Python compilation error in {config['file']}: {e}")
58+
errors.append(
59+
f"✗ {template_name}: Python compilation error in {config['file']}: {e}"
60+
)
5861
print(errors[-1])
5962
else:
6063
errors.append(f"✗ {template_name}: main file not found: {config['file']}")
6164
print(errors[-1])
6265

6366
# Check all files in complex templates
64-
if 'files' in config:
65-
for file_spec in config['files']:
66-
source = file_spec.get('source')
67+
if "files" in config:
68+
for file_spec in config["files"]:
69+
source = file_spec.get("source")
6770
if not source:
68-
errors.append(f"✗ {template_name}: file spec missing 'source' field")
71+
errors.append(
72+
f"✗ {template_name}: file spec missing 'source' field"
73+
)
6974
print(errors[-1])
7075
continue
7176

@@ -74,20 +79,23 @@ def main():
7479
print(f" ✓ File exists: {source}")
7580

7681
# Try to compile Python files
77-
if source.endswith('.py'):
82+
if source.endswith(".py"):
7883
try:
7984
import py_compile
85+
8086
py_compile.compile(source_file, doraise=True)
8187
print(f" ✓ Python file compiles: {source}")
8288
except py_compile.PyCompileError as e:
83-
errors.append(f"✗ {template_name}: Python compilation error in {source}: {e}")
89+
errors.append(
90+
f"✗ {template_name}: Python compilation error in {source}: {e}"
91+
)
8492
print(errors[-1])
8593
else:
8694
errors.append(f"✗ {template_name}: source file not found: {source}")
8795
print(errors[-1])
8896

8997
# Print summary
90-
print("\n" + "="*50)
98+
print("\n" + "=" * 50)
9199
if errors:
92100
print(f"✗ Validation failed with {len(errors)} error(s)")
93101
for error in errors:
@@ -98,5 +106,5 @@ def main():
98106
sys.exit(0)
99107

100108

101-
if __name__ == '__main__':
109+
if __name__ == "__main__":
102110
main()

agentmail/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
load_dotenv()
1313

14-
from browser_use import Agent, Browser, ChatBrowserUse, models
14+
from browser_use import Agent, Browser, ChatBrowserUse
1515
from email_tools import EmailTools
1616

1717
TASK = """

scheduler/.env.example.template

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Browser-Use API Key
2+
# Get your key at: https://cloud.browser-use.com/dashboard/settings?tab=api-keys&new
3+
BROWSER_USE_API_KEY=your-key-here
4+
5+
# Cloud Profile ID (optional - for persistent authentication)
6+
# Get your profile ID from: https://cloud.browser-use.com/#settings/profiles
7+
CLOUD_PROFILE_ID=your-profile-id-here
8+
9+
# Cloud Proxy Country Code (optional - default: us)
10+
CLOUD_PROXY_COUNTRY_CODE=us
11+
12+
# Cloud Session Timeout in minutes (optional - default: 60)
13+
CLOUD_TIMEOUT=60
14+
15+
# Scheduler Interval in minutes (optional - default: 5)
16+
SCHEDULER_INTERVAL_MINUTES=5
17+
18+
# Directory containing agent scripts (optional - default: agents)
19+
# All .py files in this directory will be automatically discovered and run
20+
# Prefix files with _ to disable them (e.g., _disabled_script.py)
21+
SCHEDULER_SCRIPTS_DIR=agents

0 commit comments

Comments
 (0)