webtap-tool 0.1.2__py3-none-any.whl → 0.1.3__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 CHANGED
@@ -47,8 +47,12 @@ def main():
47
47
  def _start_api_server_safely():
48
48
  """Start API server with error handling."""
49
49
  try:
50
- start_api_server(app.state)
51
- logger.info("API server started on http://localhost:8765")
50
+ thread = start_api_server(app.state)
51
+ if thread and app.state:
52
+ app.state.api_thread = thread
53
+ logger.info("API server started on port 8765")
54
+ else:
55
+ logger.info("Port 8765 in use by another instance")
52
56
  except Exception as e:
53
57
  logger.warning(f"Failed to start API server: {e}")
54
58
 
webtap/api.py CHANGED
@@ -5,6 +5,8 @@ PUBLIC API:
5
5
  """
6
6
 
7
7
  import logging
8
+ import os
9
+ import socket
8
10
  import threading
9
11
  from typing import Any, Dict
10
12
 
@@ -184,7 +186,39 @@ async def disable_all_filters() -> Dict[str, Any]:
184
186
  return {"enabled": [], "total": 0}
185
187
 
186
188
 
187
- def start_api_server(state, host: str = "127.0.0.1", port: int = 8765):
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
+ @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
+ # Flag to signal shutdown
218
+ _shutdown_requested = False
219
+
220
+
221
+ def start_api_server(state, host: str = "127.0.0.1", port: int = 8765) -> threading.Thread | None:
188
222
  """Start the API server in a background thread.
189
223
 
190
224
  Args:
@@ -193,8 +227,16 @@ def start_api_server(state, host: str = "127.0.0.1", port: int = 8765):
193
227
  port: Port to bind to. Defaults to 8765.
194
228
 
195
229
  Returns:
196
- Thread instance running the server.
230
+ Thread instance running the server, or None if port is in use.
197
231
  """
232
+ # Check port availability first
233
+ try:
234
+ with socket.socket() as s:
235
+ s.bind((host, port))
236
+ except OSError:
237
+ logger.info(f"Port {port} already in use")
238
+ return None
239
+
198
240
  global app_state
199
241
  app_state = state
200
242
 
@@ -208,13 +250,39 @@ def start_api_server(state, host: str = "127.0.0.1", port: int = 8765):
208
250
  def run_server(host: str, port: int):
209
251
  """Run the FastAPI server in a thread."""
210
252
  try:
211
- uvicorn.run(
253
+ config = uvicorn.Config(
212
254
  api,
213
255
  host=host,
214
256
  port=port,
215
257
  log_level="error",
216
258
  access_log=False,
217
259
  )
260
+ server = uvicorn.Server(config)
261
+
262
+ # Run with checking for shutdown flag
263
+ import asyncio
264
+
265
+ loop = asyncio.new_event_loop()
266
+ asyncio.set_event_loop(loop)
267
+
268
+ async def serve():
269
+ await server.serve()
270
+
271
+ # Start serving
272
+ task = loop.create_task(serve())
273
+
274
+ # Check for shutdown flag
275
+ while not _shutdown_requested:
276
+ loop.run_until_complete(asyncio.sleep(0.1))
277
+ if task.done():
278
+ break
279
+
280
+ # Shutdown if requested
281
+ if _shutdown_requested:
282
+ logger.info("API server shutting down")
283
+ server.should_exit = True
284
+ loop.run_until_complete(server.shutdown())
285
+
218
286
  except Exception as e:
219
287
  logger.error(f"API server failed: {e}")
220
288
 
webtap/app.py CHANGED
@@ -6,6 +6,7 @@ PUBLIC API:
6
6
  """
7
7
 
8
8
  import sys
9
+ import threading
9
10
  from dataclasses import dataclass, field
10
11
 
11
12
  from replkit2 import App
@@ -24,10 +25,12 @@ class WebTapState:
24
25
  Attributes:
25
26
  cdp: Chrome DevTools Protocol session instance.
26
27
  service: WebTapService orchestrating all domain services.
