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.
- escobar/_version.py +1 -1
- escobar/handlers.py +74 -13
- escobar/labextension/package.json +2 -2
- escobar/labextension/schemas/escobar/package.json.orig +1 -1
- escobar/labextension/static/{589.d1cb43ef7438dbdd0369.js → 589.3121fce930587f479cb5.js} +1 -1
- escobar/labextension/static/{remoteEntry.c7b59f5a7472168cb072.js → remoteEntry.3532abc0f85f2876fb20.js} +1 -1
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/package.json +2 -2
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/schemas/escobar/package.json.orig +1 -1
- 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
- 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
- {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/METADATA +1 -1
- {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/RECORD +27 -27
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/install.json +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/schemas/escobar/plugin.json +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/304.bf7e91be734e5b36cdc9.js +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/346.8a1e61ca6789b6fddfa7.js +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/379.40dd59dc19d4a6b42d25.js +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/57.17e53b4b9a954f39c4d8.js +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/648.a7d314faeacc762d891d.js +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/666.3bc65aac3a3be183ee19.js +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/874.c539726f31a1baa0267e.js +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/986.cbcf9d7c1cd8d06be435.js +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/style.js +0 -0
- {escobar-0.1.73.data → escobar-0.1.75.data}/data/share/jupyter/labextensions/escobar/static/third-party-licenses.json +0 -0
- {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/WHEEL +0 -0
- {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/entry_points.txt +0 -0
- {escobar-0.1.73.dist-info → escobar-0.1.75.dist-info}/licenses/LICENSE +0 -0
escobar/_version.py
CHANGED
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
|
-
#
|
170
|
-
self.
|
171
|
-
print(f"[ESCOBAR-WS]
|
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]
|
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: {
|
260
|
+
print(f"[ESCOBAR-WS] Attempting to connect to target: {target_url}")
|
200
261
|
|
201
|
-
logging.info(f"WebSocket connection opened, proxying to {
|
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 =
|
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 {
|
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
|
-
|
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: {
|
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: {
|
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 {
|
258
|
-
self.close(code=1011, reason=f"Failed to connect to target server: {
|
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.
|
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.
|
126
|
+
"load": "static/remoteEntry.3532abc0f85f2876fb20.js",
|
127
127
|
"extension": "./extension",
|
128
128
|
"style": "./style"
|
129
129
|
}
|