webtap-tool 0.2.3__tar.gz → 0.3.0__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.
Potentially problematic release.
This version of webtap-tool might be problematic. Click here for more details.
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/CHANGELOG.md +26 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/PKG-INFO +1 -1
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/extension/popup.html +0 -9
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/extension/popup.js +2 -25
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/pyproject.toml +1 -1
- webtap_tool-0.3.0/src/webtap/__init__.py +39 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/api.py +0 -15
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/app.py +1 -0
- webtap_tool-0.3.0/src/webtap/commands/server.py +162 -0
- webtap_tool-0.2.3/src/webtap/__init__.py +0 -64
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/.gitignore +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/ARCHITECTURE.md +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/README.md +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/data/filters.json +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/extension/manifest.json +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/llms.txt +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/VISION.md +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/cdp/README.md +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/cdp/__init__.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/cdp/query.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/cdp/schema/README.md +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/cdp/schema/cdp_protocol.json +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/cdp/schema/cdp_version.json +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/cdp/session.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/DEVELOPER_GUIDE.md +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/TIPS.md +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/__init__.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/_builders.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/_errors.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/_tips.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/_utils.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/body.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/connection.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/console.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/events.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/fetch.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/filters.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/inspect.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/javascript.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/launch.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/navigation.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/network.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/commands/setup.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/filters.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/README.md +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/__init__.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/body.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/console.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/fetch.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/main.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/network.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/setup/__init__.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/setup/chrome.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/setup/desktop.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/setup/extension.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/setup/filters.py +0 -0
- {webtap_tool-0.2.3 → webtap_tool-0.3.0}/src/webtap/services/setup/platform.py +0 -0
|
@@ -15,6 +15,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
15
|
|
|
16
16
|
### Removed
|
|
17
17
|
|
|
18
|
+
## [0.3.0] - 2025-09-19
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
### Removed
|
|
27
|
+
|
|
28
|
+
## [0.2.4] - 2025-09-19
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- New `server` command for explicit API server management (start/stop/restart/status)
|
|
32
|
+
- Server command implemented as MCP prompt returning markdown status information
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- API server no longer starts automatically - must be started explicitly with `server('start')`
|
|
36
|
+
- API server management is now explicit and user-controlled
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
### Removed
|
|
41
|
+
- Automatic API server startup on webtap launch
|
|
42
|
+
- `/release` endpoint from API (no longer needed with explicit server management)
|
|
43
|
+
|
|
18
44
|
## [0.2.3] - 2025-09-12
|
|
19
45
|
|
|
20
46
|
### Added
|
|
@@ -150,15 +150,6 @@
|
|
|
150
150
|
|
|
151
151
|
<div class="divider"></div>
|
|
152
152
|
|
|
153
|
-
<button
|
|
154
|
-
id="switchInstance"
|
|
155
|
-
style="width: 100%; font-size: 11px; margin-bottom: 8px"
|
|
156
|
-
>
|
|
157
|
-
Switch WebTap Instance
|
|
158
|
-
</button>
|
|
159
|
-
|
|
160
|
-
<div class="divider"></div>
|
|
161
|
-
|
|
162
153
|
<div class="filter-section">
|
|
163
154
|
<div
|
|
164
155
|
style="
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// API helper - communicate with WebTap service
|
|
1
|
+
// API helper - communicate with WebTap service on port 8765
|
|
2
2
|
async function api(endpoint, method = "GET", body = null) {
|
|
3
3
|
try {
|
|
4
|
-
const opts = {
|
|
4
|
+
const opts = {
|
|
5
5
|
method,
|
|
6
6
|
// Add timeout to detect unresponsive server faster
|
|
7
7
|
signal: AbortSignal.timeout(3000)
|
|
@@ -40,9 +40,6 @@ async function loadPages() {
|
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// Update instance info tooltip
|
|
44
|
-
document.getElementById("switchInstance").title =
|
|
45
|
-
`PID: ${info.pid} | Events: ${info.events}`;
|
|
46
43
|
|
|
47
44
|
const pages = info.pages || [];
|
|
48
45
|
const select = document.getElementById("pageList");
|
|
@@ -266,26 +263,6 @@ document.getElementById("disableAllFilters").onclick = async () => {
|
|
|
266
263
|
setTimeout(updateFilters, 100);
|
|
267
264
|
};
|
|
268
265
|
|
|
269
|
-
// Switch to a different WebTap instance
|
|
270
|
-
document.getElementById("switchInstance").onclick = async () => {
|
|
271
|
-
const result = await api("/release", "POST");
|
|
272
|
-
if (!result.error) {
|
|
273
|
-
document.getElementById("status").innerHTML =
|
|
274
|
-
'<span style="color: #666">Port released. Start new WebTap.</span>';
|
|
275
|
-
// Disable controls until reconnected
|
|
276
|
-
document.getElementById("connect").disabled = true;
|
|
277
|
-
document.getElementById("disconnect").disabled = true;
|
|
278
|
-
document.getElementById("fetchToggle").disabled = true;
|
|
279
|
-
// Try to reconnect after delay
|
|
280
|
-
setTimeout(() => {
|
|
281
|
-
loadPages();
|
|
282
|
-
updateStatus();
|
|
283
|
-
}, 2000);
|
|
284
|
-
} else {
|
|
285
|
-
document.getElementById("status").innerHTML =
|
|
286
|
-
`<span class="error">Error: ${result.error}</span>`;
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
266
|
|
|
290
267
|
// Update all status from server - single source of truth
|
|
291
268
|
async function updateStatus() {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""WebTap - Chrome DevTools Protocol REPL.
|
|
2
|
+
|
|
3
|
+
Main entry point for WebTap browser debugging tool. Provides both REPL and MCP
|
|
4
|
+
functionality for Chrome DevTools Protocol interaction with native CDP event
|
|
5
|
+
storage and on-demand querying.
|
|
6
|
+
|
|
7
|
+
PUBLIC API:
|
|
8
|
+
- app: Main ReplKit2 App instance
|
|
9
|
+
- main: Entry point function for CLI
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from webtap.app import app
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
"""Entry point for the WebTap REPL.
|
|
19
|
+
|
|
20
|
+
Starts in one of three modes:
|
|
21
|
+
- CLI mode (with --cli flag) for command-line interface
|
|
22
|
+
- MCP mode (with --mcp flag) for Model Context Protocol server
|
|
23
|
+
- REPL mode (default) for interactive shell
|
|
24
|
+
|
|
25
|
+
The API server for Chrome extension communication must be started
|
|
26
|
+
explicitly using the server('start') command.
|
|
27
|
+
"""
|
|
28
|
+
if "--mcp" in sys.argv:
|
|
29
|
+
app.mcp.run()
|
|
30
|
+
elif "--cli" in sys.argv:
|
|
31
|
+
# Remove --cli from argv before passing to Typer
|
|
32
|
+
sys.argv.remove("--cli")
|
|
33
|
+
app.cli() # Run CLI mode via Typer
|
|
34
|
+
else:
|
|
35
|
+
# Run REPL
|
|
36
|
+
app.run(title="WebTap - Chrome DevTools Protocol REPL")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
__all__ = ["app", "main"]
|
|
@@ -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
|
|
|
@@ -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"]
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
"""WebTap - Chrome DevTools Protocol REPL.
|
|
2
|
-
|
|
3
|
-
Main entry point for WebTap browser debugging tool. Provides both REPL and MCP
|
|
4
|
-
functionality for Chrome DevTools Protocol interaction with native CDP event
|
|
5
|
-
storage and on-demand querying.
|
|
6
|
-
|
|
7
|
-
PUBLIC API:
|
|
8
|
-
- app: Main ReplKit2 App instance
|
|
9
|
-
- main: Entry point function for CLI
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
import atexit
|
|
13
|
-
import sys
|
|
14
|
-
import logging
|
|
15
|
-
|
|
16
|
-
from webtap.app import app
|
|
17
|
-
from webtap.api import start_api_server
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def main():
|
|
23
|
-
"""Entry point for the WebTap REPL.
|
|
24
|
-
|
|
25
|
-
Starts in one of three modes:
|
|
26
|
-
- CLI mode (with --cli flag) for command-line interface
|
|
27
|
-
- MCP mode (with --mcp flag) for Model Context Protocol server
|
|
28
|
-
- REPL mode (default) for interactive shell
|
|
29
|
-
|
|
30
|
-
In REPL and MCP modes, the API server is started for Chrome extension
|
|
31
|
-
integration. The API server runs in background to handle extension requests.
|
|
32
|
-
"""
|
|
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
|
-
if "--mcp" in sys.argv:
|
|
38
|
-
app.mcp.run()
|
|
39
|
-
elif "--cli" in sys.argv:
|
|
40
|
-
# Remove --cli from argv before passing to Typer
|
|
41
|
-
sys.argv.remove("--cli")
|
|
42
|
-
app.cli() # Run CLI mode via Typer
|
|
43
|
-
else:
|
|
44
|
-
# Run REPL
|
|
45
|
-
app.run(title="WebTap - Chrome DevTools Protocol REPL")
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
__all__ = ["app", "main"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|