webtap-tool 0.7.0__tar.gz → 0.7.1__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.

Files changed (59) hide show
  1. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/CHANGELOG.md +16 -0
  2. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/PKG-INFO +1 -1
  3. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/pyproject.toml +1 -1
  4. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/cdp/session.py +92 -5
  5. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/main.py +2 -2
  6. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/.gitignore +0 -0
  7. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/ARCHITECTURE.md +0 -0
  8. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/README.md +0 -0
  9. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/data/filters.json +0 -0
  10. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/extension/content.js +0 -0
  11. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/extension/manifest.json +0 -0
  12. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/extension/sidepanel.html +0 -0
  13. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/extension/sidepanel.js +0 -0
  14. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/llms.txt +0 -0
  15. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/VISION.md +0 -0
  16. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/__init__.py +0 -0
  17. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/api.py +0 -0
  18. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/app.py +0 -0
  19. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/cdp/README.md +0 -0
  20. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/cdp/__init__.py +0 -0
  21. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/cdp/query.py +0 -0
  22. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/cdp/schema/README.md +0 -0
  23. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/cdp/schema/cdp_protocol.json +0 -0
  24. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/cdp/schema/cdp_version.json +0 -0
  25. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/DEVELOPER_GUIDE.md +0 -0
  26. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/TIPS.md +0 -0
  27. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/__init__.py +0 -0
  28. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/_builders.py +0 -0
  29. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/_tips.py +0 -0
  30. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/_utils.py +0 -0
  31. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/body.py +0 -0
  32. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/connection.py +0 -0
  33. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/console.py +0 -0
  34. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/events.py +0 -0
  35. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/fetch.py +0 -0
  36. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/filters.py +0 -0
  37. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/inspect.py +0 -0
  38. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/javascript.py +0 -0
  39. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/launch.py +0 -0
  40. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/navigation.py +0 -0
  41. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/network.py +0 -0
  42. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/selections.py +0 -0
  43. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/server.py +0 -0
  44. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/setup.py +0 -0
  45. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/commands/to_model.py +0 -0
  46. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/filters.py +0 -0
  47. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/README.md +0 -0
  48. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/__init__.py +0 -0
  49. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/body.py +0 -0
  50. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/console.py +0 -0
  51. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/dom.py +0 -0
  52. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/fetch.py +0 -0
  53. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/network.py +0 -0
  54. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/setup/__init__.py +0 -0
  55. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/setup/chrome.py +0 -0
  56. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/setup/desktop.py +0 -0
  57. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/setup/extension.py +0 -0
  58. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/setup/filters.py +0 -0
  59. {webtap_tool-0.7.0 → webtap_tool-0.7.1}/src/webtap/services/setup/platform.py +0 -0
@@ -15,6 +15,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15
15
 
16
16
  ### Removed
17
17
 
18
+ ## [0.7.1] - 2025-10-12
19
+
20
+ ### Added
21
+
22
+ ### Changed
23
+
24
+ ### Fixed
25
+ - **CRITICAL: DuckDB thread safety** - Fixed malloc corruption crashes during browsing
26
+ - Implemented dedicated database thread with queue-based communication pattern
27
+ - All database operations now serialized through single thread (WebSocket, FastAPI, REPL threads)
28
+ - Fixed `WebTapService.event_count` property bypassing queue protection
29
+ - Resolved "malloc(): unaligned tcache chunk detected" segfaults
30
+ - Pattern: All threads → `_db_execute()` → queue → `_db_worker` thread → DuckDB
31
+
32
+ ### Removed
33
+
18
34
  ## [0.7.0] - 2025-10-10
19
35
 
20
36
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webtap-tool
3
- Version: 0.7.0
3
+ Version: 0.7.1
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "webtap-tool"
3
- version = "0.7.0"
3
+ version = "0.7.1"
4
4
  description = "Terminal-based web page inspector for AI debugging sessions"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -6,6 +6,7 @@ PUBLIC API:
6
6
 
7
7
  import json
8
8
  import logging
9
+ import queue
9
10
  import threading
10
11
  from concurrent.futures import Future, TimeoutError
11
12
  from typing import Any
@@ -54,9 +55,18 @@ class CDPSession:
54
55
  self._lock = threading.Lock()
55
56
 
56
57
  # DuckDB storage - store events AS-IS
58
+ # DuckDB connections are NOT thread-safe - use dedicated DB thread
57
59
  self.db = duckdb.connect(":memory:")
60
+ self._db_work_queue: queue.Queue = queue.Queue()
61
+ self._db_result_queues: dict[int, queue.Queue] = {}
62
+ self._db_running = True
58
63
 
59
- self.db.execute("CREATE TABLE events (event JSON)")
64
+ # Start dedicated database thread
65
+ self._db_thread = threading.Thread(target=self._db_worker, daemon=True)
66
+ self._db_thread.start()
67
+
68
+ # Initialize schema via queue
69
+ self._db_execute("CREATE TABLE events (event JSON)", wait_result=False)
60
70
 
61
71
  # Live field path lookup for fast discovery
62
72
  # Maps lowercase field names to their full paths with original case
@@ -71,6 +81,78 @@ class CDPSession:
71
81
  self._last_broadcast_time = 0.0
