create-browser-app 0.1.11__tar.gz → 0.1.13__tar.gz

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.
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: create-browser-app
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: Start your Stagehand project with a single command
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
- Requires-Dist: stagehand
7
+ Requires-Dist: stagehand>=3.4.0
8
8
  Requires-Dist: click>=8.0
9
9
  Requires-Dist: rich>=13.0
10
10
  Requires-Dist: inquirer>=3.0
@@ -10,13 +10,13 @@ from rich.panel import Panel
10
10
  from rich.text import Text
11
11
  import shutil
12
12
  from .templates import TEMPLATE_BASIC, TEMPLATE_REQUIREMENTS, TEMPLATE_ENV, TEMPLATE_README, TEMPLATE_GITIGNORE
13
- from .template_fetcher import get_template_by_name, fetch_template_content
13
+ from .template_fetcher import fetch_all_template_files
14
14
 
15
15
  console = Console()
16
16
 
17
17
  @click.command()
18
18
  @click.argument('name', default='my-stagehand-app', required=False)
19
- @click.option('--template', '-t', default='basic', help='Template to use (basic or GitHub examples: quickstart, example, agent-example)')
19
+ @click.option('--template', '-t', default='basic', help='Template to use (basic or dynamically fetched from GitHub)')
20
20
  def main(name, template):
21
21
  """Start your Stagehand project with a single command"""
22
22
 
@@ -36,7 +36,11 @@ def main(name, template):
36
36
  console.print("[yellow]Stagehand[/yellow]")
37
37
  console.print("[dim]The AI Browser Framework[/dim]\n")
38
38
 
39
- project_name = name
39
+ # If user specified a template but not a custom name, use the template name
40
+ if name == 'my-stagehand-app' and template != 'basic':
41
+ project_name = template
42
+ else:
43
+ project_name = name
40
44
 
41
45
  # Sanitize project name for directory
42
46
  project_dir = project_name.lower().replace(" ", "-").replace("_", "-")
@@ -54,40 +58,40 @@ def main(name, template):
54
58
  # Create directories
55
59
  project_path.mkdir(parents=True, exist_ok=True)
56
60
 
57
- # Determine which template to use
58
- template_content = TEMPLATE_BASIC
59
-
60
- # If not using basic template, try to fetch from GitHub
61
+ # Fetch template files from GitHub (if not basic)
62
+ template_files = {}
61
63
  if template != 'basic':
62
64
  console.print(f"Fetching template [cyan]{template}[/cyan] from GitHub...")
63
- template_info = get_template_by_name(template)
64
- if template_info:
65
- fetched_content = fetch_template_content(template_info)
66
- if fetched_content:
67
- template_content = fetched_content
68
- console.print(f"[green]✓[/green] Using template from GitHub: {template}")
69
- else:
70
- console.print(f"[yellow]⚠[/yellow] Could not fetch template, using basic template")
65
+ template_files = fetch_all_template_files(template)
66
+ if template_files:
67
+ console.print(f"[green]✓[/green] Fetched {len(template_files)} files from GitHub: {template}")
71
68
  else:
72
- console.print(f"[yellow]⚠[/yellow] Template '{template}' not found, using basic template")
69
+ console.print(f"[yellow]⚠[/yellow] Could not fetch template, using basic template")
73
70
 
74
- # Create main.py
71
+ # Create main.py (from template or default)
75
72
  main_file = project_path / "main.py"
76
- main_file.write_text(template_content)
77
-
78
- # Create requirements.txt
79
- requirements_file = project_path / "requirements.txt"
80
- requirements_file.write_text(TEMPLATE_REQUIREMENTS)
73
+ main_file.write_text(template_files.get("main.py", TEMPLATE_BASIC))
81
74
 
82
- # Create .env.example
83
- env_file = project_path / ".env.example"
84
- env_file.write_text(TEMPLATE_ENV)
75
+ # Create requirements.txt or pyproject.toml (prefer template's version)
76
+ if "pyproject.toml" in template_files:
77
+ (project_path / "pyproject.toml").write_text(template_files["pyproject.toml"])
78
+ elif "requirements.txt" in template_files:
79
+ (project_path / "requirements.txt").write_text(template_files["requirements.txt"])
80
+ else:
81
+ (project_path / "requirements.txt").write_text(TEMPLATE_REQUIREMENTS)
85
82
 
86
- # Create README.md
83
+ # Create README.md (prefer template's version)
87
84
  readme_file = project_path / "README.md"
