webtap-tool 0.1.3__py3-none-any.whl → 0.1.4__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 +5 -1
- webtap/api.py +65 -58
- webtap/app.py +15 -0
- {webtap_tool-0.1.3.dist-info → webtap_tool-0.1.4.dist-info}/METADATA +1 -1
- {webtap_tool-0.1.3.dist-info → webtap_tool-0.1.4.dist-info}/RECORD +7 -7
- {webtap_tool-0.1.3.dist-info → webtap_tool-0.1.4.dist-info}/WHEEL +0 -0
- {webtap_tool-0.1.3.dist-info → webtap_tool-0.1.4.dist-info}/entry_points.txt +0 -0
webtap/__init__.py
CHANGED
|
@@ -9,6 +9,7 @@ PUBLIC API:
|
|
|
9
9
|
- main: Entry point function for CLI
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
+
import atexit
|
|
12
13
|
import sys
|
|
13
14
|
import logging
|
|
14
15
|
|
|
@@ -45,12 +46,15 @@ def main():
|
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
def _start_api_server_safely():
|
|
48
|
-
"""Start API server with error handling."""
|
|
49
|
+
"""Start API server with error handling and cleanup registration."""
|
|
49
50
|
try:
|
|
50
51
|
thread = start_api_server(app.state)
|
|
51
52
|
if thread and app.state:
|
|
52
53
|
app.state.api_thread = thread
|
|
53
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)
|
|
54
58
|
else:
|
|
55
59
|
logger.info("Port 8765 in use by another instance")
|
|
56
60
|
except Exception as e:
|
webtap/api.py
CHANGED
|
@@ -50,22 +50,54 @@ api.add_middleware(
|
|
|
50
50
|
app_state = None
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
@api.get("/
|
|
54
|
-
async def
|
|
55
|
-
"""
|
|
53
|
+
@api.get("/health")
|
|
54
|
+
async def health_check() -> Dict[str, Any]:
|
|
55
|
+
"""Quick health check endpoint for extension."""
|
|
56
|
+
return {"status": "ok", "pid": os.getpid()}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@api.get("/info")
|
|
60
|
+
async def get_info() -> Dict[str, Any]:
|
|
61
|
+
"""Combined endpoint for pages and instance info - reduces round trips."""
|
|
56
62
|
if not app_state:
|
|
57
|
-
return {"error": "WebTap not initialized", "pages": []}
|
|
63
|
+
return {"error": "WebTap not initialized", "pages": [], "pid": os.getpid()}
|
|
64
|
+
|
|
65
|
+
# Get pages
|
|
66
|
+
pages_data = app_state.service.list_pages()
|
|
67
|
+
|
|
68
|
+
# Get instance info
|
|
69
|
+
connected_to = None
|
|
70
|
+
if app_state.cdp.is_connected and app_state.cdp.page_info:
|
|
71
|
+
connected_to = app_state.cdp.page_info.get("title", "Untitled")
|
|
58
72
|
|
|
59
|
-
return
|
|
73
|
+
return {
|
|
74
|
+
"pid": os.getpid(),
|
|
75
|
+
"connected_to": connected_to,
|
|
76
|
+
"events": app_state.service.event_count,
|
|
77
|
+
"pages": pages_data.get("pages", []),
|
|
78
|
+
"error": pages_data.get("error"),
|
|
79
|
+
}
|
|
60
80
|
|
|
61
81
|
|
|
62
82
|
@api.get("/status")
|
|
63
83
|
async def get_status() -> Dict[str, Any]:
|
|
64
|
-
"""Get
|
|
84
|
+
"""Get comprehensive status including connection, events, and fetch details."""
|
|
65
85
|
if not app_state:
|
|
66
86
|
return {"connected": False, "error": "WebTap not initialized", "events": 0}
|
|
67
87
|
|
|
68
|
-
|
|
88
|
+
status = app_state.service.get_status()
|
|
89
|
+
|
|
90
|
+
# Add fetch details if fetch is enabled
|
|
91
|
+
if status.get("fetch_enabled"):
|
|
92
|
+
fetch_service = app_state.service.fetch
|
|
93
|
+
paused_list = fetch_service.get_paused_list()
|
|
94
|
+
status["fetch_details"] = {
|
|
95
|
+
"paused_requests": paused_list,
|
|
96
|
+
"paused_count": len(paused_list),
|
|
97
|
+
"response_stage": fetch_service.enable_response_stage,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return status
|
|
69
101
|
|
|
70
102
|
|
|
71
103
|
@api.post("/connect")
|
|
@@ -108,25 +140,6 @@ async def set_fetch_interception(request: FetchRequest) -> Dict[str, Any]:
|
|
|
108
140
|
return result
|
|
109
141
|
|
|
110
142
|
|
|
111
|
-
@api.get("/fetch/paused")
|
|
112
|
-
async def get_paused_requests() -> Dict[str, Any]:
|
|
113
|
-
"""Get list of currently paused fetch requests."""
|
|
114
|
-
if not app_state:
|
|
115
|
-
return {"error": "WebTap not initialized", "requests": []}
|
|
116
|
-
|
|
117
|
-
fetch_service = app_state.service.fetch
|
|
118
|
-
if not fetch_service.enabled:
|
|
119
|
-
return {"enabled": False, "requests": []}
|
|
120
|
-
|
|
121
|
-
paused_list = fetch_service.get_paused_list()
|
|
122
|
-
return {
|
|
123
|
-
"enabled": True,
|
|
124
|
-
"requests": paused_list,
|
|
125
|
-
"count": len(paused_list),
|
|
126
|
-
"response_stage": fetch_service.enable_response_stage,
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
143
|
@api.get("/filters/status")
|
|
131
144
|
async def get_filter_status() -> Dict[str, Any]:
|
|
132
145
|
"""Get current filter configuration and enabled categories."""
|
|
@@ -186,19 +199,6 @@ async def disable_all_filters() -> Dict[str, Any]:
|
|
|
186
199
|
return {"enabled": [], "total": 0}
|
|
187
200
|
|
|
188
201
|
|
|
189
|
-
@api.get("/instance")
|
|
190
|
-
async def get_instance_info() -> Dict[str, Any]:
|
|
191
|
-
"""Get info about this WebTap instance."""
|
|
192
|
-
if not app_state:
|
|
193
|
-
return {"error": "WebTap not initialized"}
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
"pid": os.getpid(),
|
|
197
|
-
"connected_to": app_state.cdp.current_page_title if app_state.cdp.is_connected else None,
|
|
198
|
-
"events": app_state.cdp.event_count,
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
202
|
@api.post("/release")
|
|
203
203
|
async def release_port() -> Dict[str, Any]:
|
|
204
204
|
"""Release API port for another WebTap instance."""
|
|
@@ -237,8 +237,9 @@ def start_api_server(state, host: str = "127.0.0.1", port: int = 8765) -> thread
|
|
|
237
237
|
logger.info(f"Port {port} already in use")
|
|
238
238
|
return None
|
|
239
239
|
|
|
240
|
-
global app_state
|
|
240
|
+
global app_state, _shutdown_requested
|
|
241
241
|
app_state = state
|
|
242
|
+
_shutdown_requested = False # Reset flag for new instance
|
|
242
243
|
|
|
243
244
|
thread = threading.Thread(target=run_server, args=(host, port), daemon=True)
|
|
244
245
|
thread.start()
|
|
@@ -249,7 +250,10 @@ def start_api_server(state, host: str = "127.0.0.1", port: int = 8765) -> thread
|
|
|
249
250
|
|
|
250
251
|
def run_server(host: str, port: int):
|
|
251
252
|
"""Run the FastAPI server in a thread."""
|
|
252
|
-
|
|
253
|
+
import asyncio
|
|
254
|
+
|
|
255
|
+
async def run():
|
|
256
|
+
"""Run server with proper shutdown handling."""
|
|
253
257
|
config = uvicorn.Config(
|
|
254
258
|
api,
|
|
255
259
|
host=host,
|
|
@@ -259,30 +263,33 @@ def run_server(host: str, port: int):
|
|
|
259
263
|
)
|
|
260
264
|
server = uvicorn.Server(config)
|
|
261
265
|
|
|
262
|
-
#
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
loop = asyncio.new_event_loop()
|
|
266
|
-
asyncio.set_event_loop(loop)
|
|
266
|
+
# Start server in background task
|
|
267
|
+
serve_task = asyncio.create_task(server.serve())
|
|
267
268
|
|
|
268
|
-
|
|
269
|
-
await server.serve()
|
|
270
|
-
|
|
271
|
-
# Start serving
|
|
272
|
-
task = loop.create_task(serve())
|
|
273
|
-
|
|
274
|
-
# Check for shutdown flag
|
|
269
|
+
# Wait for shutdown signal
|
|
275
270
|
while not _shutdown_requested:
|
|
276
|
-
|
|
277
|
-
if
|
|
271
|
+
await asyncio.sleep(0.1)
|
|
272
|
+
if serve_task.done():
|
|
278
273
|
break
|
|
279
274
|
|
|
280
|
-
#
|
|
281
|
-
if
|
|
275
|
+
# Trigger shutdown
|
|
276
|
+
if not serve_task.done():
|
|
282
277
|
logger.info("API server shutting down")
|
|
283
278
|
server.should_exit = True
|
|
284
|
-
|
|
279
|
+
# Wait a bit for graceful shutdown
|
|
280
|
+
try:
|
|
281
|
+
await asyncio.wait_for(serve_task, timeout=1.0)
|
|
282
|
+
except asyncio.TimeoutError:
|
|
283
|
+
logger.debug("Server task timeout - cancelling")
|
|
284
|
+
serve_task.cancel()
|
|
285
|
+
try:
|
|
286
|
+
await serve_task
|
|
287
|
+
except asyncio.CancelledError:
|
|
288
|
+
pass
|
|
285
289
|
|
|
290
|
+
try:
|
|
291
|
+
# Use asyncio.run() which properly cleans up
|
|
292
|
+
asyncio.run(run())
|
|
286
293
|
except Exception as e:
|
|
287
294
|
logger.error(f"API server failed: {e}")
|
|
288
295
|
|
webtap/app.py
CHANGED
|
@@ -36,6 +36,21 @@ class WebTapState:
|
|
|
36
36
|
"""Initialize service with self reference after dataclass init."""
|
|
37
37
|
self.service = WebTapService(self)
|
|
38
38
|
|
|
39
|
+
def cleanup(self):
|
|
40
|
+
"""Cleanup resources on exit."""
|
|
41
|
+
# Disconnect CDP if connected
|
|
42
|
+
if self.cdp.is_connected:
|
|
43
|
+
self.cdp.disconnect()
|
|
44
|
+
|
|
45
|
+
# Stop API server if we own it
|
|
46
|
+
if self.api_thread and self.api_thread.is_alive():
|
|
47
|
+
# Import here to avoid circular dependency
|
|
48
|
+
import webtap.api
|
|
49
|
+
|
|
50
|
+
webtap.api._shutdown_requested = True
|
|
51
|
+
# Wait up to 1 second for graceful shutdown
|
|
52
|
+
self.api_thread.join(timeout=1.0)
|
|
53
|
+
|
|
39
54
|
|
|
40
55
|
# Must be created before command imports for decorator registration
|
|
41
56
|
app = App(
|
|
@@ -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=6sTctXhLKByd8TnKJ99Y4o6xv7AbbPJ37S6YMNsQGBM,1970
|
|
3
|
+
webtap/api.py,sha256=mfAqxQfu2lF91jiI6kHQnpmwtwq0Hh9UtuV8ApENXe0,8504
|
|
4
|
+
webtap/app.py,sha256=M6-tCT6uvR5aMMYYUymYrXhq4PaUlHVYTv7MDN_svdQ,3246
|
|
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
|
|
@@ -37,7 +37,7 @@ webtap/services/fetch.py,sha256=nl6bpU2Vnf40kau4-mqAnIkhC-7Lx2vbTJKUglz9KnE,1360
|
|
|
37
37
|
webtap/services/main.py,sha256=HcXdPuI7hzsxsNvfN0npGhj_M7HObc83Lr3fuy7BMeE,5673
|
|
38
38
|
webtap/services/network.py,sha256=0o_--F6YvmXqqFqrcjL1gc6Vr9V1Ytb_U7r_DSUWupA,3444
|
|
39
39
|
webtap/services/setup.py,sha256=dXzqlXR37-chZO2qCsuiVq9N7fELXtND5jH_9QQwsws,7932
|
|
40
|
-
webtap_tool-0.1.
|
|
41
|
-
webtap_tool-0.1.
|
|
42
|
-
webtap_tool-0.1.
|
|
43
|
-
webtap_tool-0.1.
|
|
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,,
|
|
File without changes
|
|
File without changes
|