unitlab 2.3.34__py3-none-any.whl → 2.3.36__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.
unitlab/client.py CHANGED
@@ -406,8 +406,7 @@ class UnitlabClient:
406
406
  else:
407
407
 
408
408
  self.ssh_url = self.jupyter_url
409
-
410
-
409
+
411
410
  if hasattr(self.tunnel_manager, 'tunnel_url') and self.tunnel_manager.tunnel_url:
412
411
  self.jupyter_url = self.tunnel_manager.tunnel_url
413
412
  if not hasattr(self.tunnel_manager, 'ssh_url'):
unitlab/main.py CHANGED
@@ -132,7 +132,7 @@ def run_agent(
132
132
  handlers=[logging.StreamHandler()]
133
133
  )
134
134
 
135
- server_url = 'http://localhost:8000/'
135
+ server_url = 'https://api-dev/unitlab-ai.com/'
136
136
 
137
137
  if not device_id:
138
138
  import uuid
@@ -208,6 +208,57 @@ class PersistentTunnel:
208
208
 
209
209
  return True
210
210
 
211
+ def create_access_application(self):
212
+ """Create Cloudflare Access application for SSH with bypass policy"""
213
+ print("🔧 Creating Access application for SSH...")
214
+
215
+ # Create Access application
216
+ app_url = "https://api.cloudflare.com/client/v4/zones/{}/access/apps".format(self.cf_zone_id)
217
+ headers = self._get_headers()
218
+
219
+ app_data = {
220
+ "name": "SSH-{}".format(self.device_id),
221
+ "domain": "{}.{}".format(self.ssh_subdomain, self.domain),
222
+ "type": "ssh",
223
+ "session_duration": "24h",
224
+ "auto_redirect_to_identity": False
225
+ }
226
+
227
+ app_response = requests.post(app_url, headers=headers, json=app_data)
228
+
229
+ if app_response.status_code in [200, 201]:
230
+ app_id = app_response.json()["result"]["id"]
231
+ print("✅ Access application created: {}".format(app_id))
232
+
233
+ # Create bypass policy (no authentication required)
234
+ policy_url = "https://api.cloudflare.com/client/v4/zones/{}/access/apps/{}/policies".format(
235
+ self.cf_zone_id, app_id
236
+ )
237
+
238
+ policy_data = {
239
+ "name": "Public Access",
240
+ "decision": "bypass",
241
+ "include": [
242
+ {"everyone": {}}
243
+ ],
244
+ "precedence": 1
245
+ }
246
+
247
+ policy_response = requests.post(policy_url, headers=headers, json=policy_data)
248
+
249
+ if policy_response.status_code in [200, 201]:
250
+ print("✅ Bypass policy created - SSH is publicly accessible")
251
+ return True
252
+ else:
253
+ print("⚠️ Could not create bypass policy: {}".format(policy_response.text[:200]))
254
+ return False
255
+ elif "already exists" in app_response.text:
256
+ print("⚠️ Access application already exists")
257
+ return True
258
+ else:
259
+ print("⚠️ Could not create Access application: {}".format(app_response.text[:200]))
260
+ return False
261
+
211
262
  def create_tunnel_config(self, cred_file):
212
263
  """Create tunnel config file"""
213
264
  config_file = "/tmp/tunnel-config-{}.yml".format(self.tunnel_name)
@@ -400,10 +451,13 @@ class PersistentTunnel:
400
451
  # 2. Create DNS record
401
452
  self.create_dns_record()
402
453
 
403
- # 3. Create config
454
+ # 3. Create Access application for SSH
455
+ self.create_access_application()
456
+
457
+ # 4. Create config
404
458
  config_file = self.create_tunnel_config(cred_file)
405
459
 
406
- # 4. Start services
460
+ # 5. Start services
407
461
  self.start_jupyter()
408
462
  self.start_api()
409
463
  self.start_tunnel(config_file)
@@ -433,34 +487,7 @@ class PersistentTunnel:
433
487
  self.stop()
434
488
  return False
435
489
 
436
- def start_quick_tunnel(self):
437
- """Fallback to quick tunnel"""
438
- print("🔧 Using quick tunnel (temporary URL)...")
439
-
440
- # Start Jupyter first
441
- self.start_jupyter()
442
-
443
- # Start quick tunnel
444
- cloudflared = self.get_cloudflared_path()
445
- cmd = [cloudflared, "tunnel", "--url", "http://localhost:8888"]
446
-
447
- self.tunnel_process = subprocess.Popen(
448
- cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
449
- )
450
-
451
- # Get URL from output
452
- for _ in range(30):
453
- line = self.tunnel_process.stdout.readline()
454
- if "trycloudflare.com" in line:
455
- import re
456
- match = re.search(r'https://[a-zA-Z0-9-]+\.trycloudflare\.com', line)
457
- if match:
458
- self.jupyter_url = match.group(0)
459
- print("✅ Quick tunnel: {}".format(self.jupyter_url))
460
- return True
461
- time.sleep(0.5)
462
-
463
- return False
490
+
464
491
 
465
492
  def stop(self):
466
493
  """Stop everything"""