88
- readme_file.write_text(TEMPLATE_README.format(project_name=project_name))
85
+ if "README.md" in template_files:
86
+ readme_file.write_text(template_files["README.md"])
87
+ else:
88
+ readme_file.write_text(TEMPLATE_README.format(project_name=project_name))
89
+
90
+ # Create .env.example (prefer template's version)
91
+ env_file = project_path / ".env.example"
92
+ env_file.write_text(template_files.get(".env.example", TEMPLATE_ENV))
89
93
 
90
- # Create .gitignore
94
+ # Create .gitignore (always use default)
91
95
  gitignore_file = project_path / ".gitignore"
92
96
  gitignore_file.write_text(TEMPLATE_GITIGNORE)
93
97
 
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python3
2
+ import requests
3
+ from typing import Dict, List, Optional
4
+ from dataclasses import dataclass
5
+
6
+ @dataclass
7
+ class TemplateInfo:
8
+ name: str
9
+ url: str
10
+
11
+ def fetch_github_templates() -> List[str]:
12
+ """Fetch list of Python templates from GitHub API."""
13
+ try:
14
+ response = requests.get(
15
+ "https://api.github.com/repos/browserbase/templates/contents/python",
16
+ headers={"User-Agent": "create-browser-app-py"},
17
+ timeout=10
18
+ )
19
+
20
+ if response.status_code != 200:
21
+ raise Exception(f"GitHub API returned status {response.status_code}")
22
+
23
+ data = response.json()
24
+ if not isinstance(data, list):
25
+ raise Exception("Invalid response format from GitHub API")
26
+
27
+ # Filter directories and exclude README.md
28
+ template_names = []
29
+ for item in data:
30
+ if (isinstance(item, dict) and
31
+ item.get("type") == "dir" and
32
+ item.get("name") != "README.md"):
33
+ template_names.append(item["name"])
34
+
35
+ return sorted(template_names)
36
+
37
+ except Exception as e:
38
+ # Re-raise with more context for debugging
39
+ raise Exception(f"Failed to fetch templates from GitHub: {e}")
40
+
41
+ def get_template_by_name(name: str) -> Optional[TemplateInfo]:
42
+ """Get a specific template by name."""
43
+ # Normalize to lowercase to match GitHub directory naming convention
44
+ normalized_name = name.lower()
45
+ url = f"https://raw.githubusercontent.com/browserbase/templates/refs/heads/dev/python/{normalized_name}/main.py"
46
+ return TemplateInfo(name=normalized_name, url=url)
47
+
48
+ def fetch_template_content(template: TemplateInfo) -> Optional[str]:
49
+ """Fetch the content of a specific template from GitHub."""
50
+ try:
51
+ response = requests.get(
52
+ template.url,
53
+ headers={"User-Agent": "create-browser-app-py"},
54
+ timeout=10
55
+ )
56
+ if response.status_code != 200:
57
+ return None
58
+ return response.text
59
+ except Exception:
60
+ return None
61
+
62
+ def fetch_template_files(template_name: str) -> List[Dict]:
63
+ """Fetch list of files in a template directory from GitHub API."""
64
+ normalized_name = template_name.lower()
65
+ try:
66
+ response = requests.get(
67
+ f"https://api.github.com/repos/browserbase/templates/contents/python/{normalized_name}",
68
+ headers={"User-Agent": "create-browser-app-py"},
69
+ timeout=10
70
+ )
71
+ if response.status_code != 200:
72
+ return []
73
+
74
+ data = response.json()
75
+ if not isinstance(data, list):
76
+ return []
77
+
78
+ # Return file info for each file (not directories)
79
+ files = []
80
+ for item in data:
81
+ if isinstance(item, dict) and item.get("type") == "file":
82
+ files.append({
83
+ "name": item["name"],
84
+ "download_url": item.get("download_url")
85
+ })
86
+ return files
87
+ except Exception:
88
+ return []
89
+
90
+ def fetch_all_template_files(template_name: str) -> Dict[str, str]:
91
+ """Fetch all files from a template directory.
92
+
93
+ Returns dict mapping filename -> content.
94
+ """
95
+ files = fetch_template_files(template_name)
96
+ if not files:
97
+ return {}
98
+
99
+ result = {}
100
+ for file_info in files:
101
+ name = file_info["name"]
102
+ download_url = file_info.get("download_url")
103
+ if download_url:
104
+ try:
105
+ response = requests.get(
106
+ download_url,
107
+ headers={"User-Agent": "create-browser-app-py"},
108
+ timeout=10
109
+ )
110
+ if response.status_code == 200:
111
+ result[name] = response.text
112
+ except Exception:
113
+ pass # Skip files that fail to download
114
+
115
+ return result
116
+
117
+ def get_available_templates() -> List[str]:
118
+ """Get templates from GitHub API, fallback to basic only."""
119
+ try:
120
+ github_templates = fetch_github_templates()
121
+ return ["basic"] + github_templates
122
+ except Exception:
123
+ return ["basic"] # Fallback when GitHub unreachable
124
+
125
+ def list_templates() -> List[str]:
126
+ """Get a list of template names."""
127
+ return get_available_templates()
@@ -0,0 +1,156 @@
1
+ TEMPLATE_BASIC = '''import asyncio
2
+ import os
3
+
4
+ from dotenv import load_dotenv
5
+ from pydantic import BaseModel
6
+ from stagehand import AsyncStagehand
7
+
8
+ load_dotenv()
9
+
10
+
11
+ class StagehandDescription(BaseModel):
12
+ answer: str
13
+
14
+
15
+ class ModelInfo(BaseModel):
16
+ name: str
17
+ provider: str = ""
18
+ accuracy: float = 0.0
19
+
20
+
21
+ async def main():
22
+ """Basic Stagehand browser automation example."""
23
+
24
+ client = AsyncStagehand(
25
+ browserbase_api_key=os.getenv("BROWSERBASE_API_KEY"),
26
+ browserbase_project_id=os.getenv("BROWSERBASE_PROJECT_ID"),
27
+ model_api_key=os.getenv("MODEL_API_KEY"),
28
+ )
29
+
30
+ # Start a Stagehand session
31
+ session = await client.sessions.create(model_name="google/gemini-3-flash-preview")
32
+ print(f"Session started: {session.id}")
33
+ print(f"View live: https://www.browserbase.com/sessions/{session.id}")
34
+
35
+ try:
36
+ # Navigate to a webpage
37
+ await session.navigate(url="https://docs.stagehand.dev")
38
+
39
+ # Extract data using Stagehand AI
40
+ result = await session.extract(
41
+ instruction="In a few words, what is Stagehand?",
42
+ schema=StagehandDescription.model_json_schema(),
43
+ )
44
+ print(f"Extracted: {result.data.result}")
45
+
46
+ # Perform AI-powered action
47
+ await session.act(input="click on models")
48
+
49
+ # Observe elements on the page
50
+ observe_response = await session.observe(
51
+ instruction="find the graph with the list of most accurate models",
52
+ )
53
+ print(f"Found {len(observe_response.data.result)} elements")
54
+
55
+ # Extract structured data
56
+ result = await session.extract(
57
+ instruction="the most accurate model",
58
+ schema=ModelInfo.model_json_schema(),
59
+ )
60
+ print(f"Most accurate model: {result.data.result}")
61
+
62
+ finally:
63
+ await session.end()
64
+ print("Session ended")
65
+
66
+
67
+ if __name__ == "__main__":
68
+ asyncio.run(main())
69
+ '''
70
+
71
+ TEMPLATE_REQUIREMENTS = '''stagehand>=3.4.0
72
+ pydantic
73
+ python-dotenv
74
+ '''
75
+
76
+ TEMPLATE_ENV = '''# Browserbase credentials (required)
77
+ BROWSERBASE_API_KEY=your_api_key_here
78
+ BROWSERBASE_PROJECT_ID=your_project_id_here
79
+
80
+ # LLM API key (required - using Google Gemini by default)
81
+ MODEL_API_KEY=your_api_key_here
82
+ '''
83
+
84
+ TEMPLATE_README = '''# {project_name}
85
+
86
+ A Stagehand browser automation project.
87
+
88
+ ## Setup
89
+
90
+ 1. Install dependencies:
91
+ ```bash
92
+ pip install -r requirements.txt
93
+ ```
94
+
95
+ 2. Configure your environment variables in `.env`:
96
+ ```bash
97
+ BROWSERBASE_API_KEY=your_api_key_here
98
+ BROWSERBASE_PROJECT_ID=your_project_id_here
99
+ MODEL_API_KEY=your_api_key_here
100
+ ```
101
+
102
+ 3. Run the project:
103
+ ```bash
104
+ python main.py
105
+ ```
106
+
107
+ ## About Stagehand
108
+
109
+ Stagehand is the AI browser automation framework. Learn more at:
110
+ - [Stagehand Documentation](https://docs.stagehand.dev/v3/sdk/python)
111
+ - [Browserbase](https://browserbase.com)
112
+ '''
113
+
114
+ TEMPLATE_GITIGNORE = '''# Python
115
+ __pycache__/
116
+ *.py[cod]
117
+ *$py.class
118
+ *.so
119
+ .Python
120
+ build/
121
+ develop-eggs/
122
+ dist/
123
+ downloads/
124
+ eggs/
125
+ .eggs/
126
+ lib/
127
+ lib64/
128
+ parts/
129
+ sdist/
130
+ var/
131
+ wheels/
132
+ *.egg-info/
133
+ .installed.cfg
134
+ *.egg
135
+
136
+ # Virtual Environment
137
+ venv/
138
+ ENV/
139
+ env/
140
+ .venv
141
+
142
+ # Environment variables
143
+ .env
144
+ .env.local
145
+
146
+ # IDE
147
+ .vscode/
148
+ .idea/
149
+ *.swp
150
+ *.swo
151
+ *~
152
+ .DS_Store
153
+
154
+ # Logs
155
+ *.log
156
+ '''
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: create-browser-app
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: Start your Stagehand project with a single command
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
- Requires-Dist: stagehand
7
+ Requires-Dist: stagehand>=3.4.0
8
8
  Requires-Dist: click>=8.0
