webtap-tool 0.2.2__py3-none-any.whl → 0.3.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.
- webtap/__init__.py +2 -27
- webtap/api.py +0 -15
- webtap/app.py +1 -0
- webtap/commands/server.py +162 -0
- webtap/services/setup/__init__.py +28 -8
- webtap/services/setup/desktop.py +2 -2
- webtap/services/setup/platform.py +58 -28
- {webtap_tool-0.2.2.dist-info → webtap_tool-0.3.0.dist-info}/METADATA +1 -1
- {webtap_tool-0.2.2.dist-info → webtap_tool-0.3.0.dist-info}/RECORD +11 -10
- {webtap_tool-0.2.2.dist-info → webtap_tool-0.3.0.dist-info}/WHEEL +0 -0
- {webtap_tool-0.2.2.dist-info → webtap_tool-0.3.0.dist-info}/entry_points.txt +0 -0
webtap/__init__.py
CHANGED
|
@@ -9,14 +9,9 @@ PUBLIC API:
|
|
|
9
9
|
- main: Entry point function for CLI
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
import atexit
|
|
13
12
|
import sys
|
|
14
|
-
import logging
|
|
15
13
|
|
|
16
14
|
from webtap.app import app
|
|
17
|
-
from webtap.api import start_api_server
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
15
|
|
|
21
16
|
|
|
22
17
|
def main():
|
|
@@ -27,13 +22,9 @@ def main():
|
|
|
27
22
|
- MCP mode (with --mcp flag) for Model Context Protocol server
|
|
28
23
|
- REPL mode (default) for interactive shell
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
The API server for Chrome extension communication must be started
|
|
26
|
+
explicitly using the server('start') command.
|
|
32
27
|
"""
|
|
33
|
-
# Start API server for Chrome extension (except in CLI mode)
|
|
34
|
-
if "--cli" not in sys.argv:
|
|
35
|
-
_start_api_server_safely()
|
|
36
|
-
|
|
37
28
|
if "--mcp" in sys.argv:
|
|
38
29
|
app.mcp.run()
|
|
39
30
|
elif "--cli" in sys.argv:
|
|
@@ -45,20 +36,4 @@ def main():
|
|
|
45
36
|
app.run(title="WebTap - Chrome DevTools Protocol REPL")
|
|
46
37
|
|
|
47
38
|
|
|
48
|
-
def _start_api_server_safely():
|
|
49
|
-
"""Start API server with error handling and cleanup registration."""
|
|
50
|
-
try:
|
|
51
|
-
thread = start_api_server(app.state)
|
|
52
|
-
if thread and app.state:
|
|
53
|
-
app.state.api_thread = thread
|
|
54
|
-
logger.info("API server started on port 8765")
|
|
55
|
-
|
|
56
|
-
# Register cleanup to shut down API server on exit
|
|
57
|
-
atexit.register(lambda: app.state.cleanup() if app.state else None)
|
|
58
|
-
else:
|
|
59
|
-
logger.info("Port 8765 in use by another instance")
|
|
60
|
-
except Exception as e:
|
|
61
|
-
logger.warning(f"Failed to start API server: {e}")
|
|
62
|
-
|
|
63
|
-
|
|
64
39
|
__all__ = ["app", "main"]
|
webtap/api.py
CHANGED
|
@@ -199,21 +199,6 @@ async def disable_all_filters() -> Dict[str, Any]:
|
|
|
199
199
|
return {"enabled": [], "total": 0}
|
|
200
200
|
|
|
201
201
|
|
|
202
|
-
@api.post("/release")
|
|
203
|
-
async def release_port() -> Dict[str, Any]:
|
|
204
|
-
"""Release API port for another WebTap instance."""
|
|
205
|
-
logger.info("Releasing API port for another instance")
|
|
206
|
-
|
|
207
|
-
# Schedule graceful shutdown after response
|
|
208
|
-
def shutdown():
|
|
209
|
-
# Just set the flag to stop uvicorn, don't kill the whole process
|
|
210
|
-
global _shutdown_requested
|
|
211
|
-
_shutdown_requested = True
|
|
212
|
-
|
|
213
|
-
threading.Timer(0.5, shutdown).start()
|
|
214
|
-
return {"message": "Releasing port 8765"}
|
|
215
|
-
|
|
216
|
-
|
|
217
202
|
# Flag to signal shutdown
|
|
218
203
|
_shutdown_requested = False
|
|
219
204
|
|
webtap/app.py
CHANGED
|
@@ -84,6 +84,7 @@ else:
|
|
|
84
84
|
from webtap.commands import inspect # noqa: E402, F401
|
|
85
85
|
from webtap.commands import fetch # noqa: E402, F401
|
|
86
86
|
from webtap.commands import body # noqa: E402, F401
|
|
87
|
+
from webtap.commands import server # noqa: E402, F401
|
|
87
88
|
from webtap.commands import setup # noqa: E402, F401
|
|
88
89
|
from webtap.commands import launch # noqa: E402, F401
|
|
89
90
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""API server management command.
|
|
2
|
+
|
|
3
|
+
PUBLIC API:
|
|
4
|
+
- server: Manage API server (status/start/stop/restart)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import socket
|
|
8
|
+
import time
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
from webtap.app import app
|
|
12
|
+
from webtap.api import start_api_server
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
# Fixed port for API server
|
|
17
|
+
API_PORT = 8765
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _check_port() -> bool:
|
|
21
|
+
"""Check if API port is in use."""
|
|
22
|
+
with socket.socket() as s:
|
|
23
|
+
try:
|
|
24
|
+
s.bind(("127.0.0.1", API_PORT))
|
|
25
|
+
return False # Port is free
|
|
26
|
+
except OSError:
|
|
27
|
+
return True # Port is in use
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _stop_server(state) -> tuple[bool, str]:
|
|
31
|
+
"""Stop the server if this instance owns it."""
|
|
32
|
+
if not state.api_thread or not state.api_thread.is_alive():
|
|
33
|
+
return False, "Not running"
|
|
34
|
+
|
|
35
|
+
import webtap.api
|
|
36
|
+
|
|
37
|
+
webtap.api._shutdown_requested = True
|
|
38
|
+
state.api_thread.join(timeout=2.0)
|
|
39
|
+
state.api_thread = None
|
|
40
|
+
|
|
41
|
+
return True, "Server stopped"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _start_server(state) -> tuple[bool, str]:
|
|
45
|
+
"""Start the server on port 8765."""
|
|
46
|
+
# Check if already running
|
|
47
|
+
if state.api_thread and state.api_thread.is_alive():
|
|
48
|
+
return True, f"Already running on port {API_PORT}"
|
|
49
|
+
|
|
50
|
+
# Check if port is in use
|
|
51
|
+
if _check_port():
|
|
52
|
+
return False, f"Port {API_PORT} already in use by another process"
|
|
53
|
+
|
|
54
|
+
# Start the server
|
|
55
|
+
thread = start_api_server(state, port=API_PORT)
|
|
56
|
+
if thread:
|
|
57
|
+
state.api_thread = thread
|
|
58
|
+
|
|
59
|
+
# Register cleanup
|
|
60
|
+
import atexit
|
|
61
|
+
|
|
62
|
+
atexit.register(lambda: state.cleanup() if state else None)
|
|
63
|
+
|
|
64
|
+
return True, f"Server started on port {API_PORT}"
|
|
65
|
+
else:
|
|
66
|
+
return False, "Failed to start server"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@app.command(
|
|
70
|
+
display="markdown",
|
|
71
|
+
fastmcp={
|
|
72
|
+
"type": "prompt",
|
|
73
|
+
"arg_descriptions": {"action": "Server action: status (default), start, stop, or restart"},
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
def server(state, action: str = None) -> dict: # pyright: ignore[reportArgumentType]
|
|
77
|
+
"""API server status and management information.
|
|
78
|
+
|
|
79
|
+
Returns current server state and available actions.
|
|
80
|
+
"""
|
|
81
|
+
if action is None:
|
|
82
|
+
action = "status"
|
|
83
|
+
|
|
84
|
+
action = action.lower()
|
|
85
|
+
owns_server = bool(state.api_thread and state.api_thread.is_alive())
|
|
86
|
+
|
|
87
|
+
# Build markdown elements based on action
|
|
88
|
+
elements = []
|
|
89
|
+
|
|
90
|
+
if action == "status" or action not in ["start", "stop", "restart"]:
|
|
91
|
+
# Status information
|
|
92
|
+
elements.append({"type": "heading", "content": "API Server Status", "level": 2})
|
|
93
|
+
|
|
94
|
+
if owns_server:
|
|
95
|
+
elements.append({"type": "text", "content": "**Status:** Running"})
|
|
96
|
+
elements.append({"type": "text", "content": f"**Port:** {API_PORT}"})
|
|
97
|
+
elements.append({"type": "text", "content": f"**URL:** http://127.0.0.1:{API_PORT}"})
|
|
98
|
+
elements.append({"type": "text", "content": f"**Health:** http://127.0.0.1:{API_PORT}/health"})
|
|
99
|
+
elements.append({"type": "text", "content": "**Extension:** Ready to connect"})
|
|
100
|
+
else:
|
|
101
|
+
port_in_use = _check_port()
|
|
102
|
+
if port_in_use:
|
|
103
|
+
elements.append({"type": "alert", "message": "Port 8765 in use by another process", "level": "warning"})
|
|
104
|
+
elements.append({"type": "text", "content": "Cannot start server until port is free"})
|
|
105
|
+
else:
|
|
106
|
+
elements.append({"type": "text", "content": "**Status:** Not running"})
|
|
107
|
+
elements.append({"type": "text", "content": f"**Port:** {API_PORT} (available)"})
|
|
108
|
+
elements.append({"type": "text", "content": "Use `server('start')` to start the API server"})
|
|
109
|
+
|
|
110
|
+
# Available actions
|
|
111
|
+
elements.append({"type": "heading", "content": "Available Actions", "level": 3})
|
|
112
|
+
actions = [
|
|
113
|
+
"`server('start')` - Start the API server",
|
|
114
|
+
"`server('stop')` - Stop the API server",
|
|
115
|
+
"`server('restart')` - Restart the API server",
|
|
116
|
+
"`server('status')` or `server()` - Show this status",
|
|
117
|
+
]
|
|
118
|
+
elements.append({"type": "list", "items": actions})
|
|
119
|
+
|
|
120
|
+
elif action == "start":
|
|
121
|
+
if owns_server:
|
|
122
|
+
elements.append({"type": "alert", "message": f"Server already running on port {API_PORT}", "level": "info"})
|
|
123
|
+
else:
|
|
124
|
+
success, message = _start_server(state)
|
|
125
|
+
if success:
|
|
126
|
+
elements.append({"type": "alert", "message": message, "level": "success"})
|
|
127
|
+
elements.append({"type": "text", "content": f"**URL:** http://127.0.0.1:{API_PORT}"})
|
|
128
|
+
elements.append({"type": "text", "content": f"**Health:** http://127.0.0.1:{API_PORT}/health"})
|
|
129
|
+
elements.append({"type": "text", "content": "Chrome extension can now connect"})
|
|
130
|
+
else:
|
|
131
|
+
elements.append({"type": "alert", "message": message, "level": "error"})
|
|
132
|
+
|
|
133
|
+
elif action == "stop":
|
|
134
|
+
if not owns_server:
|
|
135
|
+
elements.append({"type": "text", "content": "Server not running"})
|
|
136
|
+
else:
|
|
137
|
+
success, message = _stop_server(state)
|
|
138
|
+
if success:
|
|
139
|
+
elements.append({"type": "alert", "message": message, "level": "success"})
|
|
140
|
+
else:
|
|
141
|
+
elements.append({"type": "alert", "message": message, "level": "error"})
|
|
142
|
+
|
|
143
|
+
elif action == "restart":
|
|
144
|
+
if owns_server:
|
|
145
|
+
success, msg = _stop_server(state)
|
|
146
|
+
if not success:
|
|
147
|
+
elements.append({"type": "alert", "message": f"Failed to stop: {msg}", "level": "error"})
|
|
148
|
+
return {"elements": elements}
|
|
149
|
+
time.sleep(0.5)
|
|
150
|
+
|
|
151
|
+
success, msg = _start_server(state)
|
|
152
|
+
if success:
|
|
153
|
+
elements.append({"type": "alert", "message": "Server restarted", "level": "success"})
|
|
154
|
+
elements.append({"type": "text", "content": f"**Port:** {API_PORT}"})
|
|
155
|
+
elements.append({"type": "text", "content": f"**URL:** http://127.0.0.1:{API_PORT}"})
|
|
156
|
+
else:
|
|
157
|
+
elements.append({"type": "alert", "message": f"Failed to restart: {msg}", "level": "error"})
|
|
158
|
+
|
|
159
|
+
return {"elements": elements}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
__all__ = ["server"]
|
|
@@ -10,7 +10,26 @@ from .filters import FilterSetupService
|
|
|
10
10
|
from .extension import ExtensionSetupService
|
|
11
11
|
from .chrome import ChromeSetupService
|
|
12
12
|
from .desktop import DesktopSetupService
|
|
13
|
-
from .platform import get_platform_info, ensure_directories
|
|
13
|
+
from .platform import get_platform_info, ensure_directories, APP_NAME
|
|
14
|
+
|
|
15
|
+
# Old installation paths to clean up
|
|
16
|
+
OLD_EXTENSION_PATH = ".config/webtap/extension"
|
|
17
|
+
OLD_WRAPPER_PATH = ".local/bin/wrappers/google-chrome-stable"
|
|
18
|
+
OLD_DESKTOP_PATH = ".local/share/applications/google-chrome.desktop"
|
|
19
|
+
OLD_DEBUG_DIR = ".config/google-chrome-debug"
|
|
20
|
+
|
|
21
|
+
# Path components
|
|
22
|
+
WRAPPERS_DIR = "wrappers"
|
|
23
|
+
GOOGLE_CHROME_STABLE = "google-chrome-stable"
|
|
24
|
+
|
|
25
|
+
# Size formatting constants
|
|
26
|
+
KB_SIZE = 1024
|
|
27
|
+
SIZE_FORMAT_KB = "{:.1f} KB"
|
|
28
|
+
SIZE_FORMAT_EMPTY = "empty"
|
|
29
|
+
|
|
30
|
+
# Mount point command
|
|
31
|
+
MOUNTPOINT_CMD = "mountpoint"
|
|
32
|
+
MOUNTPOINT_CHECK_FLAG = "-q"
|
|
14
33
|
|
|
15
34
|
|
|
16
35
|
class SetupService:
|
|
@@ -108,11 +127,11 @@ class SetupService:
|
|
|
108
127
|
result = {}
|
|
109
128
|
|
|
110
129
|
# Check old extension location
|
|
111
|
-
old_extension_path = Path.home() /
|
|
130
|
+
old_extension_path = Path.home() / OLD_EXTENSION_PATH
|
|
112
131
|
if old_extension_path.exists():
|
|
113
132
|
# Calculate size
|
|
114
133
|
size = sum(f.stat().st_size for f in old_extension_path.rglob("*") if f.is_file())
|
|
115
|
-
size_str =
|
|
134
|
+
size_str = SIZE_FORMAT_KB.format(size / KB_SIZE) if size > 0 else SIZE_FORMAT_EMPTY
|
|
116
135
|
|
|
117
136
|
result["old_extension"] = {"path": str(old_extension_path), "size": size_str, "removed": False}
|
|
118
137
|
|
|
@@ -128,7 +147,7 @@ class SetupService:
|
|
|
128
147
|
result["old_extension"]["error"] = str(e)
|
|
129
148
|
|
|
130
149
|
# Check old Chrome wrapper location
|
|
131
|
-
old_wrapper_path = Path.home() /
|
|
150
|
+
old_wrapper_path = Path.home() / OLD_WRAPPER_PATH
|
|
132
151
|
if old_wrapper_path.exists():
|
|
133
152
|
result["old_wrapper"] = {"path": str(old_wrapper_path), "removed": False}
|
|
134
153
|
|
|
@@ -144,12 +163,13 @@ class SetupService:
|
|
|
144
163
|
result["old_wrapper"]["error"] = str(e)
|
|
145
164
|
|
|
146
165
|
# Check old desktop entry
|
|
147
|
-
old_desktop_path = Path.home() /
|
|
166
|
+
old_desktop_path = Path.home() / OLD_DESKTOP_PATH
|
|
148
167
|
if old_desktop_path.exists():
|
|
149
168
|
# Check if it's our override (contains reference to wrapper)
|
|
150
169
|
try:
|
|
151
170
|
content = old_desktop_path.read_text()
|
|
152
|
-
|
|
171
|
+
wrapper_ref = f"{WRAPPERS_DIR}/{GOOGLE_CHROME_STABLE}"
|
|
172
|
+
if wrapper_ref in content or APP_NAME in content.lower():
|
|
153
173
|
result["old_desktop"] = {"path": str(old_desktop_path), "removed": False}
|
|
154
174
|
|
|
155
175
|
if not dry_run:
|
|
@@ -162,11 +182,11 @@ class SetupService:
|
|
|
162
182
|
pass # If we can't read it, skip it
|
|
163
183
|
|
|
164
184
|
# Check for bindfs mount
|
|
165
|
-
debug_dir = Path.home() /
|
|
185
|
+
debug_dir = Path.home() / OLD_DEBUG_DIR
|
|
166
186
|
if debug_dir.exists():
|
|
167
187
|
try:
|
|
168
188
|
# Check if it's a mount point
|
|
169
|
-
output = subprocess.run([
|
|
189
|
+
output = subprocess.run([MOUNTPOINT_CMD, MOUNTPOINT_CHECK_FLAG, str(debug_dir)], capture_output=True)
|
|
170
190
|
if output.returncode == 0:
|
|
171
191
|
result["bindfs_mount"] = str(debug_dir)
|
|
172
192
|
except (FileNotFoundError, OSError):
|
webtap/services/setup/desktop.py
CHANGED
|
@@ -168,11 +168,11 @@ class DesktopSetupService:
|
|
|
168
168
|
# Create launcher script that directly launches Chrome
|
|
169
169
|
# This avoids Rosetta warnings from nested bash scripts
|
|
170
170
|
launcher_path = macos_dir / "Chrome Debug"
|
|
171
|
-
|
|
171
|
+
|
|
172
172
|
# Get Chrome path from platform info
|
|
173
173
|
chrome_path = self.chrome["path"]
|
|
174
174
|
profile_dir = self.paths["data_dir"] / "profiles" / "default"
|
|
175
|
-
|
|
175
|
+
|
|
176
176
|
launcher_content = f"""#!/bin/bash
|
|
177
177
|
# Chrome Debug app launcher - direct Chrome execution
|
|
178
178
|
# Avoids Rosetta warnings by directly launching Chrome
|
|
@@ -7,6 +7,46 @@ from typing import Optional
|
|
|
7
7
|
|
|
8
8
|
import platformdirs
|
|
9
9
|
|
|
10
|
+
# Application constants
|
|
11
|
+
APP_NAME = "webtap"
|
|
12
|
+
APP_AUTHOR = "webtap"
|
|
13
|
+
|
|
14
|
+
# Directory names
|
|
15
|
+
BIN_DIR_NAME = ".local/bin"
|
|
16
|
+
WRAPPER_NAME = "chrome-debug"
|
|
17
|
+
TMP_RUNTIME_DIR = "/tmp"
|
|
18
|
+
|
|
19
|
+
# Chrome executable names for Linux
|
|
20
|
+
CHROME_NAMES_LINUX = [
|
|
21
|
+
"google-chrome",
|
|
22
|
+
"google-chrome-stable",
|
|
23
|
+
"chromium",
|
|
24
|
+
"chromium-browser",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
# Chrome paths for macOS
|
|
28
|
+
CHROME_PATHS_MACOS = [
|
|
29
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
30
|
+
"Applications/Google Chrome.app/Contents/MacOS/Google Chrome", # Relative to home
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# Chrome paths for Linux
|
|
34
|
+
CHROME_PATHS_LINUX = [
|
|
35
|
+
"/usr/bin/google-chrome",
|
|
36
|
+
"/usr/bin/google-chrome-stable",
|
|
37
|
+
"/usr/bin/chromium",
|
|
38
|
+
"/usr/bin/chromium-browser",
|
|
39
|
+
"/snap/bin/chromium",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# Platform identifiers
|
|
43
|
+
PLATFORM_DARWIN = "Darwin"
|
|
44
|
+
PLATFORM_LINUX = "Linux"
|
|
45
|
+
|
|
46
|
+
# Application directories
|
|
47
|
+
MACOS_APPLICATIONS_DIR = "Applications"
|
|
48
|
+
LINUX_APPLICATIONS_DIR = ".local/share/applications"
|
|
49
|
+
|
|
10
50
|
|
|
11
51
|
def get_platform_paths() -> dict[str, Path]:
|
|
12
52
|
"""Get platform-appropriate paths using platformdirs.
|
|
@@ -14,10 +54,7 @@ def get_platform_paths() -> dict[str, Path]:
|
|
|
14
54
|
Returns:
|
|
15
55
|
Dictionary of paths for config, data, cache, runtime, and state directories.
|
|
16
56
|
"""
|
|
17
|
-
|
|
18
|
-
app_author = "webtap"
|
|
19
|
-
|
|
20
|
-
dirs = platformdirs.PlatformDirs(app_name, app_author)
|
|
57
|
+
dirs = platformdirs.PlatformDirs(APP_NAME, APP_AUTHOR)
|
|
21
58
|
|
|
22
59
|
paths = {
|
|
23
60
|
"config_dir": Path(dirs.user_config_dir), # ~/.config/webtap or ~/Library/Application Support/webtap
|
|
@@ -31,7 +68,7 @@ def get_platform_paths() -> dict[str, Path]:
|
|
|
31
68
|
paths["runtime_dir"] = Path(dirs.user_runtime_dir)
|
|
32
69
|
except AttributeError:
|
|
33
70
|
# Fallback for platforms without runtime dir
|
|
34
|
-
paths["runtime_dir"] = Path(
|
|
71
|
+
paths["runtime_dir"] = Path(TMP_RUNTIME_DIR) / APP_NAME
|
|
35
72
|
|
|
36
73
|
return paths
|
|
37
74
|
|
|
@@ -44,21 +81,15 @@ def get_chrome_path() -> Optional[Path]:
|
|
|
44
81
|
"""
|
|
45
82
|
system = platform.system()
|
|
46
83
|
|
|
47
|
-
if system ==
|
|
84
|
+
if system == PLATFORM_DARWIN:
|
|
48
85
|
# macOS standard locations
|
|
49
86
|
candidates = [
|
|
50
|
-
Path(
|
|
51
|
-
Path.home() /
|
|
87
|
+
Path(CHROME_PATHS_MACOS[0]),
|
|
88
|
+
Path.home() / CHROME_PATHS_MACOS[1],
|
|
52
89
|
]
|
|
53
|
-
elif system ==
|
|
90
|
+
elif system == PLATFORM_LINUX:
|
|
54
91
|
# 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
|
-
]
|
|
92
|
+
candidates = [Path(p) for p in CHROME_PATHS_LINUX]
|
|
62
93
|
else:
|
|
63
94
|
return None
|
|
64
95
|
|
|
@@ -67,7 +98,7 @@ def get_chrome_path() -> Optional[Path]:
|
|
|
67
98
|
return path
|
|
68
99
|
|
|
69
100
|
# Try to find in PATH
|
|
70
|
-
for name in
|
|
101
|
+
for name in CHROME_NAMES_LINUX:
|
|
71
102
|
if found := shutil.which(name):
|
|
72
103
|
return Path(found)
|
|
73
104
|
|
|
@@ -84,31 +115,30 @@ def get_platform_info() -> dict:
|
|
|
84
115
|
paths = get_platform_paths()
|
|
85
116
|
|
|
86
117
|
# Unified paths for both platforms
|
|
87
|
-
paths["bin_dir"] = Path.home() /
|
|
88
|
-
wrapper_name = "chrome-debug" # Same name on both platforms
|
|
118
|
+
paths["bin_dir"] = Path.home() / BIN_DIR_NAME # User space, no sudo needed
|
|
89
119
|
|
|
90
120
|
# Platform-specific launcher locations
|
|
91
|
-
if system ==
|
|
92
|
-
paths["applications_dir"] = Path.home() /
|
|
121
|
+
if system == PLATFORM_DARWIN:
|
|
122
|
+
paths["applications_dir"] = Path.home() / MACOS_APPLICATIONS_DIR
|
|
93
123
|
else: # Linux
|
|
94
|
-
paths["applications_dir"] = Path.home() /
|
|
124
|
+
paths["applications_dir"] = Path.home() / LINUX_APPLICATIONS_DIR
|
|
95
125
|
|
|
96
126
|
chrome_path = get_chrome_path()
|
|
97
127
|
|
|
98
128
|
return {
|
|
99
129
|
"system": system.lower(),
|
|
100
|
-
"is_macos": system ==
|
|
101
|
-
"is_linux": system ==
|
|
130
|
+
"is_macos": system == PLATFORM_DARWIN,
|
|
131
|
+
"is_linux": system == PLATFORM_LINUX,
|
|
102
132
|
"paths": paths,
|
|
103
133
|
"chrome": {
|
|
104
134
|
"path": chrome_path,
|
|
105
135
|
"found": chrome_path is not None,
|
|
106
|
-
"wrapper_name":
|
|
136
|
+
"wrapper_name": WRAPPER_NAME,
|
|
107
137
|
},
|
|
108
138
|
"capabilities": {
|
|
109
|
-
"desktop_files": system ==
|
|
110
|
-
"app_bundles": system ==
|
|
111
|
-
"bindfs": system ==
|
|
139
|
+
"desktop_files": system == PLATFORM_LINUX,
|
|
140
|
+
"app_bundles": system == PLATFORM_DARWIN,
|
|
141
|
+
"bindfs": system == PLATFORM_LINUX and shutil.which("bindfs") is not None,
|
|
112
142
|
},
|
|
113
143
|
}
|
|
114
144
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
webtap/VISION.md,sha256=kfoJfEPVc4chOrD9tNMDmYBY9rX9KB-286oZj70ALCE,7681
|
|
2
|
-
webtap/__init__.py,sha256=
|
|
3
|
-
webtap/api.py,sha256=
|
|
4
|
-
webtap/app.py,sha256=
|
|
2
|
+
webtap/__init__.py,sha256=DFWJGmqZfX8_h4csLA5pKPR4SkaHBMlUgU-WQIE96Gw,1092
|
|
3
|
+
webtap/api.py,sha256=QLfwO_21uSyxBCsqei45c5Uyg7OVfaVopmBncx9ZRfw,8018
|
|
4
|
+
webtap/app.py,sha256=OC8-767GtQ_hAOxUNqD6Yu3JYLNB3ZXdzKn-A1S_RJI,3305
|
|
5
5
|
webtap/filters.py,sha256=nphF2bFRbFtS2ue-CbV1uzKpuK3IYBbwjLeLhMDdLEk,11034
|
|
6
6
|
webtap/cdp/README.md,sha256=0TS0V_dRgRAzBqhddpXWD4S0YVi5wI4JgFJSll_KUBE,5660
|
|
7
7
|
webtap/cdp/__init__.py,sha256=c6NFG0XJnAa5GTe9MLr9mDZcLZqoTQN7A1cvvOfLcgY,453
|
|
@@ -28,6 +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/server.py,sha256=LSs3l3Pb_vwmWRnYH-sA9JUPxBTQtRedjFQ4KvDaZK0,6032
|
|
31
32
|
webtap/commands/setup.py,sha256=dov1LaN50nAEMNIuBLSK7mcnwhfn9rtqdTopBm1-PhA,9648
|
|
32
33
|
webtap/services/README.md,sha256=rala_jtnNgSiQ1lFLM7x_UQ4SJZDceAm7dpkQMRTYaI,2346
|
|
33
34
|
webtap/services/__init__.py,sha256=IjFqu0Ak6D-r18aokcQMtenDV3fbelvfjTCejGv6CZ0,570
|
|
@@ -36,13 +37,13 @@ webtap/services/console.py,sha256=XVfSKTvEHyyOdujsg85S3wtj1CdZhzKtWwlx25MvSv8,37
|
|
|
36
37
|
webtap/services/fetch.py,sha256=nl6bpU2Vnf40kau4-mqAnIkhC-7Lx2vbTJKUglz9KnE,13602
|
|
37
38
|
webtap/services/main.py,sha256=HcXdPuI7hzsxsNvfN0npGhj_M7HObc83Lr3fuy7BMeE,5673
|
|
38
39
|
webtap/services/network.py,sha256=0o_--F6YvmXqqFqrcjL1gc6Vr9V1Ytb_U7r_DSUWupA,3444
|
|
39
|
-
webtap/services/setup/__init__.py,sha256=
|
|
40
|
+
webtap/services/setup/__init__.py,sha256=lfoKCAroc-JoE_r7L-KZkF85ZWiB41MBIgrR7ZISSoE,7157
|
|
40
41
|
webtap/services/setup/chrome.py,sha256=zfPWeb6zm_xjIfiS2S_O9lR2BjGKaPXXo06pN_B9lAU,7187
|
|
41
|
-
webtap/services/setup/desktop.py,sha256=
|
|
42
|
+
webtap/services/setup/desktop.py,sha256=fXwQa201W-s2mengm_dJZ9BigJopVrO9YFUQcW_TSFQ,8022
|
|
42
43
|
webtap/services/setup/extension.py,sha256=OvTLuSi5u-kBAkqWAzfYt5lTNZrduXoCMZhFCuMisew,3318
|
|
43
44
|
webtap/services/setup/filters.py,sha256=lAPSLMH_KZQO-7bRkmURwzforx7C3SDrKEw2ZogN-Lo,3220
|
|
44
|
-
webtap/services/setup/platform.py,sha256=
|
|
45
|
-
webtap_tool-0.
|
|
46
|
-
webtap_tool-0.
|
|
47
|
-
webtap_tool-0.
|
|
48
|
-
webtap_tool-0.
|
|
45
|
+
webtap/services/setup/platform.py,sha256=7yn-7LQFffgerWzWRtOG-yNEsR36ICThYUAu_N2FAso,4532
|
|
46
|
+
webtap_tool-0.3.0.dist-info/METADATA,sha256=jm3l2rpT1TBdGU8AU7NJYV7RgwpfzITuPyjh4Zb1_pk,17588
|
|
47
|
+
webtap_tool-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
48
|
+
webtap_tool-0.3.0.dist-info/entry_points.txt,sha256=iFe575I0CIb1MbfPt0oX2VYyY5gSU_dA551PKVR83TU,39
|
|
49
|
+
webtap_tool-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|