@@ -469,16 +496,16 @@ class PersistentTunnel:
469
496
  if self.tunnel_process:
470
497
  self.tunnel_process.terminate()
471
498
 
472
- # Optionally delete tunnel when stopping
473
- if self.tunnel_id:
474
- try:
475
- url = "https://api.cloudflare.com/client/v4/accounts/{}/cfd_tunnel/{}".format(
476
- self.cf_account_id, self.tunnel_id
477
- )
478
- requests.delete(url, headers=self._get_headers())
479
- print("🗑️ Tunnel deleted")
480
- except Exception:
481
- pass # Ignore cleanup errors
499
+ # # Optionally delete tunnel when stopping
500
+ # if self.tunnel_id:
501
+ # try:
502
+ # url = "https://api.cloudflare.com/client/v4/accounts/{}/cfd_tunnel/{}".format(
503
+ # self.cf_account_id, self.tunnel_id
504
+ # )
505
+ # requests.delete(url, headers=self._get_headers())
506
+ # print("🗑️ Tunnel deleted")
507
+ # except Exception:
508
+ # pass # Ignore cleanup errors
482
509
 
483
510
  def run(self):
484
511
  """Run and keep alive"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitlab
3
- Version: 2.3.34
3
+ Version: 2.3.36
4
4
  Home-page: https://github.com/teamunitlab/unitlab-sdk
5
5
  Author: Unitlab Inc.
6
6
  Author-email: team@unitlab.ai
@@ -0,0 +1,13 @@
1
+ unitlab/__init__.py,sha256=Wtk5kQ_MTlxtd3mxJIn2qHVK5URrVcasMMPjD3BtrVM,214
2
+ unitlab/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
+ unitlab/client.py,sha256=wqREtDuYc5ixeloPEGm0hp1sdUtB59sB1bIJjBcO1y0,25983
4
+ unitlab/exceptions.py,sha256=68Tr6LreEzjQ3Vns8HAaWdtewtkNUJOvPazbf6NSnXU,950
5
+ unitlab/main.py,sha256=kazASmzhPaAcf9hsPZdewcry_vplsrRLfziPxKlPT70,4425
6
+ unitlab/persistent_tunnel.py,sha256=MRbfdxfP8ceY-25zbVD-29W3U1rs7XcFHZDHoqBlkSU,22401
7
+ unitlab/utils.py,sha256=9gPRu-d6pbhSoVdll1GXe4eoz_uFYOSbYArFDQdlUZs,1922
8
+ unitlab-2.3.36.dist-info/licenses/LICENSE.md,sha256=Gn7RRvByorAcAaM-WbyUpsgi5ED1-bKFFshbWfYYz2Y,1069
9
+ unitlab-2.3.36.dist-info/METADATA,sha256=JgMYM-Z3mqgMcEEqCgS1AOwIOGFIBPXrzAu3mCAvJsY,1046
10
+ unitlab-2.3.36.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ unitlab-2.3.36.dist-info/entry_points.txt,sha256=ig-PjKEqSCj3UTdyANgEi4tsAU84DyXdaOJ02NHX4bY,45
12
+ unitlab-2.3.36.dist-info/top_level.txt,sha256=Al4ZlTYE3fTJK2o6YLCDMH5_DjuQkffRBMxgmWbKaqQ,8
13
+ unitlab-2.3.36.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- unitlab/__init__.py,sha256=Wtk5kQ_MTlxtd3mxJIn2qHVK5URrVcasMMPjD3BtrVM,214
2
- unitlab/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
- unitlab/client.py,sha256=LMZ7HFjRxzPV2IrCXlP2GETlY0vWvAxP0RrjSXOn_Jk,26015
4
- unitlab/exceptions.py,sha256=68Tr6LreEzjQ3Vns8HAaWdtewtkNUJOvPazbf6NSnXU,950
5
- unitlab/main.py,sha256=EbQNO-Z5drNQjDXJp_sIs5a3WgPoqxaXxpdFGzMWm6k,4416
6
- unitlab/persistent_tunnel.py,sha256=XDJo2PPq4EjEtI4vT68LIGUUq7WV4m0bnXYKPfY51cY,21180
7
- unitlab/utils.py,sha256=9gPRu-d6pbhSoVdll1GXe4eoz_uFYOSbYArFDQdlUZs,1922
8
- unitlab-2.3.34.dist-info/licenses/LICENSE.md,sha256=Gn7RRvByorAcAaM-WbyUpsgi5ED1-bKFFshbWfYYz2Y,1069
9
- unitlab-2.3.34.dist-info/METADATA,sha256=ctnyV_ZT0vuOzGMoH0N09v0nalsR79WQCGdi_D-ncw0,1046
10
- unitlab-2.3.34.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- unitlab-2.3.34.dist-info/entry_points.txt,sha256=ig-PjKEqSCj3UTdyANgEi4tsAU84DyXdaOJ02NHX4bY,45
12
- unitlab-2.3.34.dist-info/top_level.txt,sha256=Al4ZlTYE3fTJK2o6YLCDMH5_DjuQkffRBMxgmWbKaqQ,8
13
- unitlab-2.3.34.dist-info/RECORD,,