escobar 0.1.73__py3-none-any.whl → 0.1.75__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.
Files changed (27) hide show
  1. escobar/_version.py +1 -1
  2. escobar/handlers.py +74 -13
  3. escobar/labextension/package.json +2 -2
  4. escobar/labextension/schemas/escobar/package.json.orig +1 -1
  5. escobar/labextension/static/{589.d1cb43ef7438dbdd0369.js → 589.3121fce930587f479cb5.js} +1 -1
  6. escobar/labextension/static/{remoteEntry.c7b59f5a7472168cb072.js → remoteEntry.3532abc0f85f2876fb20.js} +1 -1
  7. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/package.json +2 -2
  8. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/schemas/escobar/package.json.orig +1 -1
  9. escobar-0.1.73.data/data/share/jupyter/labextensions/escobar/static/589.d1cb43ef7438dbdd0369.js → escobar-0.1.75.data/data/share/jupyter/labextensions/escobar/static/589.3121fce930587f479cb5.js +1 -1
  10. escobar-0.1.73.data/data/share/jupyter/labextensions/escobar/static/remoteEntry.c7b59f5a7472168cb072.js → escobar-0.1.75.data/data/share/jupyter/labextensions/escobar/static/remoteEntry.3532abc0f85f2876fb20.js +1 -1
  11. {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/METADATA +1 -1
  12. {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/RECORD +27 -27
  13. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/install.json +0 -0
  14. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/schemas/escobar/plugin.json +0 -0
  15. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/304.bf7e91be734e5b36cdc9.js +0 -0
  16. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/346.8a1e61ca6789b6fddfa7.js +0 -0
  17. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/379.40dd59dc19d4a6b42d25.js +0 -0
  18. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/57.17e53b4b9a954f39c4d8.js +0 -0
  19. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/648.a7d314faeacc762d891d.js +0 -0
  20. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/666.3bc65aac3a3be183ee19.js +0 -0
  21. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/874.c539726f31a1baa0267e.js +0 -0
  22. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/986.cbcf9d7c1cd8d06be435.js +0 -0
  23. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/style.js +0 -0
  24. {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/third-party-licenses.json +0 -0
  25. {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/WHEEL +0 -0
  26. {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/entry_points.txt +0 -0
  27. {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/licenses/LICENSE +0 -0
escobar/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # This file is auto-generated by Hatchling. As such, do not:
2
2
  # - modify
3
3
  # - track in version control e.g. be sure to add to .gitignore
4
- __version__ = VERSION = '0.1.73'
4
+ __version__ = VERSION = '0.1.75'
escobar/handlers.py CHANGED
@@ -6,6 +6,7 @@ import websockets
6
6
  import ssl
7
7
  import logging
8
8
  import time
9
+ from urllib.parse import urlparse, urlunparse
9
10
  from jupyter_server.base.handlers import JupyterHandler
10
11
  from jupyter_server.utils import url_path_join
11
12
  import tornado
@@ -153,6 +154,61 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
153
154
  WebSocket proxy handler that forwards connections from /ws to target server
154
155
  """
155
156
 
157
+ def _resolve_target_url_for_docker(self, url):
158
+ """
159
+ Resolve target URL for Docker environment.
160
+ Replace localhost/127.0.0.1 with Docker host IP when running in container.
161
+ """
162
+ # Enhanced Docker detection with multiple methods
163
+ docker_indicators = []
164
+
165
+ # Method 1: Check for /.dockerenv file
166
+ dockerenv_exists = os.path.exists('/.dockerenv')
167
+ docker_indicators.append(f"/.dockerenv exists: {dockerenv_exists}")
168
+
169
+ # Method 2: Check environment variable
170
+ docker_env = os.getenv('DOCKER_CONTAINER') == 'true'
171
+ docker_indicators.append(f"DOCKER_CONTAINER env: {docker_env}")
172
+
173
+ # Method 3: Check /proc/1/cgroup for docker
174
+ cgroup_docker = False
175
+ try:
176
+ if os.path.exists('/proc/1/cgroup'):
177
+ with open('/proc/1/cgroup', 'r') as f:
178
+ cgroup_content = f.read()
179
+ cgroup_docker = 'docker' in cgroup_content or 'containerd' in cgroup_content
180
+ docker_indicators.append(f"/proc/1/cgroup contains docker/containerd: {cgroup_docker}")
181
+ except Exception as e:
182
+ docker_indicators.append(f"/proc/1/cgroup check failed: {e}")
183
+
184
+ # Determine if we're in Docker
185
+ is_docker = dockerenv_exists or docker_env or cgroup_docker
186
+
187
+ print(f"[ESCOBAR-WS] Docker detection indicators: {docker_indicators}")
188
+ print(f"[ESCOBAR-WS] Final Docker detection result: {is_docker}")
189
+
190
+ if not is_docker:
191
+ print(f"[ESCOBAR-WS] Not in Docker container, using original URL: {url}")
192
+ return url
193
+
194
+ # Parse the URL to extract components
195
+ parsed = urlparse(url)
196
+ print(f"[ESCOBAR-WS] Parsed URL - hostname: '{parsed.hostname}', netloc: '{parsed.netloc}'")
197
+
198
+ # Check if hostname is localhost or 127.0.0.1
199
+ if parsed.hostname in ['localhost', '127.0.0.1']:
200
+ # Replace with Docker host IP
201
+ new_netloc = parsed.netloc.replace(parsed.hostname, '172.17.0.1')
202
+ new_parsed = parsed._replace(netloc=new_netloc)
203
+ new_url = urlunparse(new_parsed)
204
+
205
+ print(f"[ESCOBAR-WS] Docker hostname resolution: {url} → {new_url}")
206
+ return new_url
207
+ else:
208
+ print(f"[ESCOBAR-WS] Docker container detected, but hostname '{parsed.hostname}' is not localhost/127.0.0.1, keeping original: {url}")
209
+
210
+ return url
211
+
156
212
  def __init__(self, *args, **kwargs):
157
213
  print(f"[ESCOBAR-WS] WebSocketProxyHandler.__init__ called")
158
214
  super().__init__(*args, **kwargs)
@@ -166,9 +222,10 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
166
222
  except:
167
223
  print(f"[ESCOBAR-WS] Could not get hostname")
168
224
 
169
- # Read target URL from environment variable
170
- self.target_url = os.getenv('WEBSOCKET_PROXY_TARGET', 'ws://localhost:8777/ws')
171
- print(f"[ESCOBAR-WS] Target URL will be: {self.target_url}")
225
+ # Store raw target URL from environment variable (resolve per-connection)
226
+ self.raw_target_url = os.getenv('WEBSOCKET_PROXY_TARGET', 'ws://localhost:8777/ws')
227
+ print(f"[ESCOBAR-WS] Raw target URL stored: {self.raw_target_url}")
228
+ print(f"[ESCOBAR-WS] Docker resolution will be applied per-connection")
172
229
 
173
230
  # Debug all WebSocket-related environment variables
174
231
  websocket_env_vars = [(k, v) for k, v in os.environ.items() if 'WEBSOCKET' in k.upper()]
@@ -184,11 +241,15 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
184
241
  """Called when websocket connection is opened"""
185
242
  start_time = time.time()
186
243
 
244
+ # Apply Docker hostname resolution for this connection
245
+ target_url = self._resolve_target_url_for_docker(self.raw_target_url)
246
+
187
247
  # Log which endpoint was accessed and path normalization
188
248
  request_path = self.request.path
189
249
  print(f"[ESCOBAR-WS] === CLIENT CONNECTION OPENED ===")
190
250
  print(f"[ESCOBAR-WS] Client connected via: {request_path}")
191
- print(f"[ESCOBAR-WS] Target remains: {self.target_url}")
251
+ print(f"[ESCOBAR-WS] Raw target URL: {self.raw_target_url}")
252
+ print(f"[ESCOBAR-WS] Resolved target URL for this connection: {target_url}")
192
253
  if request_path != "/ws":
193
254
  print(f"[ESCOBAR-WS] Path normalization: {request_path} → /ws")
194
255
 
@@ -196,9 +257,9 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
196
257
  print(f"[ESCOBAR-WS] Client origin: {self.request.headers.get('Origin', 'NO_ORIGIN')}")
197
258
  print(f"[ESCOBAR-WS] Client remote IP: {self.request.remote_ip}")
198
259
  print(f"[ESCOBAR-WS] Request headers: {dict(self.request.headers)}")
199
- print(f"[ESCOBAR-WS] Attempting to connect to target: {self.target_url}")
260
+ print(f"[ESCOBAR-WS] Attempting to connect to target: {target_url}")
200
261
 
201
- logging.info(f"WebSocket connection opened, proxying to {self.target_url}")
262
+ logging.info(f"WebSocket connection opened, proxying to {target_url}")
202
263
 
203
264
  try:
204
265
  # Establish connection to target websocket server
@@ -216,15 +277,15 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
216
277
  print(f"[ESCOBAR-WS] Headers to forward: {headers}")
217
278
 
218
279
  # Determine if we need SSL based on URL scheme
219
- use_ssl = self.target_url.startswith('wss://')
280
+ use_ssl = target_url.startswith('wss://')
220
281
  ssl_context = ssl.create_default_context() if use_ssl else None
221
282
  print(f"[ESCOBAR-WS] Using SSL: {use_ssl}")
222
283
 
223
- print(f"[ESCOBAR-WS] Attempting websockets.connect() to {self.target_url}")
284
+ print(f"[ESCOBAR-WS] Attempting websockets.connect() to {target_url}")
224
285
 
225
286
  # Connect to target websocket (works with both ws:// and wss://)
226
287
  self.target_ws = await websockets.connect(
227
- self.target_url,
288
+ target_url,
228
289
  additional_headers=headers,
229
290
  ssl=ssl_context,
230
291
  ping_interval=20,
@@ -239,7 +300,7 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
239
300
  asyncio.create_task(self._forward_from_target())
240
301
 
241
302
  print(f"[ESCOBAR-WS] === CONNECTION SETUP COMPLETE ===")
242
- logging.info(f"Successfully connected to target websocket server: {self.target_url}")
303
+ logging.info(f"Successfully connected to target websocket server: {target_url}")
243
304
 
244
305
  except Exception as e:
245
306
  end_time = time.time()
@@ -247,15 +308,15 @@ class WebSocketProxyHandler(tornado.websocket.WebSocketHandler):
247
308
  print(f"[ESCOBAR-WS] Connection failed after {end_time - start_time:.2f} seconds")
248
309
  print(f"[ESCOBAR-WS] Error: {str(e)}")
249
310
  print(f"[ESCOBAR-WS] Error type: {type(e).__name__}")
250
- print(f"[ESCOBAR-WS] Target URL: {self.target_url}")
311
+ print(f"[ESCOBAR-WS] Target URL: {target_url}")
251
312
  if hasattr(e, 'errno'):
252
313
  print(f"[ESCOBAR-WS] Errno: {e.errno}")
253
314
  if hasattr(e, 'strerror'):
254
315
  print(f"[ESCOBAR-WS] Strerror: {e.strerror}")
255
316
  print(f"[ESCOBAR-WS] === END CONNECTION FAILED ===")
256
317
 
257
- logging.error(f"Failed to connect to target websocket {self.target_url}: {str(e)}")
258
- self.close(code=1011, reason=f"Failed to connect to target server: {self.target_url}")
318
+ logging.error(f"Failed to connect to target websocket {target_url}: {str(e)}")
319
+ self.close(code=1011, reason=f"Failed to connect to target server: {target_url}")
259
320
 
260
321
  async def on_message(self, message):
261
322
  """Called when a message is received from the client"""
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "escobar",
3
- "version": "0.1.73",
3
+ "version": "0.1.75",
4
4
  "description": "AI CHAT EXTENSION",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -123,7 +123,7 @@
123
123
  "outputDir": "escobar/labextension",
124
124
  "schemaDir": "schema",
125
125
  "_build": {
126
- "load": "static/remoteEntry.c7b59f5a7472168cb072.js",
126
+ "load": "static/remoteEntry.3532abc0f85f2876fb20.js",
127
127
  "extension": "./extension",
128
128
  "style": "./style"
129
129
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "escobar",
3
- "version": "0.1.73",
3
+ "version": "0.1.75",
4
4
  "description": "AI CHAT EXTENSION",
5
5
  "keywords": [
6
6
  "jupyter",