webtap-tool 0.1.4__py3-none-any.whl → 0.2.0__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.

Potentially problematic release.


This version of webtap-tool might be problematic. Click here for more details.

@@ -0,0 +1,228 @@
1
+ """Desktop/Application launcher setup (cross-platform)."""
2
+
3
+ import logging
4
+ from typing import Dict, Any
5
+
6
+ from .platform import get_platform_info
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ # Desktop entry template for Linux
11
+ LINUX_DESKTOP_ENTRY = """[Desktop Entry]
12
+ Version=1.0
13
+ Type=Application
14
+ Name=Chrome Debug
15
+ GenericName=Web Browser (Debug Mode)
16
+ Comment=Chrome with remote debugging enabled
17
+ Icon=google-chrome
18
+ Categories=Development;WebBrowser;
19
+ MimeType=application/pdf;application/rdf+xml;application/rss+xml;application/xhtml+xml;application/xhtml_xml;application/xml;image/gif;image/jpeg;image/png;image/webp;text/html;text/xml;x-scheme-handler/ftp;x-scheme-handler/http;x-scheme-handler/https;
20
+ StartupWMClass=Google-chrome
21
+ StartupNotify=true
22
+ Terminal=false
23
+ Exec={wrapper_path} %U
24
+ Actions=new-window;new-private-window;temp-profile;
25
+
26
+ [Desktop Action new-window]
27
+ Name=New Window
28
+ StartupWMClass=Google-chrome
29
+ Exec={wrapper_path}
30
+
31
+ [Desktop Action new-private-window]
32
+ Name=New Incognito Window
33
+ StartupWMClass=Google-chrome
34
+ Exec={wrapper_path} --incognito
35
+
36
+ [Desktop Action temp-profile]
37
+ Name=New Window (Temp Profile)
38
+ StartupWMClass=Google-chrome
39
+ Exec={wrapper_path} --temp
40
+ """
41
+
42
+ # Info.plist template for macOS app bundle
43
+ MACOS_INFO_PLIST = """<?xml version="1.0" encoding="UTF-8"?>
44
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
45
+ <plist version="1.0">
46
+ <dict>
47
+ <key>CFBundleExecutable</key>
48
+ <string>Chrome Debug</string>
49
+ <key>CFBundleIdentifier</key>
50
+ <string>com.webtap.chrome-debug</string>
51
+ <key>CFBundleName</key>
52
+ <string>Chrome Debug</string>
53
+ <key>CFBundleDisplayName</key>
54
+ <string>Chrome Debug</string>
55
+ <key>CFBundleVersion</key>
56
+ <string>1.0</string>
57
+ <key>CFBundleShortVersionString</key>
58
+ <string>1.0</string>
59
+ <key>CFBundlePackageType</key>
60
+ <string>APPL</string>
61
+ <key>CFBundleSignature</key>
62
+ <string>????</string>
63
+ <key>LSMinimumSystemVersion</key>
64
+ <string>10.12</string>
65
+ <key>CFBundleDocumentTypes</key>
66
+ <array>
67
+ <dict>
68
+ <key>CFBundleTypeName</key>
69
+ <string>HTML Document</string>
70
+ <key>CFBundleTypeRole</key>
71
+ <string>Viewer</string>
72
+ <key>LSItemContentTypes</key>
73
+ <array>
74
+ <string>public.html</string>
75
+ </array>
76
+ </dict>
77
+ <dict>
78
+ <key>CFBundleTypeName</key>
79
+ <string>Web Location</string>
80
+ <key>CFBundleTypeRole</key>
81
+ <string>Viewer</string>
82
+ <key>LSItemContentTypes</key>
83
+ <array>
84
+ <string>public.url</string>
85
+ </array>
86
+ </dict>
87
+ </array>
88
+ <key>CFBundleURLTypes</key>
89
+ <array>
90
+ <dict>
91
+ <key>CFBundleURLName</key>
92
+ <string>Web site URL</string>
93
+ <key>CFBundleURLSchemes</key>
94
+ <array>
95
+ <string>http</string>
96
+ <string>https</string>
97
+ </array>
98
+ </dict>
99
+ </array>
100
+ </dict>
101
+ </plist>"""
102
+
103
+
104
+ class DesktopSetupService:
105
+ """Platform-appropriate GUI launcher setup."""
106
+
107
+ def __init__(self):
108
+ self.info = get_platform_info()
109
+ self.paths = self.info["paths"]
110
+ self.chrome = self.info["chrome"]
111
+
112
+ # Unified wrapper path: ~/.local/bin/chrome-debug
113
+ self.wrapper_name = self.chrome["wrapper_name"] # chrome-debug
114
+ self.wrapper_path = self.paths["bin_dir"] / self.wrapper_name
115
+
116
+ def install_launcher(self, force: bool = False) -> Dict[str, Any]:
117
+ """Install platform-appropriate launcher.
118
+
119
+ Args:
120
+ force: Overwrite existing launcher
121
+
122
+ Returns:
123
+ Installation result
124
+ """
125
+ # Check if wrapper exists first
126
+ if not self.wrapper_path.exists():
127
+ return {
128
+ "success": False,
129
+ "message": "Chrome wrapper 'chrome-debug' not found. Run 'setup-chrome' first",
130
+ "path": None,
131
+ "details": f"Expected wrapper at {self.wrapper_path}",
132
+ }
133
+
134
+ if self.info["is_macos"]:
135
+ return self._install_macos_app(force)
136
+ else:
137
+ return self._install_linux_desktop(force)
138
+
139
+ def _install_macos_app(self, force: bool) -> Dict[str, Any]:
140
+ """Create .app bundle for macOS.
141
+
142
+ Args:
143
+ force: Overwrite existing app
144
+
145
+ Returns:
146
+ Installation result
147
+ """
148
+ app_path = self.paths["applications_dir"] / "Chrome Debug.app"
149
+
150
+ if app_path.exists() and not force:
151
+ return {
152
+ "success": False,
153
+ "message": "Chrome Debug app already exists",
154
+ "path": str(app_path),
155
+ "details": "Use --force to overwrite",
156
+ }
157
+
158
+ # Create app structure
159
+ contents_dir = app_path / "Contents"
160
+ macos_dir = contents_dir / "MacOS"
161
+ macos_dir.mkdir(parents=True, exist_ok=True)
162
+
163
+ # Create launcher script
164
+ launcher_path = macos_dir / "Chrome Debug"
165
+ launcher_content = f"""#!/bin/bash
166
+ # Chrome Debug app launcher
167
+ # Uses absolute path to wrapper
168
+ exec "{self.wrapper_path.expanduser()}" "$@"
169
+ """
170
+ launcher_path.write_text(launcher_content)
171
+ launcher_path.chmod(0o755)
172
+
173
+ # Create Info.plist
174
+ plist_path = contents_dir / "Info.plist"
175
+ plist_path.write_text(MACOS_INFO_PLIST)
176
+
177
+ logger.info(f"Created Chrome Debug app at {app_path}")
178
+
179
+ return {
180
+ "success": True,
181
+ "message": "Chrome Debug app created successfully",
182
+ "path": str(app_path),
183
+ "details": "Available in Launchpad and Spotlight search",
184
+ }
185
+
186
+ def _install_linux_desktop(self, force: bool) -> Dict[str, Any]:
187
+ """Install .desktop file for Linux.
188
+
189
+ Args:
190
+ force: Overwrite existing desktop entry
191
+
192
+ Returns:
193
+ Installation result
194
+ """
195
+ # Create separate Chrome Debug desktop entry (doesn't override system Chrome)
196
+ desktop_path = self.paths["applications_dir"] / "chrome-debug.desktop"
197
+
198
+ if desktop_path.exists() and not force:
199
+ return {
200
+ "success": False,
201
+ "message": "Desktop entry already exists",
202
+ "path": str(desktop_path),
203
+ "details": "Use --force to overwrite",
204
+ }
205
+
206
+ # Create Chrome Debug desktop entry
207
+ desktop_content = self._create_chrome_debug_desktop()
208
+
209
+ # Create directory and save
210
+ desktop_path.parent.mkdir(parents=True, exist_ok=True)
211
+ desktop_path.write_text(desktop_content)
212
+ desktop_path.chmod(0o644) # Standard permissions for desktop files
213
+
214
+ logger.info(f"Installed desktop entry to {desktop_path}")
215
+
216
+ return {
217
+ "success": True,
218
+ "message": "Installed Chrome Debug desktop entry",
219
+ "path": str(desktop_path),
220
+ "details": "Available in application menu as 'Chrome Debug'",
221
+ }
222
+
223
+ def _create_chrome_debug_desktop(self) -> str:
224
+ """Create Chrome Debug desktop entry with absolute paths."""
225
+ # Use absolute expanded path for Exec lines
226
+ wrapper_abs_path = self.wrapper_path.expanduser()
227
+
228
+ return LINUX_DESKTOP_ENTRY.format(wrapper_path=wrapper_abs_path)
@@ -0,0 +1,101 @@
1
+ """Chrome extension setup service (cross-platform)."""
2
+
3
+ import json
4
+ import logging
5
+ from typing import Dict, Any
6
+
7
+ import requests
8
+
9
+ from .platform import get_platform_info, ensure_directories
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ # GitHub URLs for extension files
14
+ EXTENSION_BASE_URL = "https://raw.githubusercontent.com/angelsen/tap-tools/main/packages/webtap/extension"
15
+ EXTENSION_FILES = ["manifest.json", "popup.html", "popup.js"]
16
+
17
+
18
+ class ExtensionSetupService:
19
+ """Chrome extension installation service."""
20
+
21
+ def __init__(self):
22
+ self.info = get_platform_info()
23
+ self.paths = self.info["paths"]
24
+
25
+ # Extension goes in data directory (persistent app data)
26
+ self.extension_dir = self.paths["data_dir"] / "extension"
27
+
28
+ def install_extension(self, force: bool = False) -> Dict[str, Any]:
29
+ """Install Chrome extension to platform-appropriate location.
30
+
31
+ Args:
32
+ force: Overwrite existing files
33
+
34
+ Returns:
35
+ Installation result
36
+ """
37
+ # Check if exists (manifest.json is required file)
38
+ if (self.extension_dir / "manifest.json").exists() and not force:
39
+ return {
40
+ "success": False,
41
+ "message": f"Extension already exists at {self.extension_dir}",
42
+ "path": str(self.extension_dir),
43
+ "details": "Use --force to overwrite",
44
+ }
45
+
46
+ # Ensure base directories exist
47
+ ensure_directories()
48
+
49
+ # Create extension directory
50
+ self.extension_dir.mkdir(parents=True, exist_ok=True)
51
+
52
+ # Download each file
53
+ downloaded = []
54
+ failed = []
55
+
56
+ for filename in EXTENSION_FILES:
57
+ url = f"{EXTENSION_BASE_URL}/{filename}"
58
+ target_file = self.extension_dir / filename
59
+
60
+ try:
61
+ logger.info(f"Downloading {filename}")
62
+ response = requests.get(url, timeout=10)
63
+ response.raise_for_status()
64
+
65
+ # For manifest.json, validate it's proper JSON
66
+ if filename == "manifest.json":
67
+ json.loads(response.text)
68
+
69
+ target_file.write_text(response.text)
70
+ downloaded.append(filename)
71
+
72
+ except Exception as e:
73
+ logger.error(f"Failed to download {filename}: {e}")
74
+ failed.append(filename)
75
+
76
+ # Determine success level
77
+ if not downloaded:
78
+ return {
79
+ "success": False,
80
+ "message": "Failed to download any extension files",
81
+ "path": None,
82
+ "details": "Check network connection and try again",
83
+ }
84
+
85
+ if failed:
86
+ # Partial success - some files downloaded
87
+ return {
88
+ "success": True, # Partial is still success
89
+ "message": f"Downloaded {len(downloaded)}/{len(EXTENSION_FILES)} files",
90
+ "path": str(self.extension_dir),
91
+ "details": f"Failed: {', '.join(failed)}",
92
+ }
93
+
94
+ logger.info(f"Extension installed to {self.extension_dir}")
95
+
96
+ return {
97
+ "success": True,
98
+ "message": "Downloaded Chrome extension",
99
+ "path": str(self.extension_dir),
100
+ "details": f"Files: {', '.join(downloaded)}",
101
+ }
@@ -0,0 +1,89 @@
1
+ """Filter setup service (cross-platform)."""
2
+
3
+ import json
4
+ import logging
5
+ from pathlib import Path
6
+ from typing import Dict, Any
7
+
8
+ import requests
9
+
10
+ from .platform import get_platform_info
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # GitHub URL for filters
15
+ FILTERS_URL = "https://raw.githubusercontent.com/angelsen/tap-tools/main/packages/webtap/data/filters.json"
16
+
17
+
18
+ class FilterSetupService:
19
+ """Filter configuration installation service."""
20
+
21
+ def __init__(self):
22
+ self.info = get_platform_info()
23
+ self.paths = self.info["paths"]
24
+
25
+ # Filters go in the current working directory's .webtap folder
26
+ # (project-specific, not global)
27
+ self.filters_path = Path.cwd() / ".webtap" / "filters.json"
28
+
29
+ def install_filters(self, force: bool = False) -> Dict[str, Any]:
30
+ """Install filters to .webtap/filters.json in current directory.
31
+
32
+ Args:
33
+ force: Overwrite existing file
34
+
35
+ Returns:
36
+ Installation result
37
+ """
38
+ # Check if exists
39
+ if self.filters_path.exists() and not force:
40
+ return {
41
+ "success": False,
42
+ "message": f"Filters already exist at {self.filters_path}",
43
+ "path": str(self.filters_path),
44
+ "details": "Use --force to overwrite",
45
+ }
46
+
47
+ # Download from GitHub
48
+ try:
49
+ logger.info(f"Downloading filters from {FILTERS_URL}")
50
+ response = requests.get(FILTERS_URL, timeout=10)
51
+ response.raise_for_status()
52
+
53
+ # Validate it's proper JSON
54
+ filters_data = json.loads(response.text)
55
+
56
+ # Quick validation - should have dict structure
57
+ if not isinstance(filters_data, dict):
58
+ return {
59
+ "success": False,
60
+ "message": "Invalid filter format - expected JSON object",
61
+ "path": None,
62
+ "details": None,
63
+ }
64
+
65
+ # Count categories for user feedback
66
+ category_count = len(filters_data)
67
+
68
+ # Create directory and save
69
+ self.filters_path.parent.mkdir(parents=True, exist_ok=True)
70
+ self.filters_path.write_text(response.text)
71
+
72
+ logger.info(f"Saved {category_count} filter categories to {self.filters_path}")
73
+
74
+ return {
75
+ "success": True,
76
+ "message": f"Downloaded {category_count} filter categories",
77
+ "path": str(self.filters_path),
78
+ "details": f"Categories: {', '.join(filters_data.keys())}",
79
+ }
80
+
81
+ except requests.RequestException as e:
82
+ logger.error(f"Network error downloading filters: {e}")
83
+ return {"success": False, "message": f"Network error: {e}", "path": None, "details": None}
84
+ except json.JSONDecodeError as e:
85
+ logger.error(f"Invalid JSON in filters: {e}")
86
+ return {"success": False, "message": f"Invalid JSON format: {e}", "path": None, "details": None}
87
+ except Exception as e:
88
+ logger.error(f"Unexpected error: {e}")
89
+ return {"success": False, "message": f"Failed to download filters: {e}", "path": None, "details": None}
@@ -0,0 +1,126 @@
1
+ """Platform detection and path management using platformdirs."""
2
+
3
+ import platform
4
+ import shutil
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ import platformdirs
9
+
10
+
11
+ def get_platform_paths() -> dict[str, Path]:
12
+ """Get platform-appropriate paths using platformdirs.
13
+
14
+ Returns:
15
+ Dictionary of paths for config, data, cache, runtime, and state directories.
16
+ """
17
+ app_name = "webtap"
18
+ app_author = "webtap"
19
+
20
+ dirs = platformdirs.PlatformDirs(app_name, app_author)
21
+
22
+ paths = {
23
+ "config_dir": Path(dirs.user_config_dir), # ~/.config/webtap or ~/Library/Application Support/webtap
24
+ "data_dir": Path(dirs.user_data_dir), # ~/.local/share/webtap or ~/Library/Application Support/webtap
25
+ "cache_dir": Path(dirs.user_cache_dir), # ~/.cache/webtap or ~/Library/Caches/webtap
26
+ "state_dir": Path(dirs.user_state_dir), # ~/.local/state/webtap or ~/Library/Application Support/webtap
27
+ }
28
+
29
+ # Runtime dir (not available on all platforms)
30
+ try:
31
+ paths["runtime_dir"] = Path(dirs.user_runtime_dir)
32
+ except AttributeError:
33
+ # Fallback for platforms without runtime dir
34
+ paths["runtime_dir"] = Path("/tmp") / app_name
35
+
36
+ return paths
37
+
38
+
39
+ def get_chrome_path() -> Optional[Path]:
40
+ """Find Chrome executable path for current platform.
41
+
42
+ Returns:
43
+ Path to Chrome executable or None if not found.
44
+ """
45
+ system = platform.system()
46
+
47
+ if system == "Darwin":
48
+ # macOS standard locations
49
+ candidates = [
50
+ Path("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"),
51
+ Path.home() / "Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
52
+ ]
53
+ elif system == "Linux":
54
+ # Linux standard locations
55
+ candidates = [
56
+ Path("/usr/bin/google-chrome"),
57
+ Path("/usr/bin/google-chrome-stable"),
58
+ Path("/usr/bin/chromium"),
59
+ Path("/usr/bin/chromium-browser"),
60
+ Path("/snap/bin/chromium"),
61
+ ]
62
+ else:
63
+ return None
64
+
65
+ for path in candidates:
66
+ if path.exists():
67
+ return path
68
+
69
+ # Try to find in PATH
70
+ for name in ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]:
71
+ if found := shutil.which(name):
72
+ return Path(found)
73
+
74
+ return None
75
+
76
+
77
+ def get_platform_info() -> dict:
78
+ """Get comprehensive platform information.
79
+
80
+ Returns:
81
+ Dictionary with system info, paths, and capabilities.
82
+ """
83
+ system = platform.system()
84
+ paths = get_platform_paths()
85
+
86
+ # Unified paths for both platforms
87
+ paths["bin_dir"] = Path.home() / ".local/bin" # User space, no sudo needed
88
+ wrapper_name = "chrome-debug" # Same name on both platforms
89
+
90
+ # Platform-specific launcher locations
91
+ if system == "Darwin":
92
+ paths["applications_dir"] = Path.home() / "Applications"
93
+ else: # Linux
94
+ paths["applications_dir"] = Path.home() / ".local/share/applications"
95
+
96
+ chrome_path = get_chrome_path()
97
+
98
+ return {
99
+ "system": system.lower(),
100
+ "is_macos": system == "Darwin",
101
+ "is_linux": system == "Linux",
102
+ "paths": paths,
103
+ "chrome": {
104
+ "path": chrome_path,
105
+ "found": chrome_path is not None,
106
+ "wrapper_name": wrapper_name,
107
+ },
108
+ "capabilities": {
109
+ "desktop_files": system == "Linux",
110
+ "app_bundles": system == "Darwin",
111
+ "bindfs": system == "Linux" and shutil.which("bindfs") is not None,
112
+ },
113
+ }
114
+
115
+
116
+ def ensure_directories() -> None:
117
+ """Ensure all required directories exist with proper permissions."""
118
+ paths = get_platform_paths()
119
+
120
+ for name, path in paths.items():
121
+ if name != "runtime_dir": # Runtime dir is often system-managed
122
+ path.mkdir(parents=True, exist_ok=True, mode=0o755)
123
+
124
+ # Ensure bin directory exists
125
+ info = get_platform_info()
126
+ info["paths"]["bin_dir"].mkdir(parents=True, exist_ok=True, mode=0o755)
@@ -1,9 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webtap-tool
3
- Version: 0.1.4
3
+ Version: 0.2.0
4
4
  Summary: Terminal-based web page inspector for AI debugging sessions
