create-browser-app 0.1.12__py3-none-any.whl → 0.1.13__py3-none-any.whl
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.
- create_browser_app/main.py +32 -28
- create_browser_app/template_fetcher.py +95 -46
- create_browser_app/templates.py +68 -52
- {create_browser_app-0.1.12.dist-info → create_browser_app-0.1.13.dist-info}/METADATA +2 -2
- create_browser_app-0.1.13.dist-info/RECORD +9 -0
- {create_browser_app-0.1.12.dist-info → create_browser_app-0.1.13.dist-info}/WHEEL +1 -1
- create_browser_app-0.1.12.dist-info/RECORD +0 -9
- {create_browser_app-0.1.12.dist-info → create_browser_app-0.1.13.dist-info}/entry_points.txt +0 -0
- {create_browser_app-0.1.12.dist-info → create_browser_app-0.1.13.dist-info}/top_level.txt +0 -0
create_browser_app/main.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
58
|
-
|
|
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
|
-
|
|
64
|
-
if
|
|
65
|
-
|
|
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]
|
|
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(
|
|
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 .
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -8,50 +8,42 @@ class TemplateInfo:
|
|
|
8
8
|
name: str
|
|
9
9
|
url: str
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
name="proxies",
|
|
42
|
-
url="https://raw.githubusercontent.com/browserbase/templates/refs/heads/dev/python/proxies/main.py",
|
|
43
|
-
),
|
|
44
|
-
TemplateInfo(
|
|
45
|
-
name="gemini-cua",
|
|
46
|
-
url="https://raw.githubusercontent.com/browserbase/templates/refs/heads/dev/python/gemini-cua/main.py",
|
|
47
|
-
),
|
|
48
|
-
]
|
|
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
|
+
|
|
49
41
|
def get_template_by_name(name: str) -> Optional[TemplateInfo]:
|
|
50
42
|
"""Get a specific template by name."""
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return
|
|
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)
|
|
55
47
|
|
|
56
48
|
def fetch_template_content(template: TemplateInfo) -> Optional[str]:
|
|
57
49
|
"""Fetch the content of a specific template from GitHub."""
|
|
@@ -67,11 +59,68 @@ def fetch_template_content(template: TemplateInfo) -> Optional[str]:
|
|
|
67
59
|
except Exception:
|
|
68
60
|
return None
|
|
69
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
|
+
|
|
70
117
|
def get_available_templates() -> List[str]:
|
|
71
|
-
"""Get
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
75
124
|
|
|
76
125
|
def list_templates() -> List[str]:
|
|
77
126
|
"""Get a list of template names."""
|
create_browser_app/templates.py
CHANGED
|
@@ -1,69 +1,84 @@
|
|
|
1
|
-
TEMPLATE_BASIC = '''
|
|
2
|
-
import asyncio
|
|
1
|
+
TEMPLATE_BASIC = '''import asyncio
|
|
3
2
|
import os
|
|
3
|
+
|
|
4
4
|
from dotenv import load_dotenv
|
|
5
5
|
from pydantic import BaseModel
|
|
6
|
+
from stagehand import AsyncStagehand
|
|
7
|
+
|
|
6
8
|
load_dotenv()
|
|
7
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
|
+
|
|
8
21
|
async def main():
|
|
9
22
|
"""Basic Stagehand browser automation example."""
|
|
10
23
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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"),
|
|
47
28
|
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
+
|
|
51
67
|
if __name__ == "__main__":
|
|
52
68
|
asyncio.run(main())
|
|
53
|
-
|
|
54
69
|
'''
|
|
55
70
|
|
|
56
|
-
TEMPLATE_REQUIREMENTS = '''stagehand
|
|
71
|
+
TEMPLATE_REQUIREMENTS = '''stagehand>=3.4.0
|
|
72
|
+
pydantic
|
|
57
73
|
python-dotenv
|
|
58
74
|
'''
|
|
59
75
|
|
|
60
|
-
TEMPLATE_ENV = '''#
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
# GOOGLE_API_KEY=your_api_key_here
|
|
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
|
|
67
82
|
'''
|
|
68
83
|
|
|
69
84
|
TEMPLATE_README = '''# {project_name}
|
|
@@ -81,6 +96,7 @@ pip install -r requirements.txt
|
|
|
81
96
|
```bash
|
|
82
97
|
BROWSERBASE_API_KEY=your_api_key_here
|
|
83
98
|
BROWSERBASE_PROJECT_ID=your_project_id_here
|
|
99
|
+
MODEL_API_KEY=your_api_key_here
|
|
84
100
|
```
|
|
85
101
|
|
|
86
102
|
3. Run the project:
|
|
@@ -90,8 +106,8 @@ python main.py
|
|
|
90
106
|
|
|
91
107
|
## About Stagehand
|
|
92
108
|
|
|
93
|
-
Stagehand is
|
|
94
|
-
- [Stagehand Documentation](https://
|
|
109
|
+
Stagehand is the AI browser automation framework. Learn more at:
|
|
110
|
+
- [Stagehand Documentation](https://docs.stagehand.dev/v3/sdk/python)
|
|
95
111
|
- [Browserbase](https://browserbase.com)
|
|
96
112
|
'''
|
|
97
113
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: create-browser-app
|
|
3
|
-
Version: 0.1.
|
|
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
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
create_browser_app/__init__.py,sha256=izlkaSvrxNqPnulbHlD8VYldR618NxRQRlKQFgQn0Gk,42
|
|
2
|
+
create_browser_app/main.py,sha256=Za2sMGbdimGFenChqkMRDageOa2dlGfV7nHPsLc_XoE,5268
|
|
3
|
+
create_browser_app/template_fetcher.py,sha256=vpt19HVQLZ8_3It9cEAJhZzAh1KLVj0-ajV4CvgKXBg,4297
|
|
4
|
+
create_browser_app/templates.py,sha256=Vt1oGrWuYfpM0Z8QsY20B7d3x2qHVoUTS3poDihsTvM,3172
|
|
5
|
+
create_browser_app-0.1.13.dist-info/METADATA,sha256=mqC7AKLl3NugReLFCT6LFvxJIXhDF6J_A7fL3C4FK5A,656
|
|
6
|
+
create_browser_app-0.1.13.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
7
|
+
create_browser_app-0.1.13.dist-info/entry_points.txt,sha256=yyAKhFZs2kraGA_ixwnRZeStL0uxWvBCMzysg8S-0VE,68
|
|
8
|
+
create_browser_app-0.1.13.dist-info/top_level.txt,sha256=sLW8imVtlXvOy1D5aGPBDxbMP3Yvonif7VxcLu_Izy4,19
|
|
9
|
+
create_browser_app-0.1.13.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
create_browser_app/__init__.py,sha256=izlkaSvrxNqPnulbHlD8VYldR618NxRQRlKQFgQn0Gk,42
|
|
2
|
-
create_browser_app/main.py,sha256=8n6Mot1AAM5Vwd7YhzUjresboJtwnt-hwFo1_jBI98Q,4864
|
|
3
|
-
create_browser_app/template_fetcher.py,sha256=iu1hhs9RDZJrwpSwdyGJRaGwboMcfkLmzh5PecnYH_U,2690
|
|
4
|
-
create_browser_app/templates.py,sha256=ipfp4zmfbD4-vhoggEW9M_EBXYvvyCzvmHew9tTgi7w,2636
|
|
5
|
-
create_browser_app-0.1.12.dist-info/METADATA,sha256=U_lzsuZJnDfMe-PpL7Lrks0VyrVwa0ri7c2fVuMA7s0,649
|
|
6
|
-
create_browser_app-0.1.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
-
create_browser_app-0.1.12.dist-info/entry_points.txt,sha256=yyAKhFZs2kraGA_ixwnRZeStL0uxWvBCMzysg8S-0VE,68
|
|
8
|
-
create_browser_app-0.1.12.dist-info/top_level.txt,sha256=sLW8imVtlXvOy1D5aGPBDxbMP3Yvonif7VxcLu_Izy4,19
|
|
9
|
-
create_browser_app-0.1.12.dist-info/RECORD,,
|
{create_browser_app-0.1.12.dist-info → create_browser_app-0.1.13.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|