28
+ api_thread: Thread running the FastAPI server (if this instance owns the port).
27
29
  """
28
30
 
29
31
  cdp: CDPSession = field(default_factory=CDPSession)
30
32
  service: WebTapService = field(init=False)
33
+ api_thread: threading.Thread | None = None
31
34
 
32
35
  def __post_init__(self):
33
36
  """Initialize service with self reference after dataclass init."""
webtap/cdp/query.py CHANGED
@@ -74,7 +74,8 @@ def build_query(
74
74
  pattern = value
75
75
  path_conditions.append(f"json_extract_string(event, '{json_path}') LIKE '{pattern}'")
76
76
  elif isinstance(value, (int, float)):
77
- path_conditions.append(f"CAST(json_extract_string(event, '{json_path}') AS NUMERIC) = {value}")
77
+ # Use string comparison for numeric values to avoid type conversion errors
78
+ path_conditions.append(f"json_extract_string(event, '{json_path}') = '{value}'")
78
79
  elif isinstance(value, bool):
79
80
  path_conditions.append(f"json_extract_string(event, '{json_path}') = '{str(value).lower()}'")
80
81
  elif value is None:
webtap/commands/launch.py CHANGED
@@ -46,23 +46,9 @@ def run_chrome(state, detach: bool = True, port: int = 9222) -> dict:
46
46
  ],
47
47
  )
48
48
 
49
- # Setup temp profile with symlinks to real profile
49
+ # Simple: use clean temp profile for debugging
50
50
  temp_config = Path("/tmp/webtap-chrome-debug")
51
- real_config = Path.home() / ".config" / "google-chrome"
52
-
53
- if not temp_config.exists():
54
- temp_config.mkdir(parents=True)
55
-
56
- # Symlink Default profile
57
- default_profile = real_config / "Default"
58
- if default_profile.exists():
59
- (temp_config / "Default").symlink_to(default_profile)
60
-
61
- # Copy essential files
62
- for file in ["Local State", "First Run"]:
63
- src = real_config / file
64
- if src.exists():
65
- (temp_config / file).write_text(src.read_text())
51
+ temp_config.mkdir(parents=True, exist_ok=True)
66
52
 
67
53
  # Launch Chrome
68
54
  cmd = [chrome_exe, f"--remote-debugging-port={port}", "--remote-allow-origins=*", f"--user-data-dir={temp_config}"]
@@ -74,7 +60,7 @@ def run_chrome(state, detach: bool = True, port: int = 9222) -> dict:
74
60
  details={
75
61
  "Port": str(port),
76
62
  "Mode": "Background (detached)",
77
- "Profile": str(temp_config),
63
+ "Profile": "Temporary (clean)",
78
64
  "Next step": "Run connect() to attach WebTap",
79
65
  },
80
66
  )
webtap/services/body.py CHANGED
@@ -12,7 +12,7 @@ logger = logging.getLogger(__name__)
12
12
 
13
13
 
14
14
  class BodyService:
15
- """Internal service for response body fetching and caching."""
15
+ """Response body fetching and caching."""
16
16
 
17
17
  def __init__(self):
18
18
  """Initialize body service."""
@@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
10
10
 
11
11
 
12
12
  class ConsoleService:
13
- """Internal service for console event queries and monitoring."""
13
+ """Console event queries and monitoring."""
14
14
 
15
15
  def __init__(self):
16
16
  """Initialize console service."""
webtap/services/fetch.py CHANGED
@@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
13
13
 
14
14
 
15
15
  class FetchService:
16
- """Internal service for fetch interception with explicit actions."""
16
+ """Fetch interception with explicit actions."""
17
17
 
18
18
  def __init__(self):
19
19
  """Initialize fetch service."""
@@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
10
10
 
11
11
 
12
12
  class NetworkService:
13
- """Internal service for network event queries and monitoring."""
13
+ """Network event queries and monitoring."""
14
14
 
15
15
  def __init__(self):
16
16
  """Initialize network service."""
@@ -81,7 +81,7 @@ class NetworkService:
81
81
  json_extract_string(event, '$.params.response.statusText') as StatusText
82
82
  FROM events
83
83
  WHERE json_extract_string(event, '$.method') = 'Network.responseReceived'
84
- AND CAST(json_extract_string(event, '$.params.response.status') AS INTEGER) >= 400
84
+ AND json_extract_string(event, '$.params.response.status') >= '400'
85
85
  ORDER BY rowid DESC LIMIT {limit}
86
86
  """