72
82
  self._broadcast_debounce = 1.0 # 1 second debounce
73
83
 
84
+ def _db_worker(self) -> None:
85
+ """Dedicated thread for all database operations.
86
+
87
+ Ensures thread safety by serializing all DuckDB access through one thread.
88
+ DuckDB connections are not thread-safe - sharing them causes malloc corruption.
89
+ """
90
+ while self._db_running:
91
+ try:
92
+ task = self._db_work_queue.get(timeout=1)
93
+
94
+ if task is None: # Shutdown signal
95
+ break
96
+
97
+ operation_type, sql, params, result_queue_id = task
98
+
99
+ try:
100
+ if operation_type == "execute":
101
+ result = self.db.execute(sql, params or [])
102
+ data = result.fetchall() if result else []
103
+ elif operation_type == "delete":
104
+ self.db.execute(sql, params or [])
105
+ data = None
106
+ else:
107
+ data = None
108
+
109
+ # Send result back if requested
110
+ if result_queue_id and result_queue_id in self._db_result_queues:
111
+ self._db_result_queues[result_queue_id].put(("success", data))
112
+
113
+ except Exception as e:
114
+ logger.error(f"Database error: {e}")
115
+ if result_queue_id and result_queue_id in self._db_result_queues:
116
+ self._db_result_queues[result_queue_id].put(("error", str(e)))
117
+
118
+ finally:
119
+ self._db_work_queue.task_done()
120
+
121
+ except queue.Empty:
122
+ continue
123
+
124
+ def _db_execute(self, sql: str, params: list | None = None, wait_result: bool = True) -> Any:
125
+ """Submit database operation to dedicated thread.
126
+
127
+ Args:
128
+ sql: SQL query or command
129
+ params: Optional query parameters
130
+ wait_result: Block until operation completes and return result
131
+
132
+ Returns:
133
+ Query results if wait_result=True, None otherwise
134
+ """
135
+ result_queue_id = None
136
+ result_queue = None
137
+
138
+ if wait_result:
139
+ result_queue_id = id(threading.current_thread())
140
+ result_queue = queue.Queue()
141
+ self._db_result_queues[result_queue_id] = result_queue
142
+
143
+ # Submit to work queue
144
+ self._db_work_queue.put(("execute", sql, params, result_queue_id))
145
+
146
+ if wait_result and result_queue and result_queue_id:
147
+ status, data = result_queue.get()
148
+ del self._db_result_queues[result_queue_id]
149
+
150
+ if status == "error":
151
+ raise RuntimeError(f"Database error: {data}")
152
+ return data
153
+
154
+ return None
155
+
74
156
  def list_pages(self) -> list[dict]:
75
157
  """List available Chrome pages via HTTP API.
76
158
 
@@ -158,6 +240,12 @@ class CDPSession:
158
240
  self.ws_thread.join(timeout=2)
159
241
  self.ws_thread = None
160
242
 
243
+ # Shutdown database thread
244
+ self._db_running = False
245
+ self._db_work_queue.put(None) # Signal shutdown
246
+ if self._db_thread.is_alive():
247
+ self._db_thread.join(timeout=2)
248
+
161
249
  self.connected.clear()
162
250
  self.page_info = None
163
251
 
@@ -245,7 +333,7 @@ class CDPSession:
245
333
 
246
334
  # CDP event - store AS-IS in DuckDB and update field lookup
247
335
  elif "method" in data:
248
- self.db.execute("INSERT INTO events VALUES (?)", [json.dumps(data)])
336
+ self._db_execute("INSERT INTO events VALUES (?)", [json.dumps(data)], wait_result=False)
249
337
  self._update_field_lookup(data)
250
338
 
251
339
  # Call registered event callbacks
@@ -332,7 +420,7 @@ class CDPSession:
332
420
 
333
421
  def clear_events(self) -> None:
334
422
  """Clear all stored events and reset field lookup."""
335
- self.db.execute("DELETE FROM events")
423
+ self._db_execute("DELETE FROM events", wait_result=False)
336
424
  self.field_paths.clear()
337
425
 
338
426
  def query(self, sql: str, params: list | None = None) -> list:
@@ -352,8 +440,7 @@ class CDPSession:
352
440
  query("SELECT * FROM events WHERE json_extract_string(event, '$.method') = 'Network.responseReceived'")
353
441
  query("SELECT json_extract_string(event, '$.params.request.url') as url FROM events")
354
442
  """
355
- result = self.db.execute(sql, params or [])
356
- return result.fetchall() if result else []
443
+ return self._db_execute(sql, params)
357
444
 
358
445
  def fetch_body(self, request_id: str) -> dict | None:
359
446
  """Fetch response body via Network.getResponseBody CDP call.
@@ -81,8 +81,8 @@ class WebTapService:
81
81
  if not self.cdp or not self.cdp.is_connected:
82
82
  return 0
83
83
  try:
84
- result = self.cdp.db.execute("SELECT COUNT(*) FROM events").fetchone()
85
- return result[0] if result else 0
84
+ result = self.cdp.query("SELECT COUNT(*) FROM events")
85
+ return result[0][0] if result else 0
86
86
  except Exception:
87
87
  return 0
88
88
 
File without changes
File without changes
File without changes
File without changes