5
5
  Author-email: Fredrik Angelsen <fredrikangelsen@gmail.com>
6
6
  Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Operating System :: MacOS :: MacOS X
8
+ Classifier: Operating System :: POSIX :: Linux
7
9
  Classifier: Programming Language :: Python :: 3.12
8
10
  Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
9
11
  Classifier: Topic :: Software Development :: Debuggers
@@ -15,6 +17,7 @@ Requires-Dist: fastapi>=0.116.1
15
17
  Requires-Dist: httpx>=0.28.1
16
18
  Requires-Dist: lxml>=6.0.1
17
19
  Requires-Dist: msgpack-python>=0.5.6
20
+ Requires-Dist: platformdirs>=4.4.0
18
21
  Requires-Dist: protobuf>=6.32.0
19
22
  Requires-Dist: pyjwt>=2.10.1
20
23
  Requires-Dist: pyyaml>=6.0.2
@@ -28,7 +28,7 @@ webtap/commands/javascript.py,sha256=QpQdqqoQwwTyz1lpibZ92XKOL89scu_ndgSjkhaYuDk
28
28
  webtap/commands/launch.py,sha256=iZDLundKlxKRLKf3Vz5at42-tp2f-Uj5wZf7fbhBfA0,2202
29
29
  webtap/commands/navigation.py,sha256=Mapawp2AZTJQaws2uwlTgMUhqz7HlVTLxiZ06n_MQc0,6071