9
9
  Requires-Dist: rich>=13.0
10
10
  Requires-Dist: inquirer>=3.0
@@ -1,4 +1,4 @@
1
- stagehand
1
+ stagehand>=3.4.0
2
2
  click>=8.0
3
3
  rich>=13.0
4
4
  inquirer>=3.0
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "create-browser-app"
7
- version = "0.1.11"
7
+ version = "0.1.13"
8
8
  description = "Start your Stagehand project with a single command"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
- "stagehand",
12
+ "stagehand>=3.4.0",
13
13
  "click>=8.0",
14
14
  "rich>=13.0",
15
15
  "inquirer>=3.0",
@@ -1,75 +0,0 @@
1
- #!/usr/bin/env python3
2
- import requests
3
- from typing import Dict, List, Optional
4
- from dataclasses import dataclass
5
-
6
- @dataclass
7
- class TemplateInfo:
8
- name: str
9
- url: str
10
-
11
- GITHUB_TEMPLATES: List[TemplateInfo] = [
12
- TemplateInfo(
13
- name="example",
14
- url="https://raw.githubusercontent.com/browserbase/stagehand-python/main/examples/example.py",
15
- ),
16
- TemplateInfo(
17
- name="cua-example",
18
- url="https://raw.githubusercontent.com/browserbase/stagehand-python/main/examples/cua-example.py",
19
- ),
20
- TemplateInfo(
21
- name="form-filling",
22
- url="https://raw.githubusercontent.com/browserbase/templates/refs/heads/dev/python/form-filling/main.py",
23
- ),
24
- TemplateInfo(
25
- name="gift-finder",
26
- url="https://raw.githubusercontent.com/browserbase/templates/refs/heads/dev/python/gift-finder/main.py",
27
- ),
28
- TemplateInfo(
29
- name="pickleball",
30
- url="https://raw.githubusercontent.com/browserbase/templates/refs/heads/dev/python/pickleball/main.py",
31
- ),
32
- TemplateInfo(
33
- name="license-verification",
34
- url="https://raw.githubusercontent.com/browserbase/templates/refs/heads/dev/python/license-verification/main.py",
35
- ),
36
- TemplateInfo(
37
- name="context",
38
- url="https://raw.githubusercontent.com/browserbase/templates/refs/heads/dev/python/context/main.py",
39
- ),
40
- TemplateInfo(
41
- name="proxies",
42
- url="https://raw.githubusercontent.com/browserbase/templates/refs/heads/dev/python/proxies/main.py",
43
- ),
44
- ]
45
-
46
- def get_template_by_name(name: str) -> Optional[TemplateInfo]:
47
- """Get a specific template by name."""
48
- for template in GITHUB_TEMPLATES:
49
- if template.name == name:
50
- return template
51
- return None
52
-
53
- def fetch_template_content(template: TemplateInfo) -> Optional[str]:
54
- """Fetch the content of a specific template from GitHub."""
55
- try:
56
- response = requests.get(
57
- template.url,
58
- headers={"User-Agent": "create-browser-app-py"},
59
- timeout=10
60
- )
61
- if response.status_code != 200:
62
- return None
63
- return response.text
64
- except Exception:
65
- return None
66
-
67
- def get_available_templates() -> List[str]:
68
- """Get a list of available template names."""
69
- default_templates = ["basic"]
70
- github_templates = [t.name for t in GITHUB_TEMPLATES]
71
- return default_templates + github_templates
72
-
73
- def list_templates() -> List[str]:
74
- """Get a list of template names."""
75
- return get_available_templates()
@@ -1,140 +0,0 @@
1
- TEMPLATE_BASIC = '''from stagehand import Stagehand
2
- import asyncio
3
- import os
4
- from dotenv import load_dotenv
5
- from pydantic import BaseModel
6
- load_dotenv()
7
-
8
- async def main():
9
- """Basic Stagehand browser automation example."""
10
-
11
- # Check all the configurations in StagehandConfig
12
- stagehand = Stagehand(
13
- env="LOCAL", # or "BROWSERBASE"
14
- verbose=1, # 0: silent, 1: info, 2: debug
15
- model_name="gpt-4.1",
16
- model_api_key=os.getenv("OPENAI_API_KEY"),
17
- )
18
- await stagehand.init()
19
-
20
- page = stagehand.page
21
- await page.goto("https://docs.stagehand.dev")
22
- result = await page.extract("In a few words, what is Stagehand?")
23
- print()
24
- print(result)
25
- print()
26
-
27
- await page.act("click on models")
28
- await page.act({
29
- "method": "click",
30
- "selector": "/html/body[1]/div[2]/div[2]/div[3]/div[2]/div[2]/a[1]",
31
- "description": "the model evaluation card",
32
- "args": []
33
- })
34
- elements = await page.observe("find the graph with the list of most accurate models")
35
- print()
36
- print(elements)
37
- print()
38
-
39
- class Model(BaseModel):
40
- name: str
41
- provider: str
42
- accuracy: float
43
-
44
- extraction = await page.extract(
45
- "the most accurate model",
46
- schema=Model
47
- )
48
- print()
49
- print(extraction)
50
- print()
51
- if __name__ == "__main__":
52
- asyncio.run(main())
53
-
54
- '''
55
-
56
- TEMPLATE_REQUIREMENTS = '''stagehand
57
- python-dotenv
58
- '''
59
-
60
- TEMPLATE_ENV = '''# Add your environment variables here
61
- # BROWSERBASE_API_KEY=your_api_key_here
62
- # BROWSERBASE_PROJECT_ID=your_project_id_here
63
- # Add your LLM key
64
- # OPENAI_API_KEY=your_api_key_here
65
- # ANTHROPIC=your_api_key_here
66
- # GOOGLE_API_KEY=your_api_key_here
67
- '''
68
-
69
- TEMPLATE_README = '''# {project_name}
70
-
71
- A Stagehand browser automation project.
72
-
73
- ## Setup
74
-
75
- 1. Install dependencies:
76
- ```bash
77
- pip install -r requirements.txt
78
- ```
79
-
80
- 2. Configure your environment variables in `.env`:
81
- ```bash
82
- BROWSERBASE_API_KEY=your_api_key_here
83
- BROWSERBASE_PROJECT_ID=your_project_id_here
84
- ```
85
-
86
- 3. Run the project:
87
- ```bash
88
- python main.py
89
- ```
90
-
91
- ## About Stagehand
92
-
93
- Stagehand is a Python library for browser automation built on Playwright. Learn more at:
94
- - [Stagehand Documentation](https://github.com/browserbase/stagehand)
95
- - [Browserbase](https://browserbase.com)
96
- '''
97
-
98
- TEMPLATE_GITIGNORE = '''# Python
99
- __pycache__/
100
- *.py[cod]
101
- *$py.class
102
- *.so
103
- .Python
104
- build/
105
- develop-eggs/
106
- dist/
107
- downloads/
108
- eggs/
109
- .eggs/
110
- lib/
111
- lib64/
112
- parts/
113
- sdist/
114
- var/
115
- wheels/
116
- *.egg-info/
117
- .installed.cfg
118
- *.egg
119
-
120
- # Virtual Environment
121
- venv/
122
- ENV/
123
- env/
124
- .venv
125
-
126
- # Environment variables
127
- .env
128
- .env.local
129
-
130
- # IDE
131
- .vscode/
132
- .idea/
133
- *.swp
134
- *.swo
135
- *~
136
- .DS_Store
137
-
138
- # Logs
139
- *.log
140
- '''