87
87
 
webtap/services/setup.py CHANGED
@@ -177,22 +177,32 @@ class SetupService:
177
177
  }
178
178
 
179
179
  wrapper_script = """#!/bin/bash
180
- # Chrome wrapper to always enable debugging
180
+ # Chrome wrapper using bindfs for perfect state sync with debug port
181
181
 
182
- REAL_CONFIG="$HOME/.config/google-chrome"
183
- DEBUG_CONFIG="/tmp/chrome-debug-profile"
182
+ DEBUG_DIR="$HOME/.config/google-chrome-debug"
183
+ REAL_DIR="$HOME/.config/google-chrome"
184
184
 
185
- if [ ! -d "$DEBUG_CONFIG" ]; then
186
- mkdir -p "$DEBUG_CONFIG"
187
- ln -sf "$REAL_CONFIG/Default" "$DEBUG_CONFIG/Default"
188
- cp "$REAL_CONFIG/Local State" "$DEBUG_CONFIG/" 2>/dev/null || true
189
- cp "$REAL_CONFIG/First Run" "$DEBUG_CONFIG/" 2>/dev/null || true
185
+ # Check if bindfs is installed
186
+ if ! command -v bindfs &>/dev/null; then
187
+ echo "Error: bindfs not installed. Install with: yay -S bindfs" >&2
188
+ exit 1
190
189
  fi
191
190
 
191
+ # Mount real profile via bindfs if not already mounted
192
+ if ! mountpoint -q "$DEBUG_DIR" 2>/dev/null; then
193
+ mkdir -p "$DEBUG_DIR"
194
+ if ! bindfs --no-allow-other "$REAL_DIR" "$DEBUG_DIR"; then
195
+ echo "Error: Failed to mount Chrome profile via bindfs" >&2
196
+ exit 1
197
+ fi
198
+ echo "Chrome debug profile mounted. To unmount: fusermount -u $DEBUG_DIR" >&2
199
+ fi
200
+
201
+ # Launch Chrome with debugging on bindfs mount
192
202
  exec /usr/bin/google-chrome-stable \\
193
203
  --remote-debugging-port=9222 \\
194
- --remote-allow-origins=* \\
195
- --user-data-dir="$DEBUG_CONFIG" \\
204
+ --remote-allow-origins='*' \\
205
+ --user-data-dir="$DEBUG_DIR" \\
196
206
  "$@"
197
207
  """
198
208
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webtap-tool
3
- Version: 0.1.2
3
+ Version: 0.1.3
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,11 +1,11 @@
1
1
  webtap/VISION.md,sha256=kfoJfEPVc4chOrD9tNMDmYBY9rX9KB-286oZj70ALCE,7681
2
- webtap/__init__.py,sha256=iMD7Y3DMnmd-psxLdG9uLeo9qXfCDDzUQ52lVo5LWOU,1633
3
- webtap/api.py,sha256=I4CyOlgJW_peDn94DIeq5945G9BAbCzOqMmf_7YUm_g,6060
4
- webtap/app.py,sha256=RgLGWUc39o1AOnXxpXg7WVbNk3X8If3Q7nbGeqPaHMw,2586
2
+ webtap/__init__.py,sha256=4MD5ir_kyfHcI3kND_As9jF-UUqGEzWKV5Cg6PKzef4,1787
3
+ webtap/api.py,sha256=1VV_YPwvGuYanDm5M_NuWUYiGIQbPd3IKicK5Jplq44,7987
4
+ webtap/app.py,sha256=ISPSmwqCZ2R_kVDmFEsBLltzfbHGUkzcczTGCs2KcDI,2738
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
8
- webtap/cdp/query.py,sha256=ULfc8ddHnX_uHezaVLblPOzV6tTyu3ohOc6XlE3wPa0,4153
8
+ webtap/cdp/query.py,sha256=x2Cy7KMolYkTelpROGezOfMFgYnbSlCvHkvvW1v_gLI,4229
9
9
  webtap/cdp/session.py,sha256=BTajmDH6nel2dvia7IX9k6C-RinkN2NxY4rxtUJAJE0,12362