30
30
  webtap/commands/network.py,sha256=hwZshGGdVsJ_9MFjOKJXT07I990JjZInw2LLnKXLQ5Y,2910
31
- webtap/commands/setup.py,sha256=ewoOTvgCAzMPcFm6cbk-93nY_BI2IXqPZGGGpc1rJiw,4459
31
+ webtap/commands/setup.py,sha256=B60hajR8tKWLJcjlhEL4zYClB5DWd57AqnwJry_cHrs,10098
32
32
  webtap/services/README.md,sha256=rala_jtnNgSiQ1lFLM7x_UQ4SJZDceAm7dpkQMRTYaI,2346
33
33
  webtap/services/__init__.py,sha256=IjFqu0Ak6D-r18aokcQMtenDV3fbelvfjTCejGv6CZ0,570
34
34
  webtap/services/body.py,sha256=XQPa19y5eUc3XJ2TuwVK6kffO1VQoKqNs33MBBz7hzU,3913
@@ -36,8 +36,13 @@ webtap/services/console.py,sha256=XVfSKTvEHyyOdujsg85S3wtj1CdZhzKtWwlx25MvSv8,37
36
36
  webtap/services/fetch.py,sha256=nl6bpU2Vnf40kau4-mqAnIkhC-7Lx2vbTJKUglz9KnE,13602
37
37
  webtap/services/main.py,sha256=HcXdPuI7hzsxsNvfN0npGhj_M7HObc83Lr3fuy7BMeE,5673
38
38
  webtap/services/network.py,sha256=0o_--F6YvmXqqFqrcjL1gc6Vr9V1Ytb_U7r_DSUWupA,3444
39
- webtap/services/setup.py,sha256=dXzqlXR37-chZO2qCsuiVq9N7fELXtND5jH_9QQwsws,7932
40
- webtap_tool-0.1.4.dist-info/METADATA,sha256=xX7oPMbWWVeiFFRYbCB7V87iEMDoqh9JXfog5AY0gXM,17457
41
- webtap_tool-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
42
- webtap_tool-0.1.4.dist-info/entry_points.txt,sha256=iFe575I0CIb1MbfPt0oX2VYyY5gSU_dA551PKVR83TU,39
43
- webtap_tool-0.1.4.dist-info/RECORD,,
39
+ webtap/services/setup/__init__.py,sha256=rCi6HjyWQmtoBu6NwB1Aw3bEklxsC-bt1jPJ8rGeNgA,6635
40
+ webtap/services/setup/chrome.py,sha256=nNeb7j_yCSx5-Urw4RqbOE6x46gY_vR-s2-TCRMrc8U,7119
41
+ webtap/services/setup/desktop.py,sha256=pMH5gk14iGEZ7SyaBNb4pYt7C1xJSx47oFzQCBIB_E4,7317
42
+ webtap/services/setup/extension.py,sha256=OvTLuSi5u-kBAkqWAzfYt5lTNZrduXoCMZhFCuMisew,3318
43
+ webtap/services/setup/filters.py,sha256=lAPSLMH_KZQO-7bRkmURwzforx7C3SDrKEw2ZogN-Lo,3220
44
+ webtap/services/setup/platform.py,sha256=RQrhvp8mLg5Bssy5Slfl5SPVGo3BlUIn3lxVm-ZmkAM,3987
45
+ webtap_tool-0.2.0.dist-info/METADATA,sha256=e3ux3On__FO9C8B1LVQ12t30A-YtDhw-vHkbevZ7SlQ,17588
46
+ webtap_tool-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
47
+ webtap_tool-0.2.0.dist-info/entry_points.txt,sha256=iFe575I0CIb1MbfPt0oX2VYyY5gSU_dA551PKVR83TU,39
48
+ webtap_tool-0.2.0.dist-info/RECORD,,