10
10
  webtap/cdp/schema/README.md,sha256=hnWCzbXYcYtWaZb_SgjVaFBiG81S9b9Y3x-euQFwQDo,1222
11
11
  webtap/cdp/schema/cdp_protocol.json,sha256=dp9_OLYLuVsQb1oV5r6MZfMzURscBLyAXUckdaPWyv4,1488452
@@ -25,19 +25,19 @@ webtap/commands/fetch.py,sha256=_TzOvJfVzPaw4ZmyI95Qb7rS3iKx2nmp_IL3jaQO_6g,7772
25
25
  webtap/commands/filters.py,sha256=trZvcbHTaF0FZC8dyMAmhmS2dlBoA82VXe5_DDS3eU8,8986
26
26
  webtap/commands/inspect.py,sha256=6PGN7iDT1oLzQJboNeYozLILrW2VsAzmtMpF3_XhD30,5746
27
27
  webtap/commands/javascript.py,sha256=QpQdqqoQwwTyz1lpibZ92XKOL89scu_ndgSjkhaYuDk,3195
28
- webtap/commands/launch.py,sha256=-DonCgu7LakyTIN5ksGjY-8CiQzQiQ2SAamBCOvLWrw,2687
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
31
  webtap/commands/setup.py,sha256=ewoOTvgCAzMPcFm6cbk-93nY_BI2IXqPZGGGpc1rJiw,4459
32
32
  webtap/services/README.md,sha256=rala_jtnNgSiQ1lFLM7x_UQ4SJZDceAm7dpkQMRTYaI,2346
33
33
  webtap/services/__init__.py,sha256=IjFqu0Ak6D-r18aokcQMtenDV3fbelvfjTCejGv6CZ0,570
34
- webtap/services/body.py,sha256=tU60N-efOvTMndDGLcz0rf921VHiRmczDEs-qtECMng,3934
35
- webtap/services/console.py,sha256=W1FGSuLl1bgZzpD9bVu98wlHGupe12vA7YqDLXFszYs,3789
36
- webtap/services/fetch.py,sha256=hKrhFda1x65GmLHO0XMkxbclMtkWt0uEv7Rp9Ui2na0,13623
34
+ webtap/services/body.py,sha256=XQPa19y5eUc3XJ2TuwVK6kffO1VQoKqNs33MBBz7hzU,3913
35
+ webtap/services/console.py,sha256=XVfSKTvEHyyOdujsg85S3wtj1CdZhzKtWwlx25MvSv8,3768
36
+ webtap/services/fetch.py,sha256=nl6bpU2Vnf40kau4-mqAnIkhC-7Lx2vbTJKUglz9KnE,13602
37
37
  webtap/services/main.py,sha256=HcXdPuI7hzsxsNvfN0npGhj_M7HObc83Lr3fuy7BMeE,5673
38
- webtap/services/network.py,sha256=EJZIlaE98v113xypPIiiTE-LiG-eCXGNXoJjzDYebhU,3480
39
- webtap/services/setup.py,sha256=DF2471WsY5pJ5gEn64EMGRW5t9mUyuTQoCTejOJeQxg,7594
40
- webtap_tool-0.1.2.dist-info/METADATA,sha256=IZzOzUP2iPmmNVcjgBNLkAqy3lmF0aEpMLVb5DaV-pE,17457
41
- webtap_tool-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
42
- webtap_tool-0.1.2.dist-info/entry_points.txt,sha256=iFe575I0CIb1MbfPt0oX2VYyY5gSU_dA551PKVR83TU,39
43
- webtap_tool-0.1.2.dist-info/RECORD,,
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.3.dist-info/METADATA,sha256=8fezx8WvoSf5sTT5-2ibcGwLDvVldjPLpGndETihBIw,17457
41
+ webtap_tool-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
42
+ webtap_tool-0.1.3.dist-info/entry_points.txt,sha256=iFe575I0CIb1MbfPt0oX2VYyY5gSU_dA551PKVR83TU,39
43
+ webtap_tool-0.1.3.dist-info/RECORD,,